ProGit這本書講的挺不錯。循序漸進。有幾個命令書中語焉不詳,卡住了挺長時間。記錄一下。
remote branch
每一個remote branch都會在本地表現為一個不可改變的靜態branch。使用git branch -a可以看到。紅色的就是remote branch。不能夠對這些branch進行改動,但是可以建立一個這些remote branch的tracking branch:
git checkout -b b1 origin/b1# orgit checkout --tracking origin/b1
這時候,建立出來的local branch就會被git看作是對應的remote branch的tracking branch。在執行git push的時候,local branch的內容就會自動被push到它的tracking branch。
預設的master就是origin/master的tracking branch。
本地的branch只能夠通過向remote branch推送(push)資料的方式來和remote branch互動。如果想建立一個remote branch,就需要建立一個branch,然後
git branch b2git push origin b2
這兩條命令建立一個本地branch b2,然後將它增加到remote branch。這時候運行 git branch -a,能看到有了一個新的remote branch。
git fetch
git fetch [remote_repo] :這個是將remote repo所有的資料:包括更新的檔案,新增/減的分支,tag,等等,全部下載到本地的local repo。但是,不會做merge。也就是說,master分支資料可能是舊的,但是origin/master上的資料已經是新的了。可以進一步運行
git checkout mastergit merge origin/master
來將兩個分支進行merge。或者更好使用git push來進行merge(前提是master確實是origin/master的tracking branch)
git pull
git pull [remote_repo branch_name]:這個命令直接從指定remote_repo的指定branch拉取相應的資料。並將遠程branch的更新和本地的tracking branch做merge。
注意。這時候,並不會把這個branch之外的資料拉下來。比如,如果遠端另一個branch有更新,或者增加了一個新的branch,這個命令並不會把這些資料拉下來。
如果直接執行git pull [remote_repo],則會將所有資料拉下來,包括其它分支的更新,包括新增的分支。同時還會將當前branch與它tracking的branch做merge。
git push
git push [remote_repo local branch:remote branch]。預設情況下,將當前branch的改動push到預設repo中它track的branch。也可以加repo和branch,將當前分支的資料push到任何一個repo的任何一個branch
刪除遠程分支
如果需要刪除一個遠程分支,則需要git push origin :b1根據 git push的定義,就是把空push到遠端b1 branch。也就是刪除了。
但是,如果別人已經在b1刪除之前執行了fetch或者pull,在本地有了b1這個branch,再次執行fetch或者pull並不會刪除這個branch。運行git branch -a也不能看出這個branch被刪除了。這時候需要運行
$ git remote show origin* remote origin Fetch URL: git@github.com:xxx/xxx.git Push URL: git@github.com:xxx/xxx.git HEAD branch: master Remote branches: master tracked refs/remotes/origin/b1 stale (use 'git remote prune' to remove) Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (up to date)
這時候能夠看到b1是stale的,可以使用git remote prune origin將它從本地repo也去掉。
Rebase
rebase就是re-base。git每個commit都有一個parent指標指向當前提交的parent,即base。rebase則是改變commit的parent,這樣的好處是可以減少版本樹的分支和複雜度,壞處是用不好的話會給別人帶來很多麻煩。
假設有一個master:7,branch f1是從master:7上cut出來的一個branch。f1是一個local的branch,用來開發一個feature。
在開發feature的時候,有人將一些修改commit到了master上。
在feature做了一個階段之後,master已經有了版本10。f1有了版本5。這時候想把f1上的修改合并到master上。首先執行git pull把master上的更新都拉到本地來。然後可以使用rebase命令將f1上的改動都rebase(gitpro上翻譯為衍合,我覺得擬合也挺好,至少是個現有的詞)到master上,然後進行提交。
git checkout f1git rebase master#orgit rebase master f1
這時候,git會先找到f1和master的交點,也就是cut f1的時候的版本(master:7),從這個版本開始,git將f1每次的提交作為一個diff檔案儲存起來。然後checkout master,將這些diff一個個應用到master上。
看起來很像是merge,不過merge會產生一個新的提交,而rebase是re-base,也就是改變f1的每個提交:改變每個提交的parent,改變每個提交指向的檔案快照。讓整個過程看起來好像就沒有f1一樣,所有的改動都是直接在master上來的。Rebase過之後,如果feature已經做完,f1可以直接被刪掉。
merge則會產生一個新的提交,不改變已有的提交。好處是比較安全,曆史不會被改動。不好之處就是版本樹會比較亂。
rebase很好很清爽。需要注意的一點是“永遠不要rebase那些已經推送到遠程repo的更新(commit)”
rebase會改變commits的內容,也就是改變曆史,對於沒有push到server上的commits無所謂,反正只有本地一個人在用。這時候使用rebase將commits rebase到公用branch(master),可以讓本地的曆史更清爽。
對於一個已經push到remote repo的commits。比如f1如果已經在remote repo上了,而且,f1:1到f1:3已經push到repo上了。這時候如果再將f1:1到f1:5 rebase到master上,然後強制push到repo上,結果就是f1這個branch的曆史被改動。當然,如果這個remote branch只有一個人用,也還無所謂。如果有人在f1:3的時候cut出來一個branch,這時候那個人就悲劇了。再次pull的時候,將會收到rebase後改動的commits,以及原來就有的commits,情況會變的異常糟糕。
如果是push到master上,影響將更大。
總之,remote repo上的commit永遠不要改動。也就是說,永遠不要將已經push出去的commit再rebase一下。rebase也無所謂,切不要push出去也不會對別人造成影響。如果push出去了,這樣改變了history,改變了server上的版本樹,情況就糟糕了。
具體圖和例子請參照Git Pro
http://progit.org/book/zh/ch3-6.html