簡體   English   中英

將 develop 合並為 master 和 master 合並為 develop 之間的區別?

[英]Difference between merging develop into master and master into develop?

將 develop 合並為 master 和 master 合並為 develop 之間的區別? 我嘗試將 develop 合並到 master 中,它給了我 34 個文件和 1,312 個添加和 324 個刪除,我嘗試將 master 合並到 develop 中,它給了我 251 個文件和 87,123 個添加和 1,321 個刪除。 我的猜測是它需要時間從 master 分離出來然后進行所有更改並將其與我們要合並到的分支中的文件中更改的文件進行比較? 我對么?

這意味着要使兩個分支相同,我們需要將 master 合並到 develop 中,然后每次當兩個分支每天都被更改 1 個月 + 由十幾個開發人員更改時,將 develop 合並到 master 中?

git-diff 給我們帶來了什么? 它是否給出了兩個分支的所有差異,或者如果我們嘗試將分支 1 合並到分支 2 中會得到什么?

要了解該問題的答案,讓我們從有關 Git 的一些事實開始:

  • Git 存儲提交,而不是文件或分支。 提交存儲庫中的歷史記錄:每個提交都有一個唯一編號( hash IDobject ID又名 OID),每個提交包含兩件事:每個文件的完整快照,以及一些元數據。 任何一次提交中的元數據都包含先前提交 hash ID 的列表,它可以讓 Git 將較晚的提交與較早的提交聯系起來。 大多數(所有“普通”)提交中只有一個先前的 hash ID,它將提交鏈接到其父級。 這允許 Git 向后工作,從最近的提交到最早的提交。

  • 分支名稱如mastermaindevelopbr1feature/tall等,只包含一個提交 hash ID。 根據定義,名稱中存儲的任何 hash ID 都是該分支上的最新提交。

僅從這兩個事實我們就可以開始可視化提交:

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

這里我們有一個像br1這樣的分支名稱,它選擇或指向該分支上的最后一次提交。 該提交有一些 hash ID,我們將其稱為H ,這樣我們就不必生成一些隨機的東西並嘗試記住它。

提交H包含所有文件的快照,但也包含元數據以說明提交H的人、原因(他們的日志消息)等。 提交H的元數據存儲了前一次提交的 hash ID,我們將其稱為G 所以提交H向后指向更早的提交G

提交G作為提交,存儲完整快照和元數據。 G中的元數據使G向后指向更早的提交F ,這也是一個提交,因此它向后指向另一個更早的提交。

當我們查看帶有git diffgit show的提交時,我們實際上為 Git 提供了兩個提交 hash ID。 我們從提交本身開始,例如H ,可能使用分支名稱br1

git show br1

Git 使用它來定位H ,然后使用H來定位其父提交G 然后 Git 將兩個快照提取到 memory 中的一個臨時區域,並進行比較。 畢竟,我們只對發生變化的文件感興趣。 (這得益於提交快照刪除重復文件內容的事實,因此如果HG主要共享大部分文件,Git 可以立即判斷,甚至不必費心提取這些文件。)

對於確實發生變化的文件,Git 計算出一個“變化配方”——差異——並將其顯示為“發生了什么”。 這對於像提交H這樣的普通提交非常有用。 但它隨着合並而崩潰。

合並

要理解git merge ,我們從合並的目標開始:合並工作 讓我們畫一幅有某種形式的分叉的一些提交的圖片,這樣我們就有了兩個不同的工作鏈,就像這樣:

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

也就是說,此時有兩個“最新”提交。 提交J是最新的; 它的父級是I ,其父級是H ,其父級是G等等。 但是提交L也是最新的; 它的父級是K ,其父級是H ,其父級是G等等。 這里J是最新的br1提交L是最新的br2提交

與往常一樣,每次提交都包含所有文件的完整快照。 為了結合兩個分支的工作,我們需要找到變化 我們該怎么做? 好吧,我們已經知道答案了:我們可以使用git diffgit show來選擇兩個提交並進行比較

每個人都首先嘗試的事情——這是行不通的——是選擇提交JL並比較它們。 但這顯示了這兩個“最新”之間的不同之處,這通常不是我們想要的。 例如,Alice 可能通過修復 README 中的錯別字並添加功能 1 來創建br1 ,而 Bob 通過修復其他文檔中的不同錯字並添加功能 2 來創建br2 。如果我們比較JL ,配方 Git 將為我們提供是:刪除 Alice 的修復和功能,並添加 Bob 的修復和功能 我們想要的是添加 Alice 的修復和功能,以及 Bob 的修復和功能

切入正題,這里的技巧是從提交H開始。 那是最好的共享提交:實際上是在兩個分支上的提交。 通過從J開始向后工作以及L開始向后工作,我們發現H是最好的共享提交。 所以我們比較HJ看看 Alice 做了什么,然后——分別!——比較HL看看 Bob 做了什么。 使我們獲得了要組合的更改集。

然后 Git 將使用一些非常簡單的規則盡力組合這些更改食譜:

  • 如果沒有人動過一個文件,使用它的任何版本:這三個都是一樣的。
  • 如果一個分支觸及某個文件而另一個分支根本不觸及它,則使用更改它的一個分支中的更改文件。
  • 如果兩者都觸及了某個文件,請嘗試逐行合並更改。 如果它們位於不同的、不重疊的線上,則可以將它們組合起來。 Git 添加了一條線不能鄰接的規則。 如果它們確實重疊,則它們必須 100% 相同。 否則,Git 將聲明一個合並沖突並讓您(程序員)收拾殘局。

這些規則經常有效,因此git merge可以從兩個分支中獲取兩組更改,將合並的更改應用於H中的快照,並將其用於新快照。 根據您喜歡的查看方式,結果是我們在添加br2更改的同時保留br1更改,或者在添加br1更改的同時保留br2更改。 請注意,與普通的數學加法一樣,無論加數的順序如何,結果都是相同的(也就是說,我們不需要定義單獨的“被加數”與“加數”,因為該操作是可交換的)。 1個

為新提交創建快照后,Git 然后進行新提交。 您提供一條日志消息,在其中解釋為什么進行合並——或者您使用蹩腳的默認消息,例如merge branch br2 ,這是大多數人真正做的——然后 Git 進行一個新的提交,就像任何提交一樣:它有快照和元數據。 新提交的特別之處在於,它有兩個父項,而不是只有一個父項:

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

請注意,我已將此圖片中的分支名稱歸檔。 每當你進行任何新的提交時——無論是git commit ,還是git merge ,或者git cherry-pick或者git revert或者其他什么——Git 都會自動為你更新當前的分支名稱,所以M現在是最新的提交。 但是更新了哪個分支名稱 好吧,這取決於您on的分支名稱git status

$ git status
On branch `br1`
...
$ git merge br2

結果是:

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

也就是說,您在“on” br1上——這就是附加到br1HEAD在這里的意思——而且您仍然在,所以新提交也是br1的最新提交。 但是,如果git status表示您On branch br2並且您已經運行git merge br1 ,那么更新的將是名稱br2


1請注意,向git merge添加選項(例如-s ours-X theirs )會改變這一點:操作不再是可交換的。


這回答了你問題的第一部分

