簡體   English   中英

使用 git 在所有本地分支機構中查找對文件的最新提交 - 在本地分支機構之間進行比較

[英]Find latest commits to a file across all local branches with git - compare across local branches

所以我正在研究一堆分支。 我記得,在其中一個分支上,我對文件做了一些超級智能的更改。 但是我不記得這是在哪個分支發生的。

是否有一個 git 命令可以顯示一個文件的所有最新更改,跨我在本地的所有分支,用於存儲庫?

例子

    /---D
   / 
  /   /---E
 /   / 
A - B - C
 \
  \
   \--F

我坐在 C 上,我知道我已經在 D、E 或 F 中對特定文件進行了超級聰明的提交。

我可以通過 go 一個一個地查看文件的內容。 但我希望有這樣的命令:

$ git magic-command-1 "path/to/target/file"

Commit 123456 on branch "F" at 2022-05-19 15:10
Commit 234567 on branch "E" at 2022-05-19 14:33
Commit 345678 on branch "D" at 2022-05-19 11:12

也許還有一些顯示差異的東西。


我試過這個:

git log -p -- cypress.development.json

但我不確定它是否顯示在所有分支機構中。 或者顯示給定更改的分支。

我還在此處閱讀了有關--all標志的內容,但 output 並未顯示在哪個分支上進行了更改:

我還查看了--source標志,但結果對我來說沒有任何意義。

不管我做什么,我都覺得我缺少一個命令來適當地比較所有本地分支中的同一個文件。

長話短說

使用git branch --contains包含您找到的 hash ID。 但是:你為什么關心? hash ID 就是您真正需要的。

您的圖表存在一個基本問題:它上面沒有分支名稱。 讓我們在上面放一些分支名稱,然后問一個關鍵問題:

    /---D   <-- br1
   / 
  /   /---E   <-- br2
 /   / 
A - B - C   <-- br3
 \
  \
   \--F   <-- br4

提交A在哪個分支上?

警告:這是一個棘手的問題,答案在下面,(我希望)中間有足夠的文字,這樣你就不能作弊並閱讀它。 而將不得不考慮這一點。 顯而易見的答案是“它在br3上”,但這是不對的。 (沒有,只是不對。)

你會想做什么

我還在這里閱讀了有關--all標志的內容...

使用此標志,然后使用git describegit branch --contains與找到的提交 hash ID,或:

我還查看了--source標志,但結果對我來說沒有任何意義。

--source標志的作用git log文檔所說的相同

--source
打印出在每次提交時在命令行上給出的 ref 名稱。

但是,正如常見的那樣,參考手冊在這里簡潔且充滿行話。 該標志為您提供了一些您需要的信息,有時它會是您需要的所有信息,但git branch --containsgit describe可能仍然更有用。

技巧問題的答案

提交A每個分支上。

這里的技巧是在 Git 中,許多提交同時在許多分支上。 一些提交可能不在任何分支上。 這讓我們陷入了一個單獨的 Git 問題,即: “分支”到底是什么意思? Git 中的分支這個詞實際上是模棱兩可的,並且被過度使用,有時甚至幾乎失去了所有意義。 但是,一旦您習慣了瘋狂的多重含義,就會發現人類通常會自動分配正確的含義:分支是分支名稱,但它也是遠程跟蹤名稱,Git 更正式地稱為提示的特定提交提交,以及一組以尖端 commit 結尾的提交 一個 Git 分支是所有這些東西,然而,當一個人說“分支”時,他們通常只指這些東西中的一個

為了理解這一點,我們需要可達性的概念。 可達性實際上是一個圖論的東西。 您繪制的圖是一個提交圖,字母AF代表實際提交。 每個實際提交都有一些獨特的、大的、丑陋的、隨機的hash ID ,但這些對人類來說太難了,所以我們大多盡可能地忽略它們,或者在這里使用像字母AF這樣的替代品。

每個提交都向后鏈接到上一個或提交。 在這里,提交C向后鏈接到提交B ,它向后鏈接到提交A 提交D也向后鏈接到AF也是如此; E向后鏈接到B ,我們已經注意到它向后鏈接到A

通過跟蹤向后指向的鏈接,Git找到了提交。 Git 使用分支名稱找到結束提交(分支尖端提交),這是人類傾向於關心和使用的。 但是 Git 從那里向后工作。

例如,當我們從br1開始時,Git 將找到提交D ,然后向后工作並找到提交A 這意味着提交A “在”或“包含在”分支br1中。 但我們也可以從br2開始並找到A ,我們可以從br3開始並找到A ,等等。 事實上,由於A是我們的第一個提交, 條條大路通羅馬A :提交A每個分支上。 它也將在未來的分支上。 1個

