作者:張龍
原文地址:
http://www.infoq.com/cn/news/2016/01/12-git-advanced-commands
眾所周知,Git目前已經(jīng)是分布式版本控制領(lǐng)域的翹楚,圍繞著Git形成了完整的生態(tài)圈。學(xué)習(xí)Git,首先當(dāng)然是學(xué)習(xí)Git的基本工作流。相比于SVN等傳統(tǒng)版本控制系統(tǒng)來說,Git是專為分布式版本控制而生的強大工具。使用Git時常用的命令有pull
、commit
、push
等,貌似很簡單。不過,有時你會遇到合并沖突的情況,Git這時會將沖突標(biāo)記出來,需要你手工來解決。有時,你會不小心將代碼提交到錯誤的分支上,并且又推送到了遠程倉庫。還有些時候,你需要切換到不同的分支,但Git卻不讓你這么做,因為還有未保存的修改。如果需要通過另一個分支的提交來為代碼打補丁該怎么做呢?本文就將介紹12個Git高級命令,合理使用這些命令可以大大提升應(yīng)用Git的效率。
1. 使用rebase而非merge來拉取上游修改
分支合并會被記錄為一次合并提交,這種做法是很有意義的。比如說,可以通過這種方式來標(biāo)識一個新特性被合并到了發(fā)布分支中。不過,當(dāng)多個團隊成員工作在一個項目中并使用常規(guī)的git pull
來同步分支時,提交時間線就會被不必要的合并提交所污染。更好的做法則是使用git rebase
將一個feature
分支變基到master
分支:
$ git checkout feature
$ git rebase master
這么做會將整個feature
分支移動到master
分支的起點,它會合并master
分支上所有新的提交。不過,相比于使用合并提交來說,變基會通過在原來的分支中為每次提交創(chuàng)建全新提交來重寫項目歷史。變基的主要好處在于你會得到一個更加整潔的項目歷史。此外,這里還有關(guān)于變基的陷阱的一些討論。
2. 在執(zhí)行g(shù)it rebase后解決合并沖突
正如能力越大責(zé)任就越大一樣。在執(zhí)行git rebase
時,你可能會遇到合并沖突的情況。合并沖突表示兩個提交修改了同一個文件的同一行,Git不知道該應(yīng)用哪一個修改。這會導(dǎo)致如下所示的錯誤消息:
Git會為你提供3個選擇來修復(fù)導(dǎo)致沖突的提交(fa39187):
- 可以運行
git rebase --abort
來完全取消變基。這么做會取消變基修改,并將分支置回到執(zhí)行g(shù)it rebase之前的狀態(tài)。 - 可以運行
git rebase --skip
來完全忽略該提交。這樣,有問題的提交所引入的變化就不會被添加到歷史中。 - 可以使用與合并沖突相同的標(biāo)準(zhǔn)步驟來解決沖突。
3. 臨時性保存修改
在工作進行中時,有些東西常常會處于凌亂的狀態(tài)。如果這時需要切換到不同的分支該怎么辦呢?Git是不允許你這么做的,因為還有尚未保存的修改。坦率地說,你并不想將半成品提交上去,后面再來修改。這個問題的解決之道就是使用git stash
命令。Stash會接收工作目錄的當(dāng)前狀態(tài)(比如說,修改了的追蹤文件與暫存區(qū)的修改等),并將其保存到未完成的修改棧中,這樣后面隨時可以再來修改。可以通過如下命令來暫存你的工作:
$ git stash
Saved working directory and index state WIP on feature: 3fc175f fix race condition
HEAD is now at 3fc175f fix race condition
現(xiàn)在,工作目錄就是干凈的了:
$ git status
# On branch feature
nothing to commit, working directory clean
這時就可以安全地切換分支做別的事情了。不過不必擔(dān)心,暫存的提交依舊還在:
$ git stash list
stash@{0}: WIP on feature: 3fc175f fix race condition
稍后,在回到feature
分支后,你就可以取回所有暫存的變更了:
$ git stash pop
On branch feature
Changes not staged for commit:
(use "git add ..." to update what will be committed)
modified: index.html
Dropped refs/stash@{0} (ac2321cc3a33ba712b8e50c99a99d3c20da9d6b8)
關(guān)于暫存,還有其他一些選項可用,如下所示:
$ git stash save "describe it" # give the stash a name
$ git stash clear # delete a stashed commit
$ git stash save --keep-index # stash only unstaged files
4. 克隆一個特定的遠程分支
如果想要從遠程倉庫中克隆一個特定的分支該怎么做呢?通常你會使用git clone
,不過這么做會將所有其他分支都一并克隆下來。一個便捷的方式是使用git remote add
:
$ git init
$ git remote add -t -f origin
$ git checkout
5. 將cherry-pick遠程提交合并到自己的分支中
更有甚者,如果只想將遠程倉庫的一個特定提交合并到自己的分支中該怎么做呢?可以使用git cherry-pick
來選擇給定SHA
值的提交,然后將其合并到當(dāng)前分支中:
$ git cherry-pick
6. 應(yīng)用來自于不相關(guān)的本地倉庫的補丁
如果需要將另一個不相關(guān)的本地倉庫的提交補丁應(yīng)用到當(dāng)前倉庫該怎么做呢?答案就是下面這條命令:
$ git --git-dir=/.git format-patch -k -1 --stdout | git am -3 -k
7. 忽略追蹤文件中的變更
如果你和你的同事操縱的是相同分支,那么很有可能需要頻繁執(zhí)行git merge
或是git rebase
。不過,這么做可能會重置一些與環(huán)境相關(guān)的配置文件,這樣在每次合并后都需要修改。與之相反,你可以通過如下命令永久性地告訴Git不要管某個本地文件:
$ git update-index --assume-unchanged
8. 每隔X秒運行一次git pull
通常,合并沖突出現(xiàn)的原因在于你正在工作的本地倉庫不再反映遠程倉庫的當(dāng)前狀態(tài)。這正是我們?yōu)槭裁疵刻煸绯恳紫葓?zhí)行一次git pull
的緣故。此外,你還可以在后臺通過腳本(或是使用GNU Screen)每隔X秒調(diào)用一次git pull
:
$ screen
$ for((i=1;i<=10000;i+=1)); do sleep X && git pull; done
9. 將子目錄分隔為新的倉庫
有時,你可能需要將Git倉庫中某個特定的目錄轉(zhuǎn)換為一個全新的倉庫。這可以通過git filter-branch
來實現(xiàn):
$ git filter-branch --prune-empty --subdirectory-filter master
# Filter the master branch to your directory and remove empty commits
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
Ref 'refs/heads/master' was rewritten
現(xiàn)在,倉庫會包含指定子目錄中的所有文件。雖然之前的所有文件都會被刪除,但他們依舊存在于Git歷史中。現(xiàn)在可以將新的本地倉庫推送到遠程了。
10. 清理
有時,Git會提示“untracked working tree files”會“overwritten by checkout”。造成這種情況的原因有很多。不過通常來說,我們可以使用如下命令來保持工作樹的整潔,從而防止這種情況的發(fā)生:
$ git clean -f # remove untracked files
$ git clean -fd # remove untracked files/directories
$ git clean -nfd # list all files/directories that would be removed
11. 將項目文件打成tar包,并且排除.git目錄
有時,你需要將項目副本提供給無法訪問GitHub倉庫的外部成員。最簡單的方式就是使用tar
或zip
來打包所有的項目文件。不過,如果不小心,隱藏的.git
目錄就會包含到tar文件中,這會導(dǎo)致文件體積變大;同時,如果里面的文件與接收者自己的Git倉庫弄混了,那就更加令人頭疼了。輕松的做法則是自動從tar
文件中排除掉.git
目錄:
$ tar cJf.tar.xz/ --exclude-vcs
12. 查找修改者
最后,如果出現(xiàn)混亂的情況,你一定想要找出是誰造成的。如果生產(chǎn)服務(wù)器宕機,那么找到罪魁禍?zhǔn)资潜容^容易的事情:只需執(zhí)行git blame
。該命令會顯示出文件中每一行的作者,提交hash
則會找出該行的上一次修改,還能看到提交的時間戳:
$ git blame
當(dāng)然,Git命令是非常多的,除了上面介紹的12個重要命令外,相信各位InfoQ讀者在日常工作過程中也有自己偏愛且好用的一些命令,不妨以評論的形式與其他讀者一同分享。