變基指南

注:譯文出自 GitHub edx,經做者 @whilgeek 提醒,對一些錯誤已糾正。git


一些說明

英文原文在此github

一些詞彙表

  • rebase 變基
  • pull request 拉取請求
  • merge 合併
  • repository 倉庫
  • commit 提交
  • squash 壓縮,擠壓

另外

有些詞翻譯過來反而不如原文精彩,就保留了,好比forkpush等等。segmentfault


(OK正文開始)服務器

當不少人同時在一個工程上工做的時候,一個拉取請求也許很快就會過期。一個「過期」的拉取請求就是一個再也不和開發主線保持同步的開發分支,它在合併到工程裏面以前須要被更新。拉取請求之因此會過期的最多見緣由是由於衝突的存在:若是兩個拉取請求修改了同一個文件中的相同的幾行,一個拉取請求被合併以後,沒被合併的拉取請求和工程之間就會存在一個衝突。有時候,一個拉取請求在沒有衝突的狀況下也會過期。或許是代碼基礎中的一個不一樣文件發生了改動,須要你在拉取請求中作出相應的改變,以保證和新架構一致。又或許是某人意外將失敗了的單元測試代碼合併進了主分支時建立了新分支。不論是什麼緣由,若是你的拉取請求已通過時了,在你的分支能被合併以前,你須要將你的分支變基到主分支最新版本。

架構

什麼是變基呢?

Alt text

爲了理解這個,咱們須要首先理解一點git運行的原理。一個git倉庫是一個樹形結構,樹上的每一個點都表明一個提交。這裏有一個簡單的關於倉庫的例子:在主分支上它有4個提交,每一個提交都有一個ID(在這裏個例子中,ID是a,b,cd)。你會注意到d目前是主分支中最新的提交(或者稱爲HEAD)。編輯器

Alt text

這裏咱們有兩個分支,mastermy-branch。你會看到mastermy-branch中都包含ab提交。而後它們開始分叉:master包含cd,而my-branch包含efb就是所謂的my-branch相對於master合併基礎,或者更通常的說,就是基礎。這頗有意義:你能夠看到my-branch是基於master的一個早期版本。單元測試

因此咱們稱my-branch是過期的,並且你想讓它和master分支的最新版本保持同步。換種說法,my-branch須要包含cd。你能夠作一個合併操做,可是這會讓這個分支包含一些怪異的合併提交,讓代碼審查更爲困難。爲了避免這樣,你能夠作一個rebase操做。學習

Alt text

當你變基的時候,git會找到你分支的base(在這個例子中是b)以及base和HEAD中的全部提交歷史(在這個例子中是ef)。而且將這些提交歷史在你要變基進去的分支(在這個例子中是master)的HEAD(頭提交)上重演一遍。參照你的修改,git建立了一些看起來就像是在master分支頂部進行修改的提交歷史:在這個圖中,這些提交被叫作ef。git並不會擦除你以前的提交:ef不會有人動的,並且若是在變基過程當中出現了某些錯誤,你徹底能夠回到以前的狀態,就像沒變過基同樣。測試

然而,另外須要注意的一點就是,git只把分支看成是標籤。主分支就是主標籤指向的,同時也是全部提交的祖先。當你給一個分支變基的時候,git移動分支標籤指向新建立的分支,my-branch如今再也不指向f,而是指向f'。退回到以前狀態的方式,僅僅是改變了分支標籤,因此它後退並指向了ffetch

修改歷史

你會注意到從ff'沒有一條直接的通路,在其餘人看來,歷史好像被忽然改變了。cd被有效注入了my-branch分支,就好像它們一直在那裏同樣。不像其餘的版本控制系統,git容許你修改你的項目的歷史 -- 可是對這種容許必定要謹慎。咱們稍後會講到這一點。

我怎麼才能變基呢?

好吧,如今你知道變基是什麼了,下一步就是學習如何去變。假定你已經fork了edx-platform,並且你建立了一個分支。就像這樣:

git clone https://github.com/my-username/dex-platform.git
cd edx-platform
git checkout -b my-branch

在你的分支裏你已經作了一些提交,而且將它們push到了Github上,並建立了一個拉取請求。你經歷了代碼審查,迴應了別人的評論,而後某人要求你將你的拉取請求變基,下面是你要作的:

將官方repo做爲一個遠程分支(僅僅在第一次的時候這樣)

在git的定義中,遠程分支就是你能提交修改的倉庫的克隆。當你從官方的edx/edx-platform中克隆出一份後。你建立了一個新的,名叫your-username/edx-platform的倉庫
,可是這兩個倉庫能夠共享所做的提交。
爲了將edx添加爲一個遠程分支,在你的本地倉庫中運行:

git remote add edx https://github.com/edx/dex-platform.git

你能夠運行git remote -v來驗證是否成功,你應該看到edx在你的遠程分支列表中。記住:這一步在每一個克隆過程當中只須要執行一次。

抓取主分支的最新版本

你的計算機須要從Github上下載關於這個官方倉庫的信息,這樣它就會知道主分支的最新版本。而後你的遠程分支就創建好了,這很簡單。在你的本地倉庫中運行:

get fetch edx

Squash你作的改變(可選的)

這一步是可選的,可是很建議你走這一步。這涉及到你在你的分支上所做的全部提交,而後將他們壓縮成一個大點的提交。這樣作的目的是爲了在變基的時候能夠更簡單地結局衝突,以及讓咱們來審查你的拉取請求。

爲了這樣作,咱們將會作一個交互式的變基。首先,找到你分支的基礎。你能夠這樣來找:

git merge-base my-branch master

這條命令會返回一個提交哈希。在下面這條命令中使用你獲得的哈希:

git rebase --interactive $${HASH}

舉例來講,若是你合併的基礎是abc123,你會運行git rebase --interactive abc123。你的文本編輯器會打開一個文本文件,其中列舉了你對你的分支所做的全部提交。 並且,在每一個提交以前都有一個寫做pick的單詞。就像這樣:

pick 1fc6c95 do something
pick 6b2481b do something else
pick dd1475d changed some things
pick c619268 fixing typos

你須要將除了第一行外全部的行以前的「pick」換作「squash」,作完的時候,應當是這樣的:

pick 1fc6c95 do something
squash 6b2481b do something else
squash dd1475d changed some things
squash c619268 fixing typos

保存並關閉這個文件,稍等片刻,一個新的文件會在你的編輯器中彈出來:包含着全部提交的全部信息。隨意修改這些提交信息,一樣的保存並關閉這個文件。保存的這些提交信息會變成一條提交信息,這是由那多條合併而來的。一旦你保存並關閉了這個文件你的這些提交就要被壓縮成一個,這一步就完成了!

終於開始變基了!

在你本地分支中運行

git rebase edx/master

git會開始在主分支的最新版本上重演你的提交。這一步你可能會遇到衝突:若是確實遇到了的話,git會暫停並讓你在繼續以前先解決衝突。就像在合併的時候解決衝突同樣:你能夠用git status來看哪些文件存在衝突,編輯這些文件以解決衝突,而後git add來表示衝突已經解決了。然而,你要運行git rebase --continue而不是git commit來告訴git能夠繼續來重演提交了。若是在作這一步以前,你已經壓縮了你的提交,你能夠一次性地解決衝突 -- 若是你沒有壓縮,你會屢次解決衝突。

用強推的方式來更新你的拉取請求

就像上面解釋的,當你變基的時候,你在修改你的分支的歷史。結果就是,若是你在變基以後作一個普通的git push,git會拒絕它,由於在服務器的分支和你的分支之間沒有一條直接的通路。因此,你要使用-f--force標誌來告訴git你知道你正在作什麼。當你在作強制推送的時候,強烈建議你將你的push.default設置改成Git2.0中默認的simple。爲了確保你的配置是正確的,運行:

git config --global push.default simple

一旦修改正確,你就能夠運行:

git push -f

而後你檢查一下你的拉取請求,它應該已經更新啦!

相關文章
相關標籤/搜索