在 Git 中,實際上不可能知道在哪個分支上創建了提交,除非您將其記錄為提交消息中的文本。 那是因為我們可以隨意創建和銷毀分支名稱:每個分支名稱只是選擇(或“指向”)提交圖的一些提交。 我們在創建分支名稱時選擇此提交。

然后,當我們檢出(切換到)分支並進行提交時,Git 進行新提交,使其向后指向我們檢出的提交,並將新提交的 hash ID 存儲到分支名稱中,以便新提交現在是提示提交。 因此,根據您的圖表,如果我們git switch br3並進行新提交,名稱br3之后將指向我們的新提交G G將向后指向C 並且提交A保留在每個分支上。

如果我們完全刪除分支名稱br1 ,提交D將變為un-findable ,因為我們找到使用分支名稱並向后工作的提交。 現在只有一種方法可以找到D ,那就是使用br1 因此,通過刪除名稱br1 ,我們“丟失”了提交D 它變得不可訪問 2個

所以可達性意味着“我們如何到達那里”。 我們從分支名稱開始提交。 有關此概念的更多信息,請參閱Think Like (a) Git


1在 Git 中,創建多個根提交可能的,因此會設置不會返回提交A新分支。 但這不是很典型,我們不會在這里介紹。

2 Git 最終將丟棄無法訪問的提交。 但是,您確實可以獲得寬限期來取回提交,通常至少為 30 天。 問題是您必須找到提交的唯一 hash ID,您可以使用分支名稱來執行此操作,但現在分支名稱已不存在……好吧,這就是兩難選擇。


可達性、 git branch --containsgit log --source

現在您了解了可達性, git branch --contains就有意義了。 您給git branch --contains一些 hash ID,例如,提交BEA的 hash ID。 git branch --contains所做的是:

  • 每個分支名稱開始,向后工作;
  • 如果到達提交,打印分支名稱

因此,當與提交 hash ID B一起使用時,這將打印br2br3 ,因為它們是可以到達B的兩個分支名稱。

git log--source選項只是打印git log在發現一些提交時使用的任何名稱。 這個其實解釋起來比較復雜,因為git log本身就很復雜!

git log所做的是走圖,打印它遇到的一些提交。 也就是說,我們給git log一些起點,例如一個或多個分支名稱或提交 hash ID。 git log命令獲取這些名稱並將它們解析為 hash 個 ID,或者獲取 hash 個 ID(已經是 hash 個 ID),並找到命名的提交。 它將每個提交放入一個優先級隊列中。

如果我們在沒有arguments 的情況下運行git log ,則git log使用特殊名稱HEAD 此名稱通常附加到一個分支名稱。 使用git switchgit checkout ,我們控制HEAD附加到哪個分支名稱 這是我們進行新提交時擴展的分支,所以它非常重要! 該分支名稱是當前分支,這就是git log默認顯示的內容:也就是說,運行git log沒有arguments 意味着git logHEAD解析為當前提交的提交 hash ID,並將其放入 (single) 隊列 ID3408 .

現在隊列中有一些提交或提交, git log從隊列中取出前面的條目。 由於隊列是優先級隊列,因此如果其中有多個條目,則有一個排序順序。 但是隊列只有一個條目是非常常見的,例如,如果我們運行git log而沒有 arguments,則當前提交是我們開始時其中的一個條目。 如果我們運行git log br1 , Git 將F的 hash ID 放入其中,同樣只有一個條目。

無論如何,從隊列中取出前面的條目后, git log現在根據您提供的任何 arguments 決定是否顯示此提交,例如--no-merges merges 或其他。 如果它應該顯示提交,它就會那樣做。 我們將此稱為訪問提交,就好像我們正在度假並去某些景點或城市或其他任何地方一樣。

接下來,顯示或不顯示提交, git log找到提交的父項或父項。 在您的示例圖中,每個提交都有一個父項,但沒有父項的提交A除外。 (一個合並提交,如果有的話,會有兩個父母。)默認情況下, git log所有父母放入隊列,除非這些父母已經被訪問過。

對於它的一個父節點,如果我們剛剛訪問過Fgit log會將F的父節點A放入隊列中。 隊列空的F是所有這一切開始時隊列中唯一的東西——所以現在隊列中又只有一個條目。 git log命令現在取出並訪問隊列的一個提交,即提交A 它顯示提交A ,如果它應該這樣做,然后將A的父母放入隊列。 沒有父母,所以隊列中什么也沒有,隊列仍然是空的。

一旦隊列像這樣為空, git log退出。 因此,通過名稱br4F開始,我們訪問提交FA並停止,這就是git log將顯示的內容。

