[英]Git rebase recursive branches
我正在編寫一門編程課程,我想在其中展示如何逐步編寫程序。 我想我可能會為此目的使用 git。 這個想法是將每節課作為一個單獨的分支,並隨着課程的進行創建新的分支。
一切都很好,直到我發現我在第 1 lesson1
犯了錯誤。 所以我去那里修理它。
現在問題出現了:我必須重新定義每個分支。 所以:
git checkout lesson2
git rebase lesson1
之后的lesson3
和lesson4
lesson3
也是lesson4
。
我每門課程大約有 20 節課,所以每個錯誤都非常痛苦。 有沒有辦法讓它自動化,或者至少讓我更容易?
順便提一句。 我用來創建圖像的工具可在此處獲得。
所以不得不回到繪圖板......
我之前建議了一個簡單的filter-branch
命令,但這有一個明顯的缺陷。 ( tl; dr - 我不再建議將此作為filter-branch --parent-filter
的用例;除非您關心原因,否則您可以跳到下一段。 )當您使用git filter-branch
重新設置父git filter-branch
時不會為有效合並重新應用更改,而是將樹保持在重新提交的父級提交中(本質上是創建新的差異)。 filter-branch
仍然是可能的,但它需要tree-filter
或index-filter
,這將開始變得相當復雜。 (如果您可以在腳本中自動修復,那么使用該腳本作為tree-filter
應該可以工作 - 可能在 rev-list 參數中有一點技巧 - 但讓我們假設一般情況下這不會那么容易。我想過編寫一種方法來將“修復”提交中的更改合並到移植中的每個提交中,但這可能會導致每次發生沖突,而且也不是那么容易......)
那么該怎么做呢? 好吧,如果沒有沖突,並且假設您可以以合理的方式遍歷引用名稱,則像 Libin Varghese 建議的腳本化方法是可以的。 但假設可能存在沖突,還有另一種方式......
所以如果你有
Bfix <--(lesson1)
/
A --- B --- C --- D --- E <--(lesson3)(HEAD)
|
(lesson2)
你要做的基本上是
1) 在Bfix
重新應用C
、 D
和E
作為C'
、 D'
和E'
(單個變基操作)
2) 將所有引用從替換的提交 ( X
) 移動到它的替換 ( X'
)
使用單個 rebase 可以最大限度地減少沖突解決的數量。 如果你只是 rebase 第 3 lesson3
那么你將擁有
(lesson1)
|
Bfix --- C' --- D' --- E' <--(lesson3)(HEAD)
/
A --- B --- C <--(lesson2)
然后你只需要重寫第一課和最后一課以外的分支的參考。 這意味着您需要從“舊提交X
”到“替換提交X'
”的映射。
在 rebase 結束時,這樣的映射會通過 stdin 傳遞到 .git/hooks/post-rewrite(如果存在)。 因此,您可以編寫一個腳本,使用git show-ref
將 ref(分支)名稱映射到“舊”SHA1 值,然后使用 stdin 上的映射來查找相應的“新”SHA1 值,並調用git update-ref
。
(我打算提供一個示例腳本,但我的測試倉庫中的鈎子有一些問題;所以如果我稍后有時間,我會回到這個。但是如果你對腳本編寫和hooks,上面概述了需要做的事情。)
start=2
end=10
for i in {$start..$end}
do
git checkout lesson$i
git rebase lesson$(($i-1)) || break
done
start=$i
假設你沒有沖突,這個循環通過第 2 課到第 10 課,執行變基。
如果 rebase 失敗,則開始設置為失敗的點。 但請確保在繼續之前解決沖突並執行rebase --continue
這是我解決問題的嘗試。 您將不得不修復我的語法錯誤,並完成自動化問題,但這可能只是一個開始。
單行
git rebase lesson1 lesson2
具有相同的效果
git checkout lesson2
git rebase lesson1
您應該重新設置上一課的基礎,以便所有中間提交同時傳輸到新分支。 您必須修復發生的任何沖突。
git rebase lesson1 lesson4
然后使用看起來像的命令將分支轉移到新的提交(如果課程是連續的)。
git branch lesson2a lesson4^2
git branch lesson3a lesson4^1
如果分支是連續的。 'git help revisions' 顯示了如何使用來自給定分支的提交消息來查找提交。
git branch lesson2a lesson4^"{/Partial lesson2 commit message}"
git branch lesson3a lesson4^"{/Partial Lesson3 commit message}"
一旦這看起來正確刪除舊提交
git branch -f lesson2 lesson2a
git branch -D lesson2a
有關 rebase 語法,請參閱“git help rebase”
和 'git help revisions' 用於指定提交的不同方式。
這是 Mark 的回答中描述的重寫后掛鈎。
(我對 shell 腳本相當沒有經驗,所以歡迎評論。)
#!/bin/bash
if [ "$1" != "rebase" ]; then
exit 0
fi
orig=`git rev-parse ORIG_HEAD`
while read line
do
IFS=' '
read -ra map <<< "$line"
old="${map[0]}"
new="${map[1]}"
heads=`git show-ref | grep -e " refs/heads" | grep "$old"`
IFS=$'\n'
for h in $heads; do
IFS=' '
read -ra ref_info <<< "$h"
ref="${ref_info[1]}"
# Don't update original branch as this causes rebase to fail
if [ "$old" != "$orig" ]; then
echo "Updating '$ref' to $new"
`git-update-ref $ref $new $old`
fi
done
done
這個開源 CLI 將執行遞歸分支變基(披露,我是貢獻者): https : //github.com/screenplaydev/graphite-cli
主要的變基遞歸可以在這里看到: https : //github.com/screenplaydev/graphite-cli/blob/dfe4390baf9ce6aeedad0631e41c9f1bdc57ef9a/src/actions/fix.ts#L60
git rebase --onto ${parentBranch.name} ${mergeBase} ${currentBranch.name}
關鍵的見解是將分支父級存儲在 git refs 中,以便在操作期間遞歸 DAG。 如果沒有父元數據,就不可能總是確定連續子分支的合並基礎。
const metaSha = execSync(`git hash-object -w --stdin`, {input: JSON.stringify(desc)}).toString();
execSync(`git update-ref refs/branch-metadata/${this.name} ${metaSha}`);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.