跳转到内容
新建笔记

Git 合并、变基与冲突处理

合并和变基都用于整合历史,但产生的提交图不同。先决定要保留怎样的历史,再处理具体冲突,避免把“保存文件”“完成合并”“推送远端”混成一个动作。

分叉: A--B--C main
\--D feature
merge: A--B--C--M main
\--D--/ M 有两个父提交
rebase: A--B--C--D' feature
D' 是重新应用 D 的新提交

快进只是移动引用;两条分支已经分叉时,即使没有文件冲突,也可能创建 merge commit。rebase 通常改变被重放提交的哈希,适合尚未公开的个人提交;已共享的历史应先按项目协作规则处理。

终端窗口
git status --short --branch
git diff
git diff --cached
git branch backup/before-integration HEAD

本地备份分支保护已提交历史,不保存未提交文件。未完成的改动可以精确 stash:

终端窗口
git stash push -u -m "work before integration" -- .gitignore src/main.c
git stash list

默认 stash 不包含未跟踪文件,-u 包含未跟踪文件,-a 还包括被忽略文件。操作完成后先 git stash apply --index 'stash@{0}',检查工作区和暂存状态,再决定是否 drop;apply 本身也可能产生冲突。

假设需要把开发分支 qt_derived 合入 main:

终端窗口
git switch main
git merge --no-ff qt_derived

若合并成功,检查代码并测试后推送。若出现冲突,先查看 git status,再逐个编辑文件,删除冲突标记并保留正确的逻辑。确认后按精确路径暂存,以下完成方式选一种:

终端窗口
git add -- .vscode/settings.json assets/assets.qrc
git diff --cached --check
git merge --continue

git commit 也能完成这一步;已经 commit 后不必再执行 merge continue。不准备继续时用 git merge --abort。开始时保留复杂未提交修改可能导致 abort 无法完整重建原状态,因此应先保护工作区。

一个实际冲突记录应如何读

跳转到“一个实际冲突记录应如何读”

原实践记录同时出现三类状态:

文件/状态解释处理方法
.vscode/settings.json both added两边分别新增同一路径比较两份设置,选择正确项目配置
assets/assets.qrc both modified两边都改了资源清单合并资源条目,检查路径、重复项与实际文件
.gitignore 普通未暂存修改不一定属于本次冲突单独保留,别被 git add . 顺手带入

记录中还出现名为 --[no-]pathspec-file-nul、--post-rewrite、git、read、show、with 的未跟踪文件,疑似误粘贴命令帮助造成。它们的名称异常不等于内容必然无用:先检查准确路径、内容和创建原因,再清理,不能直接对整个工作区运行 git clean -fd。

先进入需要重放的分支:

终端窗口
git switch feature
git fetch origin
git rebase origin/main

冲突时编辑并 git add -- <文件>,然后 git rebase --continue;放弃则 git rebase --abort。--skip 会跳过当前提交,只有确定该变更应丢弃或已等价包含时才使用。完成后比较结果、运行测试,再按 远程同步 推送。

git rebase --onto new-base old-base topic 选择 topic 中不属于 old-base 的提交重放到 new-base;两个 base 必须按提交图确定,不能仅按日期猜测。要为已有远程分支建立本地 jp2,应先 fetch,再 git switch --track -c jp2 origin/jp2。

选项含义与限制
--ff-only只接受快进,分叉时停止
--no-ff即使可快进也保留一次合并提交
--squash将合并结果放进工作区/索引;不记录合并父关系,需自行提交
-X ours对可按该选项处理的冲突偏向当前一侧,仍接纳对方非冲突修改
-s ours结果树完全取当前分支,忽略其他树;语义远强于解决冲突
octopus多头合并策略,不适合复杂手工冲突
--gpg-sign、--stat分别涉及提交签名和变化摘要,不决定冲突内容
--autostash临时保存未提交工作,恢复时仍可能冲突

空白忽略、重命名检测等 -X 选项应按实际策略文档使用;它们不能代替审查。详见 git-merge。

撤销合并后,为什么再合并没有恢复

跳转到“撤销合并后,为什么再合并没有恢复”

原记录中的合并提交 1fd7eec 有父提交 b752882 与 1a86620。必须先用 git show --no-patch --pretty=raw 1fd7eec 验证顺序,不能假定 -m 1 永远指想保留的一侧。

git revert -m 1 <merge> 创建新的反向提交,历史仍记得旧分支已合并。因此再次 merge 相同祖先历史,不会自动把已撤销内容全部带回。后续若要恢复,应讨论撤销那次 revert,或引入真正的新提交,再检查依赖关系。尚未公开的错误操作可从备份引用重新整理,但不是任何情况都需要强推。

参考:git-rebase、git-revert、git-stash。