Git中級使用者的25個提示
本文轉載自圖靈社群使用者青年的翻譯文章。
我使用 Git 大約已經有18個月時間,自認為能很好地駕馭它了。但是當我們請到 GitHub 的 Scott Chacon 來到 LVS 公司(一個博彩/遊戲軟體供應商/開發商)做專場培訓時,我在第一天就學到了大量的東西。
由於有些人總是對使用 Git 自我感覺良好,因此,我想分享一些我從社群獲取到的 Git 精品,這樣就可能會幫助那些人無需浪費大量研究時間而直接找到答案。
基本提示
1、安裝之後第一步
安裝 Git 之後,你要做的第一件事情就是去配置你的名字和郵箱,因為每一次提交都需要這些資訊:
$ git config --global user.name "Some One"
$ git config --global user.email "someone@gmail.com"
2、Git 是以指標為基礎
儲存在 git 中的所有東西都包含在一個檔案中。當你提交的時候,git 會建立一個包含提交訊息和相關資料的檔案(名稱、郵件、日期/時間、上一次提交等等),並將其連結到一個樹形檔案。樹形檔案包含一個物件列表或者其它樹。物件或二進位制大資料物件(BLOB)是提交的真正內容(一個檔案,如果你願意,雖然檔名沒有儲存在物件中,但會儲存在樹中)。所有這些檔案都以物件的 SHA-1 雜湊為檔名進行儲存。
分支和標籤只是一些檔案,這些檔案包含(基本上)一個指向提交的 SHA-1 雜湊值。使用這些引用在靈活性和速度上均有大幅提升,建立一個新的分支就和建立一個檔案一樣簡單,只是這個檔案帶有分支名稱和一個包含指向提交(你從這個提交建立分支)的SHA-1雜湊值。當然,當你使用 Git 命令列工具(或一個圖形使用者介面)時,你永遠也不會這麼做,但它就是這麼簡單。
你可能已經聽說過對 HEAD 的引用。它只是一個包含 SHA-1 引用的檔案,這個引用指向你當前的提交。如果你正在解決一個合併衝突問題,檢視一下 HEAD,你會發現,它與一個特定的分支或分支上的特定點無關,只和你現在的位置有關。
所有的分支指標儲存在 .git/refs/heads 目錄下,HEAD 在 .git/HEAD 目錄下,標籤在 .git/refs/tags 目錄下 - 你可以隨意看看。
3、兩個 Parents - 當然!
當在日誌檔案中檢視一個合併提交的訊息時,你會看到兩個 parents(與正常提交相比)。第一個parent 是你所在的分支,第二個 parents 是你併入的分支。
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
如果檔案是二進位制的,檔案比較就不是那麼容易了…你通常要做的是嘗試每個版本的二進位制檔案,並決定使用哪一個(或者在二進位制檔案編輯器手動複製部分內容)。從一個特定分支下 pull 一個檔案副本(如果你要合併主分支和分支132的話):
$ git checkout master flash/foo.fla # or...
$ git checkout feature132 flash/foo.fla
$ # Then...
$ git add flash/foo.fla
另一種方法是從 git 中檢視這個檔案 - 你能夠以其他檔名的方式進行檢視,然後將正確的檔案(當你確定它是哪一個時)複製到正常的檔名中:
$ git show master:flash/foo.fla > master-foo.fla
$ git show feature132:flash/foo.fla > feature132-foo.fla
$ # Check out master-foo.fla and feature132-foo.fla
$ # Let's say we decide that feature132's is correct
$ 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
如果你想檢視遠端伺服器的相關資訊,你可以這樣做:
# shows URLs of each remote server
$ git remote -v
# gives more details about each
$ git remote show name
你可以檢視本地分支和遠端分支之間的差別:
$ git diff master..john/master
你也能檢視不在遠端分支上的 HEAD 的變化:
$ git log remote/branch..
# Note: no final refspec after ..
6、標籤
在 Git 中存在兩種型別的標籤 - 一個輕量級標籤和一個註解標籤。記著第二個提示中說過 Git 是基於指標的,二者的區別很簡單。一個輕量級標籤無非是一個指向提交的具名指標。你可以改變它並指向另一個提交。一個註解標籤是一個指向標籤物件的具名指標,這個標籤物件擁有自己的訊息和歷史。如果有需要,標籤物件的訊息可以採用 GPG 加密簽名。
建立兩種型別的標籤其實很容易(只是一個命令列選項的差異)
$ git tag to-be-tested
$ git tag -a v1.1.0 # Prompts for a tag message
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 選項就可以一步到位:
$ git branch -m twitter-experiment
$ git branch -m feature132 twitter-experiment
8、合併分支
在將來某個時候,你想要合併你的變更。有兩種方式可以實現:
$ git checkout master
$ git merge feature83 # Or...
$ git rebase feature83
merge 和 rebase 的區別在於,merge 試圖解決變更而且建立一個融合後的新提交,而 rebase 則試圖把自你上次在其他分支上的變化,在另一個分支的 HAED 上重現。但是,在你向遠端伺服器推送一個分支之後,不要進行 rebase 操作 - 這會引發混淆/問題。
如果你不能確定哪些分支仍然有獨立的工作在進行 - 以便你能知道你需要合併哪一個分支以及刪除哪些分支,git branch 命令有兩個選項可以幫助實現這一點:
# Shows branches that are all merged in to your current branch
$ git branch --merged
# Shows branches that are not merged in to your current branch
$ git branch --no-merged
9、遠端分支
如果你有一個本地分支,你想讓它出現在遠端伺服器上,你可以使用一個推送命令:
$ git push origin twitter-experiment:refs/heads/twitter-experiment
# Where origin is our server name and twitter-experiment is the branch
更新:感謝 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
然而,如果你使用 -b 識別符號去檢出的話,新版的 Git 會自動建立跟蹤:
$ git checkout -b myfeature origin/myfeature
在臨時存放區、索引和檔案系統中儲存內容
10、臨時存放(Stashing)
在Git中,你可以把當前的工作狀態儲存在一個臨時的儲存區域堆疊,然後重新加以利用。簡單的案例如下:
$ git stash # Do something...
$ git stash pop
很多人推薦使用 git stash apply 來代替 “pop”,然而如果你真這麼做的話,你最終得到一個長長的毫無用處的儲藏清單。如果對它進行清理,“pop” 只會把它從堆疊中刪除。如果你已經使用了 git stash apply ,你可以使用如下命令從堆疊中刪除最後一項:
$ git stash drop
Git 會基於當前的提交訊息自動建立一個註釋資訊。如果你更喜歡使用一個自定義的訊息(因為它可能和之前的提交無關):
$ git stash save "My stash message"
如果你想從你的列表中(不必是最後一個)對一個特定的 stash 加以利用,你可以列出它們並像這樣來使用它:
$ 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 log’檢視最近提交歷史的話,你就不能長時間順利地使用 Git。但是,也存在一些如何更好使用它的建議。例如,你可以檢視每次提交中改變的一個補丁:
$ 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 分支,你想檢視在這些分支但卻不在主分支上的提交(備註:^ 代表非):
$ 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 # By revision
$ git show v1.0.1 # By tag
$ git show feature132 # By branch name
$ git show 12a86bc38^ # Parent of a commit
$ git show 12a86bc38~2 # Grandparent of a commit
$ git show feature132@{yesterday} # Time relative
$ git show feature132@{2.hours.ago} # Time relative
請注意,和上一節有所不同,在行尾的脫字元表示提交的 parent - 行首的脫字元則表示不在這個分支上。
16、選擇一個範圍
最簡單的方法是這樣來用:
$ git log origin/master..new
# [old]..[new] - everything you haven't pushed yet
你也可以刪除 [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
如果你真的想把分支指向一個完全不同的 SHA-1(也許你把一個分支的 HEAD 指向另一個分支,或者進一步提交),你可以按照以下方式去做:
$ git checkout FOO
$ git reset --hard SHA
實際上還有一種更便捷的方式(因為它不會先將你的工作副本變回最初 FOO 狀態,然後再指向 SHA):
$ git update-ref refs/heads/FOO SHA
18、提交到錯誤的分支
好吧,讓我們假設你提交到主分支,但應該已經建立了一個叫做 experimental 的主題分支。為了移除這些變化,你可以在當前點建立一個分支,回退 HEAD,然後檢出新的分支:
$ git branch experimental # Creates a pointer to the current master state
$ git reset --hard master~3 # Moves the master branch pointer back to 3 revisions ago
$ git checkout experimental
如果你已經在一個分支的一個分支的一個分支等上面做了些變更,這將會更復雜。然後你需要做的就是在這個分支上將其變更 rebase 到另一個的地方:
$ git branch newtopic STARTPOINT
$ git rebase oldtopic --onto newtopic
19、互動式 rebasing
這是一個很酷的特性,我之前已看過演示,但當時沒有真正搞明白,現在來看其實很簡單。比方說,你已做了3次提交,但是你想對它們進行重新排序或者編輯(或者合併它們):
$ git rebase -i master~3
然後你將編輯器開啟。你所要做的就是修改 “pick/squash/edit 的指令來進行如何提交,然後儲存/退出。在編輯之後,你可以使用 git rebase —continue 讓你的每一個指令一個一個進行。
如果你選擇編輯一個檔案,這會讓你停留在你提交時的狀態,因此你需要使用 git commit —amend 對它進行編輯。
備註:在 REBASE 過程中不要進行提交工作 - 只能新增然後使用 —continue, —skip or —abort 選項。
20、清理
如果你已經提交了一些內容到你的分支中(也許你是從SVN中的舊程式碼庫匯入的),你想從歷史中刪除掉所有的已提交內容:
$ git filter-branch --tree-filter 'rm -f *.class' HEAD
如果你已經向遠端伺服器推送過程式碼,但自那之後提交的都是一些垃圾,在推送之前你可以在本地系統上執行這樣的操作:
$ git filter-branch --tree-filter 'rm -f *.class' origin/master..HEAD
各種各樣的提示
21、之前你看過的引用
如果你知道你之前已經檢視過一個 SHA-1,但是你已經做了一些重置/回退工作,你可以使用 reflog 命令去檢視你最近看過的 SHA-1:
$ git reflog
$ git log -g # Same as above, but shows in 'log' format
22、分支命名
一個可愛的小提示 - 請記住,分支的名字並不侷限於 a-z 和 0-9 這些字元。名字中可以使用 / 和 . 來偽裝名稱空間或者版本號,例如:
$ # Generate a changelog of Release 132
$ git shortlog release/132 ^release/131
$ # Tag this as v1.0.1
$ git tag v1.0.1 release/132
23、尋找誰是始作俑者
尋找誰更改了一個檔案中的一行程式碼經常會用到。簡單命令如下:
$ git blame FILE
有時更改來自於前一個檔案(如果你已經合併了兩個檔案,或者你已經移動了一個函式),因此你可以這樣用:
$ # shows which file names the content came from
$ 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 或 rebasing的結果。如果檢查“missing” 或 “sha1 mismatch” 出了問題…尋求專業幫助吧!
25、 恢復一個丟失的分支
如果你使用 -D 選項刪除了一個分支 experimental,你可以重新建立它:
$ git branch experimental SHA1_OF_HASH
你可以使用 git reflog 來發現一個 SHA-1 雜湊值,如果你近期訪問過它的話。
另一種方法是使用 git fsck —lost-found。一個懸空的提交就是一個 lost HEAD(它只會是一個已刪除分支的 HEAD,因為當一個 HEAD^ 被 HEAD 引用時,它就沒有懸空)
作者: Andy Jeffries,Ruby on Rails 開發者 & 跆拳道教練,生活在英國倫敦。
原文: 25 Tips for Intermediate Git Users(2009)
感謝: Jodoo 幫助審閱並完成校對。
P.S. 如果您喜歡這篇文章並且希望學習程式設計技術的話,請關注一下復唧唧。
相關文章
- Mac使用者必備的25個小技巧Mac
- 2018-04-25 gitGit
- VS Code 提示 '未找到Git.請安裝Git,或在“git.path”設定中配置'Git
- 微軟將再次向Win7使用者推送升級通知 提示使用者升級win10微軟Win7Win10
- 入門級的Git操作Git
- linux中記錄GIT使用者名稱密碼的方法LinuxGit密碼
- Git中git pull/push 老是重複輸入使用者名稱密碼Git密碼
- 【git】Git commit時提示錯誤時 解決辦法GitMIT
- 『現學現忘』Git基礎 — 25、git log命令引數詳解Git
- 教你如何在CentOS中自行編譯升級GitCentOS編譯Git
- Promise 的 9 個提示Promise
- 如何Fedora 24升級至Fedora 25版?Fedora24升級至Fedora25版的詳細教程
- Git之提示There is no tracking information for the current branch.GitORM
- 使用git pull提示refusing to merge unrelated historiesGit
- git中提示 please tell me who you areGit
- CentOS 使用 sudo 提示使用者不在sudoers檔案中的解決方法CentOS
- git刪除歷史中的某個大檔案Git
- 剖析 golang 的25個關鍵字Golang
- 25個常用的Linux iptables規則Linux
- python之矩陣相加:提示使用者輸入矩陣的行數n,再提示使用者輸入矩陣的列數m,接下來,提示使用者輸入 2*n*m 個數字(每次輸入 一個數字)。輸出 C=A+B。Python矩陣
- Eclipse中Git的使用EclipseGit
- Centos中的git配置CentOSGit
- git pull如果提示merge衝突,先進行git reset --hard origin/master 後再git pullGitAST
- 修改git使用者密碼Git密碼
- 如何設計一個百萬級使用者的抽獎系統?
- 一個超級簡單易懂的使用者登入網頁網頁
- 『現學現忘』Git基礎 — 11、配置Git使用者簽名的方式Git
- 25個常見網路
- oracle中的某一個使用者名稱修改Oracle
- 6 個方便的 Git 指令碼Git指令碼
- git的幾個常用基本操作Git
- Git的11個知識點Git
- Linux CentOS升級安裝gitLinuxCentOSGit
- Git 12 歲了,為你送上 12 個 Git 的使用技巧!Git
- 不同資料夾使用不同的Git使用者Git
- 關於git提示“warning: LF will be replaced by CRLF”終極解答Git
- 25個大資料術語大資料
- git回退到某個commit git回滾到某個提交GitMIT