IDEA 中 Git Pull 冲突解决指南:识别、stash 与三栏合并
周五下午五点我把最后一版接口改完顺手在 IDEA 右下角点了一下更新控制台刷出两行红字CONFLICT (content): Merge conflict in src/main/java/...。三分钟后同一个错误又出现了一次——因为我把文件里的标记当成了垃圾代码直接手动删掉结果 IDEA 认不出这是已解决Commit 按钮一直是灰的又白折腾了二十分钟。在 IDEA 里用 git pull 撞上冲突几乎是每个用 Git 的团队每天都要碰一次的事真正让人卡住的从来不是冲突这两个字而是分不清自己撞上的是哪一种冲突、该在哪一步动手、动手之后怎么让 IDEA 相信自己已经改完了。下面这些都是我自己一路踩出来的从pull failed 到底是谁挡住了谁到本地改动是该 commit 还是该 stash再到 IDEA 三栏合并窗口里哪些按钮能点、哪些点了会后悔以及换行符、文件权限位这类看着像冲突其实不是冲突的假象。内容命令行和图形界面混着讲刚学会git clone的人能照着做带团队的人也能拿去当规范。1. 先分清 IDEA 弹的到底是哪一类失败很多人一看到 IDEA 弹红字就默认冲突了然后直接去文件里找找不到就开始怀疑人生。实际上 IDEA 在 pull 阶段的失败至少分成三种处理方式完全不一样第一步永远是看提示文本而不是看哪一行是红的。1.1 三种失败的三副面孔第一种是本地改动被拦下来了。提示通常是Your local changes to the following files would be overwritten by merge下面还会列出一串文件路径。这时候 Git 连合并都还没开始只是发现你要拉下来的内容会覆盖你还没提交的修改出于安全直接拒绝了。这种根本不叫冲突叫保护。第二种是真正的文本冲突。提示是CONFLICT (content)或者Automatic merge failed; fix conflicts and then commit the result。特点很明显git status里文件会出现在Unmerged paths下面文件内容里插进了、、这三行。这才需要你逐块选内容。第三种是分支分叉导致的拒绝。典型提示是fatal: Not possible to fast-forward, aborting用了--ff-only或者Updates were rejected because the tip of your current branch is behind its remote counterpart这个更多出现在 push但 pull 配了 rebase 策略时也会看到类似的话。它的意思是本地和远端各自都有对方没有的提交Git 不知道该按哪种方式接起来。失败类型关键提示Git 处于什么状态动手指南本地改动被拦local changes would be overwritten未开始合并commit 或 stash 后再 pull真冲突CONFLICT (content)合并进行中有未合并路径进合并工具逐块解决并 add分支分叉Not possible to fast-forward未开始合并明确选 merge 还是 rebase把这张表记住排查速度能快一倍。我见过有人遇到第一种情况跑去文件里手写那纯属给自己制造事故。1.2 冲突标记怎么读以及为什么要打开 diff3默认的冲突标记长这样 HEAD String name user.getName(); String name user.getNickName(); origin/main到之间是你当前分支HEAD的内容到之间是拉进来的内容。看起来够用了但有个致命缺陷它只告诉你两边不一样没告诉你原来是什么。很多冲突的正确解法既不是左边也不是右边而是两边都要一点——不知道原始状态你就只能靠猜。所以我强烈建议在全局配置里加一行把合并风格换成diff3Git 2.35 之后可以用zdiff3显示更紧凑git config --global merge.conflictStyle zdiff3开了之后冲突块会多出一段带|||||||的基准版本 HEAD String name user.getName(); ||||||| merged common ancestors String name user.getFullName(); String name user.getNickName(); origin/main多出来的这一段就是两个人改之前的样子。看到它你立刻能判断出哦原来叫 getFullName我改成了 getName他改成了 getNickName这种情况下正确的做法往往是合并成一个新方法名而不是二选一。这个配置对命令行和 IDEA 的内置合并工具都生效只有个别情况下 IDEA 会用自己的展示逻辑覆盖掉显示效果但底层判断依然按这个来。1.3 为什么有人弹合并工具有人只弹一行红字这个差异经常让人困惑。同一个仓库、同一个冲突同事那边弹出漂亮的三栏窗口你这边只有一行 Git Pull Failed。原因通常有三个。其一你遇到的是第一种失败本地改动被拦Git 根本没进入合并状态IDEA 没东西可显示只能弹个对话框问你是 Commit、Stash 还是 Revert。其二冲突文件是二进制或者 IDEA 判定为不可合并的类型比如图片、jar、xlsx这种情况下没有内容块可选IDEA 只给你接受我的或接受对方的两个整体选项。其三你的 IDEA 版本或者 Git 插件状态有问题合并视图没被触发。判断方法很土但很有效打开 IDEA 底部的 Git 工具窗口看有没有Merge Conflicts这个节点。有说明进入了合并状态没有说明压根还没开始合并。另外提醒一句新版 IDEA 把原来的Local Changes标签页改成了Commit工具窗口位置和名字变过好几次别因为找不到标签页就以为是插件坏了先确认版本。2. 本地改动还没提交的时候该怎么处理这是最高频的场景你改了两个文件写了半小时还没 commit同事已经推了一版上去你点 pull被告知你的本地修改会被覆盖。这时候人的第一反应是赶紧 commit 一下但 commit 完心又虚了——这半成品提交上去会不会被同事骂。2.1 commit 优先还是 stash 优先判断标准其实只有一条我的判断标准只有一条这份改动是不是一个能独立成立的逻辑单元。如果它已经能编译、能跑、语义上完整比如把用户名的字段名统一了那就直接 commit然后在 pull 之后可能需要再处理一次真正的冲突但至少历史是干净的。如果它只是改了一半、IDE 里还有红色报错、你甚至不确定方向对不对那就不要往历史里塞用 stash 暂存。有个很实际的理由支持后者半成品 commit 在 pull 之后如果发生冲突你要在一个逻辑不完整的状态下解决冲突脑子会很乱而 stash 相当于把桌上的东西先扫进抽屉桌面干净了再拉代码拉完再把抽屉倒回来。但 stash 也不是没坑最大的坑是它是全局的。git stash存的东西挂在仓库级别不分分支。你在 A 分支 stash 了一堆改动切到 B 分支手一抖git stash pop改动就落到 B 分支上了而且不报任何错。我见过有人因此把 feature 分支的改动误加到 hotfix 分支上还推送了出去。2.2 IDEA 里 stash 的具体操作和几个必须知道的开关IDEA 里 stash 的入口在Git Uncommitted Changes Stash Changes...新版是右键项目或者Git菜单下找弹出的对话框里有个很容易被忽略的复选框Include untracked files。默认是不勾的意思是你新创建但还没git add的文件不会被 stashpull 的时候它们还躺在工作区里。如果你新建的文件跟远端新增的文件重名那拉下来就会直接报untracked working tree files would be overwritten又是一次白折腾。命令行版本我更推荐参数写清楚不容易忘# -u 包含未跟踪文件-m 加备注防止自己忘记 git stash push -u -m user-profile-rename-wip # 看存货确认还在 git stash list # 恢复并删除该记录常用 git stash pop # 恢复但保留记录想留个保险就先用这个 git stash apply stash{0}pop和apply的区别值得强调pop成功恢复后会把这个 stash 记录删掉一旦恢复过程中出现冲突、你处理乱了、想重来对不起记录没了只能去git fsck --unreachable里捞。所以我的习惯是先apply确认没问题再手动drop多敲一条命令换一份安心。还有一个细节stash 恢复时如果跟新拉下来的代码冲突IDEA 的表现和普通合并冲突不太一样——它不会给你三栏合并工具而是直接在工作区留下冲突标记并让 stash 记录继续保持存在pop失败时不删除。这时候的处理方式是老老实实编辑文件、去掉标记、git add然后git stash drop手动清理别指望 IDEA 帮你收尾。2.3 只想暂存一部分改动Shelve 比 Stash 更合适STASH 是 Git 层的一存就是全部改动。如果你改了五个文件其中三个是准备提交的两个是顺手改的实验性代码那用 stash 会把三个正经改动也一起卷进去。IDEA 的Shelve中文版叫搁置就是为这个场景准备的。它是 IDE 层面的补丁机制入口在Git Uncommitted Changes Shelve Changes...可以勾选具体文件甚至可以在文件内部勾选具体的改动块。生成的 patch 文件默认放在项目的.idea/shelf/目录下。这里就引出一个团队协作的注意点.idea/shelf/不应该提交到仓库。有些团队的.gitignore写得太粗把整个.idea目录忽略了看起来省事实际会丢掉代码风格配置和运行配置有些团队又全提交结果每个人的搁置记录、工作区状态文件都互相打架。合理做法是.idea目录下只提交codeStyles/、inspectionProfiles/、runConfigurations/、vcs.xml这类团队共享的配置忽略workspace.xml、shelf/、usage.statistics.xml这些个人状态文件。Shelve 的代价是它不跟着仓库走。换台机器、重装 IDE、别人 clone 你的分支都看不到你的搁置内容。所以它适合临时挪一挪不适合长期存放。超过半天没恢复的 shelve我建议转成 stash 或者干脆提一个wip分支上去。3. IDEA 内置合并窗口的实战用法真冲突出现了IDEA 一般会自动弹出 Merge Conflicts 或者从文件列表里点进去。这个三栏窗口设计得不错但按钮的语义有些反直觉的地方用错了会制造更大的麻烦。3.1 三栏各是什么先确认清楚再动手标准布局是左边一栏是本地/当前分支的版本右边一栏是拉进来的版本中间是合并结果。顶上还会显示两边对应的分支名或提交信息。中间那栏才是最终落盘的左右两栏只是候选源。颜色约定绿色通常表示新增灰色表示删除蓝色表示修改红色表示这一块两边冲突、需要你手动决策。右侧边栏会有一列小色块点一下能直接跳到对应差异块。底部有个进度提示比如1 of 3 conflicts resolved这个数字是实时更新的。有一个例外必须记住rebase 模式下左右两栏的含义会翻转。rebase 的本质是把你本地的提交一个个重新贴到远端分支上所以在处理冲突的那一刻Git 认为的我们的ours其实是远端分支他们的theirs才是你自己的提交。IDEA 在较新版本里会在标题上标注提交信息但不同版本展示逻辑不完全一致。不要凭左右位置判断去看每一栏顶部的提交哈希和分支名这是唯一可靠的依据。命令行里可以用git log --oneline -3 HEAD MERGE_HEAD交叉验证rebase 状态下 HEAD 是远端那个点要看的是.git/rebase-merge/onto。3.2 Accept Left / Accept Right / Accept Both 到底该点哪个每个冲突块右侧有几个小按钮含义分别是Accept Left接受左侧保留当前分支的内容丢掉传入的那一块。Accept Right接受右侧保留传入的内容丢掉本地那一块。Accept Both两边都接受把两段内容按顺序拼在一起还分先左后右和先右后左两种。这个按钮最危险因为它是纯文本拼接不会帮你处理语法。两个人在同一个 import 区域各加了一个类用 Accept Both 会得到两行 import没问题但两个人在同一个 if 块里各加了一行逻辑Accept Both 很可能拼出重复变量声明编译直接挂。我的原则是默认逐块手工编辑只在两种情况下用按钮。一种是差异纯粹是格式或者顺序比如两行 import、两个配置项拼接不会产生语义问题另一种是你已经确认其中一边的改动是废弃的直接接受另一边。窗口左上角还有一个魔法棒图标作用是把所有不冲突的改动自动应用进去只留下真正需要决策的块。这个功能非常好用建议每次打开合并工具第一件事就是点它能把工作范围缩小到几个点上。另外提一句容易被忽略的功能在 IDEA 的合并窗口里可以直接在中间结果栏手动敲代码。很多人以为只能选其实中间栏是可编辑的遇到两边都要改一点再组合的情况比如左边用了新字段名右边加了空值判断直接在中间写最终版本最快写完记得把两侧的残留彻底删干净。3.3 解决完之后怎么让 IDEA 和 Git 都认账这一步是最容易翻车的。在合并窗口里点了应用、保存了文件并不等于冲突已解决。Git 判断冲突是否解决的唯一标准是这个文件有没有被重新git add过。IDEA 在你通过它的合并工具保存时会自动执行 add但如果你是手动编辑文件、或者用外部编辑器改的就得手动来。判断方式看 IDEA 的Commit工具窗口冲突文件会有一个醒目的标记通常是红色文件名或者!图标。文件上右键找Git Resolve Conflicts里有Mark as Resolved之类的选项不同版本叫法不一样有的叫 Add。命令行就是最朴素的git status # 确认 Unmerged paths 下面还剩哪些 git add file # 逐个 add别用 git add . 蒙 git status # 再看一遍确认都进了 Changes to be committed全部 add 完之后merge 模式下需要git commit收尾IDEA 会自动帮你填好Merge branch ...的提交信息rebase 模式下必须用git rebase --continue不能 commit。这里错一次就要用git rebase --abort重来代价不小。最后一步验证我每次都做这三件事一是再点一次 pull应该显示 Already up to date二是git diff --check这个命令能把残留的冲突标记和多余空白扫出来三是本地编译一次。前两条一分钟搞定第三条能拦住绝大多数手抖删多了括号的低级事故。4. 图形界面搞不定时的命令行解法IDEA 的合并工具在大范围冲突下会显得笨重——比如一个文件被两边各改了两百行三栏窗口里密密麻麻。这种时候切命令行反而快。而且有些操作在 IDEA 里根本没有入口比如策略级的合并选项。4.1 merge 和 rebase 对历史的影响不一样两种接法最本质的区别在提交历史。merge 会产生一个合并提交把两条线连起来历史是真实的但会多出很多分叉和合并记录。rebase 是把你本地的提交搬到远端最新提交之后历史变成一条直线看起来很干净代价是提交哈希全变了。对冲突处理来说两者体验差距很大merge 只需要解决一次冲突因为它是把两边的最终状态做一次三方合并rebase 可能每个提交都要解一次你有五个本地提交理论上可能解五次而且每次看到的冲突都不一样。命令上# 明确用 merge git pull --no-rebase # 明确用 rebase git pull --rebase # rebase 过程中冲突了解决后继续 git add file git rebase --continue # 这个提交的改动已经完全被远端包含了跳过它 git rebase --skip # 放弃回到 rebase 之前的状态 git rebase --abort这里有个团队层面的选择要提前说清公共分支main、develop不要 rebase 后强推别人的历史会被打乱自己的 feature 分支在合入前 rebase 一下是很好的习惯。IDEA 里可以在Settings Version Control Git的 Update method 里固定用 Merge 还是 Rebase也可以交给仓库的pull.rebase配置项决定。别让每个人凭手感选这是冲突反复出现的根源之一。顺带说下git pull --ff-only的用法。它不解决冲突但是个很好的诊断工具如果这条命令能成功说明你本地没有分叉历史是干净的如果它报Not possible to fast-forward说明确实分叉了。想保持线性历史又不想要 merge 提交的团队可以把它当成日常的默认拉取方式一旦失败再决定是 merge 还是 rebase比稀里糊涂产生一堆合并提交强。4.2 ours 和 theirs 在 rebase 下会反转这是最坑的一个坑git checkout --ours file和--theirs是快速取舍的利器但在 rebase 过程中含义是反的而且命令不会给你任何警告。状态--ours 指向--theirs 指向merge 中你当前所在分支HEAD拉进来的远端分支rebase 中你 rebase 到的目标分支远端你自己的提交原因还是前面说的那套逻辑rebase 是把你的提交重放到目标分支上所以 Git 眼中的基线变成了远端。有人在 rebase 冲突时执行git checkout --ours .想保住自己的改动结果把自己所有的修改全丢了还一路--continue推了上去。安全做法有两个。第一动手前先git status它会明确告诉你当前处于You are currently rebasing branch ...还是You are currently merging看到 rebasing 就把 ours/theirs 的含义在脑子里对调。第二也是我更推荐的用git checkout --conflictdiff3 file把冲突标记重置成带基准版本的形式再打开 IDEA 或者编辑器看着 base 手动改。慢几十秒但不会出现整批改动消失这种灾难。还有一点要注意--ours/--theirs接受的是文件路径加.会作用于全部冲突文件。在 rebase 场景下这是极其危险的一键操作我建议永远不要用.逐个文件来。4.3 几条撤退命令各自的安全边界冲突解到一半发现方向错了想回退——这时候必须清楚每条命令会把状态退到哪。# 合并进行中想彻底放弃这次合并 git merge --abort # rebase 进行中想彻底放弃 git rebase --abort # 合并/rebase 完成之后才后悔回到操作前的状态 git reset --hard ORIG_HEAD # 万能后悔药查看最近所有 HEAD 移动记录 git reflogmerge --abort和rebase --abort是最安全的它们会尽量把工作区恢复到操作开始前的样子。但有个例外如果操作前工作区就有未提交的改动abort 之后这些改动不一定还在尤其是在某些 Git 版本和--autostash组合使用的情况下。所以我的硬性习惯是任何可能产生冲突的 pull 之前先git status确认干净不干净就先 commit 或 stash。git reset --hard是真正的高危命令它会丢掉所有未提交的改动。用它之前先给自己留条后路# 建一个备份分支成本几乎为零 git branch backup/2024-xx-xx-before-reset这条命令几秒钟但救过我三次。还有个常见误解是远端分支上的东西不会丢所以 reset --hard 到 origin/main 没关系——远端确实不会丢但你自己本地那些还没推的提交会丢除非你刚好记得哈希。备份分支是最省心的保险。git merge -X ours和-X theirs这两个策略选项也要慎用。它们不是遇到冲突就选我而是在能自动合并的地方偏向某一方最终结果可能两边都沾一点非常难预测。用完之后必须完整 review 一遍 diff不能盲信。5. 那些看着像冲突、其实不是冲突的情况有一类冲突最折磨人文件里根本没有但 Git 就是认为整个文件都变了diff 显示的是整个文件删除 整个文件新增。这类问题的根因基本都在文件元信息上不在内容上。5.1 换行符导致的整文件冲突Windows 用 CRLFLinux 和 macOS 用 LF。同一个文件被不同系统的人改过Git 可能认为每一行都变了。表现特征是冲突块覆盖整个文件或者明明只有一行改动diff 却显示几百行。排查用这个命令git ls-files --eol file # 输出类似i/lf w/crlf attr/text # i 是索引里的w 是工作区的attr 是 .gitattributes 指定的三者不一致就是问题所在。根治办法是团队统一.gitattributes* textauto *.sh text eollf *.bat text eolcrlf *.png binary配合个人的core.autocrlf设置Windows 上一般truemacOS/Linux 上input。如果仓库历史里已经混进了各种换行符可以做一次全仓库规范化但这属于大动作会污染 blame最好单独开一个提交、通知全组git add --renormalize . git commit -m Normalize line endings这件事我建议在有.gitattributes之后再做否则改完还是会漂回去。5.2 文件权限位和大小写重命名引发的假冲突在 Windows 或者 WSL 环境下经常会看到文件权限模式mode 100644 变 100755导致的差异明明内容一个字没改。关掉它对权限位的敏感度就行git config core.fileMode false大小写问题更隐蔽。macOS 和 Windows 的文件系统默认大小写不敏感UserService.java和userservice.java在它们眼里是同一个文件但 Git 在 Linux 上认为是两个。有人在 Windows 上把一个文件重命名只改了大小写推上去之后别人拉下来就是一堆删除 新增甚至出现两个文件并存。绕开的办法是两步改名中间用一个临时名字过渡git mv UserService.java userservice_tmp.java git commit -m rename step 1 git mv userservice_tmp.java Userservice.java git commit -m rename step 2这样 Git 在每个提交里都能正确记录不会产生歧义。别嫌麻烦这类问题一旦进了主线清理成本比这两条命令高几个数量级。5.3.idea目录里的配置文件冲突该不该管用 IDEA 的人几乎都遇到过workspace.xml或者misc.xml冲突。我的一般处理原则是分文件对待。workspace.xml是纯个人状态文件——窗口布局、最近打开的文件、断点、运行历史全在里面。它不应该提交。如果已经提交了正确的做法是先把它加进.gitignore然后git rm --cached .idea/workspace.xml提交这一步全组的后续冲突就消失了。别直接在冲突里选一边那样下次还会冲突。而codeStyles/、inspectionProfiles/、runConfigurations/、vcs.xml这些是团队共享配置值得提交也确实需要合并。它们的冲突通常很机械因为 XML 结构清晰取两边的并集基本就对。但要注意misc.xml里的 JDK 版本配置两个人本地 JDK 版本不一样的时候贸然合并会把别人的配置改掉这种字段建议保留本地值别跟着一起提交。顺便说两个相关场景。二进制文件冲突图片、jar、xlsx没有三方合并的余地IDEA 只会给接受我的/接受对方的选错了内容就真没了所以冲突方最好线下沟通一下谁的版本是最新的。子模块冲突则是另一套逻辑git status会显示modified: submodule (new commits)处理方式通常是进到子模块里确认提交哈希然后git submodule update --init --recursive把状态对齐而不是像普通文件那样去改内容。现象根因处理方式整个文件都冲突diff 几百行换行符 CRLF/LF 混用.gitattributes 统一 --renormalize内容没改却显示变更文件权限位变化core.fileMode false重命名文件出现删除加新增大小写不敏感文件系统两步 git mv 改名workspace.xml 反复冲突个人状态文件被提交git rm --cached 并加入忽略6. 从源头上少撞几次冲突冲突本身不可怕可怕的是同一类冲突一周撞五次。真要在团队里把这件事压下去靠的不是更熟练的合并技巧而是几条约定的习惯和几个配置开关。6.1 提交粒度、拉取频率和分支寿命冲突之所以难解根本原因是改动拖得越久重叠的面积越大。一个人拿着分支改了两周才合另一个人的重构也在同一片代码上动了两周两边合起来必然是灾难。我给自己定的三条线是一个提交只做一件事每天开工第一件事是拉最新代码而不是等到要提交了才拉一个功能分支的寿命尽量控制在三天以内做不完就把已经能跑的部分先合进去剩下的开新分支继续。听起来很理想化但哪怕只做到每天拉一次冲突的规模和难度都会降一半以上——因为改动量小冲突块往往就几行闭着眼睛都能解。分支命名也顺手规范一下feature/xxx、fix/xxx、hotfix/xxx配合谁的分支谁负责解冲突的默契。别出现三个人的改动挤在一个叫dev2的分支上这种局面到那时候冲突已经没法由某一个人独立解决了。6.2 几个改一次、受益很久的 Git 配置# 冲突标记带上基准版本判断更准 git config --global merge.conflictStyle zdiff3 # 记住冲突的解决方式同一个冲突第二次自动处理 git config --global rerere.enabled true # rebase 前自动 stashrebase 完自动恢复 git config --global rebase.autoStash true # 明确 pull 的默认策略避免每次靠手感 git config --global pull.rebase false # 减少无谓的权限位噪音 git config --global core.fileMode falsererere这个名字来自 reuse recorded resolution作用是你手动解决过一次冲突后Git 会把这块怎么改的记下来下次遇到同样形态的冲突自动套用。它最常见的价值场景是长期分支反复 rebase 到主线、每次都要解同一批冲突。开启之后还能用git rerere diff查看它记住了什么用git rerere forget file清掉某条误记的记录。要提醒的是它只按冲突块的内容特征匹配语义上是否正确它不管所以自动处理完还是要 review 一遍。rebase.autoStash这个开关我在小改动场景下很依赖但它也不是没有风险自动 stash 之后如果 rebase 失败改动会挂在 stash 栈里容易被遗忘。所以恢复工作前养成git stash list看一眼的习惯。另外 IDEA 侧也值得配一配把Settings Version Control Git里的 Update method 固定成团队约定的策略把Code Style统一成同一套 schema 并提交到仓库。格式统一能消掉一大类只改了空格和缩进的假冲突——那种冲突解起来最气人因为毫无信息量。6.3 遇到冲突时的处理顺序我总结成一张清单真撞上冲突的时候脑子容易乱按这个顺序走基本不会出错。git status看当前是 merging 还是 rebasing这一步决定后面所有 ours/theirs 的含义。看是哪种失败本地改动被拦就 commit 或 stash真冲突就进合并工具。先点 IDEA 里的应用所有非冲突改动把范围缩小。逐个冲突块处理优先看|||||||的基准版本想清楚两边各自想解决什么问题。手工编辑中间结果栏移除全部标记保存。确认文件被 add或手动git addgit status里不该再有 Unmerged paths。merge 用 commit 收尾rebase 用--continue收尾。git diff --check 本地编译 跑一遍相关测试。最后分享一个我用了很多年、救过好几次急的土办法。当你把冲突解得乱七八糟、自己也说不清哪些改动是对的最省事的做法不是继续硬解而是干脆放弃这次 pullgit merge --abort或git rebase --abort然后用git branch建一个备份分支把自己的改动存下来接着git reset --hard origin/xxx直接对齐远端再从备份分支上git cherry-pick或git diff出自己需要的改动小步重新贴上去。听起来笨但在大范围冲突里它比在三栏窗口里纠结半小时快得多而且每一步都有退路。冲突处理这件事最值钱的不是解得多快而是任何时候都能完整退回去。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →