news 2026/9/26 3:07:58

Git合并冲突深度解析:从<<<<<<< HEAD到日常规避

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Git合并冲突深度解析:从<<<<<<< HEAD到日常规避

“<<<<<<< HEAD”——看到这一串符号的那一刻,别说新人,就是干了几年的人偶尔也会头皮发紧。这七个左箭头加上HEAD,翻译成人话就是:你的代码和别人的代码在同一行怼上了,Git不知道听谁的,于是把战场原封不动摆在你面前,等你来仲裁。我刚带团队那会儿,几乎每个月都能在代码评审里看到有人把“<<<<<<< HEAD”连同代码一起提交上来,甚至有人直接问“这是不是报错?要不要删掉?”今天这篇就把这件事彻底讲透:它从哪来、是什么意思、怎么解决、怎么避免,顺便把Git里另外两个容易吓到人的HEAD状态一起理清楚。

这篇内容适合所有跟代码、跟版本管理打过交道的朋友——不管你是刚入职场的应届生,还是被合并冲突折磨过的后端/前端/测试/运维,甚至只是听说过Git但还没实战过的初学者。看完你至少能明白:遇到冲突不用慌,它不是你的代码坏了,也不是同事故意跟你作对,它只是Git在尽忠职守地“不替你丢代码”。

1. 项目背景与核心需求解析

1.1 “看到分隔符为什么想逃?”——从崩溃现场说起

我先还原一下那个让新人崩溃的经典现场。你接手一个需求,改了某个订单模块的公共方法,测试通过、commit也写了,正准备push的时候弹出一个提示,说远程更新了。于是你执行了git pull,屏幕里瞬间蹦出一坨这样的东西:

<<<<<<< HEAD // 你本地改的逻辑:校验库存后直接扣减 stockService.deduct(warehouseId, skuId, count); ======= // 远程同事改的逻辑:先锁定库存再扣减 stockService.lockAndDeduct(warehouseId, skuId, count); >>>>>>> origin/feature/order

新人看到这的第一反应通常有三种:一种是不认识,以为报错了,到处问“这段是什么”;一种是认识但不敢动,怕动坏了,卡在那里发呆;还有一种是“聪明人”,把<<<<<<< HEAD、=======、>>>>>>>这几行全删了,但代码两端也没去判断谁对谁错,直接随手留一份,commit推上去,过几天线上出bug了还查不出来。

我跟你说,这三种都不丢人,因为你学校里教的基本全是git addgit commitgit push这些“主干道操作”,极少有人正儿八经教过冲突从哪来、冲突标记的语法结构是什么。面试八股文里问“合并冲突怎么解决”,多数人背的是“打开文件、手动修改、重新提交”十二字真经,但真到现场,面对一堆尖括号和等号,脑子还是空的。

这一节我们先建立最底层的心理建设:冲突不是错误,不是异常,是Git在“双写保护”——你改了文件,同事也改了文件,Git如果擅自选一份,必然丢掉另一个人的工作。它宁可停下来把选择权还给你,也不当那个背锅侠。

1.2 技术世界里的“HEAD”到底有几种脸

标题里的HEAD加上一堆热搜词,其实已经暴露了技术领域最常见的两个“HEAD”。一个是HTML结构里的<head>标签——那是网页的“头部”,放meta、charset、title这些元信息,它从第零天学HTML就认识了,长得人畜无害。另一个才是今天的重头戏:Git里的 HEAD 指针,它指的是“当前你所在的提交位置”。

这两个HEAD会在什么场合互相干扰?我遇到过真有新人在编辑器里搜<<<<<<< HEAD,搜出一堆<head>标签,然后陷入自我怀疑:“我做错了什么?为什么每个页面都有这个东西?”所以这里先划一道清晰的界:<head>是HTML的元素,带尖括号;HEAD是Git的引用,全大写或者带^、~后缀,它不出现则已,一出现多半是跟合并、分支、提交历史有关。

另外顺带说一句,深度学习方向的朋友看到“head改进”会想到yolov8的检测头,那是模型结构里的head层,跟本文的Git是两码事。此处不展开,但我会在最后用一小节聊聊“同名不同物”给协作带来的认知成本——这也是团队wiki里应该写清楚的东西。

所以当你再看到哪个社区帖子里说“HEAD 崩了”“HEAD 没了”“detached HEAD”,先判断语境:是前端在讲DOM结构,还是后端在调Git。今天这篇只解决后者。

2. 核心细节解析:冲突标记的完整语法与内在逻辑

2.1 一场合并的“身体检查报告”:冲突标记逐行解剖

Git合并冲突的标记不是乱写的,它是一份结构化的“争议报告”,由四个符号段构成:

<<<<<<< HEAD(当前分支版本) ||||||| 合并基准版本(可选,git merge 时不一定出现) =======(分隔线) >>>>>>> 对方分支名(被合并的版本)

逐行翻译一下:

  • <<<<<<< HEAD:从这里开始,到下面第一个分隔线之间,是**你当前所在分支(HEAD指向的分支)**里这段代码的样子。比如你基于main在开发,这段冲突里HEAD对应的就是你本地main(或你当前工作分支)的版本。
  • 中间区域:有时候会多出一个|||||||段落,那是Git帮你找出的“合并基准”——也就是你和对方分叉之前的共同祖先版本。它用来辅助判断,但不是必需的,具体见下文说 merge 和 rebase 的差异。
  • =======:这是分界线,上面的内容叫“ours(我方)”,下面的内容叫“theirs(对方)”,注意“我方/对方”会随着你是在merge还是rebase,以及你是否切换分支而互换身份,这也是容易搞懵的点。
  • >>>>>>> 分支名:表示这一方的内容到此结束。它后面往往跟着origin/xxx或某个feature分支名,告诉你“对方是谁”。

一个完整冲突文件通常长这样:

<<<<<<< HEAD const snackPrice = 3.5; ======= const snackPrice = 4.0; >>>>>>> origin/feature/snack-price function checkout(cart) { ... }

这个例子一目了然:本地价格定3块5,同事改成了4块,二人都在同一个常量上动了手,Git无法分辨谁是对的,只能交给你定夺。你只需要保留一份,去掉其他内容和所有标记行,保存文件,再git add即可。

2.2 为什么有时候冲突文件里会有“一堆重复的函数”?

很多新人其实不是不会“删标记行”,而是删完之后代码还是跑不起来——因为冲突段可能不是一个小块,而是整整两个函数、两个类、甚至两个文件的头部导入区。比如同事在文件顶部加了一个import xxx,你也在同一个位置加了自己的import yyy,Git会把两个import全塞进冲突区:

<<<<<<< HEAD import { OrderService } from '@/services/order'; ======= import { PaymentService } from '@/services/payment'; >>>>>>> origin/feature/payment

这时候如果你只保留一个import,但代码里明明同时用到了两个Service,必然编译报错。所以解决冲突要记住一个核心原则:**“保留两份代码”和“保留一份代码”不是非此即彼的,你需要站在功能角度判断是要合并还是要取舍。**好的工具会把上下文给你摆出来,好的习惯是解决完立刻搜索一下还有没有引用了被删掉的那一方。

我个人踩过的坑是:解决import冲突的时候只留了我自己的那行,结果同事新写的几个页面全部挂掉,因为他们的代码 import 了我删掉的那个service。所以在解决冲突时最好Ctrl+F搜一下冲突区外的同名引用,确保“删了不会炸”。

2.3 换行符、编码差异与边界情况:教科书不会讲的坑

