我如何变基一个 git 超级项目来改变子模块的哈希值?

How do I rebase a git superproject changing the hashes of the submodules?

背景

假设我们有两个 git 存储库,一个是另一个的子模块(A 将是超级项目,B 将是子模块)。项目 A 本身不是源代码,而是一个收集和跟踪有关其子模块信息的项目。 A repo 很少(如果有的话)存在于本地机器上,而是一堆脚本让它保持更新。

有一天,有人意识到 repo B 应该更好地使用 LFS,并使用 git lfs migrate import 清理了 repo。我有一个 B 的旧哈希和新哈希的列表。

我做了什么

由于 repo A 恰好是线性的(没有分支),我能够做一个 git rebase --root -i,将所有提交更改为 edit,并且 运行将子模块重置为新哈希值的简单 bash 脚本。以下是脚本示例:

#!/bin/bash
#set the submodule path and input files
submodulePath=foo
newHashesFile=NewHashes.txt
originalHashesFile=OriginalHashes.txt

while [ (test -d "$(git rev-parse --git-path rebase-merge)" || test -d "$(git rev-parse --git-path rebase-apply)" ) ]; do
    numLines=`git ls-files --stage | grep $submodulePath | wc -l`
    if [ $numLines = 1 ];
    then
        oldHash=`git ls-files --stage | grep $submodulePath | sed -e 's/^160000 \([^ ]*\) 0.*$//g'`
        echo oldHash: $oldHash
    else
        echo merge conflict
        oldHash=`git ls-files --stage | grep $submodulePath | grep '^160000 \([^ ]*\) 3.*' | sed -e 's/^160000 \([^ ]*\) 3.*$//g'`
        echo oldHash: $oldHash    
    fi

    lineNumber=`grep -n $oldHash $originalHashesFile | sed -e 's/^\([^:]*\):.*//g'`
    newHash=`head -n $lineNumber $newHashesFile | tail -n 1`

    if [ ! $lineNumber ];
    then
        echo Hash not changed
    else
        cd $submodulePath
        git reset --hard $newHash
        cd ../
    fi

    git add $submodulePath/
    git commit --amend
    git rebase --continue
done

问题

所有这些都有效,但我想知道是否有更简单的方法来做到这一点,因为我想我会被要求再次这样做。这个问题有两个部分。

  1. 有没有简单的方法告诉 git 您希望默认值是 edit 而不是 pick,而不依赖于编辑器?
  2. 是否有更简单的方法告诉 git 执行脚本的操作?如果我在超级项目中执行 git lfs migrate import 会有帮助吗?

Is there a simple way to tell git that you want the default to be edit instead of pick, not dependent on the editor?

没有。但是,有一种方法可以将命令序列编辑器设置为与其他编辑器不同的编辑器:设置环境变量 GIT_SEQUENCE_EDITOR。因此,例如,您可以这样做:

GIT_SEQUENCE_EDITOR="sed -i '' s/^pick/edit/" git rebase -i ...

(假设您的 sed 有一个 -i 可以这样工作,等等)。

Is there a simpler way of telling git to do what the script does?

鉴于你想更新每个 gitlink 哈希,我会使用 git filter-branch(而不是 git rebase)来完成它,使用 --index-filter 来更新 gitlink 哈希.我不确定这是否 更简单 但它更直接。索引过滤器本身将包括使用类似于您执行此操作的方式的 git ls-files --stage,但它本身可能使用生成的 sed 脚本或 awk 脚本。生成的 sed 可能会更快,而 awk 会更简单,特别是如果你有一个现代的 awk,你可以在其中读取哈希映射。