我正在嘗試清理我正在處理的存盤庫中的 git 歷史記錄。給定一個看起來像這樣的 git 歷史:
. -- .
/ \
... - S ... T - ... H
\ /
. -- .
那是:
- S點后面有一些任意的DAG。
- 在點 T 之后有一些任意的 DAG。
- 在點 S 和 T 之間有一些任意的 DAG。
- S 之前的圖與 T 之后的圖是不相交的(即洗掉 T 或 S 將斷開已洗掉的節點前身與 H 的連接)。
我想重寫點 S 和 T 之間的歷史(例如 squash 或線性化),以便有一些以點 T' 結尾的新 git 歷史。
... - S -- ... -- T'
關鍵約束是 T 點和 T' 點的 repo 內容完全相同,即使 git 提交不同并且我們從 S 到 T' 的方式可能已經改變。
我能做的就這么多了。在此之后我想做的(我還沒有運氣這樣做)是在 T 和 H 之間移植 DAG的確切結構以獲得:
... - S -- ... -- T' - ... H'
當然,提交哈希會改變,但重要的是 T' 和 H' 之間的圖結構、作者和其他元資料是相同的。
雖然我可以用櫻桃挑選來做到這一點:
git cherry-pick T^..H
但這似乎會導致合并沖突。我在這篇 SO 帖子中尋找答案:
例如,我想壓縮 Point1 和 Point2 之間的提交,然后在 Point2 之后應用其余的歷史記錄。
我可以這樣做壁球:
# Squash all information between point1 and point2
git checkout Point1
git reset --hard Point2
git reset --soft Point1^
git commit -am "all changes between point1 and point2"
git tag "Point2_prime"
這給了我們這個:

但我不知道如何將其余的歷史放在上面。這是我迄今為止嘗試過的:
# The state is now guarenteed to be the same as Point2, but the history has
# been modified to our liking. Now we need to replay all the other commits
# on top of this.
# Based on answers in this SO post:
# https://stackoverflow.com/questions/1994463/how-to-cherry-pick-a-range-of-commits-and-merge-them-into-another-branch
COMMIT_A=$(git rev-list -n 1 Point2)
COMMIT_B=$(git rev-list -n 1 main)
echo "COMMIT_A = $COMMIT_A"
echo "COMMIT_B = $COMMIT_B"
# I've tried the following, but they do not seem to work.
# Try with cherry pick
git cherry-pick "${COMMIT_A}..${COMMIT_B}"
# Try with rebase onto
git rebase "$COMMIT_A" "$COMMIT_B"~0 --onto HEAD
我認為因為新提交的狀態與 Point2 的狀態完全相同,所以有一種方法可以非互動地執行此操作而不會出現合并錯誤。這可能嗎?
uj5u.com熱心網友回復:
最簡單的方法是使用git replace git filter-repo技巧,記錄在這里:
父重寫
替換
$commit_A為$commit_B(例如,將所有$commit_A作為父級的提交改為具有$commit_B該父級的所有提交),并重寫歷史以使其永久化:git replace $commit_A $commit_B git filter-repo --force
在你的情況下:
git replace Point2 <sha of "all changes between point1 and point2">
git filter-repo --force
如果您沒有git-filter-repo安裝,舊git filter-branch命令也會“保留”替換物件:
# run a phony filter-branch command, you just want to have the
# "rewrite replaced commits" effect:
git filter-branch --tag-name-filter cat main
# you can instruct filter-branch to ignore commits before Point1:
git filter-branch --tag-name-filter cat ^Point1 main
# to have git-filter-repo try to rewrite all branches :
git filter-branch --tag-name-filter cat -f ^Point1 --branches
[編輯]您可以加??快“創建Point2_prime提交”程序:
# the following command creates a 'Point2_prime' commit, e.g:
# * has the same content as 'Point2'
# * has 'Point1' as a parent
# and creates the replacement rule 'Point2' -> 'that commit'
git replace --graft Point2 Point1
# you can now run :
git filter-repo --force
來自檔案:git help replace
--graft <commit> [<parent>…?]創建一個移植提交。創建一個新的提交,它的內容相同,
<commit>只是它的父級將[<parent>…?]代替<commit>' 的父級。然后創建一個替換 ref 以替換<commit>新創建的提交。
uj5u.com熱心網友回復:
您可以使用rebase名為 的命令的選項--rebase-merges。這將(嘗試)通過重新創建合并提交來保留圖形。請注意,“嘗試”不能自動解決沖突,如檔案中所述:
這些合并提交中的任何已解決的合并沖突或手動修改都必須手動解決/重新應用。
所以,一旦你創建了 squashed T',你可以簡單地運行這個命令:
git rebase T H --onto T' --rebase-merges
如果您在原始結構中沒有合并沖突,這應該沒有問題。但是,如果您要解決的合并沖突不止幾個,那么按照LeGEC 的回答git-filter-repo中所述使用可能會好得多。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/488192.html
標籤:混帐
