资讯详情

Git合并与变基:原理、冲突处理与协作安全实践

📅 2026/10/28 20:10:35 | 华诺云谱 👁 阅读
Git合并与变基:原理、冲突处理与协作安全实践
上周帮一个团队梳理提交历史图形化视图一打开我就愣了一下主干上横七竖八插着几十条短分支几乎每条都挂着“fix”“再改改”“真的最后一次”这类提交信息。聊下来才知道团队里一半人习惯点一下合并另一半人习惯先变基再推两种操作混着用历史就成了现在这副样子。这件事让我觉得有必要把Git合并和变基从头到尾讲一遍不是抄命令手册而是讲清楚它们在图结构上各自干了什么、什么时候该用哪个、出事了怎么救。**Git合并merge和变基rebase**是版本控制里最容易被混为一谈的一对操作。它们的目标一致把一条分支上的改动整合到另一条分支上。但底层做的事完全不同——一个保留分叉并生成新的合并提交一个重写提交、把分叉抹平。这个差异表面上只关系到历史好不好看实际牵涉协作安全、代码追溯、回滚成本和冲突处理策略。不管你是刚学会git commit的新手还是已经在带团队做代码评审的老手这里面的细节都值得重新过一遍因为我见过太多人是在踩过一次“变基把别人的提交弄丢”的坑之后才开始认真读文档的。后面我从底层对象模型讲到实际操作从冲突解决讲到误操作恢复把能直接抄的命令、参数推算过程、踩坑记录都摊开写。你可以照着跑一遍也可以只挑需要的章节看。1. 为什么先要把这两种操作的本质差异掰开1.1 从一次历史被拧成麻花的事故说起前面说的那个团队具体事故是这样的某开发者在本地把功能分支变基到主干然后执行了一次强制推送同一时间A同学在这条功能分支上基于旧提交继续提交了两个补丁。A同学拉取时发现本地和远端对不上按提示做了一次合并结果旧提交和新提交同时存在于历史里同一段代码被应用了两遍构建直接挂掉。排查花了将近两个小时最后靠reflog和逐个diff才把重复提交剔掉。这个事故里没有谁是“操作错误”的变基的人遵守了“先同步再推送”的习惯强制推送也是在自己的功能分支上做的A同学更是标准的“拉取—合并”流程。问题出在两个人对“提交是可变的还是不可变的”这件事认知不一致。合并把提交当成不可变的历史节点只是把它们连起来变基把提交当成可重放的补丁每次重放都生成新的提交对象。两种世界观混在一条分支上就会出这种事。所以我不太赞成那种“变基更高级、合并更土”的说法。这不是水平高低的问题是两套不同的历史管理模型选哪套取决于这条分支上有几个人在动、有没有发布出去、后续要不要靠它追溯问题。1.2 两种操作在提交图上的真实差别把两条分支想象成两条线。合并是从两条线的当前端点拉出一条新的线把两个端点连到一个新节点上这个新节点就是合并提交它有两个父提交。原来的两条线一根都没动。变基则是把你这条线上的每个节点拆下来按顺序一个一个粘到目标线的末端粘的时候每个节点都重新生成一份哈希值全变原来的线被丢弃。对比项合并 merge变基 rebase是否产生新提交产生一个合并提交双父每个被搬动的提交都重新生成原有提交哈希不变全部改变历史形状保留真实分叉呈网状拉成一条直线冲突解决次数一次性解决全部冲突可能每个提交都要解决一次对已推送分支的影响安全只新增不修改危险需要强制推送覆盖可追溯性能看出“什么时候合的、合了什么”看不出原来的分叉点回滚粒度可以整体回滚一次合并需要逐个提交处理这张表里最容易被低估的一行是“冲突解决次数”。很多人觉得变基冲突多是因为工具不好用其实这是它的工作方式决定的合并是一次性把两个终态做对比冲突只在这种终态层面出现变基是逐个提交重放中间任何一个提交跟新基底产生矛盾都要停下来。这既是缺点也是优点——缺点是要停好几次优点是每个冲突都发生在很小的上下文里解决起来其实更精准。1.3 一个常被忽略的事实变基改的是提交对象本身新手最容易犯的错是把变基理解成“把提交挪个位置”。不是挪是复制一份新的、丢掉旧的。提交对象的哈希由内容决定树对象、父提交、作者信息、提交者信息、提交说明任何一项变化都会导致哈希变化。变基时父提交变了提交者时间和提交者身份通常也会变所以哈希必然不同。这就解释了两件事。第一为什么变基之后必须强制推送——远端那条分支指向的还是旧提交你本地的历史跟它对不上普通推送会被拒绝。第二为什么别人的本地分支会因为你的变基而“多出重复提交”——他们本地还留着旧提交拉取时两条历史没共同祖先可循实际上有但在分叉点之前Git 只能做合并旧的和新的一起保留。我在实际项目里给自己定了一条硬规矩只要一条分支上除了我之外还有第二个人提交过就绝不在这条分支上做变基。这条规矩救过我很多次。功能分支本来就是个人的临时工作区随便折腾一旦共享它就从“草稿”变成了“契约”契约不能单方面修改。2. 底层原理提交对象、指针与三方合并2.1 提交对象链与分支指针的关系Git 的存储模型里一个提交对象包含几个关键字段指向树对象的指针代表这次提交的完整文件快照、零个或多个父提交指针、作者信息、提交者信息、提交说明。分支在 Git 里只是一个文件里面存着某个提交的哈希值。所谓“切换分支”本质是把 HEAD 指向另一个引用文件。理解这一点之后很多操作就变得直观了。git commit做的事是拿当前索引生成一棵树创建一个父提交指向当前 HEAD 所指提交的新提交对象然后把当前分支文件里的哈希改成新提交的哈希。整个过程中没有任何“移动文件”的动作只是产生新对象、改指针。合并和变基的区别也可以在这个层面说清楚。合并会创建一个有两个父提交的新对象第一个父是当前分支原来的端点第二个父是被合并分支的端点。变基则完全不创建合并提交它创建一串单父提交每个的父都是上一个被重放的结果或目标分支端点。2.2 fast-forward 到底快在哪很多人第一次看到“快进合并”这个词会以为是什么优化技巧其实它连提交都没创建。当目标分支的端点是当前分支端点的祖先时也就是当前分支从分叉之后一个提交都没有Git 只需要把当前分支的引用文件改成目标分支的哈希值工作区和索引跟着更新一下完事。这里的判断条件是“祖先关系”不是“谁更新”。哪怕目标分支的时间戳更早只要它在拓扑上是你的祖先就能快进。反过来只要当前分支上有任何一个目标分支没有的提交快进就不成立必须走真正的合并。我在代码评审里经常建议团队关掉默认的快进用git merge --no-ff。理由很实际快进合并不留任何痕迹事后看历史完全看不出“这里曾经有一条功能分支、它是被整体合并进来的”。等到需要定位“这个功能是哪次合入的”只能靠提交信息去猜。多一个合并提交就多一个明确的时间锚点二分查找和代码追溯都方便得多。代价是历史图上多几个节点我觉得这个代价很值。2.3 三方合并的三个来源与冲突标记的来源真正的合并要拿三份内容做对比两份分支各自的末端加上它们的共同祖先。共同祖先用git merge-base求取的是“最近的共同祖先”在有多条路径的情况下 Git 会选一个最优的这一点在复杂分叉下偶尔会产生反直觉的结果。三方的角色很清晰祖先版本是“原来长什么样”我方版本是“我改成了什么样”对方版本是“他改成了什么样”。某一行如果在祖先和我方之间没变、在祖先和对方之间变了直接取对方反之取我方两边都变了且变得不一样就是冲突。这个逻辑用一句大白话概括谁改的听谁的都改了又改得不一样就吵起来。冲突标记的格式也值得看一眼因为它直接对应三方信息 HEAD 当前分支这一段的写法 另一分支这一段的写法 feature/login中间那行是分隔符。合并时HEAD指向当前分支所以上半部分是“我方”。这个对应关系在变基时会翻转后面细说。2.4 rebase 的“重放”具体重放了什么变基的流程可以拆成五步。第一步找到当前分支和目标基底的共同祖先。第二步把从祖先之后到当前分支端点的所有提交按顺序收集起来这串提交可以用git log --oneline 目标分支..当前分支看数量就是重放的次数。第三步把当前分支的指针硬移到目标分支端点。第四步按顺序把收集到的提交一个个应用到新位置上每次应用相当于做一次三方合并——祖先是被重放提交的父提交我方是新基底对方是被重放提交的内容。第五步全部应用成功分支指向最后一个新生成的提交。关键是第四步的“三方合并”每重放一个提交Git 都要用那个提交的父提交作为基准看这个提交相对父提交改了什么然后把这部分改动打到当前基底上。这也解释了为什么变基冲突可能一个接一个——每重放一个提交都是一次独立的合并判断上下文只覆盖这一个提交的改动范围。还有个细节值得记住变基过程中提交的作者信息author保留原样提交者信息committer会更新成当前操作者和当前时间。所以变基之后git log里看到的时间是新的但“谁写的这段代码”这个信息没丢。3. 实操把合并与变基完整跑一遍3.1 构造一个可复现的实验场景想真正搞明白这两件事最好的办法是自己造一个分叉出来跑。下面这套命令我用了很多次能稳定复现各种情况你可以直接抄进终端。mkdir rebase-lab cd rebase-lab git init -b main git config user.name 某开发者 git config user.email devexample.com echo 第一行 app.txt git add app.txt git commit -m 初始化加入第一行 echo 第二行 app.txt git commit -am 主干加入第二行 git switch -c feature/login echo 登录相关 login.txt git add login.txt git commit -m 新增登录模块 echo 第三行 app.txt git commit -am 登录模块补充第三行 git switch main echo 配置相关 config.txt git add config.txt git commit -m 主干新增配置文件跑完之后git log --oneline --graph --all会看到一个典型的分叉main 上两个提交feature 上从第一个提交分出去后又走了两个。这就是后面所有实验的基础。3.2 merge 的三种形态实测与输出解读先测快进。切到 main把 feature 合并进来。因为 main 在分叉之后又有新提交所以这里不会快进会走真正的合并。想单独验证快进可以新建一条从 main 当前端点分出去的分支提交一次再合回来。git switch main git merge feature/login输出里会出现Merge made by the ort strategy.以及改动的文件统计。ort是较新版本 Git 的默认合并策略早期版本是recursive。这个策略名的变化对日常使用基本透明但如果你在脚本里依赖过输出文本就要留意。第二种形态是强制生成合并提交git merge --no-ff feature/login即使这次合并本来能快进也会硬造一个合并提交。我在主干合并时基本都用这个原因是保留分支的存在痕迹。第三种是压缩合并git merge --squash feature/login这个命令比较特殊它把对方分支的所有改动一次性算出来放进索引和工作区但不创建任何提交也不移动 HEAD不设置合并状态。你得自己git commit一次。适合那种“功能分支上有二十个碎提交但我想在主线上只留一条记录”的场景。代价是原来的提交信息全丢了如果团队习惯靠提交信息追溯慎用。三种形态的差异整理一下命令是否产生提交是否保留分支历史典型用途git merge能快进就快进否则产生合并提交保留日常整合git merge --no-ff总是产生合并提交完整保留主干合入功能git merge --squash不产生需手动提交丢弃碎提交整理成一条3.3 rebase 的完整流程与常用参数现在换个玩法把 feature 变基到 main 上。git switch feature/login git rebase main成功的话输出是Successfully rebased and updated refs/heads/feature/login.。这时候再用git log --oneline --graph --all看feature 变成一条直线接在 main 后面原来的分叉点消失了。如果中途有冲突流程是这样的# 解决冲突后 git add 冲突文件 git rebase --continue # 想放弃整个变基回到开始前的状态 git rebase --abort # 某个提交发现已经不需要了直接跳过 git rebase --skip--abort我建议任何人在刚开始用变基时都记住它是保命的。执行后分支会精确回到变基开始的那一刻包括工作区状态。还有一个参数值得单独说--onto。它的完整语义是“把 A 到 B 之间的提交搬到 C 上”。举一个真实的场景feature 分支是基于 dev 分出来的现在 dev 已经把相关的公共部分合进 main 了我想把 feature 直接挪到 main 上只搬 feature 自己独有的那几个提交。# 先看清楚范围这一步别省 git log --oneline main..feature # 假设 feature 上有 3 个独有提交把它们搬到 main 上 git rebase --onto main feature~3 featurefeature~3的意思是“feature 往回数三个提交”也就是分叉点。这个数字怎么定先用git log --oneline main..feature | wc -l数一下独有提交数量再换算成~N。数错了的后果是把不该搬的提交也搬过去或者漏搬。所以我一律先用git log确认再执行。更稳妥的写法是先给分叉点打个标记git branch feature-base $(git merge-base feature main) git rebase --onto main feature-base feature这样即使分支上有几十个提交也不怕数错。3.4 交互式变基的五个高频动作交互式变基是把提交历史当文本编辑。命令是git rebase -i 起点起点可以是HEAD~N也可以是某个提交哈希。打开后会看到类似这样的内容pick a1b2c3d 新增登录模块 pick e4f5g6h 登录模块补充第三行 pick i7j8k9l 修复一个拼写每行前面那个词就是动作改掉它就能改变对这个提交的处理方式。常用的五个动作pick原样保留。绝大多数行都是这个。reword保留改动重新写提交信息。只改文字不改内容时用它。squash把这个提交合并进上一个两个提交信息都保留会弹编辑器让你整理。fixup把这个提交合并进上一个但丢弃它的提交信息。修错别字、补一行漏掉的代码用它最省事。drop删掉这个提交。也可以直接把整行删掉效果一样。还有一个edit值得单独提它会在这个提交处暂停让你修改工作区内容改完git add再git commit --amend最后git rebase --continue。这个动作适合“某个提交里有一处逻辑写错了但不想在末尾再加一个修复提交”的场景。另外有个叫--autosquash的配合技巧我几乎每次修 bug 都用。流程是发现某个历史提交有问题先git commit --fixup那个提交的哈希提交一个修复然后git rebase -i --autosquash 起点Git 会自动把 fixup 提交排到目标提交后面并标成fixup你连编辑器都不用动。4. 冲突处理从标记解读到批量预演4.1 冲突标记怎么读该选哪一边前面提过冲突标记的格式这里说选择策略。看到一个冲突块不要急着删标记、随便留一段先搞清楚三件事祖先版本长什么样、我方为什么改、对方为什么改。第三条最容易被跳过但恰恰是决定对错的关键。看祖先版本的命令是git show :1:文件路径我方是:2:对方是:3:。这个索引编号在三方合并里是固定的很实用。把三份都看一眼很多“看起来是冲突”的情况其实能一眼判断出该怎么合并甚至可能发现两边改的是不同目的应该都保留。选择某一整边的命令是git checkout --ours 文件路径 git checkout --theirs 文件路径但要小心--ours和--theirs的含义在合并和变基里是相反的。合并时ours是当前分支theirs是被合进来的分支。变基时Git 是在把目标基底当作“我方”、把你的提交当作“对方”来做的三方合并所以ours指向的是目标分支也就是你变基过去的那个基底theirs才是你自己的提交。这个反直觉的点我见过至少三个人栽过。有人在变基冲突里执行git checkout --ours .以为保留了自己的改动结果把自己的代码全丢了。所以我在带新人时一律强调变基冲突里不要用--ours/--theirs用编辑器手改或者用git show :2:/:3:确认清楚再动手。4.2 rebase 冲突与 merge 冲突的处理差异合并冲突是一次性的解决完所有冲突文件git addgit commit结束。中途想放弃就git merge --abort。变基冲突是分段的每重放一个提交就可能停一次。停下来的状态是“部分变基”git status会告诉你当前在重放哪个提交、还剩几个。处理完当前这个git add之后git rebase --continue然后可能马上又停下来处理下一个。这个差异带来一个实操上的建议冲突多的时候先评估总量。如果一条分支有二十个提交每个都可能有冲突那这个变基很可能不是个好主意。这时候我会退回去用合并一次解决完成本低得多。判断方法是先跑git log --oneline main..feature看提交数量再跑git diff main...feature --stat看差异范围两个数字都很大的时候合并更划算。还有一个技巧git rebase --continue之后如果发现这个提交其实已经不需要了比如它改的东西在新基底里已经有了用git rebase --skip跳过不要硬解冲突。4.3 rerere把解决过的冲突记下来这个功能知道的人不多但用起来很省事。rerere是 “reuse recorded resolution” 的缩写开启后 Git 会记录你每次解决冲突的结果下次遇到完全相同的冲突自动套用。git config --global rerere.enabled true git config --global rerere.autoupdate true第一个开关打开记录第二个开关让自动套用的结果直接进索引连git add都省了。它的典型战场是长时间变基你把一条分支变基到主干解了十几个冲突结果发现搞错了git rebase --abort重来一遍。第二次跑的时候前面解过的冲突全部自动套用几秒钟就过去了。另一个场景是同一条分支反复变基——每次主干更新都同步一次如果每次冲突都差不多rerere 能省掉大量重复劳动。需要注意的是它记录的是“冲突块的文本模式 解决结果”如果上下文变化太大就匹配不上所以它不是万能的。另外团队里如果有人习惯把.git/rr-cache提交进仓库这显然不对要提醒一下那是本地缓存不该进版本库。4.4 中断、跳过与放弃的边界变基过程中三种退出方式要分清楚。--continue是“我处理完了继续下一个”--skip是“这个提交我不要了直接跳”--abort是“整个变基我不干了回到开始前”。最容易出问题的是把--skip当成--continue用。跳过之后那个提交的改动就彻底没了而且不会有任何提示。如果那个提交里除了冲突部分还夹着别的无关改动这些改动也一起消失。我一般只在确认“这个提交的内容已经完全包含在新基底里”时才用跳过判断方法是git show 提交哈希看一眼内容再git log -S关键字符串 新基底查一下是否已经有了。还有个小细节变基中断时git status的提示信息里会带上“interactive rebase in progress”或者在.git/rebase-merge、.git/rebase-apply目录存在时说明有变基在进行。如果你不确定当前状态git status永远是最先该敲的命令。5. 误操作恢复与协作安全边界5.1 reflog 是最后一道保险git reflog记录的是 HEAD 的移动历史包括提交、切换分支、合并、变基、重置。它在本地保留一段时间默认 90 天基本能救回绝大多数误操作。git reflog # 输出里每一行形如 # a1b2c3d HEAD{0}: rebase (finish): returning to refs/heads/feature # e4f5g6h HEAD{1}: rebase (start): checkout main # i7j8k9l HEAD{2}: commit: 修复一个拼写找到变基开始前的那一行然后git reset --hard HEAD{1}分支就回到了那个状态。这个操作我做过很多次成功率接近百分之百前提是那些提交还没被 Git 的垃圾回收清理掉。默认情况下悬空提交会保留两周所以别慌但动作要快。有个容易忽略的点reflog是本地记录不带推送。所以它救不了“同事强制推送覆盖了远端”这种情况——那种只能靠本地副本或者托管平台的分支保护机制。5.2 已经被推送到远端的提交怎么处理先说结论已经推送到共享分支的提交不要变基。这不是洁癖是协作成本问题。你变基并强推之后所有基于旧提交工作的人都会遇到麻烦轻则多出一堆重复提交重则冲突解决错误。如果确实需要清理比如误提交了不该提交的文件比较稳妥的做法是# 1. 先拉最新确保本地是最新的 git pull --rebase # 2. 用 revert 反向提交而不是改写历史 git revert 有问题的提交哈希 # 3. 正常推送 git pushrevert的好处是它只新增提交不改历史所有人都能安全同步。代价是历史里留下了“犯错 修正”两条记录但我觉得这个代价远小于强推带来的混乱。如果非要强推比如在个人分支上清理提交务必用--force-with-lease而不是--forcegit push --force-with-lease origin feature/login区别在于前者会检查远端分支是否还是你上次拉取时的状态如果别人在这期间推了新东西推送会被拒绝避免覆盖别人的工作。后者是无条件覆盖非常危险。我把--force从自己的命令历史里彻底删掉了只用带 lease 的版本。5.3 保护分支与推送限制光靠人的自觉不够得靠机制。主流托管平台都支持给分支设保护规则至少要配这几条禁止强制推送、禁止直接推送必须走合并请求、合并前必须通过检查。对于主干分支我一律建议禁止直接推送。这样一来即使有人本地做了变基也只能推到自己分支影响范围可控。合并请求的评审环节还能再兜一层——评审人看到提交历史异常可以要求先整理。还有一条实践给主干分支设置“合并后自动删除源分支”的规则。这条能省掉大量清理工作也能防止旧分支被误用。功能分支合并完之后就删掉下次要用重新拉永远基于最新主干开新分支从源头上减少变基的必要性。6. 团队里怎么定规矩6.1 选择矩阵什么场景用哪个规则太复杂没人记得住我给团队定的是几条很短的判断场景建议理由个人功能分支同步主干最新代码变基保持历史线性冲突在自己分支解决把功能分支合入主干合并加--no-ff保留分支痕迹方便追溯和整体回滚已推送且多人协作的分支只合并不变基变基要强推会打断别人提交历史里有大量碎提交想整理交互式变基合入前集中做一次一次性整理不影响别人紧急修复要进主干合并到主干再同步到开发分支修复只有一个源头避免两边分别改发现某个提交误提交了大文件revert或历史清理工具视是否已推送决定这张表的核心逻辑可以压缩成一句话分支是私有的就变基分支是共享的就合并。私有和共享的界限就是“有没有第二个人在上面提交过”。6.2 提交粒度与历史整洁的平衡很多人把“历史整洁”理解成“提交越少越好”我不太认同。一个提交应该对应一个逻辑完整的改动单元能独立编译、独立通过测试、能单独讲清楚“为什么改”。为了好看把五个不相关的改动压成一个提交事后用git bisect定位问题时就是灾难。我的习惯是本地开发时随便提交一个临时保存点一个提交信息写“wip”都行推送前做一次交互式变基把碎提交按逻辑合并。合并的原则是同一个功能的、同一处修改意图的合并跨模块的、不同意图的分开。整理完之后提交数量可能少一半但每个都说得清。git bisect这个角度值得展开说一句当主干上出现一个难以定位的回归问题时二分查找依赖的正是“每个提交都是可测试的”这个前提。如果提交是按“时间点”切的很多提交编译都不过二分就没法跑只能退回到人工排查。这是提交粒度影响实际工程效率的典型例子比“历史好看”这种理由硬多了。6.3 代码评审视角下的历史可读性站在评审人角度一条干净的线性历史读起来是享受一条来回穿插的网状历史读起来很累。但评审人真正需要的不是“线性”而是“能看出这组改动分几步、每步做了什么”。所以我在提交信息上有个土办法每条提交信息的第一行用动词开头说明这一步做了什么如果一条分支有多个提交让它们按执行顺序排列读起来像一个小故事。整理的时候用git rebase -i调整顺序、改措辞成本很低。还有一个细节合并提交的信息默认是Merge branch xxx into yyy信息量几乎为零。我会在合并时用--no-ff -m补一句说明这组合入解决了什么问题。事后追溯时这句话比一堆代码 diff 管用得多。7. 常见问题速查与个人踩坑记录7.1 问题速查表现象大概率原因处理方式推送被拒绝提示 non-fast-forward远端有本地没有的提交先git pull --rebase再推变基后历史里出现重复提交有人没同步就继续在旧提交上工作让所有人重新拉取必要时重建分支变基冲突里保留错了版本--ours/--theirs含义理解反了用git show :2:/:3:核对后手改rebase --continue报错说没有进行中的变基上一次已经完成或已中止git status确认再看reflog合并后发现漏了改动用了--squash但没手动提交检查是否有未提交的索引改动交互式变基后某个提交消失了行被误删或标了dropgit reflog找回原分支端点再重做合并提交里带进了不该有的文件合并前工作区有未提交改动合并前务必git status确认干净强推后同事分支出问题远端历史被改写同事用git reset --hard对齐远端或重建分支7.2 几条不太写在文档里的经验第一条动手之前先看一眼图。git log --oneline --graph --all --decorate这个组合我几乎每次做合并或变基前都会敲一遍。图形化视图能让你在动手前发现“哦这里还有第三条分支我没注意到”避免很多低级错误。图形界面工具有它的好处但命令行这个输出信息密度更高也更适合贴进沟通记录里。第二条给危险操作起别名但别起太顺手的别名。我配置过git lg这样的查看别名但从来没给reset --hard或push --force配过缩写。理由很简单危险操作多打几个字符的时间正好用来想清楚要不要做。第三条变基前先打一个临时标签。git tag before-rebase一行命令成本几乎为零出事时git reset --hard before-rebase就能回去比翻reflog直观得多。整理完确认没问题再删标签。我在处理那些“提交数量多、冲突风险高”的变基时一律这么做。第四条冲突解决完不要立刻--continue先跑一次构建。变基的每个中间状态其实都是“历史上曾经存在过的代码状态”如果那个状态编译不过说明冲突解决有问题继续下去错误会被带到后面的提交里。这个习惯帮我抓到过好几次“语法上没问题但逻辑解错了”的冲突。第五条跨分支搬单个提交用cherry-pick别用变基硬凑。有时候只是想把某个 bug 修复从开发分支搬到发布分支这时候变基是杀鸡用牛刀还容易带进不该带的提交。git cherry-pick 哈希干净利落失败了大不了git cherry-pick --abort。第六条也是最想强调的一条理解原理比记住命令重要得多。命令会变参数会增加不同版本行为有细微差别但“合并生成双父提交、变基重放生成新提交”这个底层事实不会变。把这一层想通了遇到没见过的报错也能推出来大概是什么问题而不是去搜索框里碰运气。我刚开始用 Git 那阵子把git help rebase的输出完整读过一遍那次读完之后很多之前觉得玄学的行为一下就通了推荐你也找个时间干这件事。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