再讲一个几乎没人提但在Windows/Mac/Linux混合团队里极高发的问题:换行符冲突。你的仓库如果使用的是LF(Unix换行),而某个同事的本地Git配置把core.autocrlf设成了true,一改文件就自动换成CRLF(Windows换行)。你们两个改同一个文件之后合并,Git不会觉得你们的代码有冲突,但当你打开文件时,整个文件都被标记成了修改状态,commit历史里全是“修改了换行符”。这种冲突的标记不会出现<<<<<<< HEAD,但它是比<<<<<<< HEAD更隐蔽的团队内耗来源。我后面在“避免冲突”那一节会给排查方法。

另一个边界情况是:二进制文件冲突。比如设计稿PSD、JPG截图、Excel表格、锁文件package-lock.json这些,Git没法帮你合并二进制,一旦两边都改了,直接弹冲突。这类冲突根本没法手动编辑内容,正确做法是:保住最新的一份,告诉对方重新生成。如果是package-lock.json冲突,最稳妥的方案不是手动删标记,而是删除文件后重新npm install,让工具重新生成一致结果。

3. 实操过程与核心环节实现:从冲突出现到收尾的全流程

3.1 第一现场:你该用哪套命令组合初始化冲突识别

冲突会通过哪些方式撞到你面前?我列四个最常见的入口:

  • git merge feature/someone—— 把别人的分支合并进你当前分支。
  • git pull—— 本质是 fetch + merge(或 fetch + rebase,取决于你的配置),远程和本地都动了同一区域时也会冲突。
  • git stash pop—— 把之前暂存的修改恢复到工作区时,如果当前代码和暂存内容撞车,也会弹冲突。
  • git cherry-pick—— 把某一个提交复制到当前分支时,同样可能冲突。

无论从哪个入口进来,第一步反应都应该是:稳住,不慌,别把编辑器关掉重开。然后按下面顺序做:

  1. 执行git status,Git会列出所有处于冲突状态的文件,一般出现在未合并路径(Unmerged paths)里。
  2. 打开冲突文件,或者用git diff查看差异。推荐先用git diff看整体,因为有时候直接打开1000行的文件找人会看吐。
  3. 若是单文件冲突数量不大,直接编辑文件解决;若是几十个文件同时冲突,就要有策略了(后文给方案)。

我自己的习惯是:在命令行里先执行git status --short,把冲突文件做个清单,然后逐个git diff看内容,再用编辑器解决。每次解决完一个文件,立刻git add那个文件,不要攒到最后统一add。这样即使中途出错,也能知道哪个文件是已经解决的。

3.2 单文件冲突的标准手术流程:纯手工稳赢法

这里给一套纯手工方案,不依赖任何可视化工具,适合任何环境:

第一步:定位冲突。用git diff或者编辑器全局搜索,找到所有包含<<<<<<<的位置。可以输入/在vim里搜索,或者用Ctrl+Shift+F在VSCode里搜索<<<<<<<。

第二步:读懂双方思路。从上到下逐个看,观察每段冲突内ours(上方)和theirs(下方)的逻辑差异。很多人上来就改,连对方改了什么意图都不知道——这是大忌。举个例子,你改成3块5是调价,同事改成4块是覆盖你之前的调价,那当然以同事的4块为准,而不是随手留了3块5。

第三步:写最终代码。把需要的部分留下,不需要的删掉,必须同时删除<<<<<<<、=======、>>>>>>>这三组标记行。如果你想要两段都留下,就把分隔线和标记删掉,把代码按逻辑整理在一起。

第四步:验证与提交。切换到能编译能跑的状态,跑一遍相关单测或最小验证,再次git diff --check确认没有冲突残留(这个命令专门查空白错误,顺带也能揪出漏网的冲突标记)。最后git add 文件然后git commit -m "merge: resolve conflict in xxx"。

这里要重点强调一个反直觉的事:git commit在冲突未解决时是被禁止的。Git会拒绝你提交,报“you have unmerged paths”,这是在保护你。但问题是,有些新人会用git commit -a -m "..."直接提交,发现居然成功了——这多半是因为他已经在文件里手动删了冲突标记但忘了git add,而-a把已跟踪的修改全add进去了,导致提交内容里包含他“随手选了一个版本”的结果。这种提交坑特别大,因为你根本没有认真审查对方改动。

3.3 场景示例:十分钟模拟一次完整的合并冲突

为了让你有肌肉记忆,我模拟一个最简单的场景,你可以在本地建个Git仓库跟着敲:

mkdir learn-conflict && cd learn-conflict git init echo "hello world" > demo.txt git add demo.txt && git commit -m "init: add demo.txt" # 拉一条分支并修改 git checkout -b feature/beta echo "hello beta" > demo.txt git add demo.txt && git commit -m "beta: modify demo.txt" # 回到主分支,也修改同一行 git checkout main echo "hello main" > demo.txt git add demo.txt && git commit -m "main: modify demo.txt" # 尝试合并 git merge feature/beta

你会看到输出:

Auto-merging demo.txt CONFLICT (content): Merge conflict in demo.txt Automatic merge failed; fix conflicts and then commit the result.

这时候打开demo.txt:

<<<<<<< HEAD hello main ======= hello beta >>>>>>> feature/beta

这一步做的就是把“冲突长什么样、怎么出现”刻进脑子里,下次真正遇到就不会慌了。新手最缺的就是这种能在本地随便作妖的练习场,强烈建议你花十分钟亲手跑一遍。

3.4 多文件冲突和“大规模冲突”时的分治策略

遇到几十个文件同时冲突,如果一个个打开来改,心态容易崩不说,还容易漏。我把实际项目中比较有效的分治策略给你:

  • 先按类型分:把.cs、.java、.ts这类源码文件和package-lock.json、图片、Excel等再分开。锁文件和二进制文件直接走“重新生成”路线,不要手修。
  • 再按模块分:如果项目按模块分包,冲突文件散落在多个模块里,可以先解决自己负责模块的,其他人负责的模块留给对应人员,大家在合并分支前同步一次状态。
  • 记录冲突清单:在终端保留git status输出,每解决一个文件就git add,用git status来打勾,视觉反馈特别重要。

还有种很实用的命令:git checkout --theirs 文件和git checkout --ours 文件,可以直接“整文件采用一方”。但这两个命令的语义解释起来容易绕,我待会儿单独给你讲透。

3.5 用可视化工具提高胜率:VSCode与git mergetool组合拳

手工处理不是唯一方案,可视化工具能大幅降低认知负担。我在团队里最推荐的搭配是VSCode内置的三方合并视图 + 终端里的git mergetool:

  • 如果你用VSCode,直接打开冲突文件,菜单里会出现“Accept Current Change”(接受当前)、“Accept Incoming Change”(接收传入)、“Accept Both”(两者都保留)等按钮。它的视图是“当前修改(当前分支) vs 传入修改(对方分支)”,比纯文本的<<<<<<<直观太多。
  • 如果你在终端环境,git mergetool可以调用你配置好的合并工具,比如Beyond Compare、KDiff3、Meld。配置命令如下:
git config --global merge.tool meld git config --global mergetool.meld.path /usr/bin/meld git mergetool

它会逐个打开有冲突的文件,处理完提示你保存,再把标记清理干净。处理完记得git commit。

这里有一个非常重要的提醒:可视化工具里的 Accept 按钮不代表“正确”,只代表“采用这一侧”。很多新人看到绿色的按钮就点,点完以为冲突解决了,实际上可能把同事的逻辑整块覆盖了。所以无论用什么工具,都要先读两边的代码意图,再决定点哪个按钮。

3.6 “ours”和“theirs”的身份陷阱——merge和rebase语义相反

这是实战中坑最多的知识点,单独拿出来讲。

先说merge。你在main分支上执行git merge feature/beta:

  • HEAD是main,所以ours指的是main上的内容。
  • theirs指的是feature/beta上的内容。

这符合直觉。但如果你执行的是git rebase,比如在feature/beta分支上执行git rebase main:

  • 当前所在分支是feature/beta,HEAD指向feature/beta原来的提交。
  • 此时ours(HEAD)实际上是feature/beta的内容。
  • 而theirs(被rebase上去的目标)反而是main的内容。

