应禁用自动覆盖,用 git merge --no-commit --no-ff 暂停合并,人工比对 :1(ours)、:2(base)、:3(theirs)版本语义差异,提取公共逻辑、增加条件分支或拆分配置项来安全融合双方修改,而非使用 --ours/--theirs 丢弃意图。

git merge 时如何强制保留目标分支(ours)或源分支(theirs)的修改
直接用 --ours 或 --theirs 策略能跳过冲突手动处理,但极易引入静默缺陷——这不是“保留”,而是“丢弃另一方意图”。真正安全的做法是:先禁用自动覆盖,再人工判断语义。
执行合并时加 --no-commit --no-ff,让 Git 暂停在合并提交前:
git merge --no-commit --no-ff feature/login
此时所有文件已应用三方合并结果,但尚未提交。你可以:
- 用
git status查看哪些文件被标记为“both modified” - 用
git show :2:src/main/java/TokenService.java查看 base 版本(共同祖先) - 用
git show :1:...和git show :3:...分别查看 ours(当前分支)和 theirs(被合并分支)内容 - 逐行比对逻辑差异,而非只看文本行是否重叠
为什么不能直接用 git merge -s ours/theirs
-s ours 会完全忽略被合并分支的所有变更,包括新增接口、配置项、安全补丁——哪怕只是多加了一行 if (isProd()) { log.warn(...); },也会被无声抹掉。
-s theirs 同理:当前分支上已上线的风控校验、缓存降级逻辑可能全被覆盖。这两个策略只适合极少数场景:
-
-s ours:用于撤销误合入的实验性分支(如临时调试分支),且确认其无任何有效产出 -
-s theirs:仅限将文档分支(如docs)合并进主干,且明确接受文档完全替代现有文档 - 二者都不适用于业务代码、配置、API 定义等含语义的变更
保留双方修改的实操关键点
所谓“保留双方修改”,本质是把冲突从「选 A 或 B」转化为「A + B 如何共存」。常见路径有:
- 提取公共逻辑:把两分支各自改的
jwtExpiration计算分别封装为getLoginTokenTtl()和getRefreshTokenTtl(),由调用方按场景选择 - 增加条件分支:原
if (user.isActive())被一方改为if (user.isActive() || user.isTrial()),另一方插入&& !isRateLimited(),可整合为if (shouldAllowAccess(user)),把判断逻辑移入独立方法 - 拆分配置项:
application.yml中同一 key 被设不同值,应拆成redis.timeout.login和redis.timeout.api,避免后续再冲突 - 拒绝“取平均值”或“拼字符串”式解决:比如把两个 timeout 值硬编码成
3500,或把两段 JSON schema 直接拼接——这会破坏类型契约
容易被忽略的验证环节
很多人解决完冲突就 git add . && git commit,但这时最危险的阶段才开始:
- 没跑单元测试:尤其是涉及缓存、鉴权、幂等性的逻辑,必须验证双方修改叠加后行为是否符合预期
- 没查 CI 流水线日志:某些集成测试只在 PR 合并后触发,本地无法复现
- 没核对 OpenAPI 文档生成结果:两个分支各自扩展了
responses.200.schema,合并后可能因字段名冲突导致 swagger.yaml 校验失败 - 没确认 Git blame 是否还能追溯到原始提交:如果用了
git checkout --ours强制覆盖,后续排查问题时会丢失关键上下文
真正的保留,不是让代码通过编译,而是让逻辑意图可追溯、可验证、可演进。











