适用场景
远端 Git 分支被误删,本地没有该分支引用,但 Jenkins 曾经构建过该分支,需要恢复到最后一次构建时的代码版本。若用户不希望继续使用原分支名,恢复时使用新的 restore/ 分支名。
本次验证结论
DGJ2 的 bugfix/20250410_re 被删除后,本地没有分支引用,但 Jenkins DGJ-Debug #3200 日志保留了真实尖端:
94331e93d351bea273715f0958fa0f14d127901f
该提交的父提交为 c6baa64c092c3de06bd48ddcf57787ee79afdc8d。通过提交拓扑追溯到 2025-04-10 的分支侧链,并确认最后增量修改为 application/controllers/basedata/Import.php。随后从 94331e9 创建并推送了新分支:
restore/bugfix-20250410-re
本地和远端最终均指向 94331e93d351bea273715f0958fa0f14d127901f。
标准恢复流程
1. 先保存最后构建提交 SHA
优先查看 Jenkins 最后一次构建控制台日志,关注以下证据:
Checking out Revision <SHA>
rev-parse refs/remotes/origin/<branch>^{commit}
<previous>..<SHA> <branch> -> origin/<branch>
HEAD is now at <SHA>
Checking out Revision 是恢复目标;<previous>..<SHA> 只能说明最后一次构建的增量,不代表整个分支历史。
如果 Jenkins 日志不可用,再检查本地对象库和引用:
git show -s --format='%H %P %ad %an %s' --date=iso-strict <SHA>
git reflog --all
git fsck --no-reflogs --unreachable
在提交 SHA 尚未确认前,不要执行 git gc 或 git prune。
2. 校验提交对象和分支历史
git cat-file -t <SHA>
git show -s --format='%H%n%P%n%ad%n%an%n%s' --date=iso-strict <SHA>
git log --first-parent --reverse --date=iso-strict \
<base-commit>..<SHA> \
--pretty=format:'%H|%ad|%P|%an|%s'
git log --first-parent --merges --date=iso-strict \
<base-commit>..<SHA> \
--pretty=format:'%H|%ad|%P|%an|%s'
对每个合并提交检查第二父提交。合入 develop 的第二父链应单独标记,不能把主干变更误算成分支自身变更;如果合并提交的第二父链不属于 develop,还要展开该侧链:
git log --reverse --date=iso-strict \
<base-commit>..<merge-commit>^2 \
--pretty=format:'%H|%ad|%P|%an|%s'
Git 提交对象不会保存 git branch 命令或远端创建引用的精确时间。分支创建日期应使用“最早分叉基线 + 最早分支自身提交 + Jenkins/远端日志”共同确认,并明确标注为可确认的最早活动时间。
3. 确认新分支名和引用冲突
恢复前确认工作区干净、当前不在待删除分支上,并检查新名称不存在:
git status --short --branch
git branch --list 'restore/<new-name>'
git ls-remote --heads origin 'refs/heads/restore/<new-name>'
建议使用 restore/<原分支名去除特殊字符>,例如:
restore/bugfix-20250410-re
4. 从最后提交创建并推送新分支
现代 Git:
git switch -c restore/<new-name> <SHA>
git push -u origin restore/<new-name>
旧版 Git 不支持 git switch 时:
git checkout -b restore/<new-name> <SHA>
git push -u origin restore/<new-name>
5. 恢复后验证
git status --short --branch
git show -s --format='%H%n%P%n%ad%n%s' --date=iso-strict HEAD
git ls-remote --heads origin 'refs/heads/restore/<new-name>'
必须确认:当前分支名称正确、本地 HEAD 等于目标 SHA、远端 refs/heads/restore/<new-name> 等于同一个 SHA,并且原分支名没有被意外重新创建。
如果需要删除误创建的分支
删除属于有破坏性的操作,必须先精确确认名称和指向,并确保当前不在该分支:
git branch --show-current
git branch -D <wrong-local-branch>
git push origin --delete <wrong-remote-branch>
删除错误恢复分支前,先保留目标 SHA。不要用宽泛通配符,也不要为了清理对象执行 git gc/git prune。
经验与防护要点
- Jenkins 最后一次构建的
Checking out Revision是恢复最后版本的首选证据。 - 最后一次构建的增量文件不是整个分支变更清单;完整清单需要沿第一父链回溯并展开非
develop合并侧链。 - 分支删除后,只要提交对象尚未被 Git 垃圾回收,提交仍可能通过 Jenkins 日志、本地对象库、reflog 或
git fsck找回;找到 SHA 后先做保护性分支。 - 原分支名是否复用必须由用户确认;默认使用
restore/新名称,避免误覆盖或误触发原有 Jenkins 任务。 - 不要把
develop合并带来的代码误报为恢复分支自身开发内容。 - 恢复动作完成后,必须同时验证本地引用和远端引用,不只看
git checkout是否成功。
证据来源
- Jenkins:
DGJ-Debug #3200控制台日志。 - Git 提交:
94331e93d351bea273715f0958fa0f14d127901f及其父链。 - 远端校验:
git ls-remote --heads origin。 - 项目代码仓库:
/Users/zhoujiangbin/code/docker-dev-env/www/dgj2.0。