很多人在rebase解决冲突时会误用--ours把对方的分支内容整文件覆盖了,结果把整个main的代码都吞进来,还把自己的feature改动全丢了。这就是“身份互换陷阱”。

我的经验:遇到涉及--ours、--theirs的命令,永远先git status看清楚当前在哪个分支、刚刚执行的是merge还是rebase,再动手。还有一招,用git log --oneline --graph --all -10看看三方历史,搞清楚谁是爷爷谁是儿子。

4. 常见问题排查与避坑实录:那些必须用血泪换来的经验

4.1 冲突解决到一半,人傻了——“这个分支还能不能要?”

常见心理活动:我冲突还没解决完,但临时要切到别的分支修个紧急bug,怎么办?我能不能先把半成品藏起来?

直接说结论:有未解决的冲突时,不能git checkout切分支。Git会拒绝你,理由是你有未合并路径。这时候你有几个选项:

  1. 硬着头皮先把当前冲突解决完,这是最稳的。
  2. 如果你不想继续merge,执行git merge --abort,让Git回到合并前的状态,所有冲突标记消失,你的本地修改回到合并前的样子。注意:这个操作会丢掉你在合并过程中已经做过的任何手动修改,所以要慎重。
  3. rebase场景用git rebase --abort,同理。
  4. 如果你在冲突过程中改了一半,想保留这一半:先把当前所有文件强制git add,再执行git commit,把一个“半成品合并”提交掉,然后切分支去做紧急事。这个提交虽然难看,但至少不丢代码。我常用这种方式保留现场,回来后继续做“下一次合并”来推进。

4.2 常见误区一:把<<<<<<< HEAD当成代码一起提交

这个问题我见得太多了,尤其在新手第一次独立解决冲突时:手工改了十几处,漏了最后两处,然后git add .愉快提交。过两天同事pull下来发现代码里出现了一行<<<<<<< HEAD,编译直接报错。

如何预防?两个习惯最重要:

  • 提交前搜一遍:在任何 “冲突处理后提交” 场景下,强制在项目里搜索<<<<<<<。VSCode里按Ctrl+Shift+F,搜出结果说明还有漏网之鱼,全部处理干净再提交。如果项目已经很大,可以用命令grep -rn "<<<<<<< HEAD" --include="*.java" .来扫。
  • 利用Git的原生检查:提交之前运行git diff --check,这个命令会把残留的冲突标记作为错误输出到终端。每次都跑一下,成本极低。

4.3 常见误区二:git commit报错“did not finish" 或 "unmerged paths”时强行提交

有人被Git拒绝之后,脑子一热上网搜,搜到git commit --no-verify怎么绕过钩子,甚至有人执行git rm把冲突文件直接删了——这是我见过最野的路子。要是真这么干,要么文件没了,要么冲突标记还在代码里。强制提交也许能过,但同事一拉代码就遭殃。

正确的解决路径前面已经讲过了。这里再补充一个细节:如果某个冲突文件你完全不想保留,比如同事误改了一个你正在重构的文件,你要做的是“选择把对方整个文件丢弃”,命令是git checkout --ours -- 文件路径(merge语境下),然后git add,而不是git rm。

4.4 常见误区三:pom.xml、package.json、yarn.lock 这些文件冲突能手动修吗?

先说 package-lock.json 和 yarn.lock:别手动修,别手动修,别手动修。这种文件是工具自动生成的,有成百上千条依赖路径关系,你手动改一条,可能跟另一条冲突对不上,最后两个开发者的环境各跑各的。

如果你遇到 lock 文件冲突,正确步骤一般是:

  • 手动解决掉其他源码文件冲突。
  • 删除package-lock.json里的冲突内容,或直接从某一方git checkout --theirs package-lock.json(rebase场景方向不同,前面已解释过)。
  • 在项目根目录执行npm install或yarn install,让包管理器重新解析依赖树并生成一份一致的 lock 文件。
  • 检查一下核心依赖版本确实包含你需要的那个,然后git add提交。

