本文參考於:http://www.zhanglian2010.cn/2014/07/git-pull-vs-fetch-and-merge/
使用git fetch和git pull都可以更新遠程倉庫的代碼到本地,但是它們之間還是有區別
git fetch
git fetch origin master
git log -p master..origin
/master
git merge origin
/master
- 從遠程的origin倉庫的master主分支更新最新的版本到origin/master分支上
- 比較本地的master分支和origin/master分支的差別
- 合並內容到本地master分支
git pull
相當於git fetch 和 git merge,即更新遠程倉庫的代碼到本地倉庫,然后將內容合並到當前分支。
所以,簡單的說git pull相當於git fetch后再做一個git merge。那么它們具體的區別如何分析呢,這就需要我們再認識下git了,先看看下面這張圖:
我們知道,git其實有好幾個區,工作區(workspace)、暫存區(index)、本地倉庫(local repository),當然還有遠程倉庫(remote repository)。遠程倉庫為我們保存一份代碼拷貝,如github,而工作區、暫存區和本地倉庫都在本地,這就是為什么沒有網絡我們也照樣使用git提交(commit)代碼更新,因為提交僅是提交到本地倉庫,待有網絡之后可以再推送(push)到遠程倉庫。
正如上圖所示,git fetch是將遠程倉庫的更新獲取到本地倉庫,不影響其他區域。而git pull則是一次性將遠程倉庫的代碼更新到工作區(同時也會更新本地倉庫)。
通常來說,git fetch和merge與git pull的區別已經很明顯了,但是如果想再了解下git是如何操作的,則需要我們了解下分支這個git的強大特性(分支的概念確實太牛逼了,我不確定我的理解是否是正確的)。
分支
分支(branches)是用來標記特定的代碼提交,每一個分支通過SHA1sum值來標識,所以對分支進行的操作是輕量級的——你改變的僅僅是SHA1sum值。所以為什么git提倡大家多使用分支,因為它即輕量級又靈活。簡單的說,分支有兩種:
本地分支(local branches)” ,當你輸入“git branch”時顯示的:
1
2
|
$ git branch
* master
|
遠程分支(remote branches)” ,當你輸入“git branch -r”是顯示的:
1
2
|
$ git branch -r
origin
/master
|
如果你對分支在本地是如何存儲感興趣的話,看看項目中的下面文件,文件里面存的就是一個SHA1sum值:
- .git/refs/head/[本地分支]
- .git/refs/remotes/[正在跟蹤的分支]
我們來看看遠程分支,Pro Git這本書描述的非常好。遠程分支(remote branch)是對遠程倉庫中的分支的索引。它們是一些無法移動的本地分支;只有在 Git 進行網絡交互時才會更新。遠程分支就像是書簽,提醒着你上次連接遠程倉庫時上面各分支的位置。
我們用 (遠程倉庫名)/(分支名) 這樣的形式表示遠程分支。比如我們想看看上次同 origin 倉庫通訊時 master 分支的樣子,就應該查看 origin/master 分支。如果你和同伴一起修復某個問題,但他們先推送了一個 iss53 分支到遠程倉庫,雖然你可能也有一個本地的 iss53 分支,但指向服務器上最新更新的卻應該是 origin/iss53 分支。
下面我把Pro Git中的例子摘抄過來。假設你們團隊有個地址為 git.ourcompany.com 的 Git 服務器。如果你從這里克隆,Git 會自動為你將此遠程倉庫命名為 origin,並下載其中所有的數據,建立一個指向它的 master 分支的指針,在本地命名為 origin/master,但你無法在本地更改其數據。接着,Git 建立一個屬於你自己的本地 master 分支,始於 origin 上 master 分支相同的位置,你可以就此開始工作:
這樣,我們在本地倉庫的本地分支和遠程分支都有了,並且起始於同一位置。
如果你在本地 master 分支做了些改動(在本地工作區commit了代碼到本地倉庫),與此同時,其他人向 git.ourcompany.com 推送了他們的更新,那么服務器上的 master 分支就會向前推進,而與此同時,你在本地的提交歷史正朝向不同方向發展。不過只要你不和服務器通訊,你的 origin/master 指針仍然保持原位不會移動:
注意這里的本地分支已經前移,而遠程分支還保持不動,而遠程倉庫的master其實也已經前移,所以可以說本地的遠程分支origin/master是過時的。
可以運行 git fetch origin 來同步遠程服務器上的數據到本地。該命令首先找到 origin 是哪個服務器(本例為 git.ourcompany.com),從上面獲取你尚未擁有的數據,更新你本地的數據庫(倉庫),然后把 origin/master 的指針移到它最新的位置上:
現在大家能看到git fetch的作用了嗎?
接下來還沒完,我們來看看git的高級玩兒法。為了演示擁有多個遠程分支(在不同的遠程服務器上)的項目是如何工作的,我們假設你還有另一個僅供你的敏捷開發小組使用的內部服務器 git.team1.ourcompany.com。可以用第二章中提到的 git remote add 命令把它加為當前項目的遠程分支之一。我們把它命名為 teamone,以便代替完整的 Git URL 以方便使用:
注意這里是多個遠程分支,不同的遠程服務器。
現在你可以用 git fetch teamone 來獲取小組服務器上你還沒有的數據了。由於當前該服務器上的內容是你 origin 服務器上的子集,Git 不會下載任何數據,而只是簡單地創建一個名為 teamone/master 的遠程分支,指向 teamone 服務器上 master 分支所在的提交對象 31b8e:
由此你在本地就有了兩個遠程分支,作為指向兩個遠程服務器上 master 分支的索引。
好了,不扯遠了,總結下git fetch和git pull:
- git fetch is the command that says “bring my local copy of the remote repository up to date.”
- git pull says “bring the changes in the remote repository where I keep my own code.”
由於git pull把過程的細節都隱藏了起來,以至於你不用去了解git中各種類型分支的區別和使用方法。當然,多數時候這是沒問題的,但一旦代碼有問題,你很難找到出錯的地方。看起來git pull的用法會使你吃驚,簡單看一下git的使用文檔應該就能說服你。
將下載(fetch)和合並(merge)放到一個命令里的另外一個弊端是,你的本地工作目錄在未經確認的情況下就會被遠程分支更新。
單獨進行下載和合並是一個好的做法,你可以先看看區別(diff),然后再決定是否和本地代碼合並。而且分開來做,可以清晰的區別開本地分支和遠程分支,方便選擇使用。所以盡量少用git pull,多用git fetch和merge。
參考:
http://www.oschina.net/translate/git-fetch-and-merge?cmp
http://stackoverflow.com/questions/292357/difference-between-git-pull-and-git-fetch
http://git-scm.com/book/zh/Git-%E5%88%86%E6%94%AF-%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF