git的使用進階

基本技巧

1. 安裝後的第一步

在安裝好git後,你第一件該作的事是設置你的名字和電子郵箱,由於每次提交都要用到這些信息:html

$ git config --global user.name "Some One"
$ git config --global user.email "someone@Gmail.com"

2. Git是基於指針的

保存在git裏的一切都是文件。當你建立一個提交的時候,會創建一個包含你的提交信息和相關數據(名字,郵件地址,日期/時間,前一個提交,等等)的文件,並把它連接到一個樹文件中。這個樹文件中包含了對象或其餘樹的列表。這裏的提到的對象(或二進制大對象)是和本次提交相關的實際內容(它也是一個文件,另外,儘管文件名並無包含在對象裏,可是存儲在樹中)。全部這些文件都使用對象的SHA-1哈希值做爲文件名。git

用這種方式,分支和標籤就是簡單的文件(基本上是這樣),包含指向該提交的SHA-1哈希值。使用這些索引會帶來優秀的靈活性和速度,好比建立一個新分支就是簡單地用分支名字和所分出的那個提交的SHA-1索引來建立一個文件。固然,你不須要本身作這些,而只要使用Git命令行工具(或者GUI),可是實際上就是這麼簡單。github

你也許據說過叫HEAD的索引。這只是簡單的一個文件,包含了你當前指向的那個提交的SHA-1索引值。若是你正在解決一次合併衝忽然後看到了HEAD,這並非一個特別的分支或分支上的一個必需的特殊位置,只是標明你當前所在位置。sql

全部的分支指針都保存在.git/refs/heads裏,HEAD在.git/HEAD裏,而標籤保存在.git/refs/tags裏 – 本身能夠隨便進去看看。數據庫

3. 兩個爸爸(父節點) – 你沒看錯!

在歷史中查看一個合併提交的信息時,你將看到有兩個父節點(不一樣於工做副本上的常規提交的狀況)。第一個父節點是你所在的分支,第二個是你合併過來的分支。服務器

4. 合併衝突

目前我相信你碰到過合併衝突而且解決過。一般是編輯一下文件,去掉<<<<,====,>>>>標誌,保留須要留下的代碼。有時可以看到這兩個修改以前的代碼會很不錯,好比,在這兩個如今衝突的分支以前的改動。下面是一種方式:app

$ git diff --merge
diff --cc dummy.rb  
index 5175dde,0c65895..4a00477  
--- a/dummy.rb
+++ b/dummy.rb
@@@ -1,5 -1,5 +1,5 @@@
  class MyFoo
    def say
-     puts "Bonjour"
 -    puts "Hello world"
++    puts "Annyong Haseyo"
    end
  end

若是是二進制文件,比較差別就沒那麼簡單了&hellip;一般你要作的就是測試這個二進制文件的兩個版原本決定保留哪一個(或者在二進制文件編輯器裏手工複製衝突部分)。從一個特定分支獲取文件拷貝(好比說你在合併master和feature123兩個分支):編輯器

$ git checkout master Flash/foo.fla # 或者...
$ git checkout feature132 flash/foo.fla
$ # 而後...
$ git add flash/foo.fla

另外一種方式是經過git輸出文件 – 你能夠輸出到另外的文件名,而後當你決定了要用哪一個後,再將選定的正確文件複製爲正常的文件名:ide

$ git show master:flash/foo.fla > master-foo.fla
$ git show feature132:flash/foo.fla > feature132-foo.fla
$ # 檢出master-foo.fla和feature132-foo.fla
$ # 假如說咱們決定來自feature132的文件是正確的
$ rm flash/foo.fla
$ mv feature132-foo.fla flash/foo.fla
$ rm master-foo.fla
$ git add flash/foo.fla

更新:感謝Carl在原博客文章上評論裏的提醒,你實際上能夠用「git checkout —ours flash/foo.fla」和「git checkout —theirs flash/foo.fla」來檢出特定版本的文件,而不用記住你在合併的分支名字。就我我的來講喜歡更精確一點,但這也是一種方式…函數

記着在解決完衝突後要將文件加入提交(像我上面作的那樣)。

服務器,分支和標籤

5. 遠端服務器

git的一個超強大的功能就是能夠有不止一個遠端服務器(實際上你一直都在一個本地倉庫上工做)。你並非必定都要有這些服務器的寫權限,你能夠有多個能夠讀取的服務器(用來合併他們的工做)而後寫入到另一個倉庫。添加一個新的遠端服務器很簡單:

$ git remote add john git@github.com:johnsomeone/somePRoject.git

若是你想查看遠端服務器的信息能夠這樣作:

# 顯示每一個遠端服務器的URL
$ git remote -v 

# 提供更多詳細信息
$ git remote show name

你隨時均可以查看本地分支和遠端分支的差別:

$ git diff master..john/master

你也能夠查看沒有在遠端分支上的HEAD的改動:

$ git log remote/branch..
# 注意:..後面沒有結束的特定引用

6. 標籤

