Git
英文 ▾ 主題 ▾ 最新版本 ▾ git-rebase 最後更新於 2.46.1

名稱

git-rebase - 將 commit 重新套用在另一個基準點之上

概要

git rebase [-i | --interactive] [<options>] [--exec <cmd>]
	[--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
	--root [<branch>]
git rebase (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)

描述

如果指定了 <branch>git rebase 會在執行任何操作之前自動執行 git switch <branch>。否則它會停留在目前的分支。

如果未指定 <upstream>,則會使用在 branch.<name>.remotebranch.<name>.merge 選項中設定的上游(詳情請參閱 git-config[1]),並且會假定使用 --fork-point 選項。 如果您目前不在任何分支上,或者目前的分支沒有設定上游,則 rebase 將會中止。

目前分支中但不在 <upstream> 中的所有 commit 變更都會儲存到臨時區域。這與 git log <upstream>..HEAD 顯示的 commit 集合相同;如果 --fork-point 啟用,則與 git log 'fork_point'..HEAD 顯示的相同(請參閱下面關於 --fork-point 的描述);如果指定了 --root 選項,則與 git log HEAD 顯示的相同。

如果提供了 --onto 選項,目前分支會重設為 <upstream><newbase>。這與 git reset --hard <upstream>(或 <newbase>)的效果完全相同。ORIG_HEAD 會設定為指向重設前分支的頂端。

注意
如果在 rebase 期間使用其他寫入該虛擬參考的指令(例如 git reset),則不能保證 ORIG_HEAD 在 rebase 結束時仍然指向先前分支的頂端。但是,可以使用目前分支的 reflog 來存取先前分支的頂端(即 @{1},請參閱 gitrevisions[7])。

然後,先前儲存到臨時區域的 commit 會依序逐個重新套用至目前分支。請注意,HEAD 中任何引入與 HEAD..<upstream> 中 commit 相同的文字變更的 commit 都會省略(也就是說,已經在上游接受的修補程式,但具有不同的 commit 訊息或時間戳記將會跳過)。

合併失敗可能會導致此程序無法完全自動執行。您必須解決任何此類合併失敗,然後執行 git rebase --continue。另一種方法是使用 git rebase --skip 略過導致合併失敗的 commit。若要檢查原始的 <branch> 並移除 .git/rebase-apply 工作檔案,請改用 git rebase --abort 指令。

假設存在以下歷史記錄,且目前分支為 "topic"

          A---B---C topic
         /
    D---E---F---G master

從這一點開始,以下任一指令的結果

git rebase master
git rebase master topic

會是

                  A'--B'--C' topic
                 /
    D---E---F---G master

注意: 後面的形式只是 git checkout topic 後接 git rebase master 的簡寫形式。當 rebase 結束時,topic 將保持為檢出的分支。

如果上游分支已經包含您所做的變更(例如,因為您郵寄了已在上游套用的修補程式),則該 commit 將會跳過,並且會發出警告(如果使用merge 後端)。例如,在以下歷史記錄上執行 git rebase master(其中 A'A 引入相同的變更集合,但具有不同的提交者資訊)

          A---B---C topic
         /
    D---E---A'---F master

將會產生

                   B'---C' topic
                  /
    D---E---A'---F master

以下說明如何使用 rebase --onto 將基於一個分支的主題分支移植到另一個分支,以模擬您從後一個分支 fork 出主題分支。

首先,假設您的 topic 是基於 next 分支。例如,在 topic 中開發的功能取決於 next 中找到的某些功能。

    o---o---o---o---o  master
         \
          o---o---o---o---o  next
                           \
                            o---o---o  topic

我們希望 topicmaster 分支 fork 出;例如,因為 topic 所依賴的功能已合併到更穩定的 master 分支中。我們希望樹狀結構看起來像這樣

    o---o---o---o---o  master
        |            \
        |             o'--o'--o'  topic
         \
          o---o---o---o---o  next

我們可以使用以下指令來取得此結果

git rebase --onto master next topic

--onto 選項的另一個範例是重新設定部分分支的基底。如果我們有以下情況

                            H---I---J topicB
                           /
                  E---F---G  topicA
                 /
    A---B---C---D  master

那麼指令

git rebase --onto master topicA topicB

將會產生

                 H'--I'--J'  topicB
                /
                | E---F---G  topicA
                |/
    A---B---C---D  master

當 topicB 不依賴於 topicA 時,這會很有用。

也可以使用 rebase 移除一系列的 commit。如果我們有以下情況

    E---F---G---H---I---J  topicA

那麼指令

git rebase --onto topicA~5 topicA~3 topicA

將會導致移除 commit F 和 G

    E---H'---I'---J'  topicA

如果 F 和 G 在某些方面有缺陷,或不應成為 topicA 的一部分,這會很有用。請注意,--onto 的引數和 <upstream> 參數可以是任何有效的 commit-ish。

如果發生衝突,git rebase 將會在第一個有問題的 commit 處停止,並在樹狀結構中留下衝突標記。您可以使用 git diff 來找出標記 (<<<<<<),並進行編輯以解決衝突。對於您編輯的每個檔案,您需要告訴 Git 衝突已解決,通常可以使用以下方式完成

git add <filename>

在手動解決衝突並使用所需的解決方案更新索引之後,您可以使用以下指令繼續 rebase 程序

git rebase --continue

或者,您可以使用以下指令復原 git rebase

git rebase --abort

模式選項

本節中的選項不能與任何其他選項一起使用,包括彼此之間

--continue

在解決合併衝突後重新啟動 rebase 程序。

--skip

透過略過目前的修補程式來重新啟動 rebase 程序。

--abort

中止 rebase 操作並將 HEAD 重設為原始分支。如果在開始 rebase 操作時提供了 <branch>,則 HEAD 會重設為 <branch>。否則,HEAD 會重設為開始 rebase 操作時的位置。

--quit

中止 rebase 操作,但 HEAD 不會重設回原始分支。索引和工作樹也因此保持不變。如果使用 --autostash 建立暫時的 stash 條目,它將會儲存到 stash 清單中。

--edit-todo

在互動式 rebase 期間編輯待辦事項清單。

--show-current-patch

在互動式 rebase 中或由於衝突而停止 rebase 時,顯示目前的修補程式。這與 git show REBASE_HEAD 等效。

選項

--onto <newbase>

建立新提交的起點。如果沒有指定 --onto 選項,則起點為 <upstream>。可以是任何有效的提交,而不僅僅是現有的分支名稱。

作為特殊情況,如果 A 和 B 之間恰好只有一個合併基準點,您可以使用「A...B」作為 A 和 B 合併基準點的捷徑。您可以省略 A 和 B 中的至多一個,在這種情況下,它會預設為 HEAD。

--keep-base

將建立新提交的起點設定為 <upstream><branch> 的合併基準點。執行 git rebase --keep-base <upstream> <branch> 等同於執行 git rebase --reapply-cherry-picks --no-fork-point --onto <upstream>...<branch> <upstream> <branch>

此選項適用於在 upstream 分支之上開發功能的情況。在開發功能時,upstream 分支可能會前進,並且保持在 upstream 之上進行 rebase 可能不是最好的主意,而是保持基準提交不變。由於基準提交未變更,此選項會隱含 --reapply-cherry-picks,以避免遺失提交。

儘管此選項和 --fork-point 都會尋找 <upstream><branch> 之間的合併基準點,但此選項會將合併基準點用作建立新提交的起點,而 --fork-point 則會使用合併基準點來決定要進行 rebase 的提交集合

另請參閱下方的「不相容選項」。

<upstream>

要比較的 Upstream 分支。可以是任何有效的提交,而不僅僅是現有的分支名稱。預設為目前分支的已設定 upstream。

<branch>

工作分支;預設為 HEAD

--apply

使用套用策略進行 rebase(內部呼叫 git-am)。一旦合併後端處理 apply 後端的所有功能,此選項在未來可能會變成空操作。

另請參閱下方的「不相容選項」。

--empty=(drop|keep|stop)

如何處理一開始不為空,也不是任何 upstream 提交的乾淨 cherry-pick,但在 rebase 後變成空的提交(因為它們包含已經 upstream 的變更子集)

drop

提交將會被捨棄。這是預設行為。

keep

提交將會被保留。當指定 --exec 時,會隱含此選項,除非同時指定 -i/--interactive

stop
ask

當套用提交時,rebase 將會停止,允許您選擇是否要捨棄它、編輯更多檔案,或僅提交空的變更。當指定 -i/--interactive 時,會隱含此選項。ask 是已棄用的 stop 同義詞。

請注意,一開始為空的提交會被保留(除非指定 --no-keep-empty),而乾淨的 cherry-pick 提交(由 git log --cherry-mark ... 判斷)會被偵測並作為初步步驟捨棄(除非傳遞 --reapply-cherry-picks--keep-base)。

另請參閱下方的「不相容選項」。

--no-keep-empty
--keep-empty

不要在結果中保留在 rebase 前開始為空的提交(也就是說,與其父提交相比沒有任何變更的提交)。預設是保留一開始為空的提交,因為建立這類提交需要將 --allow-empty 覆寫旗標傳遞給 git commit,表示使用者非常有意識地建立這類提交,因此想要保留它。

此旗標的使用可能很少見,因為您可以透過啟動互動式 rebase 並移除對應於您不想要之提交的行,來擺脫一開始為空的提交。此旗標的存在是為了提供便利的捷徑,例如在外部工具產生許多空的提交且您想要全部移除的情況下。

對於一開始不為空,但在 rebase 後變成空的提交,請參閱 --empty 旗標。

另請參閱下方的「不相容選項」。

--reapply-cherry-picks
--no-reapply-cherry-picks

重新套用任何 upstream 提交的所有乾淨 cherry-pick,而不是搶先捨棄它們。(如果這些提交在 rebase 後變成空的,因為它們包含已經 upstream 的變更子集,則對它們的行為由 --empty 旗標控制。)

在沒有 --keep-base 的情況下(或如果指定 --no-reapply-cherry-picks),這些提交將會自動捨棄。因為這需要讀取所有 upstream 提交,因此在具有大量需要讀取之 upstream 提交的存放庫中,這可能會很耗費資源。當使用合併後端時,將會針對每個捨棄的提交發出警告(除非指定 --quiet)。除非將 advice.skippedCherryPicks 設定為 false(請參閱 git-config[1]),否則也會發出建議。

--reapply-cherry-picks 允許 rebase 放棄讀取所有 upstream 提交,可能可以改善效能。

另請參閱下方的「不相容選項」。

--allow-empty-message

空操作。具有空訊息的 rebase 提交過去會失敗,而此選項會覆寫該行為,允許 rebase 具有空訊息的提交。現在具有空訊息的提交不會導致 rebase 停止。

另請參閱下方的「不相容選項」。

-m
--merge

使用合併策略進行 rebase(預設)。

請注意,rebase 合併的工作方式是將工作分支的每個提交,重新播放到 <upstream> 分支的頂端。因此,當發生合併衝突時,回報為我們的的一方是目前為止的 rebase 系列,從 <upstream> 開始,而他們的一方是工作分支。換句話說,兩邊已互換。

另請參閱下方的「不相容選項」。

-s <strategy>
--strategy=<strategy>

使用給定的合併策略,而不是預設的 ort。這隱含 --merge

由於 git rebase 會使用給定的策略,將工作分支的每個提交重新播放到 <upstream> 分支的頂端,因此使用 ours 策略只會清空 <branch> 的所有修補程式,這沒有什麼意義。

另請參閱下方的「不相容選項」。

-X <strategy-option>
--strategy-option=<strategy-option>

將 <strategy-option> 傳遞到合併策略。這隱含 --merge,如果沒有指定策略,則隱含 -s ort。請注意,如上述 -m 選項所述,我們的他們的已互換。

另請參閱下方的「不相容選項」。

--rerere-autoupdate
--no-rerere-autoupdate

在 rerere 機制重複使用目前衝突的已記錄解析度,來更新工作樹中的檔案後,允許它也使用解析度的結果來更新索引。--no-rerere-autoupdate 是一個好方法,可以雙重檢查 rerere 的執行情況,並在透過個別的 git add 將結果提交到索引之前,捕捉潛在的合併錯誤。

-S[<keyid>]
--gpg-sign[=<keyid>]
--no-gpg-sign

以 GPG 簽署提交。keyid 引數是可選的,預設為提交者身分;如果指定,則必須將其附加到選項,不能有空格。--no-gpg-sign 可用於反擊 commit.gpgSign 組態變數和先前的 --gpg-sign

-q
--quiet

保持安靜。隱含 --no-stat

-v
--verbose

保持詳細。隱含 --stat

--stat

顯示自上次 rebase 以來,upstream 中變更的 diffstat。diffstat 也由組態選項 rebase.stat 控制。

-n
--no-stat

不要顯示 rebase 流程的一部分的 diffstat。

--no-verify

此選項會略過 pre-rebase hook。另請參閱 githooks[5]

--verify

允許執行 pre-rebase hook,這是預設值。此選項可用於覆寫 --no-verify。另請參閱 githooks[5]

-C<n>

確保每個變更之前和之後至少有 <n> 行周圍的上下文相符。當存在的周圍上下文行較少時,它們都必須相符。依預設,永遠不會忽略任何上下文。隱含 --apply

另請參閱下方的「不相容選項」。

--no-ff
--force-rebase
-f

個別重新播放所有 rebase 的提交,而不是快轉略過未變更的提交。這可確保 rebase 分支的整個歷程記錄由新的提交組成。

在還原主題分支合併之後,您可能會發現這很有用,因為此選項會以新的提交重新建立主題分支,因此它可以成功地重新合併,而無需「還原還原」(詳細資訊請參閱 還原錯誤合併方式)。

--fork-point
--no-fork-point

在計算 <branch> 引入了哪些提交時,使用 reflog 尋找 <upstream><branch> 之間更好的共同祖先。

--fork-point 啟用時,將會使用 fork_point 而不是 <upstream> 來計算要 rebase 的提交集合,其中 fork_pointgit merge-base --fork-point <upstream> <branch> 命令的結果(請參閱 git-merge-base[1])。如果 fork_point 結果為空,則會將 <upstream> 作為回退使用。

如果在命令列上指定 <upstream>--keep-base,則預設為 --no-fork-point,否則預設為 --fork-point。另請參閱 git-config[1] 中的 rebase.forkpoint

如果您的分支基於 <upstream>,但 <upstream> 已倒轉,且您的分支包含已捨棄的提交,則可以使用此選項搭配 --keep-base,以便從您的分支中捨棄這些提交。

另請參閱下方的「不相容選項」。

--ignore-whitespace

在嘗試協調差異時,忽略空白字元的差異。目前,每個後端都實作了此行為的近似值。

apply 後端

在套用修補程式時,忽略上下文行中空白字元的變更。不幸的是,這表示如果修補程式要取代的「舊」行與現有檔案僅在空白字元上有所不同,您將會遇到合併衝突,而不是成功套用修補程式。

merge 後端

合併時,將僅具有空白字元變更的行視為未變更。不幸的是,這表示任何原本打算修改空白字元且沒有其他修改的修補程式區塊都會被捨棄,即使另一方沒有衝突的變更。

--whitespace=<選項>

此旗標會傳遞給套用修補程式的 git apply 程式(請參閱 git-apply[1])。表示 --apply

另請參閱下方的「不相容選項」。

--committer-date-is-author-date

不要使用目前時間作為提交者日期,而是使用正在變基的提交的作者日期作為提交者日期。此選項表示 --force-rebase

--ignore-date
--reset-author-date

不要使用原始提交的作者日期,而是使用目前時間作為變基提交的作者日期。此選項表示 --force-rebase

另請參閱下方的「不相容選項」。

--signoff

Signed-off-by 尾部新增至所有變基的提交。請注意,如果指定 --interactive,則只會將尾部新增至標記為要選取、編輯或重新措辭的提交。

另請參閱下方的「不相容選項」。

-i
--interactive

建立即將變基的提交清單。讓使用者在變基之前編輯該清單。此模式也可以用來分割提交(請參閱下方的分割提交)。

可以透過設定組態選項 rebase.instructionFormat 來變更提交清單格式。自訂的指令格式會自動在格式前面加上提交雜湊值。

另請參閱下方的「不相容選項」。

-r
--rebase-merges[=(rebase-cousins|no-rebase-cousins)]
--no-rebase-merges

預設情況下,變基只會從待辦事項清單中刪除合併提交,並將變基的提交放入單一的線性分支。使用 --rebase-merges 時,變基會嘗試透過重新建立合併提交來保留要變基的提交中的分支結構。這些合併提交中的任何已解決的合併衝突或手動修訂都必須手動解決/重新套用。--no-rebase-merges 可以用來反駁 rebase.rebaseMerges 組態選項和先前的 --rebase-merges

當變基合併時,有兩種模式:rebase-cousinsno-rebase-cousins。如果未指定模式,則預設為 no-rebase-cousins。在 no-rebase-cousins 模式中,不以 <upstream> 作為直接祖先的提交會保留其原始分支點,也就是說,會被 git-log[1]--ancestry-path 選項排除的提交預設會保留其原始的祖先關係。在 rebase-cousins 模式中,此類提交會改為變基到 <upstream> (或 <onto>,如果指定)。

目前只能使用 ort 合併策略重新建立合併提交;不同的合併策略只能透過明確的 exec git merge -s <strategy> [...] 命令來使用。

另請參閱下方的「變基合併」和「不相容的選項」。

-x <cmd>
--exec <cmd>

在最終歷程中,在每一行建立提交之後,附加「exec <cmd>」。<cmd> 會被解譯為一或多個 shell 命令。任何失敗的命令都會中斷變基,並傳回結束代碼 1。

您可以透過使用一個包含多個命令的 --exec 執行多個命令

git rebase -i --exec "cmd1 && cmd2 && ..."

或提供多個 --exec

git rebase -i --exec "cmd1" --exec "cmd2" --exec ...

如果使用 --autosquash,則不會為中繼提交附加 exec 行,並且只會在每個壓縮/修正系列結尾處出現。

這會在內部使用 --interactive 機制,但可以無需明確的 --interactive 來執行。

另請參閱下方的「不相容選項」。

--root

變基從 <branch> 可存取的所有提交,而不是使用 <upstream> 限制它們。這可讓您在分支上變基根提交。

另請參閱下方的「不相容選項」。

--autosquash
--no-autosquash

自動將具有特殊格式訊息的提交壓縮到先前正在變基的提交中。如果提交訊息以「squash!」、「fixup!」或「amend!」開頭,則主題行的其餘部分會被視為提交規範,如果符合先前提交的主題行或雜湊值,則會比對該提交。如果沒有提交完全符合,則會考慮將規範與提交主題的開頭進行比對。

在變基待辦事項清單中,壓縮、修正和修訂提交的動作會分別從 pick 變更為 squashfixupfixup -C,並且它們會移動到它們修改的提交之後。可以使用 --interactive 選項來檢閱和編輯待辦事項清單,然後再繼續。

建立具有壓縮標記的提交的建議方法是使用 git-commit[1]--squash--fixup--fixup=amend:--fixup=reword: 選項,這些選項會將目標提交作為引數,並自動從該提交填入新提交的主題行。

將組態變數 rebase.autoSquash 設定為 true 會預設啟用互動式變基的自動壓縮功能。可以使用 --no-autosquash 選項來覆寫該設定。

另請參閱下方的「不相容選項」。

--autostash
--no-autostash

在作業開始之前自動建立暫時存放項目,並在作業結束之後套用。這表示您可以在有未提交變更的工作樹上執行變基。不過,請謹慎使用:在成功變基之後的最終存放應用可能會導致重要的衝突。

--reschedule-failed-exec
--no-reschedule-failed-exec

自動重新排程失敗的 exec 命令。這只有在互動式模式下(或當提供 --exec 選項時)才有意義。

此選項會在開始變基之後套用。它會基於下列順序,為整個變基保留:提供給初始 git rebase 的命令列選項、rebase.rescheduleFailedExec 組態(請參閱 git-config[1] 或下方的「組態」)或預設為 false。

為整個變基記錄此選項是一種方便的功能。否則,在呼叫 git rebase --continue 時,如果存在 rebase.rescheduleFailedExec=true 組態,則開頭明確的 --no-reschedule-failed-exec 會被覆寫。目前,您無法將 --[no-]reschedule-failed-exec 傳遞給 git rebase --continue

--update-refs
--no-update-refs

自動強制更新指向正在變基的提交的任何分支。不會以此方式更新在工作樹中簽出的任何分支。

如果設定了組態變數 rebase.updateRefs,則可以使用此選項來覆寫並停用此設定。

另請參閱下方的「不相容選項」。

不相容的選項

下列選項

  • --apply

  • --whitespace

  • -C

與下列選項不相容

  • --merge

  • --strategy

  • --strategy-option

  • --autosquash

  • --rebase-merges

  • --interactive

  • --exec

  • --no-keep-empty

  • --empty=

  • --[no-]reapply-cherry-picks 在不使用 --keep-base 的情況下使用時

  • --update-refs

  • --root 在不使用 --onto 的情況下使用時

此外,下列選項配對不相容

  • --keep-base 和 --onto

  • --keep-base 和 --root

  • --fork-point 和 --root

行為差異

git rebase 有兩個主要後端:applymerge。(apply 後端過去稱為 am 後端,但該名稱導致混淆,因為它看起來像動詞而不是名詞。此外,merge 後端過去稱為互動式後端,但現在也用於非互動式情況。兩者都根據支援每個後端的較低層級功能重新命名。)這兩個後端的行為方式存在一些細微差異

空的提交

apply 後端不幸地會捨棄故意為空的提交,也就是說,開始時為空的提交,儘管這些提交在實務中很少見。它也會捨棄變成空的提交,並且沒有選項可以控制此行為。

預設情況下,merge 後端會保留故意為空的提交(不過使用 -i 時,它們會在待辦事項清單編輯器中標記為空,或者可以使用 --no-keep-empty 自動捨棄)。

與 apply 後端類似,預設情況下,merge 後端會捨棄變成空的提交,除非指定了 -i/--interactive(在這種情況下,它會停止並詢問使用者要執行哪些動作)。merge 後端也有一個 --empty=(drop|keep|stop) 選項,可用來變更處理變成空的提交的行為。

目錄重新命名偵測

由於缺乏精確的樹狀結構資訊(起因於使用修補程式中可用的有限資訊來建構假的祖先),因此在 apply 後端中會停用目錄重新命名偵測。停用的目錄重新命名偵測表示如果歷程的其中一方重新命名目錄,而另一方將新檔案新增至舊目錄,則新檔案會留在舊目錄中,且在變基時不會有任何警告,告知您可能想要將這些檔案移至新目錄。

目錄重新命名偵測會與 merge 後端搭配使用,以便在這種情況下向您發出警告。

內容

apply 後端的工作方式是建立一系列的補丁 (內部呼叫 format-patch),然後依序套用這些補丁 (內部呼叫 am)。補丁由多個程式碼區塊 (hunk) 組成,每個程式碼區塊都有行號、上下文區域和實際的變更。由於另一方很可能在檔案的前面插入或刪除了行,因此行號必須帶有一些偏移量。上下文區域旨在幫助找到如何調整行號,以便將變更套用到正確的行。但是,如果程式碼的多個區域具有相同的周圍上下文行,則可能會選取到錯誤的區域。在實際情況中,這已經導致提交被不正確地重新套用,而且沒有回報衝突。將 diff.context 設定為較大的值可能會防止此類問題,但會增加虛假衝突的機會 (因為它需要更多匹配的上下文行才能套用)。

merge 後端使用每個相關檔案的完整副本,使其免受此類問題的影響。

衝突標記的標籤

當發生內容衝突時,合併機制會嘗試使用內容來源的提交來註解每一方的衝突標記。由於 apply 後端會捨棄有關重置提交及其父提交的原始資訊 (而是根據產生補丁中的有限資訊產生新的虛假提交),因此無法識別這些提交;相反地,它必須回退到提交摘要。此外,當 merge.conflictStyle 設定為 diff3zdiff3 時,apply 後端會使用「建構的合併基礎」來標記合併基礎的內容,因此不會提供有關合併基礎提交的任何資訊。

merge 後端使用歷史記錄雙方的完整提交,因此沒有這種限制。

Hook

apply 後端傳統上不會呼叫 post-commit hook,而 merge 後端則會。兩者都呼叫了 post-checkout hook,儘管 merge 後端抑制了其輸出。此外,兩個後端都只使用重置的起始點提交來呼叫 post-checkout hook,而不是中間提交或最終提交。在每種情況下,這些 hook 的呼叫都是因為實作上的巧合,而不是設計 (兩個後端最初都是作為 shell 腳本實作,並且碰巧會調用其他命令,例如 git checkoutgit commit,這些命令會呼叫 hook)。兩個後端應該具有相同的行為,儘管不完全清楚哪個 (如果有的話) 是正確的。我們未來很可能會讓 rebase 停止呼叫這些 hook。

可中斷性

apply 後端在不適當的時機中斷時存在安全問題;如果使用者在錯誤的時間按下 Ctrl-C 試圖中止重置,則重置可能會進入無法使用後續 git rebase --abort 中止的狀態。merge 後端似乎沒有同樣的缺點。(詳情請參閱 https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/)。

提交重新措辭

當重置時發生衝突時,重置會停止並要求使用者解決。由於使用者在解決衝突時可能需要進行顯著的變更,因此在解決衝突且使用者已執行 git rebase --continue 後,重置應該開啟編輯器並要求使用者更新提交訊息。merge 後端會執行此操作,而 apply 後端會盲目地套用原始提交訊息。

其他差異

還有一些行為上的差異,大多數人可能會認為這些差異並不重要,但為了完整起見,在此提及。

  • Reflog:兩個後端在描述 reflog 中所做的變更時會使用不同的措辭,但兩者都會使用「rebase」這個詞。

  • 進度、資訊和錯誤訊息:兩個後端提供略有不同的進度和資訊訊息。此外,apply 後端會將錯誤訊息 (例如「您的檔案將被覆寫…​」) 寫入 stdout,而 merge 後端會將其寫入 stderr。

  • 狀態目錄:兩個後端將其狀態保存在 .git/ 下的不同目錄中。

合併策略

合併機制 (git mergegit pull 命令) 允許使用 -s 選項選擇後端合併策略。某些策略還可以採用其自己的選項,這些選項可以透過向 git merge 和/或 git pull 提供 -X<option> 引數來傳遞。

ort

這是提取或合併一個分支時的預設合併策略。此策略只能使用三向合併演算法來解析兩個 head。當有多個可用於三向合併的共同祖先時,它會建立共同祖先的合併樹,並將其用作三向合併的參考樹。據報告,透過對取自 Linux 2.6 核心開發歷史的實際合併提交進行測試,這可以減少合併衝突,而不會導致錯誤合併。此外,此策略可以偵測和處理涉及重新命名的合併。它不使用偵測到的副本。此演算法的名稱是首字母縮略字 (「Ostensibly Recursive’s Twin」),並且來源於它是作為先前預設演算法 recursive 的替代品而編寫的事實。

ort 策略可以採用以下選項

ours

此選項會強制偏好我們的版本,以乾淨地自動解析衝突的程式碼區塊。與我們這邊沒有衝突的其他樹的變更會反映在合併結果中。對於二進位檔案,整個內容會從我們這邊取得。

這不應與 ours 合併策略混淆,後者甚至不會查看其他樹包含的內容。它會捨棄其他樹所做的一切,並聲明我們的歷史記錄包含其中發生的所有事情。

theirs

這與 ours 相反;請注意,與 ours 不同,沒有 theirs 合併策略會與此合併選項混淆。

ignore-space-change
ignore-all-space
ignore-space-at-eol
ignore-cr-at-eol

為了三向合併,將具有指示類型空白字元變更的行視為未變更。與行中的其他變更混合的空白字元變更不會被忽略。另請參閱 git-diff[1] -b-w--ignore-space-at-eol--ignore-cr-at-eol

  • 如果他們的版本只對一行引入空白字元變更,則會使用我們的版本;

  • 如果我們的版本引入空白字元變更,但他們的版本包含實質性變更,則會使用他們的版本;

  • 否則,合併會以通常的方式進行。

renormalize

這會在解析三向合併時,執行檔案所有三個階段的虛擬檢查提取和檢查移入。此選項旨在用於合併具有不同清除篩選器或行尾正規化規則的分支。有關詳細資訊,請參閱 gitattributes[5] 中的「合併具有不同檢查移入/檢查提取屬性的分支」。

no-renormalize

停用 renormalize 選項。這會覆寫 merge.renormalize 設定變數。

find-renames[=<n>]

開啟重新命名偵測,選擇性地設定相似度臨界值。這是預設值。這會覆寫 merge.renames 設定變數。另請參閱 git-diff[1] --find-renames

rename-threshold=<n>

已淘汰的 find-renames=<n> 同義詞。

subtree[=<path>]

此選項是 subtree 策略的更進階形式,其中策略會猜測在合併時必須如何移動兩個樹以相互匹配。相反地,會將指定的路徑作為前綴 (或從開頭剝離),以使兩個樹的形狀匹配。

recursive

這只能使用三向合併演算法來解析兩個 head。當有多個可用於三向合併的共同祖先時,它會建立共同祖先的合併樹,並將其用作三向合併的參考樹。據報告,透過對取自 Linux 2.6 核心開發歷史的實際合併提交進行測試,這可以減少合併衝突,而不會導致錯誤合併。此外,這可以偵測和處理涉及重新命名的合併。它不使用偵測到的副本。這是從 Git v0.99.9k 到 v2.33.0 解析兩個 head 的預設策略。

recursive 策略採用與 ort 相同的選項。但是,還有三個 ort 會忽略的額外選項 (上面沒有說明),這些選項在 recursive 策略中可能很有用

patience

已淘汰的 diff-algorithm=patience 同義詞。

diff-algorithm=[patience|minimal|histogram|myers]

在合併時使用不同的 diff 演算法,這有助於避免因不重要的匹配行 (例如來自不同函式的括號) 而發生的錯誤合併。另請參閱 git-diff[1] --diff-algorithm。請注意,ort 專門使用 diff-algorithm=histogram,而 recursive 預設為 diff.algorithm 設定。

no-renames

關閉重新命名偵測。這會覆寫 merge.renames 設定變數。另請參閱 git-diff[1] --no-renames

resolve

這只能使用三向合併演算法來解析兩個 head (也就是目前的分支和您從中提取的另一個分支)。它會嘗試仔細偵測交叉合併歧義。它不處理重新命名。

octopus

此選項會處理兩個以上分支頭的情況,但拒絕進行需要手動解決的複雜合併。它主要用於將主題分支頭捆綁在一起。當拉取或合併多個分支時,這是預設的合併策略。

ours

此選項會解決任意數量的分支頭,但合併結果的樹狀結構始終是目前分支頭的樹狀結構,實際上忽略了所有其他分支的所有變更。它旨在用於取代側分支的舊開發歷史記錄。請注意,這與遞迴合併策略的 -Xours 選項不同。

subtree

這是修改過的 ort 策略。當合併樹狀結構 A 和 B 時,如果 B 對應於 A 的子樹,則會先調整 B 以符合 A 的樹狀結構,而不是在同一層級讀取樹狀結構。此調整也會對共同的祖先樹狀結構執行。

對於使用 3 向合併的策略 (包括預設的 ort),如果在兩個分支上都進行了變更,但稍後在其中一個分支上還原了該變更,則合併結果中將會存在該變更;有些人會覺得這種行為令人困惑。這是因為在執行合併時,只會考慮分支頭和合併基礎,而不是個別的提交。因此,合併演算法會將已還原的變更視為沒有變更,並改為替換已變更的版本。

注意事項

您應該了解在您共用的存放庫上使用 git rebase 的影響。另請參閱下方的從上游變基中復原。

當執行變基時,如果存在 pre-rebase 鉤子,則會先執行該鉤子。您可以使用此鉤子來執行健全性檢查,並在不適當時拒絕變基。請參閱範本 pre-rebase 鉤子指令碼以取得範例。

完成後,<branch> 將成為目前分支。

互動模式

互動式變基表示您有機會編輯已變基的提交。您可以重新排序提交,也可以移除它們 (刪除不良或不需要的修補程式)。

互動模式適用於此類型的工作流程

  1. 有一個很棒的想法

  2. 在程式碼上進行修改

  3. 準備一系列的提交內容

  4. 提交

其中第 2 點包含數個以下實例

a) 定期使用

  1. 完成值得提交的內容

  2. commit

b) 獨立修復

  1. 發現某些東西無法運作

  2. 修復它

  3. 提交它

有時在 b.2. 中修復的內容無法附加到其所修復的並不完美的提交,因為該提交深埋在修補程式系列中。這正是互動式變基的用途:在大量的「a」和「b」之後使用它,透過重新排列和編輯提交,以及將多個提交合併為一個。

從您要保留為原樣的最後一個提交開始

git rebase -i <after-this-commit>

將會啟動一個編輯器,其中包含目前分支中 (忽略合併提交) 在指定提交之後的所有提交。您可以隨意重新排序此清單中的提交,也可以移除它們。此清單看起來大致如下

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

單行描述純粹是為了讓您方便;git rebase 不會查看它們,而是查看提交名稱 (在此範例中為「deadbee」和「fa1afe1」),因此請勿刪除或編輯名稱。

透過將指令「pick」替換為指令「edit」,您可以告知 git rebase 在套用該提交後停止,以便您可以編輯檔案和/或提交訊息、修改提交,然後繼續變基。

若要中斷變基 (就像「edit」指令會做的那樣,但不會先挑選任何提交),請使用「break」指令。

如果您只想編輯提交的提交訊息,請將指令「pick」替換為指令「reword」。

若要捨棄提交,請將指令「pick」替換為「drop」,或直接刪除相符的行。

如果您想要將兩個或多個提交摺疊為一個,請將第二個及後續提交的指令「pick」替換為「squash」或「fixup」。如果提交具有不同的作者,則摺疊的提交將歸因於第一個提交的作者。摺疊的提交的建議提交訊息是第一個提交訊息與由「squash」指令識別的訊息串連,省略由「fixup」指令識別的提交訊息,除非使用「fixup -c」。在這種情況下,建議的提交訊息僅是「fixup -c」提交的訊息,並且會開啟一個編輯器,讓您編輯訊息。「fixup -c」提交的內容 (修補程式) 仍然會併入摺疊的提交中。如果有多個「fixup -c」提交,則會使用最後一個提交的訊息。您也可以使用「fixup -C」來取得與「fixup -c」相同的行為,只是不會開啟編輯器。

當「pick」被替換為「edit」或當指令因合併錯誤而失敗時,git rebase 將會停止。當您完成編輯和/或解決衝突時,可以使用 git rebase --continue 繼續。

例如,如果您想要重新排序最後 5 個提交,以便讓 HEAD~4 成為新的 HEAD。若要達成此目的,您可以像這樣呼叫 git rebase

$ git rebase -i HEAD~5

並將第一個修補程式移至清單的末尾。

您可能想要重新建立合併提交,例如,如果您的歷程記錄如下

           X
            \
         A---M---B
        /
---o---O---P---Q

假設您想要將從「A」開始的側分支變基到「Q」。請確定目前的 HEAD 是「B」,然後呼叫

$ git rebase -i -r --onto Q O

重新排序和編輯提交通常會建立未測試的中繼步驟。您可能會想要透過執行測試,或至少使用「exec」指令 (捷徑「x」) 在歷程記錄中的中繼點重新編譯來檢查您的歷程記錄編輯是否破壞任何內容。您可以透過建立如下的待辦事項清單來執行此動作

pick deadbee Implement feature XXX
fixup f1a5c00 Fix to feature XXX
exec make
pick c0ffeee The oneline of the next commit
edit deadbab The oneline of the commit after
exec cd subdir; make test
...

當指令失敗 (即以非 0 狀態結束) 時,互動式變基將會停止,以便讓您有機會修正問題。您可以使用 git rebase --continue 繼續。

「exec」指令會在 Shell (預設的 Shell,通常為 /bin/sh) 中啟動指令,因此您可以使用 Shell 功能 (例如「cd」、「>」、「;」…)。指令會從工作樹的根目錄執行。

$ git rebase -i --exec "make test"

此指令可讓您檢查中繼提交是否可編譯。待辦事項清單會變成如下

pick 5928aea one
exec make test
pick 04d0fda two
exec make test
pick ba46169 three
exec make test
pick f4593f9 four
exec make test

分割提交

在互動模式中,您可以使用動作「edit」來標記提交。然而,這並不一定表示 git rebase 會預期此編輯的結果正好是一個提交。事實上,您可以還原提交,也可以新增其他提交。這可用於將提交分割為兩個

  • 使用 git rebase -i <commit>^ 開始互動式變基,其中 <commit> 是您要分割的提交。事實上,只要包含該提交,任何提交範圍都可以。

  • 使用動作「edit」標記您要分割的提交。

  • 當需要編輯該提交時,請執行 git reset HEAD^。其效果是 HEAD 會倒退一個,索引也會跟著倒退。然而,工作樹會保持不變。

  • 現在,將您要包含在第一個提交中的變更新增至索引。您可以使用 git add (可能是互動式) 或 git gui (或兩者) 來執行此動作。

  • 使用現在適當的提交訊息來提交目前索引。

  • 重複最後兩個步驟,直到您的工作樹是乾淨的為止。

  • 使用 git rebase --continue 繼續變基。

如果您不完全確定中繼修訂版本是否一致 (它們是否可以編譯、通過測試套件等等),您應該在每次提交後使用 git stash 來儲藏尚未提交的變更、測試,並在必要時修改提交。

從上游變基中復原

變基 (或任何其他形式的重寫) 其他人已在其上建立工作的分支是一個不好的想法:它的下游任何人將被迫手動修正其歷程記錄。本節說明如何從下游的角度進行修正。然而,真正的修正方法是避免一開始就變基上游。

為了說明,假設您處於某人開發子系統分支,而您正在處理依賴於此子系統主題的情況。您可能會得到如下的歷程記錄

    o---o---o---o---o---o---o---o  master
	 \
	  o---o---o---o---o  subsystem
			   \
			    *---*---*  topic

如果 子系統 針對 master 進行變基,則會發生以下情況

    o---o---o---o---o---o---o---o  master
	 \			 \
	  o---o---o---o---o	  o'--o'--o'--o'--o'  subsystem
			   \
			    *---*---*  topic

如果您現在像往常一樣繼續開發,並最終將 topic 合併到 subsystem,則 subsystem 的提交將永遠重複

    o---o---o---o---o---o---o---o  master
	 \			 \
	  o---o---o---o---o	  o'--o'--o'--o'--o'--M	 subsystem
			   \			     /
			    *---*---*-..........-*--*  topic

通常不贊成這種重複,因為它們會使歷程記錄雜亂,使其更難追蹤。若要清理,您需要將 topic 上的提交移植到新的 subsystem 提示,即變基 topic。這會產生漣漪效應:從 topic 下游的任何人都會被迫重新變基,依此類推!

有兩種修正方法,將在以下小節中討論

簡單情況:變更完全相同。

如果 子系統 變基是簡單的變基且沒有衝突,則會發生這種情況。

困難情況:變更不相同。

如果 子系統 變基有衝突,或使用 --interactive 來省略、編輯、擠壓或修復提交;或者如果上游使用了 commit --amendreset 或像 filter-repo 的完整歷程記錄重寫指令,則會發生這種情況。

簡單情況

只有在 subsystem 上的變更 (根據差異內容的修補程式 ID) 在 subsystem 進行變基之前和之後完全相同時,才會運作。

在這種情況下,修復很簡單,因為 git rebase 知道要跳過新上游中已存在的變更(除非給定 --reapply-cherry-picks)。所以如果你說(假設你正在 topic 分支上):

    $ git rebase subsystem

你將會得到已修復的歷史紀錄。

    o---o---o---o---o---o---o---o  master
				 \
				  o'--o'--o'--o'--o'  subsystem
						   \
						    *---*---*  topic

困難的情況

如果 subsystem 的變更與 rebase 之前的變更不完全對應,事情會變得更加複雜。

注意
雖然「簡單情況恢復」有時在困難情況下看似成功,但可能會產生意想不到的後果。例如,透過 git rebase --interactive 移除的 commit 會被復活

概念是手動告訴 git rebase「舊 subsystem 在哪裡結束,而你的 topic 在哪裡開始」,也就是它們之間舊的合併基礎點在哪裡。你必須找到一種方法來命名舊 subsystem 的最後一個 commit,例如:

  • 使用 subsystem 的 reflog:在 git fetch 之後,subsystem 的舊頂端在 subsystem@{1}。後續的 fetch 會增加這個數字。(請參閱 git-reflog[1]。)

  • 相對於 topic 的頂端:知道你的 topic 有三個 commit,舊 subsystem 的頂端一定是 topic~3

然後你可以透過以下指令將舊的 subsystem..topic 移植到新的頂端(對於 reflog 的情況,並且假設你已經在 topic 分支上):

    $ git rebase --onto subsystem subsystem@{1}

「困難情況」恢復的連鎖反應特別糟糕:每個 topic 的下游使用者現在都必須執行「困難情況」恢復!

Rebase 合併

互動式 rebase 命令最初設計用於處理個別的 patch 系列。因此,將合併 commit 從待辦事項列表中排除是有意義的,因為開發人員可能在處理分支時合併了當時的 master,最終只是將所有 commit rebase 到 master 上(跳過合併 commit)。

但是,開發人員可能會有合理的理由想要重新建立合併 commit:在處理多個相關分支時保留分支結構(或「commit 拓撲」)。

在以下範例中,開發人員在一個 topic 分支上工作,該分支重構了按鈕的定義方式,並在另一個 topic 分支上使用該重構來實作「回報錯誤」按鈕。 git log --graph --format=%s -5 的輸出可能看起來像這樣:

*   Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one

開發人員可能想要將這些 commit rebase 到較新的 master,同時保留分支拓撲,例如,當第一個 topic 分支預期會比第二個分支更早整合到 master 時,比如說,解決與進入 master 的 DownloadButton 類別變更的合併衝突。

可以使用 --rebase-merges 選項執行此 rebase。它會產生一個類似這樣的待辦事項列表:

label onto

# Branch: refactor-button
reset onto
pick 123456 Extract a generic Button class from the DownloadButton one
pick 654321 Use the Button class for all buttons
label refactor-button

# Branch: report-a-bug
reset refactor-button # Use the Button class for all buttons
pick abcdef Add the feedback button
label report-a-bug

reset onto
merge -C a1b2c3 refactor-button # Merge 'refactor-button'
merge -C 6f5e4d report-a-bug # Merge 'report-a-bug'

與一般的互動式 rebase 相反,除了 pick 命令外,還有 labelresetmerge 命令。

當執行 label 命令時,該命令會將標籤與目前的 HEAD 關聯。這些標籤會建立為工作區本機參考(refs/rewritten/<label>),在 rebase 完成時會被刪除。這樣一來,連結到同一個儲存庫的多個工作區中的 rebase 操作就不會互相干擾。如果 label 命令失敗,它會立即重新排程,並提供有關如何繼續操作的有用訊息。

