簡體   English   中英

git 日志沒有正確返回文件的歷史

[英]git log does not return the history of a file correctly

我對 git 日志命令有一個奇怪的問題。 雖然這個命令:

git log --pretty=format: --name-only --diff-filter=A

返回列表中的.xyz.yml文件,但是當我嘗試運行此命令時:

git log --pretty="%ad" --diff-filter=A -- .xyz.yml

要檢索此文件添加到此存儲庫的時間,它返回空。

有什么解決辦法嗎?

  • 我確定我在命令中寫了確切的名稱(所以我相信它與區分大小寫沒有任何關系)
  • 同樣的問題發生在其他存儲庫中
  • 我正在使用 centos
  • 我用 python 和 GitPython lib 和 git 命令行檢查了問題

我會很感激任何線索。

編輯:

當我嘗試獲取完整歷史記錄時:

git log --full-history -- .xyz.yml

output 顯示了簡短且不完整的歷史記錄

commit b26d833b9da805d5d58c429a4af2d1a5c5b0bad9
Author: author name
Date:   Mon Dec 19 14:07:17 2016 -0500

Code config (#606)

* Create .xyz.yml

Created a Code config that uses your setup (also, enabled our Duplication engine).

* Update .xyz.yml

* Update .xyz.yml

* Update .xyz.yml

* Update .xyz.yml

* Update .xyz.yml

* Update .xyz.yml

* Update .xyz.yml

即使文件不再存在於頭中,歷史記錄也不會顯示任何刪除...

我還查看了 GitHub 用戶界面中的提交歷史記錄,我在那里看到了一個完全不同的世界:

在此處輸入圖像描述

第一次提交日期甚至與我在 --full-history 中可以找到的日期不同。

(注意:如果您還不知道提交如何存儲每個文件的完整快照,並通過它們的元數據信息鏈接在一起,請參閱我的答案here 。)

編輯前后的問題是不同的,但這兩個問題都與正在發生的事情相關。 git log命令可以執行 Git 調用History Simplification的操作。 git log文檔中搜索這個兩個詞的短語,你會發現一個以這個措辭奇怪的段落開頭的部分:

有時您只對歷史的一部分感興趣,例如修改特定 <path> 的提交。 但是歷史簡化有兩個部分,一個是選擇提交,另一個是如何去做,因為有多種策略可以簡化歷史。

在我們在這里處理奇怪的措辭之前,請注意,只有在您要求時才會簡化歷史。 索取方法如下:

  • 使用本節中描述的顯式選項之一,和/或
  • 列出一些path arguments,如git log --.xyz.yml :這里的.xyz.yml是一個路徑。 (在某些情況下--是可選的,並將 arguments 的其余部分標記為路徑。如果命名路徑存在於當前提交中並且與其他git log選項不同,則--不是必需的。這是一個好主意但是,始終習慣於使用它,這樣您就不必弄清楚這個特定的git log調用是否需要它。)

由於在您遇到麻煩的情況下,您確實使用了--.xyz.yml ,因此您確實要求簡化歷史記錄,即使您沒有意識到您要求它。 這就是我添加評論的原因; 您使用--full-history解決問題的回復證明默認模式簡化實際上是問題所在。

然后你問:

完整歷史返回介紹提交。 什么原因?

答案在於文檔所稱的Default mode

將歷史簡化為最簡單的歷史,解釋樹的最終 state。 最簡單,因為如果最終結果相同,它會修剪一些側枝(即合並具有相同內容的分支)

這仍然是相當莫名其妙的。 最初的段落討論了選擇提交以及如何進行 我認為這里缺少的是文檔從不談論git log的真正工作原理

我們需要知道——文檔沒有說明git log的工作方式是掃描提交隊列。 這個隊列是一個優先級隊列,即“更高優先級”的提交會浮到隊列的最前面並首先被檢查; 已經在隊列中的“較低優先級”提交被這個較高優先級的提交推到行尾。 因此, git log命令一次只處理此隊列中的一個提交

隊列本身最初是從您在命令行上指定的任何提交中加載的。 例如,您可以運行:

git log branch1 branch2 branch3

這使用git rev-parse將每個branch1branch2branch3轉換為提交 hash ID。 生成的三個提交 hash ID(假設我們得到三個不同的 ID)被加載到隊列中。 如果我們得到重復,此時隊列中有兩個甚至一個提交 hash ID。 例如,如果名稱branch2branch3 select 相同的提交,而branch1選擇不同的提交,則隊列現在只有兩個提交。

(如果你不選擇任何起始提交, git log將使用HEAD作為起始點提交。它的姊妹命令git rev-list沒有這個特殊功能,所以任何時候你使用git rev-list代替 ZBA9F11ECC3497D9993B933 git log ,確保給它一個明確的起點。)

git log代碼現在進入其主循環。 這個循環:

  • 從隊列中獲取最高優先級的提交;
  • 根據一些git log arguments決定是否打印
  • 根據其他git log arguments 決定是否將其父提交放入隊列

當我們詢問git log來說明類似.xyz.yml的文件時,是否打印提交的決定必須將提交的快照與其父快照進行比較。 我們現在想瀏覽一下本節的文檔

更詳細的解釋如下。

假設您將foo指定為 <paths>。 我們將調用修改foo的提交,TREESAME。 和 rest TREESAME。 (在為foo過濾的差異中,它們分別看起來不同且相等。)[snip]

(閱讀 rest 並在閱讀此答案的 rest 之前或之后完成他們的示例。)

Git 在內部真正要做的是為此提交拍攝快照(無論它是什么),並刪除您列出的文件之外的所有文件。 在這種情況下,您列出的一個文件是.xyz.yml 在他們的示例中,一個文件被命名為foo 但是你可以在這里給出一個目錄路徑,Git 將刪除除該目錄中的文件或多個路徑之外的所有文件,Git 也會刪除除這些路徑之外的所有文件。 這一切都適用於所謂的 TREESAM 測試。 當我們查看單個文件時,最容易理解,因為提交具有文件的某個特定版本,或者提交完全沒有文件:這是僅有的兩種可能性。 因此,如果兩個提交都缺少文件,或者兩者都有文件並使用相同版本的文件,則兩個提交是“相同的”(TREESAME)。

如果我們有一個普通的、日常的、非合並的提交和一個父提交,這一切都非常簡單。 考慮以下簡單的提交鏈:

... <-F <-G <-H

在這里,提交H有一些快照。 H的父母提交G有一些快照。 當然, G的父母F也有一些快照,依此類推。 可能每個快照都是不同的,但如果我們將它們剝離為一個感興趣的文件——文件foo或文件.xyz.yml GH可能具有相同的文件。 GH互為 TREESAM。 但是,提交F中的副本可能不同: FG不是 TREESAME。

這意味着 Git不會提及提交H 它對文件沒有任何更改 Git提到提交G :與其父F相比,它對文件進行了更改 這是 TREESAME 概念的第一次使用:通過向 Git 詢問特定文件,它僅將非 TREESAME 的提交打印到其父提交:至少我們詢問的文件中的一個已更改。

合並提交很棘手

這僅處理簡單的普通提交,例如FGH 合並提交呢? 我們的分支中可能有這些提交:

       I--J
      /    \
...--H      M--N--...
      \    /
       K--L

當 Git 對 (M, N) 對進行 TREESAME 測試時,該部分很簡單。 雖然M是一個合並提交,但它有一個快照,就像任何提交一樣。 因此,我們將MN中的快照縮減為感興趣的文件,並確定結果是否為 TREESAME。 如果是這樣,我們不打印N ,然后轉到M 如果沒有,我們打印N ,然后繼續M

現在我們必須決定提交M是否對其父級是 TREESAME。 但是等一下, M沒有父母。 M兩個父母, JL 我們應該比較哪一個?

Git 的答案是比較所有這些:嘗試 TREESAME( J , M )TREESAME( L , M )。 Git 現在知道M是否對所有父母、某些父母或沒有父母都是 TREESAME。 如果M任何父級都是 TREESAME,則不打印; 否則,它會打印。 現在真正的並發症開始了。

在合並提交時, git log可以將部分或全部父級放入隊列

打印或未打印提交M后, git log現在必須決定:

  • 我是否將提交J放入隊列?
  • 我是否將提交L放入隊列中?

不做歷史簡化時,Git 會將雙親都放入隊列中。 (好吧,如果您使用了--first-parent選項,則不會。但是,由於您沒有使用,我們將完全忽略該選項。)但是在進行歷史簡化時,默認選項是:

  • 使用--full-history ,所有父母 go 進入隊列。
  • 如果沒有--full-history ,請選擇一個 TREESAME 父母(從所有可能TREESAME 父母中隨機選擇)。 如果沒有父級是 TREESAME,則選擇所有父級。 將這些放入隊列中。

(請注意,某些合並提交可能有 3 個或更多父級;相同的規則適用於這些多父級合並提交。這里我們只有一個雙父級合並,因此短語“所有父級”的意思是“雙親”。)

現在,假設我們感興趣的文件是在提交KL中引入的。 提交HIJ中沒有它,而且——重要的是——它在M中也沒有:合並省略了 file 由於提交M ,在剝離除我們一個感興趣的文件之外的所有文件后,在相同的剝離后提交JTREESAME,Git 跟隨M回到J完全忽略提交L (注意:可能是L中也沒有我們的文件,但無論出於何種原因,Git 選擇跟隨提交J而不是L作為其單個 TREESAME 提交,同時進行歷史簡化。)

在這種情況下,歷史簡化代碼完全阻止git log查看底行 commits 隊列從不包含首次引入文件的提交K 查找文件的掃描永遠不會找到它,因為 Git 從不仔細閱讀引入文件歷史記錄(提交)。

歷史簡化的目標

這種簡化背后的想法是解釋為什么您擁有現在擁有的文件。 通過遵循從合並提交M返回到提交L的歷史記錄,在我們的示例中,我們永遠找不到文件.xyz.yml 但這就是我們想要的,因為文件.xyz.yml不在當前提交N中,也不在我們開始的任何地方。 我們已經要求 Git 解釋那里的文件 文件.xyz.yml不存在,因此對它存在的原因的解釋是它從未在感興趣的歷史中存在:解釋為什么它仍然不存在的歷史。

你的目標不同

當然,您的目標是弄清楚它是在哪里引入的,是在哪里丟失的。 事實是它在合並時丟失了,當時有人決定:我們的合並結果中不需要這個愚蠢的.xyz.yml 讓我們把它擋在外面! 也就是說,它某個提交L中,而不是在其直接后繼合並M中。

我知道這一點的方式來自您的最終git log output,當您取出--diff-filter選項時:

 git log --full-history --.xyz.yml

我們看到一個添加文件的提交,以及一些修改它的提交,但我們沒有看到任何刪除它的提交。 我們沒有看到這個提交的原因是因為我們的合並M對其父級中的至少一個TREESAME :在我們的示例中是J 所以合並提交M根本不打印。

如果它打印出來,我們仍然有一個潛在的問題,因為打印合並的方式有點時髦。 所有提交都打印了它們的 hash ID 和日志消息。 如果您要求--name-status--patch ,您可能會得到某種git diff的結果。 對於普通提交,這是與(單個)父提交的差異。 但是,對於git log ,有一個問題:

  • 如果您不要求-c--cc-mgit log懶惰地完全跳過打印差異;
  • 如果你確實要求-c--cc ,你會得到一個組合的 diff

組合差異省略了一些文件。 特別是,它們省略了至少一個父級和合並具有相同版本文件的任何文件,或者在這種情況下,兩者都缺少該文件。 因此,組合差異不會提及LM之間的文件被刪除 只有-m style diff 會在此處提及刪除

-m選項對合並提交進行“虛擬拆分”。 如果合並X有父母P1P2P3 , ..., Pn ,你會得到n diffs: P1 -vs- XP2 -vs- XP3 -vs- X ,等等到Pn -vs- X . 那么,對於我們的特殊情況,我們會得到兩個差異: J vs ML vs M J -vs- M差異對於.xyz.yml根本不會顯示任何內容,但L -vs- M會顯示刪除。

(請注意, -m還修改了git log決定是否打印合並的方式:現在它已經被拆分,如果它不是 TREESAME 到至少一個父級,它就會被打印出來。這也很重要,在這里。)

底線

如果您想找出某個文件被刪除的位置,您可能需要git log --full-history --diff-filter=D -m -- path 這會強制git log到 go 並檢查合並本身是否是文件在您開始的提交中不存在的原因。

暫無
暫無

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

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