目标
跳转到“目标”处理这种状态:本地分支和远程分支显示同时领先、落后,但双方某些提交产生的文件内容完全相同。目标是保留真正的新改动,跳过重复补丁,形成线性历史并完成非强制推送。
本次实例最初显示:
v20-noddr...origin/v20-noddr [ahead 2, behind 1]
> 6e82349 本地实际更新> e1032c1 本地重复提交< ab26824 远程重复提交e1032c1 与 ab26824 的提交哈希、作者信息和说明不同,但树对象相同,说明两次提交的最终文件快照一致。
为什么不能直接 pull
跳转到“为什么不能直接 pull”git pull 的整合方式取决于选项与配置。先 fetch 再查看提交图,才能决定使用 merge、rebase 还是只允许快进;不能因为树相同就直接 force push。这个案例采用以下顺序:
- 检查并保护工作区。
- 确认重复提交是否真的具有相同树对象。
- 建立本地备份引用。
- 获取并清理远程跟踪引用。
- 将真正的本地提交 rebase 到远程分支。
- 普通推送并比较远程提交号。
诊断
跳转到“诊断”先查看分支、工作区和分叉图:
git status --short --branchgit branch -vvgit fetch origingit log --oneline --decorate --graph --left-right origin/v20-noddr...v20-noddr不要只依据提交说明判断内容。使用树对象比较两个提交的完整文件快照:
git show -s --format=%T e1032c1git show -s --format=%T ab26824git diff --stat e1032c1 ab26824git diff --name-status e1032c1 ab26824本实例中两个 %T 输出一致,两个 diff 也为空,因此可以确认最终快照相同。仍应检查父提交和各自差异;树相同本身并不保证任意 rebase 都可以无冲突跳过对应提交。
保护工作区
跳转到“保护工作区”执行 rebase 前必须先处理未提交内容。Vivado 等 IDE 可能只因打开工程就改写绝对路径、UUID、运行目录或仿真元数据,这类变化不能未经检查就提交或丢弃。
git diff --checkgit diff --statgit diff -- path/to/project.xpr若需要暂存这些变化,应限定精确路径并写清原因:
git stash push -m "IDE automatic project rewrite" -- path/to/project.xpr不要在工作区有未知改动时执行 reset --hard 或开始 rebase。
线性同步
跳转到“线性同步”工作区干净后,先给当前状态建立本地备份引用:
git switch v20-noddrgit branch backup/v20-noddr-before-sync-20260817 HEADgit fetch --prune origingit rebase origin/v20-noddr本实例的 rebase 输出包含:
warning: skipped previously applied commit e1032c1Successfully rebased and updated refs/heads/v20-noddr.Git 识别出 e1032c1 的补丁已由远程提交实现,因此跳过它,只把实际更新重新应用到 ab26824 之后。rebase 后实际更新的提交哈希由 6e82349 变为 ee85e14,这是重建提交的正常结果。
推送与验证
跳转到“推送与验证”先确认只领先远程一个真实提交,再使用普通推送:
git status --short --branchgit diff --check origin/v20-noddr...HEADgit log --oneline --decorate --graph -n 5git push origin v20-noddr最后不要只相信 git push 的文字提示,应读取远程引用并和本地 HEAD 比较:
git rev-parse HEADgit ls-remote --heads origin v20-noddrgit status --short --branch本实例最终本地和远程都指向:
ee85e14bd6fa9b06f0475b8960833ae2badd8d5b工作区干净,分支不再领先或落后,整个过程没有 merge commit、force push 或历史分支删除。
若此前创建了 IDE 修改的 stash,应选择正确条目 git stash apply --index,检查并解决可能的冲突后,再删除该 stash;不要为了得到干净状态而丢弃原有修改。
经验总结
跳转到“经验总结”ahead/behind描述提交图关系,不等同于文件内容不同。- 比较提交树比比较提交信息更可靠;树相同表示完整文件快照相同。
git fetch --prune的清理范围取决于 refspec;默认分支映射通常清理失效的远程跟踪分支,自定义标签映射应另外检查。- rebase 可能自动跳过已在上游实现的补丁,这是本场景需要的行为。
- rebase 会改变被重新应用提交的哈希,所以推送前必须重新记录最终提交号。
- IDE 自动改写与功能修改要分开审查,不能因为工程“验证通过”就把所有元数据变化一并提交。
- 同步完成的判据是本地引用、远程引用和工作区状态一致,不只是命令返回成功。
回滚
跳转到“回滚”如果 rebase 尚未完成且结果不正确:
git rebase --abort如果 rebase 已完成但尚未推送,可以从事先创建的备份分支检查或恢复提交。不要在未确认目标提交和工作区状态时使用破坏性重置。