未记录事实核验 · 状态:现行内容
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用范围与待维护事项
本文适用于已掌握提交、分支和远程协作的读者,建议在可丢弃的练习仓库中操作;主流程针对文本文件的普通 merge 冲突,不覆盖二进制、删除/修改等冲突。开始前请提交或妥善暂存未提交改动,abort 不保证恢复复杂的原有工作区。rebase 示例仅用于未共享的个人提交,共享分支须先遵循团队约定。SQL 为教学片段,无完整表结构与测试数据;删除标记并暂存不代表业务正确,合并后仍需测试过滤、关联和聚合结果。
Git 冲突解决 - 合并冲突处理与最佳实践
abstract 本章概要
- 理解合并冲突产生的原因
- 掌握冲突标记的阅读方式
- 学会手动解决冲突的完整步骤
- 了解减少冲突的最佳实践
冲突是什么,为什么会发生
合并冲突不是 bug,也不是有人出错了,它是两个人同时改了同一个地方的自然结果,Git 不知道该保留谁的版本,只能请你来做仲裁。
典型场景:
远程 main(你同事 push 的):
...
WHERE order_date >= '2024-01-01'
AND order_status = 'completed' ← 同事加了状态过滤
...
你的本地分支(你改的):
...
WHERE order_date >= '2024-01-01'
AND user_type = 'VIP' ← 你加了用户类型过滤
...
你和同事都改了同一行附近的代码,Git 无法自动判断是应该保留哪个条件,或者两个都要保留,于是产生了冲突。
哪些操作会触发冲突
git merge:合并分支时git pull:拉取远程代码时(本质是 fetch + merge/rebase)git rebase:变基操作时git cherry-pick:挑选提交时
冲突标记:读懂 Git 的语言
当冲突发生时,Git 会修改产生冲突的文件,插入特殊标记:
SELECT
user_id,
SUM(order_amount) AS total_amount
FROM orders
WHERE order_date >= '2024-01-01'
<<<<<<< HEAD
AND user_type = 'VIP'
=======
AND order_status = 'completed'
>>>>>>> feature/add-status-filter
GROUP BY user_id
理解这三段:
| 标记 | 含义 |
|---|---|
<<<<<<< HEAD |
冲突开始,HEAD 是你当前所在的位置 |
======= |
分隔线 |
>>>>>>> feature/add-status-filter |
冲突结束,这是来自哪个分支的内容 |
在本文的普通 merge 示例中,======= 以上是当前分支的内容,以下是被合并分支的内容。rebase 时不能简单按“我的/对方的”判断:上侧通常是变基目标加上已重放的提交,下侧是当前正在重放的提交;编辑器的 Current/Incoming 也应按这一上下文理解。
解决冲突的完整步骤
第一步:发现冲突
# 示例明确使用 merge 方式拉取;存在重叠修改时可能发现冲突
git pull --no-rebase
# 输出:
# Auto-merging sql/orders_etl.sql
# CONFLICT (content): Merge conflict in sql/orders_etl.sql
# Automatic merge failed; fix conflicts and then commit the result.
# 查看哪些文件有冲突
git status
# 输出:
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
# both modified: sql/orders_etl.sql
第二步:打开冲突文件
用你熟悉的编辑器打开有冲突的文件,找到所有 <<<<<<< 标记。
如果用 VS Code,它会直观地高亮显示冲突区域,并提供按钮:
- "Accept Current Change":保留你的版本
- "Accept Incoming Change":保留对方的版本
- "Accept Both Changes":保留两者(上下拼接)
- "Compare Changes":并排对比
第三步:手动决策
对于上面的例子,正确的解决方式可能是:两个条件都需要,应该合并:
-- 解决后的结果(删掉所有冲突标记,保留正确内容)
SELECT
user_id,
SUM(order_amount) AS total_amount
FROM orders
WHERE order_date >= '2024-01-01'
AND user_type = 'VIP' -- 保留 VIP 过滤
AND order_status = 'completed' -- 同时保留状态过滤
GROUP BY user_id
你需要根据业务逻辑来决定:
- 保留我的版本
- 保留对方的版本
- 合并两者
- 写一个全新的版本
warning 解决冲突前必须理解业务含义
不要机械地"保留所有"或者"保留最新的"。数据代码的冲突往往有业务含义,必须理解每个改动的目的,才能做出正确的合并决策。不确定的时候,和改动的作者沟通一下再动手。
第四步:标记冲突已解决
# 把解决好的文件加入暂存区
git add sql/orders_etl.sql
# 确认所有冲突都解决了
git status
# 此时 sql/orders_etl.sql 从 "Unmerged" 变成了 "Changes to be committed"
第五步:完成合并
# 以下两种提交方式二选一,不要依次执行
# 方式一:使用 Git 自动生成的 merge commit 信息模板
git commit
# 方式二:直接指定更有说明性的信息
git commit -m "merge: 合并feature/add-status-filter,同时保留VIP和状态过滤条件"
Rebase 时的冲突处理
Rebase 时产生的冲突处理稍有不同,因为 rebase 是一个提交逐个应用的过程,可能产生多轮冲突。
git pull --rebase
# 产生冲突...
# 1. 解决冲突(同上:编辑文件,删除冲突标记)
# 2. 暂存解决后的文件
git add sql/orders_etl.sql
# 3. 继续 rebase(不是 commit!)
git rebase --continue
# 如果下一个提交也有冲突,重复步骤 1-3
# 如果中途想放弃 rebase,回到 rebase 前的状态
git rebase --abort
使用 merge 工具
如果文件很长,冲突很多,手动处理容易遗漏。可以用 merge 工具:
# 使用配置的 merge 工具打开所有冲突文件
git mergetool
# 查看当前配置的 merge 工具
git config merge.tool
配置 VS Code 为 merge 工具
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
配置 vimdiff
git config --global merge.tool vimdiff
git config --global merge.conflictstyle diff3
merge.conflictstyle diff3 会在文本冲突标记中加入公共祖先区段,展示当前侧、公共祖先和传入侧的内容,并不直接生成三列界面;vimdiff 的窗口布局由合并工具决定。下面是普通 merge 的示意:
<<<<<<< HEAD
AND user_type = 'VIP'
||||||| merged common ancestors ← diff3 模式会显示公共祖先(改之前)
AND 1=1
=======
AND order_status = 'completed'
>>>>>>> feature/add-status-filter
数据项目中冲突的常见类型
类型一:SQL 逻辑冲突(最常见)
两人同时修改了同一个 SQL 文件的过滤条件、JOIN 关系或 GROUP BY 字段。
处理方式:理解两个改动的业务背景,判断是否都需要,合并时可能需要调整整个查询的逻辑。
类型二:配置文件冲突
调度配置、连接配置等文件被多人修改:
<<<<<<< HEAD
schedule: "0 2 * * *" # 你改成了凌晨2点
=======
schedule: "0 3 * * *" # 同事改成了凌晨3点
>>>>>>> feature/adjust-schedule
处理方式:和相关人员沟通,确认最终的调度时间。
类型三:数据模型冲突
dim_user.sql 建表语句中,两人分别加了不同的字段:
<<<<<<< HEAD
user_level VARCHAR(20), -- 你加的用户等级字段
=======
user_channel VARCHAR(50), -- 同事加的渠道来源字段
>>>>>>> feature/add-channel
处理方式:通常是两个都需要,合并保留:
user_level VARCHAR(20),
user_channel VARCHAR(50),
预防冲突的最佳实践
冲突是可以减少的,好的工作习惯能让你少踩很多坑。
实践一:频繁 pull,小步提交
# 每天开始工作前,先更新代码
git pull --rebase
# 功能完成一部分就提交,不要攒着
# 小步提交便于定位、审查和处理冲突;是否发生冲突仍取决于改动重叠及分支差异
实践二:沟通先于修改
改一个大家都在用的核心文件(如 dim_user.sql、ods_layer.sql)前,先在群里说一声。
"我今天要改 dim_user.sql,加用户等级字段,大概改以下几处,有人也在改这个文件吗?"
实践三:文件职责单一
把大文件拆分成职责单一的小文件,每个文件只负责一件事。这样两个人修改的文件重叠概率大大降低。
bad: all_etl.sql(所有 ETL 逻辑都在这)
good:
etl_orders.sql
etl_users.sql
etl_products.sql
etl_payments.sql
实践四:功能分支要短命
分支存活时间越长,和主干差异越大,合并时冲突越多。
- 功能分支生命周期:可将不超过 3 天(小功能)到 2 周(复杂功能)作为团队讨论的经验目标,并非通用上限
- 开发期间定期同步主干;未共享的个人提交可使用 rebase,共享分支应先遵循团队约定,避免擅自改写他人依赖的历史
# 工作区干净且功能分支提交尚未共享时,将其变基到最新主干
git switch feature/my-feature
git fetch origin
git rebase origin/main
实践五:.gitignore 排除自动生成文件
自动生成的文件(如 PyCharm 的 .idea 文件夹、Jupyter 的 .ipynb_checkpoints)经常产生无意义的冲突。用 .gitignore 排除它们。详见 07-Git进阶技巧。
处理冲突时的心理准备
tip 冲突不是灾难
初学者看到冲突往往会慌。其实冲突很正常,处理起来也有固定套路:找到标记 → 理解两边的改动 → 决策 → 删除标记 → add → commit。按步骤来,不要急。
tip 不确定就问
如果你不理解对方的改动是什么意思,不要猜,直接问那个人。几分钟的沟通能避免你做出错误的合并决策,否则引入了业务 bug 比冲突本身麻烦多了。
冲突解决快速参考
# 查看有哪些文件有冲突
git status
# 列出尚未解决冲突的文件名;查看具体差异可用 git diff -- <文件路径>
git diff --name-only --diff-filter=U
# 解决冲突(编辑文件,删除冲突标记)后
git add <resolved-file>
# 所有冲突解决后
git commit # 完成 merge
# 或者(rebase 时)
git rebase --continue
# 想放弃,恢复到 merge/rebase 前
git merge --abort
git rebase --abort
小结
| 步骤 | 操作 |
|---|---|
| 1. 发现冲突 | git status 查看哪些文件 |
| 2. 打开文件 | 编辑器看 <<<<<<< 标记 |
| 3. 理解冲突 | 搞清楚两边改动的意图 |
| 4. 做决策 | 保留谁的/合并/重写 |
| 5. 删除标记 | 删除所有 <<< === >>> |
| 6. 标记完成 | git add <file> |
| 7. 提交 | git commit |
掌握检查
- [ ] 能识别冲突标记的三个区域:
<<<<<<< HEAD(当前分支内容)、=======(分隔线)、>>>>>>> branch(来源分支内容) - [ ] 解决冲突后知道完整的收尾流程:删除所有冲突标记 →
git add→git commit(不需要写-m,Git 会自动填写合并信息) - [ ] 知道
git merge --abort可以放弃正在进行的合并,回到合并前的状态 - [ ] 理解 rebase 和 merge 在冲突处理上的区别:rebase 的冲突需要
git rebase --continue推进 - [ ] 知道减少冲突的实践方法:定期同步主干、协调对共享文件的修改;
git pull --rebase同步的是当前分支配置的上游,不一定是 main,共享提交使用 rebase 前须遵循团队约定
实践练习
基础题:手动制造一个冲突:在 main 和 feature 分支上修改同一文件的同一行,然后合并,练习识别冲突标记、做出决策(保留哪一边还是合并两者),完成完整的冲突解决流程。
进阶题:制造一个 SQL 文件的冲突:两个分支从同一提交出发,修改同一行或重叠的 SQL 代码区段,合并时练习处理冲突,模拟真实业务场景中和同事沟通后做出合并决策的过程。若一个只改 WHERE 条件、另一个只改不重叠的 JOIN 逻辑,Git 可能自动合并;此时仍需检查组合后的业务逻辑。
下一步:Git进阶技巧 - 学习 stash、rebase、tag 等进阶技巧,提升日常工作效率。