防微杜渐 —— 为什么一次 Sync 会变成一次 merge?

前言

为什么团队开发项目总在强调分支隔离?

别搞那么复杂,直接在 develop 上改吧,改完同步一下就行。

  • "就改一个小功能,没必要专门切分支吧?"

  • "只是修个样式,走完整套流程也太重了。"

  • "平时大家不也都是这么干的,好像也没出过什么事。"

这些话在大多数时候都是"对的" 直到某一次事故,将会教会你分支隔离的意义

分支隔离做了什么

在此之前笔者也不觉得分支隔离是必须的,只是习惯性作为一个习惯,除了感觉能带来合并时候的 review 方便没有感受其他实质性帮助,直到今天一个三人协同的远端分支出现了分叉;

直到这次事故发生:

只是点了一次 Sync,远端 develop 为什么会突然分叉?

猛然意识到:

分支隔离并不是为了规范流程,而是为了避免某些"以为只是同步,其实是在做 merge的时刻。

场景复原

下文统一用 A、B、C 代指三明成员,梳理输出对应关键信息,特有信息已做混淆处理

先看当时发生了什么。

在一个不到 1 小时的时间窗口里:

  • B 把一个 release 分支合进了主线
  • A 在本地继续开发,并提交了一次代码
  • C 又在远端主线上推进了一次修改

问题在于:

A 的本地分支,其实已经不是最新的。但他自己并没有意识到这一点。 于是他做了一个非常"日常"的操作:
👉 点了一下 VS Code 的 Sync

也就是这一刻,事情开始失控

关键提交如下:

时间 提交 角色 含义
10:49:22 +08 COMMIT_B1 B branch_release_A 合入 branch_mainline
11:11:55 +08 COMMIT_A1 A A 在本地 branch_mainline 上继续开发并提交
11:17:29 +08 COMMIT_C1 C C 在远端 branch_mainline 线上继续推进字体修改
11:20:05 +08 MERGE_A2 A A 解决冲突后生成的 merge commit,但结果树已异常
11:52:48 +08 MERGE_A3 A 再次 Sync 后,把错误树和 C 的提交一起写到了远端

COMMIT_A1COMMIT_B1COMMIT_C1 并不是按一条线串行发生的,而是多人在共享 branch_mainline 上并行推进的。

bash 复制代码
远端 mainline:
COMMIT_B1 ── COMMIT_C1
        \
         (A本地分支)
          COMMIT_A1
                \
                 MERGE_A2(错误)
                        \
                         MERGE_A3 → push(污染远端)

1️⃣ A 在"过期主线"上开发

  • 本地 branch_mainline 落后于远端
  • 但仍直接在上面提交(COMMIT_A1

2️⃣ 同一时间多人直接操作主线

  • B、C、A 都在直接改 branch_mainline
  • 没有隔离分支(feature 分支)

👉 导致:历史出现分叉(diverge)

为什么出现了分叉

这次问题表面上看,是 VS Code 的同步按钮把远端 branch_mainline 弄分叉了。

我一直都是用这个功能提交的,为什么今天出了问题

问题的关键不在 push,而是在第一次 Sync 时,Git 已经进入了 merge。

换句话说:

A 以为自己是在"同步代码",

但实际上,Git 在本地帮他做了一次合并。

bash 复制代码
git pull --tags origin branch_mainline

# 实际含义:fetch + merge
git push origin branch_mainline:branch_mainline

如果用流程表示,大概是这样:

flowchart TD A[共同旧基点 BASE_A] A --> B[A 本地提交 COMMIT_A1] A --> C[远端前进到 COMMIT_B1] B --> D[第一次 Sync
git pull 触发 merge] C --> D D --> E[本地生成 MERGE_A2
分支开始偏离] C --> F[远端继续前进到 COMMIT_C1] E --> G[第二次 Sync] F --> G G --> H[MERGE_A3 被 push 到远端] H --> I[远端 branch_mainline 出现并行线
且尖端携带错误树]
  • 第一次 Sync 没有把 A 直接更新到远端最新,而是把 A 的本地提交和远端状态做了 merge。
  • 第二次 Sync 又把这条已经偏离的本地线继续推回远端,于是远端也开始偏移。

由于:

sql 复制代码
git pull  → git merge

结果:

  • 产生 MERGE_A2
  • 冲突被错误解决
  • 形成"错误的代码树"

不是工具问题,是协同策略隐患

这时候一个很自然的问题就会出现:

"我一直都是这么点 Sync 的,为什么这次出问题?"

一直以来笔者也认为 Sync 按钮只是执行简单的 pull 与 push 操作,但查看 Git 执行日志出现了 merge ???

真正决定行为的,是当前分支的状态。

当出现下面条件时:

  • 本地有提交
  • 远端也有新提交
  • pull.rebase=false

那一次普通的 pull,就一定会变成:

👉 fetch + merge

Git 的同步语义执行是依赖于 pull.rebase 配置的,这造成了

  • 如果本地只是落后远端,可能是快进更新。
  • 如果本地和远端都前进了,而且 pull.rebase=false,那 pull 就一定会触发 merge。

VS Code 并没有做错什么,它一直都是这么工作的。

长歪的结果树

merge 不是"选最近的那棵树",而是基于共同祖先做三方合并,把当前暂存区里的结果写成新树

这就是真正危险的地方 👉 merge 的结果,不一定都有问题,但麻烦的是 它看起来是对的

很多人会以为:

  • 冲突解决了 → 就安全了
  • 能提交 → 就没问题

但 Git 并不会帮你保证结果树是对的。它只是把你当前暂存区的内容,写成一个新的 commit。

所以一旦冲突处理不完整,或者误操作:

在不知不觉中,远端内容将被覆盖

sequenceDiagram participant B as B participant A as A 本地 branch_mainline participant V as VS Code Sync participant R as 远端 branch_mainline participant C as C B->>R: 合入 COMMIT_B1 A->>A: 基于旧基点提交 COMMIT_A1 A->>V: 点击 Sync #1 V->>R: pull branch_mainline R-->>V: 返回包含 COMMIT_B1 的最新状态 V->>A: 在本地做 merge A->>A: 形成 MERGE_A2 Note over A: 本地分支开始偏离 C->>R: 追加 COMMIT_C1 A->>V: 点击 Sync #2 V->>R: 再次 pull 后 push V->>A: 本地合入 COMMIT_C1 V->>R: 推送 MERGE_A3 R->>R: 远端出现并行线
bash 复制代码
本地 `branch_mainline` 跟踪远端 `branch_mainline` 的 pull / push 关系;
`pull.rebase=false`;
`11:12:45` 的 `git pull --tags origin branch_mainline`;
`11:15:22` 的未解决冲突报错;
`11:15:36` 的单文件 `git add`;
`11:20:05` 的 `MERGE_A2`;
`11:52:48` 的再次 `pull`;
`11:52:50` 的 `push origin branch_mainline:branch_mainline`;

`git show -m --stat MERGE_A2` 和 `git show -m --stat MERGE_A3` 的父节点差异。

终结

这不是 VS Code 的 Bug,而是"共享主线被当成日常开发分支"之后,被 GUI 按钮放大的协作事故。

本质问题在于:

👉 共享主线,被当成了日常开发分支

很多团队不是不会用 Git,习惯用一个"看起来很轻"的操作,去触发一件"实际上很重"的行为。

没出问题的时候,大家会觉得"这样改更快"。


tip:如果本机还需要保留 branch_mainline 用于查看和同步,建议至少配置:

bash 复制代码
git config pull.ff only

这样一旦本地和远端分叉,Git 会直接失败,而不是自动 merge。

共享主线,不应该被当成日常开发分支

问题从来不是 Sync 按钮,而是我们在用错误的方式使用主线分支。

分支隔离的意义,也不是"规范流程",而是: 避免在错误的时间、错误的分支上发生 merge

每条规则背后,必然有案例曾经发生

相关推荐
dyxal3 小时前
Windows 下 Git 离线安装与 VS Code 便携版集成完全指南
windows·git·elasticsearch
K3v3 小时前
【git】自动化合并推送脚本
git·自动化
鹓于3 小时前
Android APK开发到发布全流程指南
git·github
花哥码天下4 小时前
Git 多远程仓库管理
git
花间相见19 小时前
【终端效率工具01】—— Yazi:Rust 编写的现代化终端文件管理器,告别繁琐操作
前端·ide·git·rust·极限编程
蜡台20 小时前
Git 常用配置:修改地址 ,提交模板配置
git·gitlab·template·git commit·gitbash
拽着尾巴的鱼儿21 小时前
git不追踪已提交至仓库的本地又修改文件
git
We་ct1 天前
Git 核心知识点全解析
开发语言·前端·git·gitee·github
无限进步_1 天前
【C++】寻找字符串中第一个只出现一次的字符
开发语言·c++·ide·windows·git·github·visual studio