簡體   English   中英

將 Dev 分支中的文件與 Master 分支對應項取消關聯,以便它們在合並后保持獨立

[英]Disassociating Files in Dev Branch from Master Branch Counterparts So They Remain Separate After Merging

背景

我有兩個(實際上是很多分支)已經分道揚鑣,需要合並以生產流程。

dev 分支文件名與 master 分支相同。 dev 分支中的一些具有共同名稱的文件“准備好”用於生產,而其他文件“未准備好”,因為它們需要重新工作和/或預計會產生令人討厭的沖突。

對於名稱相同的“未就緒”文件,我很想將它們與主對應文件分離,以便在分支合並后它們可以保持獨立。 我曾嘗試將開發分支文件名重命名為其他名稱,例如git mv NewFile.txt DevFile.txt ,但是除了正常的文件內容合並行為之外,合並僅包含重命名。

核心問題

是否有某種方法可以解除 master 和 dev 分支中的文件的關聯,以便文件在合並后保持獨立? 理想情況下,同時還保留他們的歷史?

更多詳細信息

我試圖在 github 的公共倉庫中包含一個簡化的工作示例。 所有 shell 命令添加/mv/merge 等都包含在 script.sh 中。 要克隆/查看,也許您可以使用git clone git@github.com:NedScandrett1/TestRepo.git訪問示例

是否有某種方法可以解除 master 和 dev 分支中的文件的關聯,以便文件在合並后保持獨立?

不完全是。 但是,您可能能夠“足夠接近”。 見下文。

理想情況下,同時還保留他們的歷史?

文件沒有歷史記錄,在 Git 中。 在 Git 中,提交歷史,如果你運行:

git log

你會看到所有的提交——所有的歷史——從當前的提交開始並向后工作。

Git 總是像這樣向后工作,因為提交本身在內部向后工作:

  • 每個提交都有每個文件的完整快照,以及一些元數據:關於提交本身的信息。

  • 快照存儲 Git 在您或任何人制作快照時所知道的所有文件。 這些都不能更改:它們是提交的一部分,提交的編號——它的 hash ID——取決於這些文件內容。

  • 元數據告訴 Git 提交的人、時間和原因(他們的日志消息)。 在此提交元數據中,Git 包含一個列表(通常只有一個提交),其中包含先前提交的 hash ID。 這也永遠無法更改:此特定提交的 hash ID,即它在您的 Git 數據庫中“我們擁有的所有提交”的編號,由這些內容(數據和元數據)決定。

元數據負責這個向后連接的字符串。 如果我們繪制一組簡單的單父提交,將它們的實際 hash ID 替換為我們自己的理智的一個大寫字母名稱,我們會得到如下所示的內容:

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

其中H代表序列中最后一個提交的 hash ID(提交編號)。 (Side note: Git finds this hash ID in the branch name , which by definition holds the hash ID of the last commit in the chain. Updating the branch is done by sticking a new hash ID into the branch name. That hash ID, whatever它是,成為分支中的最后一次提交。)

我們——人類,也就是——喜歡提交視為一組更改。 為此,Git 必須使用兩個快照。 例如,要查看H發生了什么變化,Git 將提取GH的快照,並比較它們。 對於相同的東西,Git 什么也沒說; 無論有什么不同, Git 告訴我們如何更改G中的副本以匹配H中的副本。 這向我們展示了發生了什么變化。 但事實上,我們所擁有的只是快照。

正如我在括號旁注中所說,分支名稱僅包含某個鏈中最后一次提交的 hash ID。 在某個鏈條中排在最后並不意味着它是最后一個。 例如,如果我們有:

...--G--H--I   <-- last

我們可以添加一個指向現有提交H名稱:

...--G--H   <-- interesting
         \
          I   <-- last

現在,名為interesting的分支是在H結束的提交鏈,而名為last的分支是在I結束的提交鏈。 提交H最后一個分支last ,它不是最后一個分支last

考慮到這一點,我們可以回顧一下第一個問題:

我曾嘗試將開發分支文件名重命名為其他名稱,例如git mv NewFile.txt DevFile.txt ,但是除了正常的文件內容合並行為之外,合並只是簡單地合並了重命名。 ...這樣...文件在合並后保持獨立...

當我們使用git merge時,這就是我們真正在做的事情:

          I--J   <-- br1 (HEAD)
         /
...--G--H
         \
          K--L   <-- br2

我們已經運行git checkout br1來選擇提交J作為當前提交,通過分支名稱br1作為當前分支 在這些圖中,這就是br1所附的(HEAD)告訴我們的。 然后我們運行git merge br2

Git 現在:

  1. 使用(從J向后和從L向后的連接)來查找合並基礎提交 這是兩個分支上最好的共享提交。 通常只有一個最佳共享提交,在這個特定的圖中,很明顯:它是提交H 提交G也是共享的,但它不如H ,因為它“更靠后”。

  2. 運行一對git diff --find-renames命令(或其內部等效命令,真的)。 Git 需要找出自共享起點(在本例中為提交H )以來發生了什么變化,以分別在提交JL中生成快照。 這需要兩個單獨git diff命令。

    如果您重命名了某些文件,那么--find-renames部分可以解決這個問題。 所有 Git 都有快照 提交H有一些文件集,提交J有一些文件集,提交L有一些文件集。 如果提交HNewFile.txt但沒有DevFile.txt ,並且提交LDevFile.txt但沒有NewFile.txt ,那么,也許這些真的是“同一個文件”,不管這意味着什么。

    找出這種“相同性”(如果有的話)是--find-renames選項的工作。 使用git diff時,您可以控制查找重命名設置。 當你使用git mergegit merge控制它們; 但請繼續閱讀。

  3. 為了繼續合並,Git 現在嘗試合並在步驟 2 中發現的兩組更改。這里有很多細節,我們將直接跳過。 :-) 假設 Git能夠組合這些,Git 然后將組合更改應用於H中找到的文件。 會保留我們的更改br1中的H -vs- J ),同時添加它們的更改( br2 中的H br2 L )。

  4. 如果 Git 能夠自行組合所有這些,它將 go on 進行新的合並提交,除非我們在git merge行上告訴它--no-commit-n 如果 Git 遇到合並沖突,它會停止並給我們留下一大堆需要清理的東西。 這就是我們在步驟 3 中跳過的那些細節變得重要的時候。 如果它停止 - 每個--no-commit或由於沖突 - 它仍然會記錄正確的內容以在我們完成合並時進行新的合並提交

合並完成后,我們最終得到:

          I--J
         /    \
...--G--H      M   <-- br1 (HEAD)
         \    /
          K--L   <-- br2

提交M是一個合並提交,它的特點是它向后鏈接到提交J提交L

當 Git 在歷史中倒退時,一次提交一個,它有一點問題,因為它現在必須同時倒退到兩個提交。 無需詳細說明,Git 在這里所做的一件事是它沒有顯示發生了什么變化,因為只有當我們選擇一個單一的前提交到 go 和我們的一個后提交時,才顯示更改的內容才有效。 對於合並提交,有兩個(或更多)“之前”提交。 (存在兩個以上父級的合並,稱為octopus merges ,但我們不會在這里處理它們。)

(您可以在此處使用一些技巧,例如-m來“拆分”合並或--first-parent以避免查看合並提交的第一個父級以外的任何內容。我們也不會在這里介紹它們;它們是對以后合並錯誤的取證工作更有用,而不是現在實現良好的合並。)

一般重命名檢測和合並

這種“檢測重命名”操作對於以下方面很重要:

  • git diff ,如果你打開它;
  • git log --follow ,我們稍后會描述;
  • git merge ,你已經遇到過。

git merge運行它的兩個git diff操作時,合並是控制重命名檢測的操作。 但是git merge有命令行選項來告訴它如何控制它:

  • -X find-renames= number告訴 Git 使用什么 *rename 閾值;
  • -X no-renames完全禁用重命名檢測。

(在非常舊的 Git 版本中, -X find-renames=...拼寫為-X rename-threshold=...

啟用重命名檢測時,其默認值為50 這個值是一個百分比,盡管它的百分比到底多少有點不確定。 這意味着可能的值范圍是從零到 100。然而,零實際上永遠不會發生,並且 100 保留用於“文件內容完全匹配”,因此有用的值通常是 1 到 99。默認值50表示“50% 相似”。

每當 Git 比較兩個文件時——我們稱它們為 L 和 R,對於左側和右側文件——它可以計算“相似度指數”值。 這是基於觀察 Git 在嘗試確定 R 中保留 L 中的多少字節時所做的觀察,以及 R 中的多少字節是全新的,或者需要從 L 中插入新的東西東西,什么的。 實際的計算是模糊的:它沒有記錄在任何地方。 但是,如果你自己運行git diff --find-renames ,並給它兩次提交 hash ID,Git 會告訴你什么相似性索引was. 所以:

git diff --find-renames=01 --name-status L R

將比較提交 L 和提交 R,對於 R 中的每個新文件並從 L 中丟失,嘗試猜測該文件是否被重命名。 使用此處的01值,它將接受低至“相似度指數 1%”(1% 相似度)的匹配並將其作為重命名。 然后--name-status選項使git diff僅打印出文件名和狀態-es,並且R這意味着檢測到重命名)將跟隨實際相似性。