pom.xml(Maven)的情况稍好一些,但如果你不确定dependencyManagement里插的是哪一段,也别硬手改,最好是找能跑通构建的那一方重新导出。

4.5 场景常客:detached HEAD——另一个会把新人送走的状态

标题里那句 “the repository is in the detached head state” 很多人也遇到过。这不是冲突,但同样吓人。它发生的时候你会莫名发现自己“不在任何一个分支上”,提交了代码之后发现git log里看不到自己,吓得以为代码丢了。

简单解释:HEAD默认是指向某个分支的引用,比如refs/heads/main。但当HEAD不再指向一个分支,而是直接指向一个具体的提交哈希时,就进入了detached HEAD。这在git checkout <commit-id>检查历史版本、或者某些CI脚本里很常见。

在detached HEAD状态下做的提交,确实不再是任何分支的一部分,但并不是立即丢失——只要你还能找到那个提交哈希,就可以用git branch 分支名 <哈希>把它拽回来。比如:

git branch rescue-branch <detached-commit-hash> git checkout rescue-branch

我会专门提醒团队成员:看到detached head别慌,先git log --oneline -1看看当前HEAD位置,如果你只是想看代码,直接git checkout main回来就行;如果你刚提交了重要改动,先把提交哈希记下来,再另起分支接住。代码没丢,只是“没人领着”。

4.6 换行符冲突与“幽灵diff”的排查三板斧

前面提到的换行符问题,在这里给排查方法。如果你遇到的情况是:一个人改了一行,合并后git diff显示整个文件都变了,命令行输出里全是 “^M” 符号或者编辑器右下角显示CRLF,那八成是换行符在捣乱。

排查三板斧:

# 1. 查看仓库里配置的换行符规范 cat .gitattributes # 2. 查看某个文件当前使用的换行符 file 文件路径 # 3. 查看你本地的autocrlf设置 git config core.autocrlf

推荐的根治办法是在仓库根目录统一放一个.gitattributes,固定常见文件类型的换行符:

* text=auto *.sh text eol=lf *.bat text eol=crlf *.java text eol=lf

这样不同系统环境下,Git会按照规则自动转换换行符,而不是每台机器各自按自己的偏好乱转。加了.gitattributes后,建议先把仓库里所有文件重新标准化一次(git add --renormalize .再提交),之后冲突率会明显下降。

4.7 一个独立小节:为什么yolov8 head 改进这种搜索词能关联到 Git 崩溃事件?

热搜词里出现了类似“yolov8 head改进”这样的条目,这其实侧面说明了一个现象:很多人搜“head”相关的问题时输入非常含混,搜索引擎把yolov8 head、html head、git head全搅在一块。我在带新人时专门做过一次科普,叫“同名不同物清单”:

  • Git HEAD:指针,指当前提交,大小写敏感。
  • HTML<head>:网页头部区域,带尖括号,全小写。
  • 计算机网络里的 head 概念:如 head 请求方法,用于只获取响应头。
  • 深度学习模型 head:检测头 / 分类头,位于骨干网络后端。
  • 数据结构里的链表 head:单链表第一个节点。

如果你是一个团队的tech lead,我建议你把这个清单写进新人wiki第一页。这种认知摩擦产生的成本,远比表面看起来大得多——你以为新人在折磨Git,其实他连搜什么关键词都搜不准。

5. 如何从根源上减少冲突:团队协作的工程化心法

5.1 小步提交与其背后的原理

冲突的本质是“两个人改了同一块区域”。既然是概率问题,那就降低概率。最有效的手段不是禁止修改公共区域,而是缩小每次提交的改动范围。