在git裏有兩種類型的標籤 – 輕量級標籤和帶註釋標籤。記住技巧2裏說過git是基於指針的,這二者之間的差別也很簡單。輕量級標籤只是一個簡單的指向一次提交的帶名字指針。你隨時均可以將它指向另外一個提交。帶註釋標籤是一個指向標籤對象的帶名字指針,帶有本身的信息和歷史。由於有本身的信息,它能夠根據須要用GPG簽名。

創建這兩種類型的標籤都很簡單(只有一個命令行開關的差別)

$ git tag to-be-tested
$ git tag -a v1.1.0 # 會提示輸入標籤的信息

7. 創建分支

在git裏創建分支很是簡單(並且像閃電同樣快,由於它只須要建立一個小於100字節的文件)。用普通方式創建新分支並切換過去:

$ git branch feature132
$ git checkout feature132

固然,若是你肯定本身直接切換到新建的分支,能夠用一個命令實現:

$ git checkout -b feature132

若是你想重命名一個本地分支也很簡單(能夠顯示發生了什麼的較長的方式):

$ git checkout -b twitter-experiment feature132
$ git branch -d feature132

更新:你也能夠(像Brian Palmer在原博客文章的評論裏提出的)只用「git branch」的-m開關在一個命令裏實現(像Mike提出的,若是你只指定了一個分支參數,就會重命名當前分支):

$ git branch -m twitter-experiment
$ git branch -m feature132 twitter-experiment

8. 合併分支

也許在未來的某個時候,你但願將改動合併。有兩種方式:

$ git checkout master
$ git merge feature83 # 或者...
$ git rebase feature83

merge和rebase之間的差異是merge會嘗試處理改動並創建一個新的混合了二者的提交。rebase會嘗試把你從一個分支最後一次分離後的全部改動,一個個加到該分支的HEAD上。不過,在已經將分支推到遠端服務器後不要再rebase了 – 這會引發衝突/問題。

若是你不肯定在哪些分支上還有獨有的工做 – 因此你也不知道哪些分支須要合併而哪些能夠刪除,git branch有兩個開關能夠幫你:

# 顯示已經所有合併到當前分支的分支
$ git branch --merged

# 顯示沒有合併到當前分支的分支
$ git branch --no-merged

9. 遠端分支

若是你在本地有一個分支但願推到遠端服務器上,你能夠用一行命令推送上去:

$ git push origin twitter-experiment:refs/heads/twitter-experiment
# origin是咱們服務器的名字,而twitter-experiment是分支名字

更新:感謝Erlend在原博客文章上的評論 – 這個實際上和git push origin twitter-experiment效果同樣,不過使用完整的語法,你能夠在二者之間使用不一樣的分支名(這樣本地分支能夠是add-ssl-support而遠端是issue-1723)。

若是你想在遠端服務器上刪除一個分支(注意分支名前面的冒號):

$ git push origin :twitter-experiment

若是你想查看全部遠端分支的狀態能夠這樣作:

$ git remote show origin

這個命令可能會列出服務器上一些之前有過但如今已經不在了的分支。若是碰到這種狀況你能夠用下面的命令從你本地分支裏清理掉:

$ git remote prune

最後,若是你想在本地跟蹤一個遠端分支,普通的方式是:

$ git branch --track myfeature origin/myfeature
$ git checkout myfeature

不過,新版的git在使用-b標記檢出分支時會自動設定跟蹤:

$ git checkout -b myfeature origin/myfeature

在儲藏點,索引和文件系統中保存內容

10. 儲藏

在git裏你能夠把當前工做狀態放進一個儲藏堆棧中,而後能夠再取出來。最簡單的情形是下面這樣:

$ git stash
# 作點其餘事情...
$ git stash pop

許多人建議使用git stash apply來代替pop,不過若是這樣作的話最後會遺留一個很長的儲藏列表。而「pop」會在所有加載後自動從堆棧中移除。若是使用過git stash apply,你也可使用下面的命令從堆棧上移除最後一項:

$ git stash drop

git會基於當前的提交信息自動建立評論。若是你更但願有自定義信息的話(由於它可能和前一個提交沒有任何聯繫):

$ git stash save "My stash message"

若是你但願從列表中取出一個特定的儲藏點(不必定非得是最後一個)能夠先列出它們而後用下面的方式取出:

$ git stash list
  stash@{0}: On master: Changed to German
  stash@{1}: On master: Language is now Italian
$ git stash apply stash@{1}

11. 交互式添加

在subversion的世界裏你只能修改文件而後提交全部改動。而在git裏你有強大得多的方式來提交部分文件或者甚至是部分補丁。提交部分文件或文件中的部分改動你須要進入交互式模式:

$ git add -i
           staged     unstaged path

*** Commands ***
  1: status      2: update   3: revert   4: add untracked
  5: patch      6: diff     7: quit     8: help
What now>

這會讓你進入一個基於菜單的交互式提示。你可使用命令中的數字或高亮的字母(若是你在終端裏打開了高亮的話)來進入相應的模式。而後就只是輸入你但願操做的文件的數字了(你可使用這樣的格式,1或者1-4或2,4,7)。

