注:譯文出自 GitHub edx,經做者 @whilgeek 提醒,對一些錯誤已糾正。git
英文原文在此github
有些詞翻譯過來反而不如原文精彩,就保留了,好比fork
,push
等等。segmentfault
(OK正文開始)服務器
當不少人同時在一個工程上工做的時候,一個拉取請求也許很快就會過期。一個「過期」的拉取請求就是一個再也不和開發主線保持同步的開發分支,它在合併到工程裏面以前須要被更新。拉取請求之因此會過期的最多見緣由是由於衝突的存在:若是兩個拉取請求修改了同一個文件中的相同的幾行,一個拉取請求被合併以後,沒被合併的拉取請求和工程之間就會存在一個衝突。有時候,一個拉取請求在沒有衝突的狀況下也會過期。或許是代碼基礎中的一個不一樣文件發生了改動,須要你在拉取請求中作出相應的改變,以保證和新架構一致。又或許是某人意外將失敗了的單元測試代碼合併進了主分支時建立了新分支。不論是什麼緣由,若是你的拉取請求已通過時了,在你的分支能被合併以前,你須要將你的分支變基到主分支最新版本。
架構
爲了理解這個,咱們須要首先理解一點git運行的原理。一個git倉庫是一個樹形結構,樹上的每一個點都表明一個提交。這裏有一個簡單的關於倉庫的例子:在主分支上它有4個提交,每一個提交都有一個ID(在這裏個例子中,ID是a
,b
,c
和d
)。你會注意到d
目前是主分支中最新的提交(或者稱爲HEAD
)。編輯器
這裏咱們有兩個分支,master
和my-branch
。你會看到master
和my-branch
中都包含a
和b
提交。而後它們開始分叉:master
包含c
和d
,而my-branch
包含e
和f
。b
就是所謂的my-branch
相對於master
的合併基礎
,或者更通常的說,就是基礎
。這頗有意義:你能夠看到my-branch
是基於master
的一個早期版本。單元測試
因此咱們稱my-branch
是過期的,並且你想讓它和master
分支的最新版本保持同步。換種說法,my-branch
須要包含c
和d
。你能夠作一個合併操做,可是這會讓這個分支包含一些怪異的合併提交,讓代碼審查更爲困難。爲了避免這樣,你能夠作一個rebase
操做。學習
當你變基的時候,git會找到你分支的base(在這個例子中是b
)以及base和HEAD中的全部提交歷史(在這個例子中是e
和f
)。而且將這些提交歷史在你要變基進去的分支(在這個例子中是master)的HEAD(頭提交)上重演一遍。參照你的修改,git建立了一些看起來就像是在master分支頂部進行修改的提交歷史:在這個圖中,這些提交被叫作e
和f
。git並不會擦除你以前的提交:e
和f
不會有人動的,並且若是在變基過程當中出現了某些錯誤,你徹底能夠回到以前的狀態,就像沒變過基同樣。測試
然而,另外須要注意的一點就是,git只把分支看成是標籤。主分支就是主標籤指向的,同時也是全部提交的祖先。當你給一個分支變基的時候,git移動分支標籤指向新建立的分支,my-branch
如今再也不指向f
,而是指向f'
。退回到以前狀態的方式,僅僅是改變了分支標籤,因此它後退並指向了f
。fetch
你會注意到從f
到f'
沒有一條直接的通路,在其餘人看來,歷史好像被忽然改變了。c
和d
被有效注入了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上,並建立了一個拉取請求。你經歷了代碼審查,迴應了別人的評論,而後某人要求你將你的拉取請求變基,下面是你要作的:
在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
這一步是可選的,可是很建議你走這一步。這涉及到你在你的分支上所做的全部提交,而後將他們壓縮成一個大點的提交。這樣作的目的是爲了在變基的時候能夠更簡單地結局衝突,以及讓咱們來審查你的拉取請求。
爲了這樣作,咱們將會作一個交互式的變基。首先,找到你分支的基礎。你能夠這樣來找:
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
而後你檢查一下你的拉取請求,它應該已經更新啦!