拿合并一个两百行的新功能为例。有人喜欢一口气写完所有代码才commit,在同事眼里,这是一个“巨大的变更集”,如果同事同时在重构这一片业务逻辑,两人必撞车。但如果把需求拆成“先加工具类”“再加DAO层”“再接入Service”“最后改接口”,每个提交都是小块,同事的改动可能只跟其中一个提交重合,且每个小块都更容易用git bisect定位问题。小步提交不是效率低,而是给合并失败时留下的“后悔药”更多。

5.2 高频同步,而不是憋大招才与主干对齐

我在实际带项目中有一个规矩:任何分支生存期超过三天,必须至少与主干同步两次。很多新人的习惯是独立开发一周,最后git merge main,一次面对三十个冲突文件。这种“憋大招式同步”非常打击信心——看到冲突列表的第一眼,人就想辞职。

更好的节奏是:主干有更新,就尽快git merge main或者git rebase main到自己的分支。每次只消解几个小冲突,心态从容,错误率也低。浓度高了就切走十分钟再回来,效率反而好。

5.3 公共模块的“排他声明”:从源头避开雷区

对于工具类、常量类、配置类、数据库访问层这些极高冲突概率的公共区域,我建议团队约定“修改前在群里或者企微里打个招呼”。比如有人要改OrderStatusEnum,先说一声,别人看到就暂缓自己的改动,等合并完再动。这个方法虽然土,但在20人以下的技术团队里出奇地有效,因为冲突不只靠工具能解决,还得靠人协调。

当然,进了规模更大的团队,就要靠代码所有权(CODEOWNERS)、模块负责人等机制。GitHub的CODEOWNERS就是干这个的,你可以用它对关键文件做强制评审,降低无意识的并行修改。

5.4 让Git替你挡掉一部分傻瓜错误:Hooks与CI检查

给仓库加一个pre-commit钩子,在提交前自动搜索冲突标记。如果检测到<<<<<<< HEAD,直接拦截这次提交。这样漏网之鱼就根本进不了仓库历史。示例的Git钩子脚本很简单:

#!/bin/sh if grep -rn "^<<<<<<< HEAD" --include="*" . | grep -v "^Binary"; then echo "Found unresolved conflict markers. Aborting commit." exit 1 fi

配合CI流水线,在pull request阶段也跑一次同样检查,写进pipeline的lint步骤,就能让“冲突标记被提交”这个愚蠢错误永远发生在本地而不是线上代码评审里,既保护自己,也保护同事。

6. 尾声:这玩意没那么可怕

最后再讲一个真实的故事。我第一次独立带小团队时,一个入职刚满月的新人因为merge冲突解决到一半误操作,把自己一天的工作成果覆盖了,差点在工位上哭出来。后来我教她在本地建一个永远不会被push的“垃圾分支”,专门用来练习合并、练习rebase、练习故意制造冲突,两个下午下来,她再见到<<<<<<< HEAD已经能面不改色心不跳地处理。

现在回想,真正让她崩溃的不是冲突本身,而是没人告诉她“这个标记到底在说什么,我有哪些选择,出了错怎么撤退”。所以这篇文的落脚点就一句话:把冲突当成Git在帮你做“变更仲裁”,而不是当成门槛。先搞清楚三个符号段分别是谁的代码,再决定留谁删谁,最后用git add+git commit给这次仲裁盖章。跑几次本地练习,后面再遇到就不会慌了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 3:06:58

Claude Code Templates:用CLI和MCP模板一键配置AI编程助手

1. 项目缘起与核心价值拆解第一次看到claude-code-templates这个项目名&#xff0c;我的直觉是&#xff1a;这大概率是一个围绕 Claude Code 做“脚手架”和“模板库”的工程化项目。事实也确实如此。Claude Code 是 Anthropic 推出的命令行 AI 编程助手&#xff0c;它能在终端…

作者头像 李华
网站建设 2026/9/26 3:06:19

FLIM系统硬件架构设计:从光学分类到电路选型与调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 3:05:04

2026主流网盘横评:选数字管家,不是比速度

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华