所以我正在研究一堆分支。我記得,在其中一個分支上,我對檔案做了一些超級智能的更改。但我不記得這發生在哪個分支。
是否有一個 git 命令,它可以向我顯示一個檔案的所有最新更改,跨越我在本地擁有的所有分支,用于存盤庫?
例子
/---D
/
/ /---E
/ /
A - B - C
\
\
\--F
我坐在 C 上,我知道我已經在 D、E 或 F 中對特定檔案進行了超級聰明的提交。
我可以一一瀏覽,查看檔案的內容。但我希望有這樣的命令:
$ 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-flag 的資訊,但輸出未顯示更改是在哪個分支上進行的:
我還查看了--source-flag,但結果對我來說沒有任何意義。
無論我做什么,我都覺得我缺少一個命令來適當地比較所有本地分支中的同一檔案。
uj5u.com熱心網友回復:
TL;博士
git branch --contains與您找到的哈希 ID 一起使用。但是:你為什么在乎?哈希 ID 是您真正需要的。
長
您的圖表存在一個基本問題:它上面沒有分支名稱。讓我們在上面放一些分支名稱,然后問一個關鍵問題:
/---D <-- br1
/
/ /---E <-- br2
/ /
A - B - C <-- br3
\
\
\--F <-- br4
在哪個分支上提交A?
警告:這是一個技巧問題!答案在下面,(我希望)中間有足夠的文字,這樣你就不能只是欺騙和閱讀它,而是不得不考慮這一點。顯而易見的答案是“開啟br3”,但這是不對的。(沒有錯,只是不對。)
你會想做什么
我還在這里讀到了一些關于
--all-flag 的東西......
使用此標志,然后使用git describe或git branch --contains與找到的提交哈希 ID 一起使用,或者:
我還查看了
--source-flag,但結果對我來說沒有任何意義。
該--source標志執行檔案中所說的操作:git log
--source
列印出在達到每個提交的命令列上給出的參考名稱。
但是,通常情況下,參考手冊很簡潔,并且在這里充滿了行話。該標志為您提供了一些您需要的資訊,有時它會是您需要的一切,但git branch --contains或者git describe可能仍然更有用。
技巧問題的答案
提交A在每個分支上。
這里的訣竅是,在 Git 中,許多提交同時在許多分支上。有些提交可能不在分支上。這讓我們進入了一個單獨的 Git 問題,即:“分支”到底是什么意思?Git 中的分支 這個詞實際上是模棱兩可的,并且被過度使用,有時甚至到了幾乎失去所有意義的地步。但是,一旦您習慣了瘋狂的多重含義,事實證明,人類通常會自動分配正確的含義:分支是分支名稱,但它也是遠程跟蹤名稱,Git 更正式地稱為提示的特定提交提交,和一個一組以提示 commit 結束的提交。Git 分支包含所有這些東西,然而,當人們說“分支”時,它們通常只表示其中之一。
為了理解這一點,我們需要可達性的概念。可達性實際上是一個圖論的東西。您繪制的圖表是一個提交圖A,其中的字母F代表實際提交。每個實際提交都有一些獨特的、又大又丑且看起來隨機的哈希 ID,但這些對人類來說太難了,所以我們大多會盡可能地忽略它們,或者在這里使用像這些字母這樣的替代A品F。
每個提交都向后鏈接到以前的或父提交。在這里, commitC反向鏈接到 commit B,它反向鏈接到 commit A。D向后提交鏈接A,也是如此F;E向后鏈接到B,我們已經注意到向后鏈接到A。
通過跟蹤向后指向的鏈接,Git找到了提交。Git使用分支名稱來查找最終提交(分支提示提交) ,這是人類傾向于關心和使用的內容。但隨后 Git 從那里向后作業。
當我們以 . 開頭時br1,Git 會找到 commit D,然后向后作業并找到 commit A。這意味著提交A是“on”或“contained in”, branch br1。但是我們也可以從br2and findA開始,我們可以從br3and find開始A,等等。事實上,既然A是我們的第一次提交,條條大路通羅馬 A:提交A在每個分支上。它也將在未來的分支上。1
在 Git 中,實際上不可能知道提交是在哪個分支上創建的,除非您將其記錄為提交訊息中的文本。那是因為我們可以隨意創建和銷毀分支名稱:每個分支名稱只是選擇(或“指向”)提交圖中的某個提交。我們在創建分支名稱時選擇此提交。
那么,當我們簽出(切換到)分支并進行新提交時,Git 會進行新提交,使其向后指向我們簽出的提交,并將新提交的哈希 ID 存盤到分支名稱中,以便新提交現在是提示提交。因此,根據您的圖表,如果我們git switch br3進行新的提交,則名稱br3將指向我們G之后的新提交;G將向后指向C; 并且提交A保留在每個分支上。
如果我們完全洗掉分支名稱br1,則提交D變為un-findable,因為我們使用分支名稱查找提交并向后作業。現在只有一種方法可以找到D,那就是使用br1. 因此,通過洗掉name br1,我們“丟失”了 commit D。它變得遙不可及。2
所以可達性意味著“我們如何到達那里”。我們從分支名稱獲得提交。有關此概念的更多資訊,請參閱Think Like (a) Git。
1在Git中,可以創建多個根提交,從而設定不會回傳到 commit的新分支A。但這不是很典型,我們不會在這里介紹。
2 Git 最終會丟棄一個無法訪問的提交。但是,您確實有一個寬限期來取回提交,通常至少為 30 天。問題是您必須找到提交的唯一哈希 ID,您可以使用分支名稱來執行此操作,但是現在分支名稱已經消失了……嗯,這就是兩難境地。
可達性git branch --contains, 和git log --source
現在您了解了可達性,git branch --contains這將是有意義的。你給git branch --contains一些哈希 ID,例如 commitB或Eor的哈希 ID A。什么git branch --contains是:
- 從每個分支名稱開始,向后作業;
- 如果到達提交,列印分支名稱
因此,當與提交哈希 ID 一起使用時,B這將列印br2 和 br3,因為它們是可以到達的兩個分支名稱B。
簡單地列印在它找到一些提交時使用的任何名稱的--source選項。這實際上是git loggit log解釋起來更復雜,因為git log它本身就很復雜!
什么git log是走圖,列印它遇到的一些提交。也就是說,我們給出git log了一些起點,例如一個或多個分支名稱或提交哈希 ID。這git log命令采用這些名稱并將它們決議為哈希 ID,或者采用哈希 ID(已經是哈希 ID),并找到命名的提交。它將每個提交放入優先級佇列。
如果我們不git log帶引數運行,則使用特殊名稱。此名稱通常附加到一個分支名稱。使用or ,我們控制附加到哪個分支名稱;這是我們進行新提交時擴展的分支,所以它非常重要!該分支名稱是當前分支,這就是默認顯示的內容:也就是說,不帶引數運行意味著決議git logHEADgit switchgit checkout HEADgit loggit loggit logHEAD為當前提交的提交哈希 ID,并將該(單個)哈希 ID 放入佇列中。
現在佇列中有一些提交或提交,git log將最前面的條目從佇列中取出。由于佇列是優先佇列,因此如果其中有多個條目,則存在排序順序。但是佇列只有一個條目是非常常見的!例如,如果我們git log不帶引數運行,則當我們開始時,當前提交就是其中的一個條目。如果我們運行git log br1,Git 會將F的哈希 ID 放入其中,并且再次只有一個條目。
無論如何,從佇列中取出最前面的條目,現在根據您給出的任何引數或其他任何引數來決定是否顯示此提交。如果它應該顯示提交,它會這樣做。我們稱之為訪問 commit,就好像我們正在度假并去某些景點或城市或其他地方一樣。git log--no-merges
接下來,在顯示或未顯示提交后,git log找到提交的父級或父級。在您的示例圖中,每個提交都只有一個父級,但沒有A父級的提交除外。(如果有的話,一個合并提交將有兩個父級。)默認情況下,將所有父級放入佇列中,除非這些父級已經被訪問過。git log
如果我們剛剛訪問過它的一個父級,F會將git log'F父級A放入佇列中。佇列是空F的——在這一切開始時它是唯一的東西——所以現在佇列中再次只有一個條目。該git log命令現在取出并訪問佇列中的一個提交,即 commit A。它顯示 commit A,如果它應該這樣做,然后將A's parents 放入佇列中。沒有父母,所以佇列中沒有任何內容,佇列保持為空。
一旦佇列像這樣為空,git log就退出。因此,從Fvia name開始br4,我們訪問提交F并A停止,這就是git log將顯示的內容。
另一方面,如果我們運行git log --all,代碼會將D、E、C和F所有內容放入佇列中。現在有四個條目,所以優先級真的很重要。此優先級導致對其輸出git log進行排序。默認排序基于每次提交中存盤的提交者日期,之后的提交具有更高的優先級。因此,如果 commitF是最新的 commit,那就是最先出現的 commit。
我們將訪問F,將其列印出來并將其父項A放入佇列:佇列現在包含A、 D, E和C(按日期順序)。假設E具有下一個最高優先級的日期:git log將彈出E佇列,訪問它,然后插入B佇列。然后git log將從佇列中取出最高優先級的提交——讓我們繼續主題并說這是D——并訪問那個。這將放入A佇列中,但它已經在那里了;它不會進入兩次。我們現在訪問C,它想加入B佇列,但它已經在那里了;然后我們訪問,這是佇列中的最后一件事,并且沒有將任何內容放入佇列中,所以B,它想加入A佇列,但它已經在那里了;我們參觀Agit log終于停止了。
對于任何給定的提交,該標志只是用最先導致 Git 進行此提交的名稱來--source注釋每個輸出。所以對于,就是。3 對于,即或,取決于是訪問過的還是首先訪問過的。Cbr3Bbr2 br3git logCE
訪問順序取決于優先順序。至少在某種程度上,您可以使用--topo-order或之類的選項來控制它--author-date-order。但是在一張大圖中,尤其是其中有很多分支和合并操作的圖中,很難知道許多名稱中的哪一個可能首先到達某個提交。只有在像你這樣的小而簡單的圖表中,你才能得到可預測的東西。
3有git log --all你會看到refs/heads/br3而不只是br3。這只是分支的全名。所有分支都有短名稱 likebr3和完整名稱 like 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 路徑規范,它只顯示該檔案的差異。因此,您將看到在該檔案中,在該提交中,相對于其父檔案發生了什么變化。
您甚至可以從該提交中提取整個檔案,覆寫當前的作業樹副本,使用git restore:
git restore --source=a123456 --worktree -- path/to/file
當然,你應該首先確保你沒有任何有價值的 path/to/file東西,因為作業樹中的副本不在 Git 中,并且在你告訴 Git覆寫它之后 Git 無法取回它。只有提交的檔案實際上存盤在 Git 中。
這——檔案的保存副本,或來自父級的更改——通常是你所關心的。一旦你有了提交的哈希 ID,這些就很容易獲得。該哈希 ID 是提交的“真實名稱”:它始終可以識別一個特定的提交。
在 Git 中,分支名稱的意義在于幫助您找到提交。哈希 ID 是他們的真實姓名。它們太丑了,無法處理:一旦找到它們,我們就必須使用滑鼠剪切和粘貼或其他方式。但是,如果您運行了一個列印您關心的提交的哈希 ID 的命令,只需用滑鼠抓住它并開始作業!
4具有兩個或多個父級的合并提交在這里會導致問題。每個提交都包含每個檔案的完整快照。因此,為了查看某些提交中發生了什么變化,我們讓 Git 使用其從提交到父級的反向鏈接。父級也有一個完整的快照,所以父級提交包含相同的檔案,除非檔案本身是全新的。然后 Git 可以從每次提交中提取檔案并比較這兩個檔案并告訴您發生了什么變化。
但是合并提交有兩個或多個父級。這就是首先將其定義為合并提交的原因。由于它確實有至少兩個父級,我們不再知道使用哪個父級提交來獲取檔案的“早期”版本。有兩個或多個早期版本!該git log命令用于git log -p將提交顯示為補丁時,默認作弊:它根本不費心顯示任何內容。默認情況下,該git show命令會更努力地作業,執行 Git 稱為組合 diff的操作。不過,我們不會在這里詳細介紹。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/480928.html
標籤:混帐
下一篇:Git在切換分支時洗掉檔案
