【git】git为什么会出现双向合并

Git 分支合并(merge)的核心逻辑是 "整合两个分支的提交历史",你觉得"相互合并",本质是 Git 会把两个分支从"共同祖先提交"之后的所有修改都整合到一起,最终形成一个统一的提交历史------并非"分支A合并到B,B的修改就不影响A",而是双向整合(但分支本身不会自动切换,需手动操作)。

要理解这个问题,关键要搞懂 Git 合并的底层逻辑和"分支的本质":

一、先明确:Git 分支的本质是"提交指针"

Git 里的分支,并不是一个独立的文件夹或副本,而是一个 指向某个提交(Commit)的指针

比如:

  • main 分支指针指向它的最新提交(如 C3);
  • 你从 main 拉的 feature 分支指针,初始也指向 C3(共同祖先);
  • 之后你在 feature 改代码提交 C4C5feature 指针就移动到 C5;同时别人在 main 改代码提交 C6main 指针移动到 C6

此时两个分支的提交历史是:
C0 → C1 → C2 → C3(共同祖先)→ C4 → C5(feature)
C0 → C1 → C2 → C3(共同祖先)→ C6(main)

二、merge 的核心逻辑:"整合分歧,而非单向覆盖"

当你执行 merge 时(比如"把 feature 合并到 main"),Git 做的不是"用 feature 覆盖 main",而是:

  1. 找到两个分支的 共同祖先提交 (上例中的 C3);
  2. 对比:mainC3C6 的所有修改(A组修改);featureC3C5 的所有修改(B组修改);
  3. 把 A 组和 B 组修改 同时整合 到一起,生成一个新的"合并提交"(如 C7);
  4. 最后,只有当前所在的分支指针会移动到合并提交 C7(另一个分支指针不变)。

整个过程的结果:

  • main 分支的历史变成:C0→C1→C2→C3→C6→C7(包含了 featureC4C5 所有修改);
  • feature 分支的历史仍然是:C0→C1→C2→C3→C4→C5(指针没动,还是指向 C5)。

你觉得"相互合并",其实是 main 整合了 feature 的修改,而 feature 并没有自动整合 main 的修改------除非你再切换到 feature 分支,执行 git merge main,把 mainC6C7 修改整合到 feature 里。

三、为什么会有"双向影响"的错觉?

  1. 合并是"当前分支吸收目标分支的修改"

    比如:

    • 切换到 main,执行 git merge featuremain 吸收 feature 的修改(main 有了 feature 的代码);
    • 切换到 feature,执行 git merge mainfeature 吸收 main 的修改(feature 有了 main 的代码);
      如果你先后做了这两步,就会感觉"两个分支互相合并了"------但这是两次独立的合并操作,不是 Git 自动"双向合并"。
  2. 共同祖先的存在,让"双向修改"被统一整合

    两个分支的修改都基于共同祖先,Git 会把双方的修改都保留(除非有冲突,需要手动解决)。比如:

    • main 改了 a.txt 的第1行;
    • feature 改了 a.txt 的第5行;
      合并后,a.txt 会同时保留"第1行的修改"和"第5行的修改"------这不是"相互覆盖",而是"双向整合",本质是 Git 要保留所有分支的有效修改。

四、关键澄清:merge 不会"自动让两个分支同步"

很多人误以为"合并后两个分支就一样了",但实际是:

  • 只有 当前执行 merge 的分支 会被更新(指针移到合并提交);
  • 目标分支(被合并的分支)的指针仍然停留在原来的最新提交,不会自动同步当前分支的修改。

比如:

  1. 切换到 maingit merge featuremain 有了 feature 的修改(main 指针到 C7);
  2. 此时 feature 指针还是 C5feature 分支里没有 main 之前的 C6 修改;
  3. 只有当你切换到 feature,再执行 git merge mainfeature 才会吸收 mainC6C7 修改,此时两个分支的提交历史才完全一致。

五、举个具体例子,一看就懂

假设初始状态(共同祖先 C3):

复制代码
main:    C0 → C1 → C2 → C3 → C6
feature: C0 → C1 → C2 → C3 → C4 → C5
第一步:main 合并 feature
bash 复制代码
git checkout main
git merge feature

Git 整合 C4C5(feature 的修改)和 C6(main 的修改),生成合并提交 C7

复制代码
main:    C0 → C1 → C2 → C3 → C6 → C7(包含 C4、C5 的修改)
feature: C0 → C1 → C2 → C3 → C4 → C5(无变化)
第二步:feature 合并 main
bash 复制代码
git checkout feature
git merge main

Git 整合 C6C7(main 的修改)到 feature,生成合并提交 C8

复制代码
main:    C0 → C1 → C2 → C3 → C6 → C7
feature: C0 → C1 → C2 → C3 → C4 → C5 → C8(包含 C6、C7 的修改)

此时两个分支的代码完全一致,这就是你感知到的"相互合并"------但这是两次手动操作的结果,不是 Git 自动双向合并。

总结

Git merge 的"相互合并"本质是:

  • 合并的核心是 整合两个分支的分歧修改(从共同祖先之后的所有修改),而非单向覆盖;
  • 只有"当前分支"会吸收目标分支的修改,目标分支不会自动同步;
  • 若想让两个分支完全一致,需要在两个分支上分别执行 merge(或用 rebase 同步历史)。

简单说:merge 是"当前分支拿目标分支的修改",不是"两个分支互相推送修改"------你看到的"相互合并",是两次合并操作的结果,而非 Git 的默认行为。

相关推荐
笨鸟不是菜鸟4 小时前
gitignore文件如何添加忽略文件或文件夹
git
灰色人生qwer4 小时前
git add . 添加超长文件名报错了怎么办?
git·python·elasticsearch
星海拾遗13 小时前
git rebase记录
大数据·git·elasticsearch
ljh57464911914 小时前
PhpStorm 2022.3 版本中,修改使用 Git 提交时看到弹出式的对话框模式
ide·git·php·phpstorm
云闲不收16 小时前
git rebase
git
江上清风山间明月16 小时前
git pull和git checkout在恢复文件的区别
git·pull·checkout
海鸥8117 小时前
in argocd ‘/tmp/_argocd-repo/../.git/index.lock‘: No space left on
git·argocd
尔嵘18 小时前
git操作
大数据·git·elasticsearch
好评12418 小时前
Linux文件上传git
linux·运维·git