另一方面,如果我們運行git log --all ,代碼會將DECF全部放入隊列。 現在有四個條目,因此優先級非常重要。 此優先級導致git log對其 output 進行排序。默認排序基於每次提交中存儲的提交者日期,以后的提交具有更高的優先級。 因此,如果提交F最新的提交,那么它就是第一個出現的提交。

我們將訪問F ,將其打印出來並將其父A放入隊列:隊列現在包含A 、 D , EC (按日期順序)。 假設E具有下一個最高優先級日期: git log會將E彈出隊列,訪問它,並將B插入隊列。 然后git log將從隊列中取出優先級最高的提交——讓我們繼續主題並說這是D並訪問那個。 這會將A放入隊列中,但它已經在那里了; 它不會兩次出現 go。 我們現在訪問C ,它想要將B放入隊列中,但它已經在那里了; 然后我們訪問B ,它想要將A放入隊列中,但它已經在那里了; 然后我們訪問A ,這是隊列中的最后一個東西,沒有隊列中放入任何東西,因此git log最終停止。

--source標志簡單地注釋每個output ,對於任何給定的提交,名稱首先導致 Git 到此提交 所以對於C ,這是br3 3對於B ,它是br2br3 ,具體取決於git log是先訪問C還是先訪問E

訪問順序取決於優先順序。 至少在某種程度上,您可以使用諸如--topo-order或 --author --author-date-order類的選項來控制它。 但是在一個大圖中,尤其是其中有很多分支和合並操作的圖中,很難知道許多名字中的哪一個可能首先到達某個提交。 只有在像你這里這樣的小而簡單的圖表中,你才能得到一些可預測的東西。


3使用git log --all您將看到refs/heads/br3而不僅僅是br3 那只是分支的全名 所有分支都有像br3這樣的短名稱,以及像refs/heads/br3這樣的完整名稱。 我喜歡把全名想象成他們的媽媽(或配偶)生他們的氣時說的話,有點像這些 ST:TOS 剪輯中的 Stella Mudd


分支名稱無關緊要

在頂部,我問你為什么關心某些提交“在”哪個分支。 有時您實際上會關心,然后問哪些分支包含此提交的問題就可以了。 但是,如果您只想查看文件,或將其視為更改,只需告訴 Git 向您顯示文件或顯示更改:

git show a123456:path/to/file

或者:

git show a123456 -- path/to/file

前者顯示存儲在命名提交中的命名文件的內容。 后者采用命名提交(使用縮寫提交 hash a123456 ),找到其父項(單數4 ),並在兩次提交上運行git diff 然后,由於最后的-- path/to/file pathspec ,它只顯示該文件的差異。 因此,您將看到在那個文件中,在那個提交中,相對於它的父文件發生了什么變化。

您甚至可以該提交中提取整個文件,覆蓋當前的工作樹副本,使用git restore

git restore --source=a123456 --worktree -- path/to/file

當然,你應該首先確保你path/to/file中沒有任何有價值的東西,因為你的工作樹中的副本不在 Git 中,並且 Git 在你告訴 Git覆蓋它之后無法取回它。 只有提交的文件實際存儲在 Git 中。

這個——文件的保存副本,或者來自父文件的更改——通常是你所關心的。 一旦您擁有提交的hash ID ,這些就很容易獲得。 那個 hash ID 是提交的“真名”:它總是可以識別一個特定的提交

分支名稱的重點,在 Git 中,只是為了幫助您找到提交。 hash ID 是他們的真實姓名。 它們實在是太難對付了:一旦找到它們,我們就必須使用鼠標剪切和粘貼或其他方式。 但是,如果您運行的命令打印了您關心的提交的 hash ID,只需用鼠標抓住它並開始工作!


4具有兩個或更多父項的合並提交會在此處引起問題。 每個提交都包含每個文件的完整快照 因此,為了查看某些提交中發生了什么變化,我們讓 Git 使用其從提交到父級的向后鏈接。 父級也有一個完整的快照,所以父級提交包含相同的文件,除非文件本身是全新的。 然后 Git 可以從每次提交中提取文件並比較這兩個文件並告訴您發生了什么變化。

但是合並提交有兩個或多個父項。 這就是首先將其定義為合並提交的原因。 由於它至少有兩個父提交,我們不再知道使用哪個父提交來獲取文件的“較早”版本。 有兩個或更多的早期版本! git log命令,當用作git log -p將提交顯示為補丁時,默認情況下作弊:它根本不顯示任何內容 git show命令在默認情況下更努力地工作,做一些 Git 調用組合差異 不過,我們不會在這里詳細介紹 go。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM