基于分支创建的子分支在原分支合并后引发Git冲突的解决问询
问题场景
半年前重装Windows系统后,遇到了之前未出现的Git合并冲突问题:
日常从development分支创建特性分支A并提交PR评审,期间基于A分支创建依赖的特性分支B继续开发。当A分支通过普通合并(非squash合并)合并到development后,将B分支合并到development时,若B修改了A曾改动过的文件,即便development上这些文件无额外变更,仍会触发合并冲突。
曾使用git merge origin/development -X ours临时解决,但该方案存在隐患——后续将development合并到main分支时可能需要绕过PR评审流程,风险较高。
操作示例
git checkout developmentgit pullgit checkout -b feature/108030git add .git commit -m "Add PWA outtake list page"git push(已通过git config设置上游分支)git checkout -b feature/108031- 经PR将feature/108030合并到development
git add .git commit -m "initial setup of outtake details"(修改了feature/108030曾改动的文件)git push(此时创建feature/108031到development的PR时出现合并冲突)
Git历史记录(执行git fetch后)
* 58f7585c9 (HEAD -> feature/108031) initial setup of outtake details * 42733401f (origin/feature/108030, feature/108030) Add PWA outtake list page | * 02ea582a5 (origin/development) Merged PR 55362: Add PWA outtake list page | * 4b369996d Merged PR 55354: Feature/108092: Reuse unit serial on found more weight |/ * d8527f292 (development) Merged PR 55224: Feature/108092: Filter units without weight on batch details
现有Git配置(已翻译)
diff.astextplain.textconv=astextplain → 文本文件差异转换工具:astextplain filter.lfs.clean=git-lfs clean -- %f → LFS过滤器清理命令:git-lfs clean -- %f filter.lfs.smudge=git-lfs smudge -- %f → LFS过滤器还原命令:git-lfs smudge -- %f filter.lfs.process=git-lfs filter-process → LFS过滤器处理命令:git-lfs filter-process filter.lfs.required=true → 必须启用LFS过滤器 http.sslbackend=openssl → HTTP SSL后端:openssl http.sslcainfo=C:/Program Files/Git/mingw64/etc/ssl/certs/ca-bundle.crt → HTTP SSL证书路径:C:/Program Files/Git/mingw64/etc/ssl/certs/ca-bundle.crt core.autocrlf=true → 自动转换行尾格式:true(Windows下将LF转换为CRLF,提交时转回LF) core.fscache=true → 文件系统缓存:启用 core.symlinks=false → 不支持符号链接 pull.rebase=false → 拉取时默认使用合并,不启用变基 credential.helper=manager → 凭证管理器:manager credential.https://dev.azure.com.usehttppath=true → Azure DevOps凭证使用HTTP路径:true init.defaultbranch=master → 初始化仓库默认分支:master user.name=REDACTED → 用户名:已隐藏 user.email=REDACTED → 用户邮箱:已隐藏 core.editor=nano → 默认编辑器:nano push.autosetupremote=true → 推送时自动设置上游分支:true merge.ff=true → 合并时优先使用快进合并:true core.repositoryformatversion=0 → 仓库格式版本:0 core.filemode=false → 不检测文件权限变化(适配Windows系统) core.bare=false → 非裸仓库 core.logallrefupdates=true → 记录所有引用更新 core.symlinks=false → 不支持符号链接 core.ignorecase=true → 文件名不区分大小写(适配Windows系统) remote.origin.url=REDACTED → 远程仓库origin地址:已隐藏 remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* → 远程origin拉取规则:拉取所有分支到本地remotes/origin目录下 branch.main.remote=origin → main分支关联的远程仓库:origin branch.main.merge=refs/heads/main → main分支合并对应的远程分支:refs/heads/main branch.development.remote=origin → development分支关联的远程仓库:origin branch.development.merge=refs/heads/development → development分支合并对应的远程分支:refs/heads/development
冲突原因分析
由于使用普通合并将A分支并入development,development的提交历史中会新增一个合并节点。而B分支基于A分支创建,其提交历史包含A的修改记录。当尝试合并B到development时,Git会以共同祖先节点d8527f292为基准进行对比:
- development侧的文件是合并A后的版本
- B侧的文件是A的版本加上B的修改
Git无法自动识别A的修改已被合并到development,因此判定为冲突。
可行的预防/解决方法
方法1:将B分支变基到最新的development
该方法会重写B分支的提交历史,将B的修改重新应用到development的最新提交之后,让Git能识别A的修改已存在于base分支中。
步骤:
git fetch origin git checkout feature/108031 git rebase origin/development # 若变基过程中出现冲突,手动解决后执行: # git add . # git rebase --continue git push -f origin feature/108031
注意:仅在B分支未被其他团队成员拉取使用时使用强制推送,避免影响他人工作。
方法2:将最新的development合并到B分支(常规合并)
该方法不会修改B分支的历史,仅将development的最新状态合并到B中,让B分支提前包含A的合并记录,后续PR时就不会冲突。
步骤:
git fetch origin git checkout feature/108031 git merge origin/development # 若出现冲突(仅当development在A合并后有其他修改同一文件的提交时才会发生),手动解决后提交 git push origin feature/108031
方法3:调整分支创建时机(预防措施)
若业务允许,可等A分支合并到development后,再基于最新的development创建B分支。但此方法不适用于需要在A评审期间并行开发B的场景。
总结
推荐优先使用方法2(常规合并),无需修改历史,风险更低;若希望保持分支历史整洁,且B分支仅个人使用,可选择方法1(变基)。避免使用-X ours策略,以免埋下后续合并的隐患。
内容的提问来源于stack exchange,提问作者user30232135

