首頁 > 軟體

25個 Git 進階技巧

2020-06-16 18:00:29

我已經使用git差不多18個月了,覺得自己對它應該已經非常了解。然後來自GitHub的Scott Chacon過來給LVS做培訓(LVS是一個賭博軟體供應商和開發商,從2013年開始的合同),而我在第一天裡就學到了很多。

作為一個對git感覺良好的人,我覺得分享從社群裡掌握的一些有價值的資訊,也許能幫某人解決問題而不用做太深入研究。

GitHub 教學系列文章: 

GitHub 使用教學圖文詳解  http://www.linuxidc.com/Linux/2014-09/106230.htm 

Git 標籤管理詳解 http://www.linuxidc.com/Linux/2014-09/106231.htm 

Git 分支管理詳解 http://www.linuxidc.com/Linux/2014-09/106232.htm 

Git 遠端倉庫詳解 http://www.linuxidc.com/Linux/2014-09/106233.htm 

Git 本地倉庫(Repository)詳解 http://www.linuxidc.com/Linux/2014-09/106234.htm 

Git 伺服器搭建與用戶端安裝  http://www.linuxidc.com/Linux/2014-05/101830.htm 

Git 概述 http://www.linuxidc.com/Linux/2014-05/101829.htm 

分享實用的GitHub 使用教學 http://www.linuxidc.com/Linux/2014-04/100556.htm 

基本技巧

1. 安裝後的第一步

在安裝好git後,你第一件該做的事是設定你的名字和電子郵箱,因為每次提交都要用到這些資訊:

  1. $ git config --global user.name "Some One"
  2. $ 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. 合併衝突

目前我相信你碰到過合併衝突並且解決過。通常是編輯一下檔案,去掉<<<<,====,>>>>標誌,保留需要留下的程式碼。有時能夠看到這兩個修改之前的程式碼會很不錯,比如,在這兩個現在衝突的分支之前的改動。下面是一種方式:

  1. $ git diff --merge
  2. diff --cc dummy.rb
  3. index 5175dde,0c65895..4a00477
  4. --- a/dummy.rb
  5. +++ b/dummy.rb
  6. @@@-1,5-1,5+1,5@@@
  7. classMyFoo
  8. def say
  9. - puts "Bonjour"
  10. - puts "Hello world"
  11. ++ puts "Annyong Haseyo"
  12. end
  13. end

如果是二進位制檔案,比較差異就沒那麼簡單了...通常你要做的就是測試這個二進位制檔案的兩個版本來決定保留哪個(或者在二進位制檔案編輯器裡手工複製衝突部分)。從一個特定分支獲取檔案拷貝(比如說你在合併master和feature123兩個分支):

  1. $ git checkout master flash/foo.fla # 或者...
  2. $ git checkout feature132 flash/foo.fla
  3. $ # 然後...
  4. $ git add flash/foo.fla

另一種方式是通過git輸出檔案 - 你可以輸出到另外的檔名,然後當你決定了要用哪個後,再將選定的正確檔案複製為正常的檔名:

  1. $ git show master:flash/foo.fla > master-foo.fla
  2. $ git show feature132:flash/foo.fla > feature132-foo.fla
  3. $ # 檢出master-foo.fla和feature132-foo.fla
  4. $ # 假如說我們決定來自feature132的檔案是正確的
  5. $ rm flash/foo.fla
  6. $ mv feature132-foo.fla flash/foo.fla
  7. $ rm master-foo.fla
  8. $ git add flash/foo.fla

更新:感謝Carl在原部落格文章上評論裡的提醒,你實際上可以用“git checkout —ours flash/foo.fla”和“git checkout —theirs flash/foo.fla”來檢出特定版本的檔案,而不用記住你在合併的分支名字。就我個人來說喜歡更精確一點,但這也是一種方式...

記著在解決完衝突後要將檔案加入提交(像我上面做的那樣)。

伺服器,分支和標籤

5. 遠端伺服器

git的一個超強大的功能就是可以有不止一個遠端伺服器(實際上你一直都在一個本地倉庫上工作)。你並不是一定都要有這些伺服器的寫許可權,你可以有多個可以讀取的伺服器(用來合併他們的工作)然後寫入到另外一個倉庫。新增一個新的遠端伺服器很簡單:

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

如果你想檢視遠端伺服器的資訊可以這樣做:

  1. # 顯示每個遠端伺服器的URL
  2. $ git remote -v
  3. # 提供更多詳細資訊
  4. $ git remote show name

你隨時都可以檢視本地分支和遠端分支的差異:

  1. $ git diff master..john/master

你也可以檢視沒有在遠端分支上的HEAD的改動:

  1. $ git log remote/branch..
  2. # 注意:..後面沒有結束的特定參照

6. 標籤

在git裡有兩種型別的標籤 - 輕量級標籤和帶注釋標籤。記住技巧2裡說過git是基於指標的,這兩者之間的差異也很簡單。輕量級標籤只是一個簡單的指向一次提交的帶名字指標。你隨時都可以將它指向另一個提交。帶注釋標籤是一個指向標籤物件的帶名字指標,帶有自己的資訊和歷史。因為有自己的資訊,它可以根據需要用GPG簽名。

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

  1. $ git tag to-be-tested
  2. $ git tag -a v1.1.0# 會提示輸入標籤的資訊

7. 建立分支

在git裡建立分支非常簡單(而且像閃電一樣快,因為它只需要建立一個小於100位元組的檔案)。用普通方式建立新分支並切換過去:

  1. $ git branch feature132
  2. $ git checkout feature132

當然,如果你確定自己直接切換到新建的分支,可以用一個命令實現:

  1. $ git checkout -b feature132

如果你想重新命名一個本地分支也很簡單(可以顯示發生了什麼的較長的方式):

  1. $ git checkout -b twitter-experiment feature132
  2. $ git branch -d feature132

更新:你也可以(像Brian Palmer在原部落格文章的評論裡提出的)只用“git branch”的-m開關在一個命令裡實現(像Mike提出的,如果你只指定了一個分支引數,就會重新命名當前分支):

  1. $ git branch -m twitter-experiment
  2. $ git branch -m feature132 twitter-experiment

8. 合併分支

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

  1. $ git checkout master
  2. $ git merge feature83 # 或者...
  3. $ git rebase feature83

merge和rebase之間的差別是merge會嘗試處理改動並建立一個新的混合了兩者的提交。rebase會嘗試把你從一個分支最後一次分離後的所有改動,一個個加到該分支的HEAD上。不過,在已經將分支推到遠端伺服器後不要再rebase了 - 這會引起衝突/問題。

如果你不確定在哪些分支上還有獨有的工作 - 所以你也不知道哪些分支需要合併而哪些可以刪除,git branch有兩個開關可以幫你:

  1. # 顯示已經全部合併到當前分支的分支
  2. $ git branch --merged
  3. # 顯示沒有合併到當前分支的分支
  4. $ git branch --no-merged

9. 遠端分支

如果你在本地有一個分支希望推到遠端伺服器上,你可以用一行命令推播上去:

  1. $ git push origin twitter-experiment:refs/heads/twitter-experiment
  2. # origin是我們伺服器的名字,而twitter-experiment是分支名字

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

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

  1. $ git push origin :twitter-experiment

如果你想檢視所有遠端分支的狀態可以這樣做:

  1. $ git remote show origin

這個命令可能會列出伺服器上一些以前有過但現在已經不在了的分支。如果碰到這種情況你可以用下面的命令從你本地分支裡清理掉:

  1. $ git remote prune

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

  1. $ git branch --track myfeature origin/myfeature
  2. $ git checkout myfeature

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

  1. $ git checkout -b myfeature origin/myfeature

在儲藏點,索引和檔案系統中儲存內容

10. 儲藏

在git裡你可以把當前工作狀態放進一個儲藏堆疊中,然後可以再取出來。最簡單的情形是下面這樣:

  1. $ git stash
  2. # 做點其他事情...
  3. $ git stash pop

許多人建議使用git stash apply來代替pop,不過如果這樣做的話最後會遺留一個很長的儲藏列表。而“pop”會在全部載入後自動從堆疊中移除。如果使用過git stash apply,你也可以使用下面的命令從堆疊上移除最後一項:

  1. $ git stash drop

git會基於當前的提交資訊自動建立評論。如果你更希望有自定義資訊的話(因為它可能和前一個提交沒有任何聯絡):

  1. $ git stash save "My stash message"

如果你希望從列表中取出一個特定的儲藏點(不一定非得是最後一個)可以先列出它們然後用下面的方式取出:

  1. $ git stash list
  2. stash@{0}:On master:Changed to German
  3. stash@{1}:On master:Languageis now Italian
  4. $ git stash apply stash@{1}

11. 互動式新增

在subversion的世界裡你只能修改檔案然後提交所有改動。而在git裡你有強大得多的方式來提交部分檔案或者甚至是部分修補程式。提交部分檔案或檔案中的部分改動你需要進入互動式模式:

  1. $ git add -i
  2. staged unstaged path
  3. ***Commands***
  4. 1: status 2: update 3: revert 4: add untracked
  5. 5: patch 6: diff 7: quit 8: help
  6. What now>

這會讓你進入一個基於選單的互動式提示。你可以使用命令中的數位或高亮的字母(如果你在終端裡開啟了高亮的話)來進入相應的模式。然後就只是輸入你希望操作的檔案的數位了(你可以使用這樣的格式,1或者1-4或2,4,7)。

如果你想進入修補程式模式(互動式模式下按‘p’或‘5’),你也可以直接進入:

  1. $ git add -p
  2. diff --git a/dummy.rb b/dummy.rb
  3. index 4a00477..f856fb0 100644
  4. --- a/dummy.rb
  5. +++ b/dummy.rb
  6. @@-1,5+1,5@@
  7. classMyFoo
  8. def say
  9. - puts "Annyong Haseyo"
  10. + puts "Guten Tag"
  11. end
  12. end
  13. Stagethis hunk [y,n,q,a,d,/,e,?]?

你可以看到下方會有一些選項供選擇用來新增該檔案的這個改動、該檔案的所有改動,等等。使用‘?’命令可以詳細解釋這些選項。

12. 從檔案系統裡儲存/取回改動

有些專案(比如Git專案本身)在git檔案系統中直接儲存額外檔案而並沒有將它們加入到版本控制中。

讓我們從在git中儲存一個隨機檔案開始:

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

這樣這個目標檔案就已經儲存到資料庫中了,但是如果你沒有設定一個指向它的指標的話它會被當做垃圾回收。最簡單的方式是設定一個標籤:

  1. $ git tag myfile 51fc03a9bb365fae74fd2bf66517b30bf48020cb

注意這裡我們使用了標籤myfile。當我們需要使用這個檔案的時候可以這樣做:

  1. $ git cat-file blob myfile

這個對於一些工具檔案很有用,開發者可能會用到(密碼,GPG金鑰,等等)但是又不希望每次都檢出到硬碟(尤其是在實際工作中)。

更多詳情見請繼續閱讀下一頁的精彩內容http://www.linuxidc.com/Linux/2015-05/117314p2.htm


IT145.com E-mail:sddin#qq.com