簡體   English   中英

Git rebase 遞歸分支

[英]Git rebase recursive branches

我正在編寫一門編程課程,我想在其中展示如何逐步編寫程序。 我想我可能會為此目的使用 git。 這個想法是將每節課作為一個單獨的分支,並隨着課程的進行創建新的分支。 正常狀態

一切都很好,直到我發現我在第 1 lesson1犯了錯誤。 所以我去那里修理它。 修復第 1 課

現在問題出現了:我必須重新定義每個分支。 所以:

git checkout lesson2
git rebase lesson1

在此處輸入圖片說明

之后的lesson3lesson4 lesson3也是lesson4 在此處輸入圖片說明

我每門課程大約有 20 節課,所以每個錯誤都非常痛苦。 有沒有辦法讓它自動化,或者至少讓我更容易?

順便提一句。 我用來創建圖像的工具可在此處獲得

所以不得不回到繪圖板......

我之前建議了一個簡單的filter-branch命令,但這有一個明顯的缺陷。 tl; dr - 我不再建議將此作為filter-branch --parent-filter的用例;除非您關心原因,否則您可以跳到下一段。 )當您使用git filter-branch重新設置父git filter-branch時不會為有效合並重新應用更改,而是將樹保持在重新提交的父級提交中(本質上是創建新的差異)。 filter-branch仍然是可能的,但它需要tree-filterindex-filter ,這將開始變得相當復雜。 (如果您可以在腳本中自動修復,那么使用該腳本作為tree-filter應該可以工作 - 可能在 rev-list 參數中有一點技巧 - 但讓我們假設一般情況下這不會那么容易。我想過編寫一種方法來將“修復”提交中的更改合並到移植中的每個提交中,但這可能會導致每次發生沖突,而且也不是那么容易......)

那么該怎么做呢? 好吧,如果沒有沖突,並且假設您可以以合理的方式遍歷引用名稱,則像 Libin Varghese 建議的腳本化方法是可以的。 但假設可能存在沖突,還有另一種方式......

所以如果你有

        Bfix <--(lesson1)
       /
A --- B --- C --- D --- E <--(lesson3)(HEAD)
            |
        (lesson2)

你要做的基本上是

1) 在Bfix重新應用CDE作為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

TL;DR 使用/復制Graphite CLI的實現

這個開源 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}`);

https://github.com/screenplaydev/graphite-cli/blob/dfe4390baf9ce6aeedad0631e41c9f1bdc57ef9a/src/wrapper-classes/branch.ts#L102-L109

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM