天天看點

Git Rebase手冊 – Rebase權威指南

作者:秋葉Motivation

開發人員工具箱中最強大的工具之一是git rebase. 然而,它卻因複雜且容易被誤解而臭名昭著。

事實是,如果您了解它的實際用途,就會git rebase發現它是一個非常優雅且簡單的工具,可以在 Git 中實作許多不同的事情。

在之前的文章中,您了解了Git 差異是什麼、合并是什麼以及Git 如何解決合并沖突。在這篇文章中,您将了解 Git rebase 是什麼,為什麼它與 merge 不同,以及如何自信地進行 rebase

(更|多優質内|容:java567 點 c0m)

開始之前的注意事項

  1. 我還制作了一個涵蓋這篇文章内容的視訊。如果您想在閱讀的同時觀看,可以在這裡找到。
  2. 如果您想使用我使用的存儲庫并親自嘗試指令,您可以在此處擷取存儲庫。
  3. 我正在寫一本關于 Git 的書!您有興趣閱讀初始版本并提供回報嗎?

好的,你準備好了嗎?

簡短回顧 - 什麼是 Git Merge?

在幕後,git rebase和git merge是非常非常不同的東西。那麼為什麼人們總是比較他們呢?

原因在于它們的用途。使用 Git 時,我們通常在不同的分支中工作并對這些分支引入更改。

在之前的教程中,我舉了一個例子,約翰和保羅(披頭士樂隊的成員)正在共同創作一首新歌。他們從main分支開始,然後各自發散,修改歌詞并送出自己的更改。

然後,兩人想要內建他們的更改,這是使用 Git 時經常發生的事情。

一段不同的曆史——paul_branch并且john_branch背離了main(來源:Brief)

有兩種主要方法可以內建 Git 中不同分支中引入的更改,或者換句話說,不同的送出和送出曆史記錄。這些是合并和變基。

在之前的教程中,我們已經git merge非常了解了。我們看到,在執行合并時,我們建立一個合并送出- 該送出的内容是兩個分支的組合,并且它還有兩個父級,每個分支一個。

是以,假設您位于分支上john_branch(假設上圖中描述的曆史記錄),然後運作git merge paul_branch. 您将進入這種狀态 – 在 上john_branch,有兩個父母的新送出。第一個是執行合并之前指向的john_branch分支上的送出,在本例中為“Commit 6”。HEAD第二個是paul_branch“Commit 9”指向的送出。

運作結果git merge paul_branch:有兩個父級的新合并送出(來源:Brief)

再看看曆史圖表:您建立了一個分歧的曆史。您實際上可以看到它在哪裡分支以及在哪裡再次合并。

是以,在使用時git merge,您不會重寫曆史記錄 - 而是向現有曆史記錄添加送出。具體來說,是建立分歧曆史記錄的送出。

與 有何git rebase不同git merge?

使用時git rebase,會發生不同的情況。

讓我們從大局開始:如果您在 上paul_branch,并使用git rebase john_branch,Git 将轉到 John 分支和 Paul 分支的共同祖先。然後,它采用 Paul 分支上的送出中引入的更新檔,并将這些更改應用到 John 的分支。

是以,在這裡,您通常rebase會擷取在一個分支(Paul 的分支)上送出的更改,然後在另一個分支上重播它們john_branch。

運作結果:上面git rebase john_branch的送出被“重放” (來源:Brief)paul_branch``john_branch

等等,這是什麼意思?

我們現在将一點一點地了解這一點,以確定您完全了解幕後發生的事情

cherry-pick作為 Rebase 的基礎

将變基視為執行是有用的git cherry-pick- 一個指令接受一次送出,通過計算父級送出和送出本身之間的差異來計算此送出引入的更新檔,然後cherry-pick“重放”此差異。

讓我們手動執行此操作。

如果我們通過執行以下指令來檢視“Commit 5”引入的差異git diff main <SHA_OF_COMMIT_5>:

運作git diff觀察“Commit 5”引入的更新檔(來源:Brief)

(如果您想使用我使用的存儲庫并親自嘗試指令,您可以在此處擷取存儲庫)。

您可以看到,在此送出中,約翰開始創作一首名為“Lucy in the Sky with Diamonds”的歌曲:

git diff“Commit 5”引入的更新檔的輸出(來源: Brief)

提醒一下,您還可以使用以下指令git show獲得相同的輸出:

git show <SHA_OF_COMMIT_5>           

現在,如果您cherry-pick進行此送出,您将在活動分支上專門引入此更改。切換到main第一個:

git checkout main(或git switch main)

并建立另一個分支,隻是為了清楚起見:

git checkout -b my_branch(或git switch -c my_branch)

建立my_branch分支main(來源:Brief)

而cherry-pick這個送出:

git cherry-pick <SHA_OF_COMMIT_5>           

用于cherry-pick将“Commit 5”中引入的更改應用到main(來源:Brief)

考慮日志( 的輸出git lol):

的輸出git lol(來源:Brief)

(git lol是我添加到 Git 中的别名,以便以圖形方式直覺地檢視曆史記錄。您可以在此處找到它)。

看來您複制粘貼了“Commit 5”。請記住,即使它具有相同的送出消息,并引入相同的更改,甚至在本例中指向與原始“Commit 5”相同的樹對象 - 它仍然是一個不同的送出對象,因為它是使用不同的時間戳。

檢視更改,使用git show HEAD:

的輸出git show HEAD(來源:Brief)

它們與“Commit 5”相同。

當然,如果您檢視該檔案(例如,通過使用nano lucy_in_the_sky_with_diamonds.md),它将處于與原始“Commit 5”之後相同的狀态。

涼爽的!

好的,您現在可以删除新分支,這樣它就不會每次都出現在您的曆史記錄中:

git checkout main
 git branch -D my_branch           

Beyond cherry-pick– 如何使用git rebase

您可以将git rebase其視為一種依次執行多個cherry-pick操作的方法,即“重播”多個送出。這不是您可以做的唯一事情rebase,但它是我們解釋的一個很好的起點。

是時候一起玩了git rebase!

之前,你paul_branch并入john_branch. 如果您基于重建立立 paul_branch基礎, 會發生什麼john_branch?你會得到一段非常不同的曆史。

從本質上講,我們似乎采用了 上的送出中引入的更改paul_branch,并在 上重播了它們john_branch。結果将是一個線性曆史。

為了了解這個過程,我将提供高層次的視圖,然後深入研究每個步驟。将一個分支變基到另一分支之上的過程如下:

  1. 尋找共同祖先。
  2. 确定要“重播”的送出。
  3. 對于每次送出X,計算diff(parent(X), X)并将其存儲為patch(X).
  4. 遷往HEAD新基地。
  5. 将生成的更新檔按順序應用到目标分支上。每次,建立一個具有新狀态的新送出對象。

使用與現有變更集相同的變更集進行新送出的過程也稱為“重放”這些送出,這是我們已經使用過的術語。

是時候實踐 Rebase 了

從Paul的分支開始:

git checkout paul_branch           

這是曆史:

執行前送出曆史記錄git rebase(來源:Brief)

現在,到了令人興奮的部分:

git rebase john_branch           

并觀察曆史:

rebase後的曆史(來源:Brief)

(是我在視訊中gg介紹的外部工具的别名)。

是以,随着git merge你被添加到曆史中,随着git rebase你重寫曆史。您建立新的送出對象。此外,結果是線性曆史圖,而不是發散圖。

rebase後的曆史(來源:Brief)

本質上,我們“複制”了“Commit 4”之後引入的送出paul_branch,并将它們“粘貼”到john_branch.

該指令稱為“rebase”,因為它更改了運作它的分支的基本送出。也就是說,在您的情況下,在運作之前git rebase, 的基礎paul_branch是“Commit 4” - 因為這是分支“誕生”的地方(來自main)。通過rebase,您要求 Git 給它另一個基礎 - 也就是說,假裝它是從“Commit 6”誕生的。

為此,Git 采用了以前的“Commit 7”,并将此送出中引入的更改“重播”到“Commit 6”上,然後建立了一個新的送出對象。這個對象與原來的“Commit 7”有三個方面的不同:

  1. 它有不同的時間戳。
  2. 它有一個不同的父送出 - “Commit 6”而不是“Commit 4”。
  3. 它指向的樹對象不同 - 因為更改是引入到“Commit 6”指向的樹,而不是“Commit 4”指向的樹。

請注意此處的最後一次送出“Commit 9'”。它所代表的快照(即它指向的樹)與通過合并兩個分支得到的樹完全相同。Git 存儲庫中檔案的狀态與您使用git merge. 隻是曆史不同,當然還有送出對象。

現在,您可以簡單地使用:

git checkout main
 git merge paul_branch           

嗯...如果您運作最後一條指令會發生什麼? 檢查後再次考慮送出曆史記錄main:

變基和檢出後的曆史main(來源:Brief)

main合并和意味着什麼paul_branch?

事實上,Git 可以簡單地執行快進合并,因為曆史記錄是完全線性的(如果您需要有關快進合并的提醒,請檢視這篇文章)。結果,main現在paul_branch指向相同的送出:

快進合并的結果(來源:Brief)

Git 中的進階變基

現在您已經了解了 rebase 的基礎知識,是時候考慮更進階的情況了,在這些情況下,指令的附加開關和參數rebase将派上用場。

在前面的示例中,當您僅表示rebase(沒有附加開關)時,Git 會重播從共同祖先到目前分支尖端的所有送出。

但 rebase 是一種超級力量,它是一個全能的指令,能夠……嗯,重寫曆史。如果您想修改曆史記錄以使其成為您自己的曆史記錄,它會派上用場。

通過再次指向“Commit 4”來撤消上次合并main:

git reset -–hard <ORIGINAL_COMMIT 4>           

“撤消”最後一次合并操作(來源:Brief)

并使用以下指令撤消變基:

git checkout paul_branch
 git reset -–hard <ORIGINAL_COMMIT 9>           

“撤消”變基操作(來源:Brief)

請注意,您獲得的曆史記錄與以前的曆史記錄完全相同:

可視化“撤消”變基操作後的曆史記錄(來源:Brief)

再次需要明确的是,當無法從目前 .commit 9 通路時,“Commit 9”并不會消失HEAD。相反,它仍然存儲在對象資料庫中。當您git reset現在更改HEAD為指向此送出時,您能夠檢索它及其父送出,因為它們也存儲在資料庫中。很酷吧?

好的,快速檢視 Paul 引入的更改:

git show HEAD           

git show HEAD顯示“Commit 9”引入的更新檔(來源:Brief)

在送出圖中繼續向後移動:

git show HEAD~           

git show HEAD~(同git show HEAD~1)顯示“Commit 8”引入的更新檔(來源:Brief)

并進一步承諾:

git show HEAD~2           

git show HEAD~2顯示“Commit 7”引入的更新檔(來源:Brief)

是以,這些改變很好,但也許保羅不想要這樣的曆史。相反,他希望看起來好像他将“Commit 7”和“Commit 8”中的更改作為單個送出引入。

為此,您可以使用互動式變基。為此,我們将-i(或--interactive) 開關添加到rebase指令中:

git rebase -i <SHA_OF_COMMIT_4>           

或者,由于main指向“Commit 4”,我們可以簡單地運作:

git rebase -i main           

通過運作此指令,您可以告訴 Git 使用新的基礎“Commit 4”。是以,您要求 Git 傳回“Commit 4”之後引入的所有送出,并且可以從 current 通路這些送出HEAD,并重播這些送出。

對于重播的每個送出,Git 都會詢問我們想用它做什麼:

git rebase -i main提示您選擇每次送出要執行的操作(來源:Brief)

在這種情況下,将送出視為更新檔是很有用的。也就是說,“Commit 7”如““Commit 7”在其父級之上引入的更新檔”一樣。

一種選擇是使用pick. 這是預設行為,它告訴 Git 重放此送出中引入的更改。在這種情況下,如果您保持原樣 - 以及pick所有送出 - 您将獲得相同的曆史記錄,并且 Git 甚至不會建立新的送出對象。

另一種選擇是squash。壓縮的送出會将其内容“折疊”到其前面的送出的内容中。是以,在我們的例子中,Paul 希望将“Commit 8”壓縮為“Commit 7”:

将“Commit 8”壓縮為“Commit 7”(來源:Brief)

如您所見,git rebase -i提供了其他選項,但我們不會在本文中讨論所有選項。如果您允許運作變基,系統将提示您為新建立的送出選擇一條送出消息(即引入“Commit 7”和“Commit 8”更改的消息):

提供送出消息:(Commits 7+8來源:Brief)

看看曆史:

互動式rebase之後的曆史(來源:Brief)

正是我們想要的!我們有paul_branch“Commit 9”(當然,它是與原始“Commit 9”不同的對象)。這裡指向“Commits 7+8”,這是一個單一的送出,同時引入了原始“Commit 7”和原始“Commit 8”的更改。該送出的父級是“Commit 4”,main指向哪裡。你有john_branch。

互動式變基後的曆史 - 可視化(來源:Brief)

哦,哇,這不是很酷嗎?

git rebase讓您可以無限制地控制任何分支的形狀。您可以使用它來重新排序送出,或删除不正确的更改,或回顧修改更改。或者,您也許可以将分支的基礎移動到另一個送出(您希望的任何送出)。

如何使用--onto開關git rebase

讓我們再考慮一個例子。再次進入main:

git checkout main           

并删除指向的指針paul_branch,john_branch這樣您就不會再在送出圖中看到它們:

git branch -D paul_branch
 git branch -D john_branch           

現在分支main到一個新分支:

git checkout -b new_branch           

創造new_branch不同于main(來源:Brief)

new_branch與此不同的幹淨曆史main(來源:Brief)

現在,在此處添加一些更改并送出它們:

nano code.py           

new_branch添加該功能code.py(來源:Brief)

git add code.py
 git commit -m "Commit 10"           

回到main:

git checkout main           

并引入另一個變化:

在檔案開頭添加了文檔字元串(來源:Brief)

是時候準備并送出這些更改了:

git add code.py
 git commit -m "Commit 11"           

還有另一個變化:

添加@Author到文檔字元串(來源:Brief)

也送出此更改:

git add code.py
 git commit -m "Commit 12"           

哦等等,現在我意識到我希望您将“Commit 11”中引入的更改作為new_branch. 啊。你能做什麼?

回顧一下曆史:

引入“Commit 12”後的曆史(來源:Brief)

我想要的是,我不希望“Commit 10”僅駐留在分支上main,而是希望它同時位于main分支和new_branch. 從視覺上看,我想将它移到圖表中:

從視覺上看,我希望你“推動”“Commit 10”(來源:Brief)

你能看到我要去哪裡嗎?

嗯,正如我們所了解的,rebase 允許我們基本上重放在“Commit 10”中引入的更改new_branch,就好像它們最初是在“Commit 11”而不是“Commit 4”上進行的。

為此,您可以使用 的其他參數git rebase。您會告訴 Git,您想要擷取main和的共同祖先new_branch(即“Commit 4”)之間引入的所有曆史記錄,并将該曆史記錄的新基礎設為“Commit 11”。為此,請使用:

git rebase -–onto <SHA_OF_COMMIT_11> main new_branch           

rebase前後的曆史,“Commit 10”已被“推送”(來源:Brief)

看看我們美麗的曆史!

rebase前後的曆史,“Commit 10”已被“推送”(來源:Brief)

讓我們考慮另一個案例。

假設我開始在一個分支上工作,并且錯誤地從 開始工作feature_branch_1,而不是從 開始工作main。

是以,要模拟這一點,請建立feature_branch_1:

git checkout main
 git checkout -b feature_branch_1           

并擦除,new_branch這樣您就不會再在圖表中看到它:

git branch -D new_branch           

建立一個簡單的 Python 檔案,名為1.py:

一個新檔案,1.py,包含print('Hello world!')(來源:Brief)

暫存并送出此檔案:

git add 1.py
 git commit -m  "Commit 13"           

現在(錯誤地)從以下分支出來feature_branch_1:

git checkout -b feature_branch_2           

并建立另一個檔案2.py:

建立2.py(來源:Brief)

也暫存并送出該檔案:

git add 2.py
 git commit -m  "Commit 14"           

并引入更多代碼2.py:

修改2.py(來源:Brief)

也暫存并送出這些更改:

git add 2.py
 git commit -m  "Commit 15"           

到目前為止你應該有這樣的曆史:

引入“Commit 15”後的曆史(來源:Brief)

傳回feature_branch_1并編輯1.py:

git checkout feature_branch_1           

修改1.py(來源:Brief)

現在暫存并送出:

git add 1.py
 git commit -m  "Commit 16"           

您的曆史記錄應該如下所示:

引入“Commit 16”後的曆史(來源:Brief)

說現在你意識到你犯了一個錯誤。你實際上想feature_branch_2從樹枝中誕生main,而不是從……中誕生feature_branch_1。

你怎樣才能做到這一點?

--onto嘗試根據曆史圖以及您對指令标志的了解來思考它rebase。

好吧,您想要将 上的第一個送出feature_branch_2(即“Commit 14”)的父級“替換”到main分支頂部(在本例中為“Commit 12”),而不是在分支頂部feature_branch_1(在本例中為“”)送出 13 英寸。同樣,您将建立一個新的基礎,這次 是為了第一次送出feature_branch_2.

您想要移動“Commit 14”和“Commit 15”(來源:Brief)

你會怎麼做?

首先,切換到feature_branch_2:

git checkout feature_branch_2           

現在您可以使用:

git rebase -–onto main <SHA_OF_COMMIT_13>           

是以,您feature_branch_2基于main而不是feature_branch_1:

執行rebase後的送出曆史(來源:Brief)

該指令的文法是:

git rebase --onto <new_parent> <old_parent>           

如何在單個分支上變基

git rebase您還可以在檢視單個分支的曆史記錄時使用。

讓我們看看你是否可以在這裡幫助我。

假設我工作feature_branch_2并專門編輯了該檔案code.py。我首先将所有字元串更改為用雙引号而不是單引号括起來:

更改'為"in code.py(來源:Brief)

然後,我上演并承諾:

git add code.py
 git commit -m "Commit 17"           

然後我決定在檔案的開頭添加一個新函數:

添加功能another_feature(來源:Brief)

我再次上演并承諾:

git add code.py
 git commit -m "Commit 18"           

現在我意識到我實際上忘記将單引号更改為雙引号main(正如您可能已經注意到的那樣),是以我也這樣做了:

改成(來源'main':簡報)"main"

當然,我策劃并承諾了這一改變:

git add code.py
 git commit -m "Commit 19"           

現在,回顧一下曆史:

引入“Commit 19”後的送出曆史(來源:Brief)

這不太好,不是嗎?我的意思是,我有兩個彼此相關的送出,“Commit 17”和“Commit 19”(将's 變成"s),但它們被不相關的“Commit 18”(我在其中添加了一個新函數)分開。我們可以做什麼? 你能幫我嗎?

直覺上,我想在這裡編輯曆史記錄:

這些是我要編輯的送出(來源:Brief)

那麼,你會怎麼做?

你是對的!

我可以在“Commit 15”之上将曆史記錄從“Commit 17”重新設定為“Commit 19”。要做到這一點:

git rebase --interactive --onto <SHA_OF_COMMIT_15> <SHA_OF_COMMIT_15>           

請注意,我指定“Commit 15”作為送出範圍的開頭,不包括此送出。而且我不需要明确指定HEAD為最後一個參數。

rebase --onto在單個分支上使用(來源: Brief)

按照您的建議并運作rebase指令後(謝謝!)我得到以下螢幕:

互動式變基(來源:Brief)

那麼我該怎麼辦呢?我想将“Commit 19”放在“Commit 18”之前,是以它位于“Commit 17”之後。我可以更進一步,将它們壓在一起,如下所示:

互動式變基 - 更改送出和壓縮的順序(來源:Brief)

現在,當我收到送出消息提示時,我可以提供消息“Commit 17+19”:

提供送出消息(來源:Brief)

現在,看看我們美麗的曆史:

由此産生的曆史(來源:Brief)

再次感謝!

更多變基用例+更多實踐

到目前為止,我希望您對 rebase 的文法感到滿意。真正了解它的最好方法是考慮各種情況并自己找出解決方法。

對于即将到來的用例,我強烈建議您在介紹完每個用例後停止閱讀,然後嘗試自己解決它。

如何排除送出

假設您在另一個存儲庫上有此曆史記錄:

另一個送出曆史(來源:Brief)

在使用它之前,将标簽存儲到“Commit F”,以便稍後可以傳回:

git tag original_commit_f           

現在,您實際上不希望包含“Commit C”和“Commit D”中的更改。您可以像以前一樣使用互動式變基并删除它們的更改。或者,可以再次使用git rebase -–onto。您将如何使用--onto來“删除”這兩個送出?

您可以HEAD在“Commit B”之上進行變基,其中舊的父級實際上是“Commit D”,現在它應該是“Commit B”。再回顧一下曆史:

再次回顧曆史(來源:Brief)

變基使“Commit B”成為“Commit E”的基礎,意味着“移動”“Commit E”和“Commit F”,并給它們另一個基礎—— “ Commit B”。你能自己想出這個指令嗎?

git rebase --onto <SHA_OF_COMMIT_B> <SHA_OF_COMMIT_D> HEAD           

請注意,使用上面的文法不會移動main到指向新的送出,是以結果是“分離” HEAD。如果您使用gg或其他顯示可從分支通路的曆史記錄的工具,它可能會讓您感到困惑:

變基并--onto産生分離結果HEAD(來源:Brief)

但如果您隻是使用git log(或我的别名git lol),您将看到所需的曆史記錄:

由此産生的曆史(來源:Brief)

我不了解你,但這些事情讓我真的很高興。

順便說一句,您可以省略HEAD上一個指令,因為這是第三個參數的預設值。是以隻需使用:

git rebase --onto <SHA_OF_COMMIT_B> <SHA_OF_COMMIT_D>           

會有同樣的效果。最後一個參數實際上告訴 Git 目前的 rebase 送出序列的結尾在哪裡。git rebase --onto是以帶有三個參數的文法是:

git rebase --onto <new_parent> <old_parent> <until>           

如何跨分支移動送出

假設我們得到了與之前相同的曆史記錄:

git checkout original_commit_f           

現在我隻想要“送出 E”位于基于“送出 B”的分支上。也就是說,我想要一個新分支,從“Commit B”分支,隻有“Commit E”。

目前的曆史,考慮“Commit E”(來源:Brief)

那麼,這對于 rebase 來說意味着什麼呢?考慮上面的圖檔。我應該重新設定哪些送出(或哪些送出),以及哪個送出将成為新的基礎?

我知道我可以在這裡依靠你

我想要的是僅采用“Commit E”,并且僅此送出,并将其基礎更改為“Commit B”。換句話說,将“送出 E”中引入的更改重播到“送出 B”上。

你能将該邏輯應用到 的文法中嗎git rebase?

這是(為了簡潔,這次我寫<COMMIT_B>的是<SHA_OF_COMMIT_B>):

git rebase –-onto <COMMIT_B> <COMMIT_D> <COMMIT_E>           

現在曆史看起來是這樣的:

rebase後的曆史(來源:Brief)

驚人的!

關于沖突的注意事項

請注意,執行變基時,您可能會像合并時一樣遇到沖突。您可能會遇到沖突,因為在變基時,您試圖在不同的基礎上應用更新檔,也許更新檔不适用。

例如,再次考慮以前的存儲庫,具體來說,考慮“Commit 12”中引入的更改,由 指向main:

git show main           

“Commit 12”中引入的更新檔(來源:Brief)

我已經在上一篇文章git diff中詳細介紹了 的格式,但作為一個快速提醒,此送出訓示 Git 在兩行上下文之後添加一行:

```
 This is a sample file           

在這三行上下文之前:

```
 def new_feature():
   print('new feature')           

假設您正在嘗試将“Commit 12”重新設定為另一個送出。如果由于某種原因,這些上下文行并不像您要變基到的送出的更新檔中那樣存在,那麼您将遇到沖突。要了解有關沖突以及如何解決沖突的更多資訊,請參閱本指南。

縮小大局

比較變基和合并(來源:Brief)

git merge在本指南的開頭,我首先提到和 之間的相似之處git rebase:兩者都用于整合不同曆史中引入的變化。

但是,正如您現在所知,它們的運作方式非常不同。合并會産生發散的曆史記錄,而變基則會産生線性曆史記錄。這兩種情況都可能發生沖突。上表中還有一列需要密切關注。

現在您知道什麼是“Git rebase”,以及如何使用互動式 rebase,或者rebase --onto,正如我希望您同意的那樣,git rebase它是一個超級強大的工具。然而,與合并相比,它有一個巨大的缺點。

Git rebase 改變了曆史。

這意味着您不應該對存儲庫本地副本之外存在的送出進行變基,而其他人可能已經基于這些送出進行了送出。

換句話說,如果唯一有問題的送出是您在本地建立的送出 - 繼續,使用 rebase,盡情發揮。

但是,如果送出已被推送,這可能會導緻一個巨大的問題 - 因為其他人可能會依賴這些送出,而您稍後會覆寫這些送出,然後您和他們将擁有不同版本的存儲庫。

正如我們所看到的,這與merge不修改曆史不同。

例如,考慮我們重新設定基礎并導緻此曆史記錄的最後一個案例:

rebase後的曆史(來源:Brief)

現在,假設我已經将此分支推送到遠端。在我推送分支後,另一位開發人員将其拉出并從“Commit C”分支出來。另一位開發人員不知道與此同時,我正在本地重新調整我的分支,并稍後再次推送它。

這會導緻不一緻:其他開發人員所使用的送出在我的存儲庫副本上不再可用。

我不會在本指南中詳細說明這到底是什麼原因,因為我的主要資訊是您絕對應該避免這種情況。如果您對實際發生的情況感興趣,我将在下面留下一個有用資源的連結。現在,讓我們總結一下我們所涵蓋的内容。

回顧

在本教程中,您了解了git rebase,一個重寫 Git 曆史記錄的超級強大工具。您考慮了一些git rebase有用的用例,以及如何使用一個、兩個或三個參數(帶或不帶開關)來使用它--onto。

我希望我能夠讓您相信這git rebase很強大,而且一旦您掌握了要點,它就非常簡單。它是一個“複制粘貼”送出(或更準确地說,更新檔)的工具。它是一個值得擁有的有用工具。

(更|多優質内|容:java567 點 c0m)

繼續閱讀