-
1. 起步
-
2. Git 基礎
-
3. Git 分支
-
4. 伺服器上的 Git
- 4.1 協議
- 4.2 在伺服器上部署 Git
- 4.3 生成 SSH 公鑰
- 4.4 架設伺服器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方託管服務
- 4.10 小結
-
5. 分散式 Git
-
A1. 附錄 A: Git 在其他環境
- A1.1 圖形介面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小結
-
A2. 附錄 B: 在應用程式中嵌入 Git
-
A3. 附錄 C: Git 命令
7.1 Git 工具 - 版本選擇
到目前為止,您已經學習了大部分日常命令和工作流程,這些是您管理或維護 Git 儲存庫以進行原始碼控制所需的內容。您已經完成了跟蹤和提交檔案的基本任務,並且您已經掌握了暫存區、輕量級主題分支和合並的強大功能。
現在,您將探索 Git 的一些非常強大的功能,這些功能可能不是您日常使用的,但您可能在某個時候需要用到。
版本選擇
Git 允許您透過多種方式引用單個提交、提交集或提交範圍。它們不一定顯而易見,但瞭解它們很有用。
單個版本
您可以透過完整的 40 個字元的 SHA-1 雜湊值來引用任何單個提交,但也有更人性化的方式來引用提交。本節概述了您可以引用任何提交的各種方法。
簡短 SHA-1
Git 足夠智慧,如果您提供 SHA-1 雜湊值的前幾個字元,它就能弄清楚您指的是哪個提交,只要該部分雜湊值至少有四個字元長且沒有歧義;也就是說,物件資料庫中沒有其他物件可以擁有以相同字首開頭的雜湊值。
例如,要檢查您添加了某些功能的特定提交,您可以先執行 git log 命令來查詢該提交
$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
Fix refs handling, add gc auto, update tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
Add some blame and merge stuff
在這種情況下,假設您對以 1c002dd… 開頭的雜湊值的提交感興趣。您可以使用以下任何一種 git show 變體來檢查該提交(假設較短的版本沒有歧義)
$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d
Git 可以為您的 SHA-1 值找到一個簡短且唯一的縮寫。如果您將 --abbrev-commit 傳遞給 git log 命令,輸出將使用較短的值但保持其唯一性;它預設為使用七個字元,但如果必要,它會使其更長以保持 SHA-1 的唯一性
$ git log --abbrev-commit --pretty=oneline
ca82a6d Change the version number
085bb3b Remove unnecessary test code
a11bef0 Initial commit
通常,八到十個字元足以在專案中保持唯一。例如,截至 2019 年 2 月,Linux 核心(一個相當大的專案)擁有超過 875,000 個提交,其物件資料庫中有近七百萬個物件,沒有兩個物件的 SHA-1 值在前 12 個字元中是相同的。
|
注意
|
關於 SHA-1 的簡短說明
很多人都會擔心在某個時候,由於偶然的巧合,他們的儲存庫中會有兩個不同的物件雜湊到相同的 SHA-1 值。那該怎麼辦? 如果您碰巧提交了一個雜湊值與儲存庫中先前*不同*物件的 SHA-1 值相同的物件,Git 將會看到該物件已存在於您的 Git 資料庫中,並假定它已經被寫入,然後簡單地重用它。如果您稍後嘗試簽出該物件,您將始終獲得第一個物件的資料。 但是,您應該意識到這種情況發生的可能性有多麼微乎其微。SHA-1 摘要是 20 位元組或 160 位。要確保 50% 的衝突機率,所需的隨機雜湊物件數量約為 280(計算衝突機率的公式為 這裡有一個例子,讓您對獲取 SHA-1 衝突需要付出多大的努力有一個概念。如果地球上所有 65 億人都從事程式設計,並且每秒鐘,每個人都在生成相當於整個 Linux 核心歷史(650 萬個 Git 物件)的程式碼,並將其推送到一個巨大的 Git 儲存庫中,大約需要 2 年時間,該儲存庫才會包含足夠的物件,使其具有 50% 的單個 SHA-1 物件衝突的機率。因此,自然發生的 SHA-1 衝突的可能性比您的程式設計團隊中的每個成員在同一晚因不相關的事件而被狼攻擊和殺死還要低。 如果您投入數千美元的計算能力,就有可能合成兩個具有相同雜湊值的檔案,正如 2017 年 2 月在 https://shattered.io/ 上所證明的那樣。Git 正在轉向使用 SHA256 作為預設雜湊演算法,它對沖突攻擊的抵抗力更強,並且有程式碼可以幫助緩解這種攻擊(儘管它不能完全消除它)。 |
分支引用
引用特定提交的一種直接方法是,如果它是分支的最新提交;在這種情況下,您可以在任何需要提交引用的 Git 命令中簡單地使用分支名稱。例如,如果您想檢查分支上最後一個提交物件,以下命令是等效的,假設 topic1 分支指向提交 ca82a6d…
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1
如果您想檢視分支指向哪個特定的 SHA-1,或者如果您想檢視這些示例中的任何一個最終會歸結為哪個 SHA-1,您可以使用一個名為 rev-parse 的 Git 管道工具。您可以參考 Git Internals 來獲取更多關於管道工具的資訊;基本上,rev-parse 用於較低級別的操作,而不是為日常操作設計的。但是,當您需要檢視實際發生的情況時,它有時會很有幫助。您可以在此處對您的分支執行 rev-parse。
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949
RefLog 簡寫
當您在工作中時,Git 在後臺做的一件事就是維護一個“reflog”——一個關於您的 HEAD 和分支引用在過去幾個月內的去向的日誌。
您可以使用 git reflog 檢視您的 reflog
$ git reflog
734713b HEAD@{0}: commit: Fix refs handling, add gc auto, update tests
d921970 HEAD@{1}: merge phedders/rdocs: Merge made by the 'recursive' strategy.
1c002dd HEAD@{2}: commit: Add some blame and merge stuff
1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
95df984 HEAD@{4}: commit: # This is a combination of two commits.
1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD
每次您的分支尖端因任何原因更新時,Git 都會將該資訊儲存在臨時歷史記錄中。您也可以使用 reflog 資料來引用舊的提交。例如,如果您想檢視儲存庫 HEAD 的第五個先前值,您可以使用 reflog 輸出中看到的 @{5} 引用
$ git show HEAD@{5}
您也可以使用此語法檢視某個分支在特定時間點的位置。例如,要檢視您的 master 分支昨天在什麼位置,您可以輸入
$ git show master@{yesterday}
這將顯示您 master 分支的尖端在昨天在什麼位置。此技術僅適用於 reflog 中仍然存在的資料,因此您無法使用它來查詢幾個月之前的提交。
要以 git log 輸出的格式檢視 reflog 資訊,您可以執行 git log -g
$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: commit: Fix refs handling, add gc auto, update tests
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
Fix refs handling, add gc auto, update tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
需要注意的是,reflog 資訊嚴格來說是本地的——它只記錄*您*在*您的*儲存庫中所做的事情。在其他人儲存庫的副本中,這些引用不會相同;此外,在您最初克隆一個儲存庫後,您將有一個空的 reflog,因為您的儲存庫中還沒有發生任何活動。執行 git show HEAD@{2.months.ago} 僅在您至少兩個月前克隆了專案時才會顯示匹配的提交——如果您克隆的時間晚於此,您只會看到您的第一個本地提交。
|
提示
|
將 reflog 視為 Git 的 shell 歷史記錄
如果您有 UNIX 或 Linux 背景,您可以將 reflog 視為 Git 的 shell 歷史記錄版本,這強調了其中的內容顯然只與您和您的“會話”相關,與其他可能在同一臺機器上工作的人無關。 |
|
注意
|
在 PowerShell 中轉義大括號
在使用 PowerShell 時,大括號
|
祖先引用
指定提交的另一種主要方式是透過其祖先。如果您在引用末尾放置一個 ^(插入符),Git 會將其解析為該提交的父提交。假設您檢視專案的歷史記錄
$ git log --pretty=format:'%h %s' --graph
* 734713b Fix refs handling, add gc auto, update tests
* d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd Add some blame and merge stuff
|/
* 1c36188 Ignore *.gem
* 9b29157 Add open3_detach to gemspec file list
然後,您可以透過指定 HEAD^ 來檢視前一個提交,這意味著“HEAD 的父提交”
$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
|
注意
|
在 Windows 中轉義插入符
在
|
您也可以在 ^ 之後指定一個數字來識別您想要*哪個*父提交;例如,d921970^2 表示“d921970 的第二個父提交”。此語法僅對合並提交有用,合併提交有多個父提交——合併提交的*第一個*父提交來自您合併時所在的那個分支(通常是 master),而合併提交的*第二個*父提交來自被合併的分支(例如,topic)
$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
Add some blame and merge stuff
$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date: Wed Dec 10 22:22:03 2008 +0000
Some rdoc changes
另一個主要的祖先規範是 ~(波浪號)。這也引用第一個父提交,因此 HEAD~ 和 HEAD^ 是等效的。當您指定一個數字時,區別就會顯現出來。HEAD~2 表示“第一個父提交的第一個父提交”,或者“祖父”——它會遍歷您指定的次數的第一個父提交。例如,在前面列出的歷史記錄中,HEAD~3 將是
$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
Ignore *.gem
這也寫成 HEAD~~~,再次表示第一個父提交的第一個父提交的第一個父提交
$ git show HEAD~~~
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
Ignore *.gem
您也可以組合這些語法——您可以使用 HEAD~3^2 來獲取前一個引用的第二個父提交(假設它是一個合併提交),依此類推。
提交範圍
現在您已經學會了如何指定單個提交,讓我們看看如何指定提交範圍。這對於管理您的分支特別有用——如果您有很多分支,您可以使用範圍規範來回答諸如“這個分支上有哪些工作還沒有被合併到我的主分支?”之類的問題。
雙點
最常見的範圍規範是雙點語法。這基本上要求 Git 解析從一個提交可達但從另一個提交不可達的提交範圍。例如,假設您的提交歷史看起來像 範圍選擇的示例歷史。
假設您想看看您的 experiment 分支中有哪些內容尚未合併到您的 master 分支。您可以透過 master..experiment 來要求 Git 只顯示那些提交的日誌——這意味著“從 experiment 可達但從 master 不可達的所有提交”。為了在這些示例中簡潔明瞭,提交物件的字母將代替實際的日誌輸出,並按照它們顯示的順序排列
$ git log master..experiment
D
C
另一方面,如果您想看到相反的情況——master 中所有不在 experiment 中的提交——您可以顛倒分支名稱的順序。experiment..master 顯示 master 中所有無法從 experiment 訪問的內容
$ git log experiment..master
F
E
如果您想保持 experiment 分支的最新狀態並預覽您將要合併的內容,這很有用。此語法的另一個常見用途是檢視您將要推送到遠端的內容
$ git log origin/master..HEAD
此命令顯示您當前分支中所有不在 origin 遠端的 master 分支中的提交。如果您執行 git push 並且當前分支正在跟蹤 origin/master,那麼 git log origin/master..HEAD 列出的提交就是將被傳輸到伺服器的提交。您也可以省略語法的一側,讓 Git 假定為 HEAD。例如,您可以透過輸入 git log origin/master.. 來獲得與上一個示例相同的結果——如果省略一側,Git 會替換為 HEAD。
多個點
雙點語法是一個方便的簡寫,但也許您想指定多個分支來指示您的版本,例如檢視當前分支中不存在的幾個分支中的提交。Git 允許您透過在您不想檢視可達提交的任何引用之前使用 ^ 字元或 --not 來做到這一點。因此,以下三個命令是等效的
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA
這很好,因為使用此語法,您可以在查詢中指定兩個以上的引用,而雙點語法無法做到。例如,如果您想檢視從 refA 或 refB 可達但從 refC 不可達的所有提交,您可以使用以下任一命令:
$ git log refA refB ^refC
$ git log refA refB --not refC
這使得一個非常強大的版本查詢系統,應該可以幫助您弄清楚您的分支中有哪些內容。
三點
最後一種主要的範圍選擇語法是三點語法,它指定了*任一*兩個引用都可達但*兩個都不可達*的所有提交。回顧 範圍選擇的示例歷史 中的示例提交歷史。如果您想檢視 master 或 experiment 中的內容,但不是任何共同的引用,您可以執行
$ git log master...experiment
F
E
D
C
同樣,這會為您提供正常的 log 輸出,但只顯示這四個提交的提交資訊,並按傳統的提交日期排序。
在這種情況下,與 log 命令一起使用的常用開關是 --left-right,它會顯示每個提交在範圍的哪一側。這有助於使輸出更有用
$ git log --left-right master...experiment
< F
< E
> D
> C
有了這些工具,您可以更輕鬆地讓 Git 知道您想要檢查哪個或哪些提交。