reset 命令會將 HEAD、索引和工作區重設為指定的修訂版本。它類似於 exec git reset --hard <label>,但拒絕覆寫未追蹤的檔案。如果 reset 命令失敗,它會立即重新排程,並提供有關如何編輯待辦事項列表的有用訊息(這通常發生在 reset 命令手動插入到待辦事項列表且包含錯字時)。

merge 命令會將指定的修訂版本合併到當時的 HEAD。使用 -C <original-commit> 時,會使用指定合併 commit 的 commit 訊息。當 -C 變更為小寫的 -c 時,訊息會在成功合併後於編輯器中開啟,以便使用者編輯訊息。

如果 merge 命令因合併衝突以外的任何原因(即合併操作甚至沒有開始)而失敗,它會立即重新排程。

預設情況下,merge 命令會對一般合併使用 ort 合併策略,對章魚合併使用 octopus 合併策略。可以使用在呼叫 rebase 時使用 --strategy 引數為所有合併指定預設策略,或者可以在命令的互動式列表中使用 exec 命令明確呼叫帶有 --strategy 引數的 git merge 來覆寫特定合併。請注意,像這樣明確呼叫 git merge 時,您可以利用標籤是工作區本機參考的事實(例如,參考 refs/rewritten/onto 會對應到標籤 onto),以便參考您想要合併的分支。

注意:第一個命令 (label onto) 會標記 commit 所 rebase 到的修訂版本;名稱 onto 只是一種慣例,用於向 --onto 選項致意。

也可以透過新增 merge <merge-head> 形式的命令,從頭開始引入全新的合併 commit。這種形式會產生一個試驗性的 commit 訊息,並始終開啟編輯器以讓使用者編輯它。這在 topic 分支結果證明要解決的不只是一個問題,並想要分割成兩個或甚至更多 topic 分支時非常有用。考慮這個待辦事項列表:

pick 192837 Switch from GNU Makefiles to CMake
pick 5a6c7e Document the switch to CMake
pick 918273 Fix detection of OpenSSL in CMake
pick afbecd http: add support for TLS v1.3
pick fdbaec Fix detection of cURL in CMake on Windows

此列表中與 CMake 無關的一個 commit 很可能受到修復切換到 CMake 所引入的所有錯誤的動機,但它解決了不同的問題。要將這個分支分割成兩個 topic 分支,可以像這樣編輯待辦事項列表:

label onto

pick afbecd http: add support for TLS v1.3
label tlsv1.3

reset onto
pick 192837 Switch from GNU Makefiles to CMake
pick 918273 Fix detection of OpenSSL in CMake
pick fdbaec Fix detection of cURL in CMake on Windows
pick 5a6c7e Document the switch to CMake
label cmake

reset onto
merge tlsv1.3
merge cmake

設定

本節中此行下方的所有內容都是從 git-config[1] 文件中有選擇地包含的。內容與那裡找到的內容相同。

rebase.backend

用於 rebase 的預設後端。可能的選擇是 applymerge。如果合併後端獲得 apply 後端的所有剩餘功能,則此設定在未來可能會變成未使用。

rebase.stat

是否顯示自上次 rebase 以來上游發生變更的 diffstat。預設值為 False。

rebase.autoSquash

如果設定為 true,則預設會為互動模式啟用 git-rebase[1]--autosquash 選項。可以使用 --no-autosquash 選項覆寫此設定。

rebase.autoStash

設定為 true 時,會在操作開始前自動建立一個臨時的 stash 項目,並在操作結束後套用它。這表示你可以在髒工作區上執行 rebase。但是,請謹慎使用:成功 rebase 後的最終 stash 應用程式可能會導致嚴重的衝突。此選項可以使用 git-rebase[1]--no-autostash--autostash 選項覆寫。預設值為 false。

rebase.updateRefs

如果設定為 true,則預設啟用 --update-refs 選項。

rebase.missingCommitsCheck

如果設定為 "warn",則 git rebase -i 會在移除某些 commit 時(例如,刪除了一行)印出警告,但 rebase 仍會繼續。如果設定為 "error",則會印出先前的警告並停止 rebase,然後可以使用 git rebase --edit-todo 來更正錯誤。如果設定為 "ignore",則不會執行任何檢查。若要在沒有警告或錯誤的情況下捨棄 commit,請使用待辦事項列表中的 drop 命令。預設值為 "ignore"。

rebase.instructionFormat

一個格式字串,如 git-log[1] 中所指定,用於互動式 rebase 期間的待辦事項列表。格式將會自動在格式前面加上 commit 的雜湊值。

rebase.abbreviateCommands

如果設定為 true,git rebase 會在待辦事項列表中使用縮寫的命令名稱,產生類似這樣的內容:

	p deadbee The oneline of the commit
	p fa1afe1 The oneline of the next commit
	...

而不是

	pick deadbee The oneline of the commit
	pick fa1afe1 The oneline of the next commit
	...

預設值為 false。

rebase.rescheduleFailedExec

自動重新排程失敗的 exec 命令。這只有在互動模式(或提供了 --exec 選項時)才有意義。這與指定 --reschedule-failed-exec 選項相同。

rebase.forkPoint

如果設定為 false,則預設設定 --no-fork-point 選項。

rebase.rebaseMerges

預設是否以及如何設定 --rebase-merges 選項。可以是 rebase-cousinsno-rebase-cousins 或布林值。設定為 true 或 no-rebase-cousins 等同於 --rebase-merges=no-rebase-cousins,設定為 rebase-cousins 等同於 --rebase-merges=rebase-cousins,設定為 false 等同於 --no-rebase-merges。在命令列上傳遞 --rebase-merges(無論是否帶有引數)都會覆寫任何 rebase.rebaseMerges 設定。

rebase.maxLabelLength

從 commit 主旨產生標籤名稱時,將名稱截斷為此長度。預設情況下,名稱會截斷為略小於 NAME_MAX 的長度(以允許例如為相應的鬆散參考寫入 .lock 檔案)。

sequence.editor

git rebase -i 用於編輯 rebase 指令檔案的文字編輯器。此值會在使用時由 shell 解譯。可以使用 GIT_SEQUENCE_EDITOR 環境變數覆寫此設定。如果未設定,則會改用預設的 commit 訊息編輯器。

GIT

git[1] 套件的一部分

scroll-to-top