若是你想進入補丁模式(交互式模式下按‘p’或‘5’),你也能夠直接進入:

$ git add -p    
diff --git a/dummy.rb b/dummy.rb  
index 4a00477..f856fb0 100644  
--- a/dummy.rb
+++ b/dummy.rb
@@ -1,5 +1,5 @@
 class MyFoo
   def say
-    puts "Annyong Haseyo"
+    puts "Guten Tag"
   end
 end
Stage this hunk [y,n,q,a,d,/,e,?]?

你能夠看到下方會有一些選項供選擇用來添加該文件的這個改動、該文件的全部改動,等等。使用‘?’命令能夠詳細解釋這些選項。

12. 從文件系統裏保存/取回改動

有些項目(好比Git項目自己)在git文件系統中直接保存額外文件而並無將它們加入到版本控制中。

讓咱們從在git中存儲一個隨機文件開始:

$ echo "Foo" | git hash-object -w --stdin
51fc03a9bb365fae74fd2bf66517b30bf48020cb

這樣這個目標文件就已經保存到數據庫中了,可是若是你沒有設定一個指向它的指針的話它會被當作垃圾回收。最簡單的方式是設定一個標籤:

$ git tag myfile 51fc03a9bb365fae74fd2bf66517b30bf48020cb

注意這裏咱們使用了標籤myfile。當咱們須要使用這個文件的時候能夠這樣作:

$ git cat-file blob myfile

這個對於一些工具文件頗有用,開發者可能會用到(密碼,GPG密鑰,等等)可是又不但願每次都檢出到硬盤(尤爲是在實際工做中)。

日誌以及有哪些改動?

13. 查看日誌

長時間使用 Git 的話,不會沒用過‘git log’來查看最近的提交。不過,有一些技巧來更好地應用。好比,你可使用下面的命令來查看每次提交的具體改動:

$ git log -p

或者你能夠僅僅查看有哪些文件改動:

$ git log --stat

有個很不錯的別名你能夠試試,會顯示簡短提交名和一個不錯的分支圖並在一行裏顯示提交信息(有點像gitk,可是是在命令行下):

$ git config --global alias.lol "log --pretty=oneline --abbrev-commit --graph --decorate"
$ git lol
* 4d2409a (master) Oops, meant that to be in Korean
* 169b845 Hello world

14. 搜索日誌

若是你想找特定提交者能夠這樣作:

$ git log --author=Andy

更新:感謝Johannes的評論,我已經去掉了以前這裏的一些有混淆的地方。

或者你想在提交信息裏找一些相關字段:

$ git log --grep="Something in the message"

也有一個更強大的叫作pickaxe的命令用來查找包含了刪除或添加的某個特定內容的提交(好比,該內容第一次出現或被刪除)。這能夠告訴你何時增長了一行(但這一行裏的某個字符後面被改動過就不行了):

$ git log -S "TODO: Check for admin status"

假如你改動了一個特定的文件,好比lib/foo.rb

$ git log lib/foo.rb

好比說你有一個feature/132分支和feature/145分支,而後你想看看這兩個分支上不在master分支裏的提交(注意符號是不在的意思):

$ git log feature/132 feature/145 ^master

你也可使用ActiveSupport格式的日期來縮小到某個日期範圍:

$ git log --since=2.months.ago --until=1.day.ago

默認狀況下會用OR來組合查詢,但你能夠輕易地改成AND(若是你有超過一條的查詢標準)

$ git log --since=2.months.ago --until=1.day.ago --author=andy -S "something" --all-match

15. 查看/修改版本

有不少方式能夠用來引用一個版本,看你記得哪一個:

$ git show 12a86bc38 # 根據版本
$ git show v1.0.1 # 根據標籤
$ git show feature132 # 根據分支名
$ git show 12a86bc38^ # 一次提交的父節點
$ git show 12a86bc38~2 # 一次提交的祖父節點
$ git show feature132@{yesterday} # 時間相關
$ git show feature132@{2.hours.ago} # 時間相關

注意和以前部分有些不一樣,末尾的的意思是該提交的父節點 – 開始位置的的意思是不在這個分支。

16. 選擇範圍

最簡單的方式:

$ git log origin/master..new
# [old]..[new] - 全部你尚未推送的提交

你也能夠省略[new],將使用當前的HEAD。

時光回溯和後悔藥

17. 重置改動

若是你尚未提交的話能夠用下面的命令輕鬆地取消改動:

$ git reset HEAD lib/foo.rb

一般會使用‘unstage’的別名,由於上面的看上去有些不直觀。

$ git config --global alias.unstage "reset HEAD"
$ git unstage lib/foo.rb

若是你已經提交了該文件,你能夠作兩件事 – 若是是最後一次提交你還能夠改正:

$ git commit --amend

這會取消最後一次提交,把工做分支回退到提交前標記了全部改動的狀態,並且提交信息也都準備好能夠修改或直接提交。