[將 develop 合並到 master 和 master 合並到 develop 之間有什么區別?

一個將推進名稱master ,另一個將推進名稱develop 也就是說,您將擁有:

       o--...--o
      /         \
...--o           M   <-- master (HEAD)
      \         /
       o--...--o   <-- develop

或者:

       o--...--o   <-- master (HEAD)
      /         \
...--o           M   <-- develop (HEAD)
      \         /
       o--...--o

當你完成時。 M中的快照將是相同的。

rest 要么不重要要么至關重要

由於提交很重要,因此用於訪問合並結果的名稱並不是很重要。 好吧,除了一件事:如果您繼續使用該名稱進行更多提交,並且想要訪問這些“更多提交”,那么您擁有的名稱 Git advance 絕對是至關重要的。

那是:

       o--...--o
      /         \
...--o           M--N   <-- master (HEAD)
      \         /
       o--...--o   <-- develop

和:

       o--...--o
      /         \
...--o           M   <-- master (HEAD)
      \         /
       o--...--o--N   <-- develop

是完全不同的圖片。 那是因為我們通過從某個現有提交的快照開始,更改一些內容並提交結果來進行新的提交。 因此, N的快照中的內容將取決於您是否從提交M (合並結果)開始。

但請注意,我們可以隨時重命名這兩個分支:

       o--...--o
      /         \
...--o           M   <-- smorgas (HEAD)
      \         /
       o--...--o   <-- bord

重要的不是名稱,而是提交。 所以你應該按照你想要的方式安排提交,並使用有助於你實現目標的名稱。 要正確地做到這一點,您首先需要很好地理解您的目標實際什么。 對 Git 來說,重要的是提交; 分支名稱只是 Git 幫助您找到正確 OID 的方式,以便您可以找到您關心的提交。

它的rest

我嘗試將 develop 合並到 master 中,它給了我 34 個文件和 1,312 個添加和 324 個刪除,我嘗試將 master 合並到 develop 中,它給了我 251 個文件和 87,123 個添加和 1,321 個刪除。

任何時候 Git 說“添加了一個文件,刪除了R個文件,更改了C 個文件”(加上在更改的文件中添加或刪除的行數),Git 運行git diff 當我們有這個:

...--F--G--H--I--J

並且我們將提交Hgit show一起使用,這里很簡單:Git 正在比較H的父提交G與提交H以獲得該組數字。 但是當我們有:

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

Git 試圖告訴我們關於提交M的一些信息,好吧, M兩個父母,而不僅僅是一個。 那么 Git 在它運行的git diff中使用了哪一個來得出數字?

這里的答案是 Git 使用合並的第一個父級。 當你運行時:

git switch somebranch
git merge otherbranch

並獲得一個在somebranch進行的新合並提交,它的第一個父級剛才在somebranch尖端的提交,它的第二個父級(仍然)在otherbranch尖端的提交。 所以如果我們切換到br2並合並br1 ,你會在這里得到不同的數字。 如果我們將父編號添加到圖中,我們可以直觀地展示它是如何工作的:

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

對比:

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

因此,即使M中的快照是相同的,您也會看到不同的統計數據,因為 Git 正在比較MJL

這意味着要使兩個分支相同,我們需要每次都將 master 合並到 develop 中,然后再將 develop 合並到 master 中......

可以這樣做,但要小心! 並非每個git merge實際上都會進行合並提交 如果你強制git merge進行合並提交,你開始:

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

然后運行git switch br2 && git merge --no-ff br1 ,你得到這個:

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

其中N的第一個父級是LN的第二個父級是M 提交MN將(通常為2 )具有相同的快照,但它們具有不同的編號並且是不同的提交。

但是git merge並不總是進行合並提交。 考慮以下分支結構:

...--G--H   <-- main (HEAD)
         \
          I--J   <-- feature

在這里,我們使用了兩次提交來開發一個新功能。 我們現在准備合並它。 如果我們運行git merge feature ,真正的合並必須:

  • 找到合並基礎:那是提交H
  • H H比較,以查看我們更改了什么,但這顯然什么都沒有,因為HH
  • H中的快照與J中的快照進行比較,以查看它們更改了什么;
  • 將我們的未更改的內容添加到他們的更改的內容中,並將這些更改應用到H中的內容,給出他們的快照;
  • 進行新的合並提交。

結果如下所示:

...--G--H------M   <-- main (HEAD)
         \    /
          I--J   <-- feature

M中的快照與J中的快照匹配。 但是 Git 走了一條捷徑。 它對自己說:好吧,呃,如果我 diff 提交H反對自己,那顯然是空的。 合並結果將包含來自J的快照。 如果我只使用提交J怎么辦? 如果您允許它這樣做, git merge使用提交 J ,給出:

...--G--H
         \
          I--J   <-- feature, main (HEAD)

之后就沒有理由為圖中的扭結煩惱了:

...--G--H--I--J   <-- feature, main (HEAD)

如果你讓 Git 這樣做,這就是你切換到developgit merge master時你會得到的那種合並:

       o--...--o
      /         \
...--o           M   <-- develop (HEAD), master
      \         /
       o--...--o

現在只有一個提交M兩個分支上都是最新的。

如果你現在develop上進行新的提交,你會得到:

       o--...--o
      /         \
...--o           M   <-- master
      \         / \
       o--...--o   N   <-- develop (HEAD)

注意提交N的父級是提交M :它是一個普通的單親非合並提交。 如果您在擁有此名稱時首先刪除名稱develop ,這與您得到的結果相同:

       o--...--o
      /         \
...--o           M   <-- master (HEAD)
      \         /
       o--...--o   [the name develop *was* here, but now is deleted]

然后從提交M創建一個develop

git branch develop    # or git switch -c develop

以便新名稱指向提交M

某些 web 托管站點(例如 GitHub)不允許您通過其 web 界面執行快進非真正合並操作。 對於這些站點,您必須使用命令行 Git,或刪除並重新創建分支名稱,才能在需要時獲得這種特殊效果。 何時以及是否需要它是您的選擇!


2您可以通過多種方式擊敗它,包括兩種選擇和所謂的邪惡融合


結論

您需要知道 Git 實際上做了什么以及您想要什么。 人們為 Git 想出了各種花哨的分支模型,但只是選擇一個並遵循它而不理解為什么有人選擇model 會給你帶來麻煩。 分支名稱 Git 的要點是使用人類可讀的名稱自動查找最新的提交,並且因為人類這樣做,所以您需要了解人類的想法; 這個,而不是 Git 本身,才是真正困難的原因。

暫無
暫無

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

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