Git filter-branch 或 filter-repo 来更新子模块 gitlink?

问题描述 投票:0回答:3

我有 git 存储库 A,它使用 B 作为子模块。

B 的历史记录在 LFS 迁移后已被重写,但如果 A 仍能使其整个历史记录正常运行,我会很高兴。 LFS 迁移后,我确实有子模块 B 的 OldSHA1 > NewSHA1 映射,现在我只想将 OldSHA1 gitlinks 重写到存储库 A 中的 NewSHA1。

我尝试在存储库 A 上运行 filter-repo 命令,并使用完整的 OldSHA1==>NewSHA1 映射作为参数,但它似乎没有获取 gitlinks。

我还尝试了过滤分支,如本线程中详细介绍的重写子模块历史后带有子模块的存储库,这似乎正在寻找我想要完成的确切事情。我尝试使用单个 OldSHA1=>NewSHA1 映射来执行此操作,这是我尝试运行的命令:

git filter-branch --commit-filter '
  if [ "$GIT_COMMIT" = <OLDSHA1> ];
  then
    cd <SUBMODULE_ABSOLUTE_PATH>;
    git checkout <NEWSHA1>;
    cd ..;
    git add -u;
    git commit -m "updated gitlink";
  else
    git commit-tree "$@";
  fi' HEAD 

但我不断收到以下错误:

fatal: reference is not a tree: <NEWSHA1>

不知何故,git checkout似乎没有拾取子模块B的树。我什至尝试使用 git -C AbsolutePathToSubModule checkout 指定路径,但我得到了同样的错误。

那么,有几个问题:

  • 我在这里做错了什么明显的事情吗?
  • 有更好的方法来实现这一点吗?看起来我“只是”想用对象数据库中的另一个字符串替换一个字符串,但我找不到一个简单的方法来做到这一点
  • 有没有办法像filter-repo那样在整个repo上执行此操作?或者我应该在每个分支上运行这个。

感谢您提供有关如何实现此目标的任何帮助、建议和线索!

编辑1:

在评论中回答后,我将脚本编辑为:

git filter-branch --commit-filter '
  if [ "$GIT_COMMIT" = <SpecificCommitID> ];
  then
    git update-index --add --cacheinfo 160000,<SpecificNewSha1>,<SubmodulePath>;
  fi
  git commit-tree "$@";
  ' HEAD

但是没有效果:(

WARNING: Ref 'refs/heads/develop' is unchanged

编辑2:

非常感谢用户@torek!这是一个可以帮助任何人入门的片段:

git filter-branch --index-filter '
if [ "$(git rev-parse --quiet --verify :<SUBMODULEPATH>)" = <OLDSHA1> ];
then
  git update-index --cacheinfo 160000,<NEWSHA1>,<SUBMODULEPATH>;
fi' HEAD --all

从那时起,您必须循环所有 OLDSHA1/NEWSHA1 对,或使用 case) 字典,如下面的答案所示

再次非常感谢!

git git-submodules git-filter-branch git-history-rewrite
3个回答
2
投票

这个:

git filter-branch --commit-filter '
  if [ "$GIT_COMMIT" = <SpecificCommitID> ];
  then
    git update-index --add --cacheinfo 160000,<SpecificNewSha1>,<SubmodulePath>;
  fi
  git commit-tree "$@";
  ' HEAD

不是您想要的,因为它测试 superproject 提交的哈希 ID。您需要在索引条目中测试子模块提交的哈希ID,例如:

if [ "$(git rev-parse --quiet --verify :SubmodulePath)" = oldhash ]; then ...; fi

当然,必须测试所有旧的重写子模块哈希 ID 才能通过映射函数运行它们。

(这在过滤器存储库中肯定会更容易,您可以在其中使用字典查找。)


如果您使用:

sm_hash=$(git rev-parse :submodule-path)

或类似于前缀测试,请记住考虑索引中不存在子模块路径的情况,以便

:submodule-path
无法正确解析。我认为--quiet --verify
会在这里做正确的事情(安静地不产生输出),但值得首先测试。

一旦获得哈希值,您就可以执行以下操作:

case $sm_hash in old1) new=new1;; old2) new=new2;; ... oldN) new=newN;; *) new=$sm_hash esac
作为默认的穷人字典查找,但如果子模块哈希未更改或为空,您将需要

跳过更新子模块哈希。


1
投票
最简单的方法是,将旧 ID 和新 ID 放在

shamap

 文件中,

git filter-branch --setup ' declare -A newsha while read old new; do newsha[$old]=$new; done <shamap ' --index-filter ' if oldsha=`git rev-parse :submodulepath 2>&-` then git update-index --cacheinfo 160000,${newsha[$oldsha]-$oldsha},submodulepath fi '
如果您使用的是 Mac,则需要 

brew install bash

 来解决被忽视的 GNU 安装中的问题之一。
    


0
投票
使用bash语法的注释,

declare -A ...

,将不起作用。 
git filter-branch
 是 Bourne shell 脚本(请参阅 
https://github.com/git/git/blob/a82fb66fed250e16d3010c75404503bea3f0ab61/git-filter-branch.sh#L1),并且 Bourne shell 没有关联数组。

© www.soinside.com 2019 - 2024. All rights reserved.