--diff-filter=R添加到我們的git diff使其打印R狀態文件,以便我們查看每個檢測到的重命名的實際相似性索引值是多少。

如果 Git沒有檢測到您希望它檢測到的重命名,您可以嘗試將git merge-X find-renames=25或更小的值合並。 請注意, 2表示 20%,而不是 2%; 你必須寫022%來表示 2%。

如果 Git正在檢測您希望它檢測到的重命名,您可以嘗試將您的git merge-X find-renames=75或更大的值,甚至-X no-renames renames 合並。

git log --follow

注意當使用git log --follow時,你必須給 Git 一個路徑名:

git log --follow path/to/file.ext

Git 在這里所做的是從通常的反向、一次提交遍歷一些提交圖開始。 假設此時我們有這張圖:

          I--J
         /    \
...--G--H      M--N--O   <-- somebranch (HEAD)
         \    /
          K--L

第一個比較是提交N與提交O Git 通過在內部運行git diff --find-renames NO來檢查名為path/to/file.ext的文件是否被修改(打開了一些快捷方式以不打擾查看其他文件名)。 如果該文件的副本在NO中都相同,則 Git 將移回N而不顯示提交O 如果文件在兩個提交中都存在但不同,則 Git 顯示提交O並移回N 如果文件在N中不存在,但從NO的差異可以找到rename ,則 Git 顯示提交O並移回N ,但這一次,因為文件已從old/path/to/file.ext , Git 現在開始尋找該名稱。

所以,如果我們從O移動到N ,我們現在正在尋找提交N的任何名稱。 我們現在比較提交M ——一個合並提交:它仍然有一個快照,就像任何其他提交一樣——提交N ,以查看文件是否更改和/或更改了名稱。 如果它確實更改或更改名稱(或兩者), git log --follow顯示提交M 如果沒有,它不會顯示M

在這里,因為M是一個合並提交,所以事情變得很棘手。 如果我們沒有禁用Git 調用的歷史簡化功能,則 Git 現在會查看M的所有父項,以找到文件(無論此時其名稱是什么)匹配任何父項。 如果它找到其中之一,Git 將 go 僅向下合並的那條腿 這是在行動的歷史簡化。

您可以使用--full-history來防止這種情況,但這與 `--find-renames 的交互作用很差,正如我們將看到的那樣。

假設名稱還沒有改變,所以我們仍然有path/to/file.ext 這是我們在提交ONM中的文件名。 這也是提交L中的名稱,但在提交K中,文件不同和/或具有不同的名稱。

由於文件在L匹配,我們的git log --follow將從M走到L ,並將繼續查看提交L ,然后K ,然后H ,然后G ,等等。

如果我們添加--full-history ,我們的git log --follow也會M走到J 但是,如果它已經從LK的分支,並且文件在LK的轉換中被重命名,Git 將在JI提交中尋找錯誤的文件名

結論

這里要帶走的是:

  • 沒有文件歷史記錄之類的東西。 只有提交歷史,因為提交就是歷史。
  • 然而,Git 有一些工具——例如git log --follow可以通過選擇提交歷史的有趣部分來嘗試生成一個合成的、減少的“文件歷史”。
  • 這可以很好地工作,但它也可以 go 錯誤。 注意歷史簡化。
  • --follow今天做的--follow 不太好。 它依賴於重命名檢測——這並不糟糕,但一開始也不是很好——並在此基礎上添加了一些非常草率的假設,這使得它變得更加糟糕。 改進它會很困難(大約十年前我看過這個,從那時起我和其他任何人都沒有修復它)。
  • 合並依賴於這個相同的重命名檢測系統。

可能可以通過對重命名檢測閾值大驚小怪獲得有用的結果,但請注意,您可能希望對文件進行某種更改(即使只是添加一個大注釋)以降低重命名相似性索引一點點。

暫無
暫無

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

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