[英]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.