轉自:http://www.oschina.net/translate/git-fetch-and-merge??
---------------------------------------------------------------------------------
本文有點長而且有點亂,但就像Mark Twain?Blaise Pascal的笑話里說的那樣:我沒有時間讓它更短些。在Git的郵件列表里有很多關于本文的討論,我會盡量把其中相關的觀點列在下面。 我最常說的關于git使用的一個經驗就是: 不要用git pull,用git fetch和git merge代替它。 git pull的問題是它把過程的細節都隱藏了起來,以至于你不用去了解git中各種類型分支的區別和使用方法。當然,多數時候這是沒問題的,但一旦代碼有問題,你很難找到出錯的地方。看起來git pull的用法會使你吃驚,簡單看一下git的使用文檔應該就能說服你。 將下載(fetch)和合并(merge)放到一個命令里的另外一個弊端是,你的本地工作目錄在未經確認的情況下就會被遠程分支更新。當然,除非你關閉所有的安全選項,否則git pull在你本地工作目錄還不至于造成不可挽回的損失,但很多時候我們寧愿做的慢一些,也不愿意返工重來。 | ![]() Andy 4人頂 頂?翻譯的不錯哦! |
分支(Branches) 在說git pull之前,我們需要先澄清分支的概念(branches)。很多人像寫代碼似的用一行話來描述分支是什么,例如:
我認為你應該這樣來理解分支的概念:它是用來標記特定的代碼提交,每一個分支通過SHA1sum值來標識,所以對分支進行的操作是輕量級的--你改變的僅僅是SHA1sum值。 | ![]() Andy 3人頂 頂?翻譯的不錯哦! |
這個定義或許會有意想不到的影響。比如,假設你有兩個分支,“stable” 和 “new-idea”, 它們的頂端在版本 E 和 F: A-----C----E ("stable")\B-----D-----F ("new-idea") 所以提交(commits) A, C和 E 屬于“stable”,而?A, B, D 和 F 屬于 “new-idea”。如果之后你用下面的命令?將“new-idea”?merge 到 “stable” : git checkout stable # Change to work on the branch "stable"git merge new-idea # Merge in "new-idea" …那么你會得到這個: A-----C----E----G ("stable")\ /B-----D-----F ("new-idea") 要是你繼續在“new idea” 和“stable”分支提交, 會得到: A-----C----E----G---H ("stable")\ /B-----D-----F----I ("new-idea") 因此現在A, B, C, D, E, F, G 和 H 屬于 “stable”,而A, B, D, F 和 I 屬于 “new-idea”。 當然了,分支確實有些特殊的屬性——其中最重要的是,如果你在一個分支進行作業并創建了一個新的提交(commits),該分支的頂端將前進到那個提交(commits)。這正是你所希望的。當用git merge?進行合并(merge)的時候,你只是指定了要合并到當前分支的那個并入分支,以及當前分支的當前進展。 | ![]() super0555 3人頂 頂?翻譯的不錯哦! |
另一個表明使用分支會有很大幫助的觀點的常見情形是:假設你直接工作在一個項目的主要分支(稱為“主版本”),當你意識到你所做的可能是一個壞主意時已經晚了,這時你肯定寧愿自己是工作在一個主題分支上。如果提交圖看起來像這樣: last version from another repository|vM---N-----O----P---Q ("master") 那么你把你的工作用下面的一組命令分開做(如圖顯示的是執行它們之后所更改的狀態): git branch dubious-experimentM---N-----O----P---Q ("master" and "dubious-experiment")git checkout master# Be careful with this next command: make sure "git status" is# clean, you're definitely on "master" and the# "dubious-experiment" branch has the commits you were working# on first...git reset --hard <SHA1sum of commit N>("master")M---N-------------O----P---Q ("dubious-experiment")git pull # Or something that updates "master" from# somewhere else...M--N----R---S ("master")\O---P---Q ("dubious-experiment") 這是個看起來我最終做了很多的事情。 | ![]() 趙亮-碧海情天 4人頂 頂?翻譯的不錯哦! |
分支類型 分支這個術語不太容易理解,而且在git的開發過程中發生了很多變化。但簡單來說git的分支只有兩種: a)“本地分支(local branches)” ,當你輸入“git branch”時顯示的。例如下面這個小例子: $ git branchdebianserver* masterb)“遠程跟蹤分支(Remote-tracking branches)” ,當你輸入“git branch -r”是顯示的,如: ? $ git branch -rcognac/masterfruitfly/serverorigin/albertorigin/antorigin/contriborigin/cross-compile從上面的輸出可以看到,跟蹤分支的名稱前有一個“遠程的”標記名稱(如?:origin, cognac, fruitfly)后面跟一個“/”,然后遠程倉庫里分支的真正名稱。(“遠程名稱”是一個代碼倉庫別名,和本地目錄或URL是一個含義,你可以通過"git remote"命令自由定義額外的“遠程名稱”。但“git clone”命令默認使用的是“origin”這個名稱。) | ![]() Andy 4人頂 頂?翻譯的不錯哦! |
如果你對分支在本地是如何存儲感興趣的話,看看下面文件:?
不管如何相似,它們還是有一個特別重大的區別:?
| ![]() 幾點人 2人頂 頂?翻譯的不錯哦! |
基于遠程跟蹤分支創建本地分支如果你想基于遠程跟蹤分支創建本地分支(在本地分支上工作),你可以使用如下命令:git branch –track或git checkout –track -b,兩個命令都可以讓你切換到新創建的本地分支。例如你用git branch -r命令看到一個遠程跟蹤分支的名稱為“origin/refactored”是你所需要的,你可以使用下面的命令: git checkout --track -b refactored origin/refactored在上面的命令里,“refactored”是這個新分支的名稱,“origin/refactored”則是現存遠程跟蹤分支的名稱。(在git最新的版本里,例子中‘-track’選項已經不需要了,如果最后一個參數是遠程跟蹤分支,這個參數會被默認加上。) | ![]() Andy 2人頂 頂?翻譯的不錯哦! |
“–track”選項會設置一些變量,來保持本地分支和遠程跟蹤分支的相關性。他們對下面的情況很有用:
Your branch and the tracked remote branch 'origin/master'have diverged, and respectively have 3 and 384 differentcommit(s) each.或者: Your branch is behind the tracked remote branch'origin/master' by 3 commits, and can be fast-forwarded.允許使用的配置變量是:“branch.<local-branch-name>.merge”和“branch.<local-branch-name>.remote”,但通常情況下你不用考慮他們的設置。 | ![]() Andy 2人頂 頂?翻譯的不錯哦! |
當從遠程代碼倉庫創建一個本地分支之后,你會注意到,“git branch -r”能列出很多遠程跟蹤分支,但你的電腦上只有一個本地分支,你需要給上面的命令設置一個參數,來指定本地分支和遠程分支的對應。 有一些術語上的說法容易混淆需要注意一下:“track”在當作參數"-track"使用時,意思指通過本地分支對應一個遠程跟蹤分支。在遠程跟蹤分支中則指遠程代碼倉庫中的跟蹤分支。有點繞口。。。下面我們來看一個例子,如何從遠程分支中更新本地代碼,以及如何把本地分支推送到一個新的遠程倉庫中。 | ![]() Andy 3人頂 頂?翻譯的不錯哦! |
前面那些行顯示出“git fetch”命令會將哪些文件下載到本地,這些文件一旦下載到本地之后,就可以在本地進行任意操作了。 “git fetch”命令執行完畢之后,還不會立即將下載的文件合并到你當前工作目錄里,這就給你了一個選擇下一步操作的機會,要是想將從遠程分支下載的文件更新到你的工作目錄里,你需要執行一個“合并(merge)”操作。例如,我當前的本地分支為”master“(執行git checkout master后),這時我想執行合并操作: git merge origin/master(?幾句題外話:合并的時候有可能你還沒有對遠程分支提交過任何的更改,或者可能是一個復雜的合并。) | ![]() Andy 3人頂 頂?翻譯的不錯哦! |
如果你只是想看看本地分支和遠程分支的差異,你可以使用下面的命令: git diff master origin/master單獨進行下載和合并是一個好的做法,你可以先看看下載的是什么,然后再決定是否和本地代碼合并。而且分開來做,可以清晰的區別開本地分支和遠程分支,方便選擇使用。 | ![]() Andy 2人頂 頂?翻譯的不錯哦! |
把你的變更推送到一個遠程倉庫如何通過其他的方式呢? 假設你對 “experimental”分支做了變更并且希望把他push到"origin"遠程倉庫中去. 你可以這樣做:
? 你可能將會收到:遠程倉庫無法fast-forward該分支的錯誤信息, 這將意味著可能有別人push了不同的變更到了這個分支上.所以,你需要fetch和merge別人的變更并再次嘗試push操作. | ![]() _Raymond 2人頂 頂?翻譯的不錯哦! |
擴展閱讀: 如果這個分支在遠程倉庫里對應不同的名稱(如:experiment-by-bob),你應該這么做:?git push origin experimental:experiment-by-bob git push的操作不會牽扯遠程跟蹤分支(origin/experimental),只有在你下次進行git fetch時才會被更新。 上面這個說法不對,根據Deskin Miller的評論糾正:當推送到對應的遠程分支后,你的遠程跟蹤分支就會被更新。 | ![]() Andy 2人頂 頂?翻譯的不錯哦! |
?
為什么不用 git 的 pull?雖然?git pull?大部分時候是好的,特別是如果你用CVS類型的方式使用Git時,它可能正適合你。然而,如果你想用一個更地道的方式(建立很多主題分支,當你需要時隨時改寫本地歷史,等等)使用Git,那么習慣把?git fetch?和?git merge?分開做會有很大幫助。 |
?