[英]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.*$/\1/g'`
echo oldHash: $oldHash
else
echo merge conflict
oldHash=`git ls-files --stage | grep $submodulePath | grep '^160000 \([^ ]*\) 3.*' | sed -e 's/^160000 \([^ ]*\) 3.*$/\1/g'`
echo oldHash: $oldHash
fi
lineNumber=`grep -n $oldHash $originalHashesFile | sed -e 's/^\([^:]*\):.*/\1/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
所有这一切都奏效了,但我想知道是否有更简单的方法来做到这一点,因为我想我会被要求再次这样做。 这个问题有两个部分。
edit
而不是pick
,而不依赖于编辑器?git lfs migrate import
,会有帮助吗?有没有一种简单的方法可以告诉 git 您希望默认为编辑而不是选择,而不依赖于编辑器?
不,但是,有一种方法可以将命令序列编辑器设置为与其他编辑器不同的编辑器:设置环境变量GIT_SEQUENCE_EDITOR
。 因此,例如,您可以执行以下操作:
GIT_SEQUENCE_EDITOR="sed -i '' s/^pick/edit/" git rebase -i ...
(假设您的sed
有一个-i
以这种方式工作,等等)。
有没有更简单的方法告诉 git 执行脚本的操作?
Given that you want to update each gitlink hash, I'd use git filter-branch
(rather than git rebase
) to do it, with an --index-filter
that does the gitlink hash updates. 我不确定这是否更简单,但更直接。 索引过滤器本身将包括使用git ls-files --stage
类似于您执行此操作的方式,但它本身可能使用生成的sed
脚本或awk
脚本。 Generated-sed 可能会更快,而 awk 会更简单,特别是如果您有一个现代 awk ,您可以在其中读取 hash 映射。
多年来不得不这样做几次之后,我听取了torek 的建议,并将过于冗长的 bash 脚本编写为单个git filter-branch
。 我将其张贴在这里,供其他用户和未来的我使用。
首先,只是为了阐明我是如何执行lfs migrate import
的(而且我确定我为其中一些行走了很长的路):
# Make sure we have the up-to-date remote branches
git submodule update --init SubmodulePath/
cd SubmodulePath/
git fetch --all
# Create local branches that mirror the remote ones
git branch -lr | grep -v "origin/HEAD" | sed 's/^.*origin\///' |
xargs -I @ git branch @ origin/@ --force
#Find all files that git identifies as binary and create the lfs migrate command, then run it
git log --all --numstat | grep '^-' | cut -f3 | sed 's|^.*/\(.*\)|\1|' | sed 's|^.*\.\([^.]*\)|\1|' |
sort -u --ignore-case | sed 's|\([^0-9]\)|[\L\1\U\1]|g' | awk '{print}' ORS=',*.' |
sed 's|^\(.*\),\*\.$|git lfs migrate import --everything --object-map=LFSImport.txt --include="*.\1"|' | . /dev/stdin
然后我将 LFSImport 移动到另一个目录(我也将它提交给子模块 repo)并使用index-filter
运行filter-branch
:
git filter-branch -f --index-filter '
numLines=`git ls-files --stage | grep SubmodulePath | wc -l`
if [ $numLines = 1 ];
then
echo
oldHash="$(git rev-parse --quiet --verify :SubmodulePath)"
echo oldHash: $oldHash
newHash="$(grep $oldHash /path/to/LFSImport.txt | cut -d , -f2)"
echo newHash: $newHash
git update-index --add --cacheinfo 160000 $newHash SubmodulePath
fi
' HEAD
我可能应该在$newHash
上添加一个检查以查看它是否不为空(它在我的一次提交中,但我手动只是将它设置为其他不存在的东西)。 正如 torek 提到的,这更干净、更快,而且工作得很好,如果不是更好的话。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.