若是你已經提交過屢次並且但願所有回退,你能夠將分支重置到合適的位置。

$ git checkout feature132
$ git reset --hard HEAD~2

若是你實際上但願將分支指向一個徹底不一樣的SHA1(也許你要將一個分支的HEAD替換到另外一個分支,或者以後的某次提交)你可使用下面的較長的方式:

$ git checkout FOO
$ git reset --hard SHA

實際上有一個快速的方式(不須要先把你的工做分支切換到FOO再前進到SHA):

$ git update-ref refs/heads/FOO SHA

18. 提交到了錯誤的分支

好吧,假如說你已經提交到了master,但卻應該建立一個叫experimental的主題分支更合適。要移動這些改動,你能夠在當前位置建立分支,回退HEAD再檢出新分支:

$ git branch experimental   # 建立一個指向當前master的位置的指針
$ git reset --hard master~3 # 移動master分支的指針到3個版本以前
$ git checkout experimental

若是你的改動是在分支的分支的分支上會更復雜。那樣你須要作的是將分支基礎切換到其餘地方:

$ git branch newtopic STARTPOINT
$ git rebase oldtopic --onto newtopic

19. 交互式切換基礎

這是一個我以前看過展現卻沒真正理解過的很讚的功能,如今以爲它就很簡單了。假如說你提交了3次可是你但願更改順序或編輯(或者合併):

$ git rebase -i master~3

而後這會啓動你的編輯器並帶有一些指令。你所要作的就是修改這些指令來選擇/插入/編輯(或者刪除)提交和保存/退出。而後在編輯完後你能夠用git rebase –continue命令來讓每一條指令生效。

若是你有修改,將會切換到你提交時所處的狀態,以後你須要使用命令git commit –amend來編輯。

注意:在rebase的時候千萬不要提交 – 只能先添加而後使用參數–continue,–skip或–abort。

20. 清理

若是你提交了一些內容到你的分支(也許你從SVN導入了一些舊倉庫),而後你但願把某個文件從歷史記錄中所有刪掉:

$ git filter-branch --tree-filter 'rm -f *.class' HEAD

若是你已經推送到origin了,但以後提交了一些垃圾改動,你也能夠在推送前在本地系統裏這樣作:

$ git filter-branch --tree-filter 'rm -f *.class' origin/master..HEAD

其餘技巧

21. 你查看過的前一個引用

若是你知道本身以前查看過一個SHA-1,可是隨後作了一些重置/回退的操做,你可使用reflog命令來列出最近查看過的SHA-1記錄:

$ git reflog
$ git log -g # 和上面同樣,可是使用'log'格式輸出

22. 分支命名

一個可愛的小技巧 – 別忘了分支名並不限於a-z和0-9。名字中能夠用/和.將很是方便用來創建僞命名空間或版本,例如:

$ # 生成版本132的改動歷史
$ git shortlog release/132 ^release/131
$ # 貼上v1.0.1的標籤
$ git tag v1.0.1 release/132

23. 找出誰是兇手

一般找出來誰改動了某個文件裏的某行代碼會頗有用。實現這個功能的最簡單命令是:

$ git blame FILE

有時候這些改動來自其餘文件(若是你合併了兩個文件,或者你移動了某個函數)因此你可使用下面的命令:

$ # 顯示內容來自哪一個文件
$ git blame -C FILE

有時候經過點擊各個改動而後回到很早很早之前來跟蹤改動會很不錯。有一個很好的內建GUI命令來作這個:

$ git gui blame FILE

24. 數據維護

一般git不須要常常維護,它把本身照顧的很好。不過,你能夠經過下面的命令查看數據統計:

$ git count-objects -v

若是佔用不少空間的話,你能夠選擇在你的本地倉庫作垃圾回收。這不會影響推送或其餘人,卻會讓一些命令運行更快並且減小空間佔用:

$ git gc

常常運行完整性檢查也頗有意義:

$ git fsck --full

你也能夠在末尾加上–auto參數(若是你在服務器上經過crontab常常/天天都運行這個命令的話),而後它只會在必要的時候才執行fsck動做。

在檢查的時候,看到「dangling」或「unreachable」是正常的,一般這是由回退HEAD或切換基礎的結果。而看到「missing」或「sha1 mismatch」就不對了…找專業人士幫忙吧!

25. 恢復遺失的分支

若是你使用-D參數刪除了experimental分支,能夠用下面的命令從新創建:

$ git branch experimental SHA1_OF_HASH

若是你最近訪問過的話,你一般能夠用git reflog來找到SHA1哈希值。

另外一種方式是使用git fsck —lost-found。其中一個dangling的提交就是丟失的HEAD(它只是已刪除分支的HEAD,而HEAD被引用爲當前的HEAD因此它並不處於dangling狀態)

原文連接:

我已經使用git差很少18個月了,以爲本身對它應該已經很是瞭解。而後來自GitHub的Scott Chacon過來給LVS作培訓,而我在第一天裏就學到了不少。

做爲一個對git感受良好的人,我以爲分享從社區裏掌握的一些有價值的信息,也許能幫某人解決問題而不用作太深刻研究。

25個 Git 進階技巧

基本技巧

1. 安裝後的第一步

在安裝好git後,你第一件該作的事是設置你的名字和電子郵箱,由於每次提交都要用到這些信息:

$ git config --global user.name "Some One"
$ git config --global user.email "someone@gmail.com"

2. Git是基於指針的

保存在git裏的一切都是文件。當你建立一個提交的時候,會創建一個包含你的提交信息和相關數據(名字,郵件地址,日期/時間,前一個提交,等等)的文件,並把它連接到一個樹文件中。這個樹文件中包含了對象或其餘樹的列表。這裏的提到的對象(或二進制大對象)是和本次提交相關的實際內容(它也是一個文件,另外,儘管文件名並無包含在對象裏,可是存儲在樹中)。全部這些文件都使用對象的SHA-1哈希值做爲文件名。

用這種方式,分支和標籤就是簡單的文件(基本上是這樣),包含指向該提交的SHA-1哈希值。使用這些索引會帶來優秀的靈活性和速度,好比建立一個新分支就是簡單地用分支名字和所分出的那個提交的SHA-1索引來建立一個文件。固然,你不須要本身作這些,而只要使用Git命令行工具(或者GUI),可是實際上就是這麼簡單。

你也許據說過叫HEAD的索引。這只是簡單的一個文件,包含了你當前指向的那個提交的SHA-1索引值。若是你正在解決一次合併衝忽然後看到了HEAD,這並非一個特別的分支或分支上的一個必需的特殊位置,只是標明你當前所在位置。

全部的分支指針都保存在.git/refs/heads裏,HEAD在.git/HEAD裏,而標籤保存在.git/refs/tags裏 – 本身能夠隨便進去看看。

3. 兩個爸爸(父節點) – 你沒看錯!

在歷史中查看一個合併提交的信息時,你將看到有兩個父節點(不一樣於工做副本上的常規提交的狀況)。第一個父節點是你所在的分支,第二個是你合併過來的分支。

4. 合併衝突

目前我相信你碰到過合併衝突而且解決過。一般是編輯一下文件,去掉<<<<,====,>>>>標誌,保留須要留下的代碼。有時可以看到這兩個修改以前的代碼會很不錯,好比,在這兩個如今衝突的分支以前的改動。下面是一種方式:

$ git diff --merge
diff --cc dummy.rb  
index 5175dde,0c65895..4a00477  
--- a/dummy.rb
+++ b/dummy.rb
@@@ -1,5 -1,5 +1,5 @@@
  class MyFoo
    def say
-     puts "Bonjour"
 -    puts "Hello world"
++    puts "Annyong Haseyo"
    end
  end

若是是二進制文件,比較差別就沒那麼簡單了…一般你要作的就是測試這個二進制文件的兩個版原本決定保留哪一個(或者在二進制文件編輯器裏手工複製衝突部分)。從一個特定分支獲取文件拷貝(好比說你在合併master和feature123兩個分支):

$ git checkout master flash/foo.fla # 或者...
$ git checkout feature132 flash/foo.fla
$ # 而後...
$ git add flash/foo.fla

另外一種方式是經過git輸出文件 – 你能夠輸出到另外的文件名,而後當你決定了要用哪一個後,再將選定的正確文件複製爲正常的文件名:

$ git show master:flash/foo.fla > master-foo.fla
$ git show feature132:flash/foo.fla > feature132-foo.fla
$ # 檢出master-foo.fla和feature132-foo.fla
$ # 假如說咱們決定來自feature132的文件是正確的
$ rm flash/foo.fla
$ mv feature132-foo.fla flash/foo.fla
$ rm master-foo.fla
$ git add flash/foo.fla

更新:感謝Carl在原博客文章上評論裏的提醒,你實際上能夠用「git checkout —ours flash/foo.fla」和「git checkout —theirs flash/foo.fla」來檢出特定版本的文件,而不用記住你在合併的分支名字。就我我的來講喜歡更精確一點,但這也是一種方式…

記着在解決完衝突後要將文件加入提交(像我上面作的那樣)。

服務器,分支和標籤

5. 遠端服務器

git的一個超強大的功能就是能夠有不止一個遠端服務器(實際上你一直都在一個本地倉庫上工做)。你並非必定都要有這些服務器的寫權限,你能夠有多個能夠讀取的服務器(用來合併他們的工做)而後寫入到另一個倉庫。添加一個新的遠端服務器很簡單:

$ git remote add john git@github.com:johnsomeone/someproject.git

若是你想查看遠端服務器的信息能夠這樣作:

# 顯示每一個遠端服務器的URL
$ git remote -v 

# 提供更多詳細信息
$ git remote show name

你隨時均可以查看本地分支和遠端分支的差別:

$ git diff master..john/master

你也能夠查看沒有在遠端分支上的HEAD的改動:

$ git log remote/branch..
# 注意:..後面沒有結束的特定引用

6. 標籤

在git裏有兩種類型的標籤 – 輕量級標籤和帶註釋標籤。記住技巧2裏說過git是基於指針的,這二者之間的差別也很簡單。輕量級標籤只是一個簡單的指向一次提交的帶名字指針。你隨時均可以將它指向另外一個提交。帶註釋標籤是一個指向標籤對象的帶名字指針,帶有本身的信息和歷史。由於有本身的信息,它能夠根據須要用GPG簽名。

創建這兩種類型的標籤都很簡單(只有一個命令行開關的差別)

$ git tag to-be-tested
$ git tag -a v1.1.0 # 會提示輸入標籤的信息

7. 創建分支

在git裏創建分支很是簡單(並且像閃電同樣快,由於它只須要建立一個小於100字節的文件)。用普通方式創建新分支並切換過去:

$ git branch feature132
$ git checkout feature132

固然,若是你肯定本身直接切換到新建的分支,能夠用一個命令實現:

$ git checkout -b feature132

若是你想重命名一個本地分支也很簡單(能夠顯示發生了什麼的較長的方式):

$ git checkout -b twitter-experiment feature132
$ git branch -d feature132

更新:你也能夠(像Brian Palmer在原博客文章的評論裏提出的)只用「git branch」的-m開關在一個命令裏實現(像Mike提出的,若是你只指定了一個分支參數,就會重命名當前分支):

$ git branch -m twitter-experiment
$ git branch -m feature132 twitter-experiment

8. 合併分支

也許在未來的某個時候,你但願將改動合併。有兩種方式:

$ git checkout master
$ git merge feature83 # 或者...
$ git rebase feature83

merge和rebase之間的差異是merge會嘗試處理改動並創建一個新的混合了二者的提交。rebase會嘗試把你從一個分支最後一次分離後的全部改動,一個個加到該分支的HEAD上。不過,在已經將分支推到遠端服務器後不要再rebase了 – 這會引發衝突/問題。

若是你不肯定在哪些分支上還有獨有的工做 – 因此你也不知道哪些分支須要合併而哪些能夠刪除,git branch有兩個開關能夠幫你:

# 顯示已經所有合併到當前分支的分支
$ git branch --merged

# 顯示沒有合併到當前分支的分支
$ git branch --no-merged

9. 遠端分支

若是你在本地有一個分支但願推到遠端服務器上,你能夠用一行命令推送上去:

$ git push origin twitter-experiment:refs/heads/twitter-experiment
# origin是咱們服務器的名字,而twitter-experiment是分支名字

更新:感謝Erlend在原博客文章上的評論 – 這個實際上和git push origin twitter-experiment效果同樣,不過使用完整的語法,你能夠在二者之間使用不一樣的分支名(這樣本地分支能夠是add-ssl-support而遠端是issue-1723)。

若是你想在遠端服務器上刪除一個分支(注意分支名前面的冒號):

$ git push origin :twitter-experiment

若是你想查看全部遠端分支的狀態能夠這樣作:

$ git remote show origin

這個命令可能會列出服務器上一些之前有過但如今已經不在了的分支。若是碰到這種狀況你能夠用下面的命令從你本地分支裏清理掉:

$ git remote prune

最後,若是你想在本地跟蹤一個遠端分支,普通的方式是:

$ git branch --track myfeature origin/myfeature
$ git checkout myfeature

不過,新版的git在使用-b標記檢出分支時會自動設定跟蹤:

$ git checkout -b myfeature origin/myfeature

在儲藏點,索引和文件系統中保存內容

10. 儲藏

在git裏你能夠把當前工做狀態放進一個儲藏堆棧中,而後能夠再取出來。最簡單的情形是下面這樣:

$ git stash
# 作點其餘事情...
$ git stash pop

許多人建議使用git stash apply來代替pop,不過若是這樣作的話最後會遺留一個很長的儲藏列表。而「pop」會在所有加載後自動從堆棧中移除。若是使用過git stash apply,你也可使用下面的命令從堆棧上移除最後一項:

$ git stash drop

git會基於當前的提交信息自動建立評論。若是你更但願有自定義信息的話(由於它可能和前一個提交沒有任何聯繫):

$ git stash save "My stash message"

若是你但願從列表中取出一個特定的儲藏點(不必定非得是最後一個)能夠先列出它們而後用下面的方式取出:

$ git stash list
  stash@{0}: On master: Changed to German
  stash@{1}: On master: Language is now Italian
$ git stash apply stash@{1}

11. 交互式添加

在subversion的世界裏你只能修改文件而後提交全部改動。而在git裏你有強大得多的方式來提交部分文件或者甚至是部分補丁。提交部分文件或文件中的部分改動你須要進入交互式模式:

$ git add -i
           staged     unstaged path

*** Commands ***
  1: status      2: update   3: revert   4: add untracked
  5: patch      6: diff     7: quit     8: help
What now>

這會讓你進入一個基於菜單的交互式提示。你可使用命令中的數字或高亮的字母(若是你在終端裏打開了高亮的話)來進入相應的模式。而後就只是輸入你但願操做的文件的數字了(你可使用這樣的格式,1或者1-4或2,4,7)。

若是你想進入補丁模式(交互式模式下按‘p’或‘5’),你也能夠直接進入:

$ git add -p    
diff --git a/dummy.rb b/dummy.rb  
index 4a00477..f856fb0 100644  
--- a/dummy.rb
+++ b/dummy.rb
@@ -1,5 +1,5 @@
 class MyFoo
   def say
-    puts "Annyong Haseyo"
+    puts "Guten Tag"
   end
 end
Stage this hunk [y,n,q,a,d,/,e,?]?

你能夠看到下方會有一些選項供選擇用來添加該文件的這個改動、該文件的全部改動,等等。使用‘?’命令能夠詳細解釋這些選項。

12. 從文件系統裏保存/取回改動

有些項目(好比Git項目自己)在git文件系統中直接保存額外文件而並無將它們加入到版本控制中。

讓咱們從在git中存儲一個隨機文件開始:

$ echo "Foo" | git hash-object -w --stdin
51fc03a9bb365fae74fd2bf66517b30bf48020cb

這樣這個目標文件就已經保存到數據庫中了,可是若是你沒有設定一個指向它的指針的話它會被當作垃圾回收。最簡單的方式是設定一個標籤:

$ git tag myfile 51fc03a9bb365fae74fd2bf66517b30bf48020cb

注意這裏咱們使用了標籤myfile。當咱們須要使用這個文件的時候能夠這樣作:

$ git cat-file blob myfile

這個對於一些工具文件頗有用,開發者可能會用到(密碼,GPG密鑰,等等)可是又不但願每次都檢出到硬盤(尤爲是在實際工做中)。

日誌以及有哪些改動?

13. 查看日誌

長時間使用 Git 的話,不會沒用過‘git log’來查看最近的提交。不過,有一些技巧來更好地應用。好比,你可使用下面的命令來查看每次提交的具體改動:

$ git log -p

或者你能夠僅僅查看有哪些文件改動:

$ git log --stat

有個很不錯的別名你能夠試試,會顯示簡短提交名和一個不錯的分支圖並在一行裏顯示提交信息(有點像gitk,可是是在命令行下):

$ git config --global alias.lol "log --pretty=oneline --abbrev-commit --graph --decorate"
$ git lol
* 4d2409a (master) Oops, meant that to be in Korean
* 169b845 Hello world

14. 搜索日誌

若是你想找特定提交者能夠這樣作:

$ git log --author=Andy

更新:感謝Johannes的評論,我已經去掉了以前這裏的一些有混淆的地方。

或者你想在提交信息裏找一些相關字段:

$ git log --grep="Something in the message"

也有一個更強大的叫作pickaxe的命令用來查找包含了刪除或添加的某個特定內容的提交(好比,該內容第一次出現或被刪除)。這能夠告訴你何時增長了一行(但這一行裏的某個字符後面被改動過就不行了):

$ git log -S "TODO: Check for admin status"

假如你改動了一個特定的文件,好比lib/foo.rb

$ git log lib/foo.rb

好比說你有一個feature/132分支和feature/145分支,而後你想看看這兩個分支上不在master分支裏的提交(注意符號是不在的意思):

$ git log feature/132 feature/145 ^master

你也可使用ActiveSupport格式的日期來縮小到某個日期範圍:

$ git log --since=2.months.ago --until=1.day.ago

默認狀況下會用OR來組合查詢,但你能夠輕易地改成AND(若是你有超過一條的查詢標準)

$ git log --since=2.months.ago --until=1.day.ago --author=andy -S "something" --all-match

15. 查看/修改版本

有不少方式能夠用來引用一個版本,看你記得哪一個:

$ git show 12a86bc38 # 根據版本
$ git show v1.0.1 # 根據標籤
$ git show feature132 # 根據分支名
$ git show 12a86bc38^ # 一次提交的父節點
$ git show 12a86bc38~2 # 一次提交的祖父節點
$ git show feature132@{yesterday} # 時間相關
$ git show feature132@{2.hours.ago} # 時間相關

注意和以前部分有些不一樣,末尾的的意思是該提交的父節點 – 開始位置的的意思是不在這個分支。

16. 選擇範圍

最簡單的方式:

$ git log origin/master..new
# [old]..[new] - 全部你尚未推送的提交

你也能夠省略[new],將使用當前的HEAD。

時光回溯和後悔藥

17. 重置改動

若是你尚未提交的話能夠用下面的命令輕鬆地取消改動:

$ git reset HEAD lib/foo.rb

一般會使用‘unstage’的別名,由於上面的看上去有些不直觀。

$ git config --global alias.unstage "reset HEAD"
$ git unstage lib/foo.rb

若是你已經提交了該文件,你能夠作兩件事 – 若是是最後一次提交你還能夠改正:

$ git commit --amend

這會取消最後一次提交,把工做分支回退到提交前標記了全部改動的狀態,並且提交信息也都準備好能夠修改或直接提交。

若是你已經提交過屢次並且但願所有回退,你能夠將分支重置到合適的位置。

$ git checkout feature132
$ git reset --hard HEAD~2

若是你實際上但願將分支指向一個徹底不一樣的SHA1(也許你要將一個分支的HEAD替換到另外一個分支,或者以後的某次提交)你可使用下面的較長的方式:

$ git checkout FOO
$ git reset --hard SHA

實際上有一個快速的方式(不須要先把你的工做分支切換到FOO再前進到SHA):

$ git update-ref refs/heads/FOO SHA

18. 提交到了錯誤的分支

好吧,假如說你已經提交到了master,但卻應該建立一個叫experimental的主題分支更合適。要移動這些改動,你能夠在當前位置建立分支,回退HEAD再檢出新分支:

$ git branch experimental   # 建立一個指向當前master的位置的指針
$ git reset --hard master~3 # 移動master分支的指針到3個版本以前
$ git checkout experimental

若是你的改動是在分支的分支的分支上會更復雜。那樣你須要作的是將分支基礎切換到其餘地方:

$ git branch newtopic STARTPOINT
$ git rebase oldtopic --onto newtopic

19. 交互式切換基礎

這是一個我以前看過展現卻沒真正理解過的很讚的功能,如今以爲它就很簡單了。假如說你提交了3次可是你但願更改順序或編輯(或者合併):

$ git rebase -i master~3

而後這會啓動你的編輯器並帶有一些指令。你所要作的就是修改這些指令來選擇/插入/編輯(或者刪除)提交和保存/退出。而後在編輯完後你能夠用git rebase –continue命令來讓每一條指令生效。

若是你有修改,將會切換到你提交時所處的狀態,以後你須要使用命令git commit –amend來編輯。

注意:在rebase的時候千萬不要提交 – 只能先添加而後使用參數–continue,–skip或–abort。

20. 清理

若是你提交了一些內容到你的分支(也許你從SVN導入了一些舊倉庫),而後你但願把某個文件從歷史記錄中所有刪掉:

$ git filter-branch --tree-filter 'rm -f *.class' HEAD

若是你已經推送到origin了,但以後提交了一些垃圾改動,你也能夠在推送前在本地系統裏這樣作:

$ git filter-branch --tree-filter 'rm -f *.class' origin/master..HEAD

其餘技巧

21. 你查看過的前一個引用

若是你知道本身以前查看過一個SHA-1,可是隨後作了一些重置/回退的操做,你可使用reflog命令來列出最近查看過的SHA-1記錄:

$ git reflog
$ git log -g # 和上面同樣,可是使用'log'格式輸出

22. 分支命名

一個可愛的小技巧 – 別忘了分支名並不限於a-z和0-9。名字中能夠用/和.將很是方便用來創建僞命名空間或版本,例如:

$ # 生成版本132的改動歷史
$ git shortlog release/132 ^release/131
$ # 貼上v1.0.1的標籤
$ git tag v1.0.1 release/132

23. 找出誰是兇手

一般找出來誰改動了某個文件裏的某行代碼會頗有用。實現這個功能的最簡單命令是:

$ git blame FILE

有時候這些改動來自其餘文件(若是你合併了兩個文件,或者你移動了某個函數)因此你可使用下面的命令:

$ # 顯示內容來自哪一個文件
$ git blame -C FILE

有時候經過點擊各個改動而後回到很早很早之前來跟蹤改動會很不錯。有一個很好的內建GUI命令來作這個:

$ git gui blame FILE

24. 數據維護

一般git不須要常常維護,它把本身照顧的很好。不過,你能夠經過下面的命令查看數據統計:

$ git count-objects -v

若是佔用不少空間的話,你能夠選擇在你的本地倉庫作垃圾回收。這不會影響推送或其餘人,卻會讓一些命令運行更快並且減小空間佔用:

$ git gc

常常運行完整性檢查也頗有意義:

$ git fsck --full

你也能夠在末尾加上–auto參數(若是你在服務器上經過crontab常常/天天都運行這個命令的話),而後它只會在必要的時候才執行fsck動做。

在檢查的時候,看到「dangling」或「unreachable」是正常的,一般這是由回退HEAD或切換基礎的結果。而看到「missing」或「sha1 mismatch」就不對了…找專業人士幫忙吧!

25. 恢復遺失的分支

若是你使用-D參數刪除了experimental分支,能夠用下面的命令從新創建:

$ git branch experimental SHA1_OF_HASH

若是你最近訪問過的話,你一般能夠用git reflog來找到SHA1哈希值。

另外一種方式是使用git fsck —lost-found。其中一個dangling的提交就是丟失的HEAD(它只是已刪除分支的HEAD,而HEAD被引用爲當前的HEAD因此它並不處於dangling狀態)

相關文章
相關標籤/搜索