🌿 Git Workflows — Git高级工作流
v1.0.0Git高级工作流指南,包括特性分支、Git Flow、GitHub Flow等工作流模式。
详细分析 ▾
运行时依赖
版本
初始版本
安装命令
点击复制技能文档
面向实际开发的高级 Git 操作。涵盖交互式变基、二分查找、worktree、reflog 恢复、subtree、submodule、稀疏检出、冲突解决和单体仓库模式。
使用场景
- 合并前清理提交历史(交互式变基)
- 查找哪个提交引入了 Bug(bisect)
- 同时在多个分支上工作(worktree)
- 恢复丢失的提交或撤销错误(reflog)
- 跨仓库管理共享代码(subtree/submodule)
- 解决复杂的合并冲突
- 跨分支或 Fork 挑选提交(cherry-pick)
- 处理大型单体仓库(稀疏检出)
交互式变基
压缩、重排、编辑提交
# 交互式变基最近 5 个提交 git rebase -i HEAD~5
# 变基到 main(分叉以来的所有提交) git rebase -i main
编辑器会打开一个选择列表:
pick a1b2c3d Add user model
pick e4f5g6h Fix typo in user model
pick i7j8k9l Add user controller
pick m0n1o2p Add user routes
pick q3r4s5t Fix import in controller
可用命令:
pick = 原样使用提交
reword = 使用提交但编辑消息
edit = 在此提交后停止以修改
squash = 合并到前一个提交(保留两条消息)
fixup = 合并到前一个提交(丢弃此消息)
drop = 完全删除该提交
常见模式
# 将修复提交压缩到其父提交 # 将修复提交的 "pick" 改为 "fixup": pick a1b2c3d Add user model fixup e4f5g6h Fix typo in user model pick i7j8k9l Add user controller fixup q3r4s5t Fix import in controller pick m0n1o2p Add user routes# 重排提交(只需移动行) pick i7j8k9l Add user controller pick m0n1o2p Add user routes pick a1b2c3d Add user model
# 将一个提交拆分为两个 # 标记为 "edit",然后当它停止时: git reset HEAD~ git add src/model.ts git commit -m "Add user model" git add src/controller.ts git commit -m "Add user controller" git rebase --continue
自动压缩(自动排列的提交消息)
# 提交修复时,引用要压缩到的提交 git commit --fixup=a1b2c3d -m "Fix typo" # 或 git commit --squash=a1b2c3d -m "Additional changes"
# 稍后,使用自动压缩变基 git rebase -i --autosquash main # fixup/squash 提交会自动放在其目标之后
中止或继续
git rebase --abort # 取消并恢复原始状态
git rebase --continue # 解决冲突或编辑后继续
git rebase --skip # 跳过当前提交并继续
Bisect(查找 Bug)
通过提交进行二分搜索
# 开始二分查找 git bisect start# 将当前提交标记为坏提交(有 Bug) git bisect bad
# 标记一个已知的好提交(Bug 出现之前) git bisect good v1.2.0 # 或:git bisect good abc123
# Git 检出中间提交。测试它,然后: git bisect good # 如果此提交没有 Bug git bisect bad # 如果此提交有 Bug
# 重复直到 Git 识别出确切的提交 # "abc123 is the first bad commit"
# 完成 — 返回原始分支 git bisect reset
自动化二分查找(使用测试脚本)
# 完全自动:Git 在每个提交上运行脚本 # 脚本必须以 0 退出表示好,1 退出表示坏 git bisect start HEAD v1.2.0 git bisect run ./test-for-bug.sh
# 测试脚本示例 cat > /tmp/test-for-bug.sh << 'EOF' #!/bin/bash # 如果 Bug 不存在返回 0,如果 Bug 存在返回 1 npm test -- --grep "login should redirect" 2>/dev/null EOF chmod +x /tmp/test-for-bug.sh git bisect run /tmp/test-for-bug.sh
处理构建失败的二分查找
# 如果某个提交无法编译,跳过它 git bisect skip
# 跳过已知损坏的提交范围 git bisect skip v1.3.0..v1.3.5
Worktree(并行分支)
同时在多个分支上工作
# 为不同分支添加工作树 git worktree add ../myproject-hotfix hotfix/urgent-fix # 创建一个新目录,检出该分支# 添加带新分支的工作树 git worktree add ../myproject-feature -b feature/new-thing
# 列出工作树 git worktree list
# 完成后移除工作树 git worktree remove ../myproject-hotfix
# 清理过期的工作树引用 git worktree prune
使用场景
# 审查 PR 同时保持当前工作不受影响 git worktree add ../review-pr-123 origin/pr-123# 在 feature 分支开发时在 main 上运行测试 git worktree add ../main-tests main cd ../main-tests && npm test
# 并排比较分支行为 git worktree add ../compare-old release/v1.0 git worktree add ../compare-new release/v2.0
Reflog(恢复)
查看 Git 记住的一切
# 显示 reflog(所有 HEAD 移动) git reflog # 输出: # abc123 HEAD@{0}: commit: Add feature # def456 HEAD@{1}: rebase: moving to main # ghi789 HEAD@{2}: checkout: moving from feature to main# 显示特定分支的 reflog git reflog show feature/my-branch
# 显示带时间戳的 reflog git reflog --date=relative
从错误中恢复
# 撤销错误的变基(在 reflog 中找到变基前的提交) git reflog # 找到:"ghi789 HEAD@{5}: checkout: moving from feature to main"(变基前) git reset --hard ghi789# 恢复已删除的分支 git reflog # 找到该分支的最后一个提交 git branch recovered-branch abc123
# 在 reset --hard 后恢复 git reflog git reset --hard HEAD@{2} # 回退 2 个 reflog 条目
# 恢复丢弃的 stash git fsck --unreachable | grep commit # 或 git stash list # 如果还在的话 git log --walk-reflogs --all -- stash # 查找丢弃的 stash 提交
Cherry-Pick
将特定提交复制到另一个分支
# 挑选单个提交 git cherry-pick abc123# 挑选多个提交 git cherry-pick abc123 def456 ghi789
# 挑选范围(排他起始,包含结束) git cherry-pick abc123..ghi789
# 挑选但不提交(仅暂存更改) git cherry-pick --no-commit abc123
# 从其他远程/Fork 挑选 git remote add upstream https://github.com/other/repo.git git fetch upstream git cherry-pick upstream/main~3 # upstream main 的第 3 个提交
处理 cherry-pick 期间的冲突
# 如果出现冲突: # 1. 在文件中解决冲突 # 2. 暂存已解决的文件 git add resolved-file.ts # 3. 继续 git cherry-pick --continue
# 或中止 git cherry-pick --abort
Subtree 和 Submodule
Subtree(更简单 — 将代码复制到你的仓库)
# 添加 subtree git subtree add --prefix=lib/shared https://github.com/org/shared-lib.git main --squash# 从上游拉取更新 git subtree pull --prefix=lib/shared https://github.com/org/shared-lib.git main --squash
# 将本地更改推送回上游 git subtree push --prefix=lib/shared https://github.com/org/shared-lib.git main
# 将 subtree 拆分为独立分支(用于提取) git subtree split --prefix=lib/shared -b shared-lib-standalone
Submodule(指向特定提交的另一个仓库的指针)
# 添加 submodule git submodule add https://github.com/org/shared-lib.git lib/shared# 克隆带 submodule 的仓库 git clone --recurse-submodules https://github.com/org/main-repo.git
# 克隆后初始化 submodule(如果忘了 --recurse) git submodule update --init --recursive
# 将 submodule 更新到最新 git submodule update --remote
# 移除 submodule git rm lib/shared rm -rf .git/modules/lib/shared # 如果 .gitmodules 条目仍然存在则删除
何时使用哪个
Subtree:更简单,克隆者无需特殊命令,代码在你的仓库中。 适用于:共享库、供应商代码、上游变更不频繁。
Submodule:指向确切提交,仓库更小,分离清晰。 适用于:大型依赖、独立发布周期、多个贡献者。
稀疏检出(单体仓库)
仅检出你需要的目录
# 启用稀疏检出 git sparse-checkout init --cone# 选择目录 git sparse-checkout set packages/my-app packages/shared-lib
# 添加另一个目录 git sparse-checkout add packages/another-lib
# 列出已检出的内容 git sparse-checkout list
# 禁用(再次检出所有内容) git sparse-checkout disable
使用稀疏检出克隆(大型单体仓库)
# 部分克隆 + 稀疏检出(对超大仓库最快) git clone --filter=blob:none --sparse https://github.com/org/monorepo.git cd monorepo git sparse-checkout set packages/my-service
# 无检出克隆(仅元数据) git clone --no-checkout https://github.com/org/monorepo.git cd monorepo git sparse-checkout set packages/my-service git checkout main
冲突解决
理解冲突标记
<<<<<<< HEAD(或 "ours")
当前分支的更改
=======
来自入站分支的更改
>>>>>>> feature-branch(或 "theirs")
解决策略
# 全部接受我们的(当前分支胜出) git checkout --ours path/to/file.ts git add path/to/file.ts# 全部接受他们的(入站分支胜出) git checkout --theirs path/to/file.ts git add path/to/file.ts
# 对所有文件接受我们的 git checkout --ours . git add .
# 使用合并工具 git mergetool
# 查看三方差异(基础、我们的、他们的) git diff --cc path/to/file.ts
# 显示共同祖先版本 git show :1:path/to/file.ts # 基础(共同祖先) git show :2:path/to/file.ts # 我们的 git show :3:path/to/file.ts # 他们的
变基冲突工作流
# 变基期间,冲突逐个提交出现 # 1. 在文件中修复冲突 # 2. 暂存修复 git add fixed-file.ts # 3. 继续到下一个提交 git rebase --continue # 4. 重复直到完成
# 如果解决后提交变为空 git rebase --skip
Rerere(重用已记录的解决方案)
# 全局启用 rerere git config --global rerere.enabled true# Git 会记住你如何解决冲突 # 下次出现相同冲突时,自动解决
# 查看已记录的解决方案 ls .git/rr-cache/
# 忘记一个错误的解决方案 git rerere forget path/to/file.ts
Stash 模式
# 带消息暂存 git stash push -m "WIP: refactoring auth flow"# 暂存特定文件 git stash push -m "partial stash" -- src/auth.ts src/login.ts
# 暂存包括未跟踪的文件 git stash push -u -m "with untracked"
# 列出暂存 git stash list
# 应用最近的暂存(保留在暂存列表中) git stash apply
# 应用并从暂存列表中移除 git stash pop
# 应用特定的暂存 git stash apply stash@{2}
# 查看暂存内容 git stash show -p stash@{0}
# 从暂存创建分支 git stash branch new-feature stash@{0}
# 丢弃特定暂存 git stash drop stash@{1}
# 清除所有暂存 git stash clear
Blame 和日志考古
# 查看每行的修改者(带日期) git blame src/auth.ts# Blame 特定行范围 git blame -L 50,70 src/auth.ts
# 在 blame 中忽略空白更改 git blame -w src/auth.ts
# 查找某行何时被删除(搜索所有历史) git log -S "function oldName" --oneline
# 查找正则模式何时被添加/删除 git log -G "TODO.hack" --oneline
# 跟踪文件的重命名历史 git log --follow --oneline -- src/new-name.ts
# 显示每行的最后修改提交,忽略移动 git blame -M src/auth.ts
# 显示带文件更改的日志 git log --stat --oneline -20
# 显示影响特定文件的所有提交 git log --oneline -- src/auth.ts
# 显示特定提交的差异 git show abc123
标签和发布
# 创建带注释的标签(发布推荐) git tag -a v1.2.0 -m "Release 1.2.0: Added auth module"# 创建轻量标签 git tag v1.2.0
# 为过去的提交打标签 git tag -a v1.1.0 abc123 -m "Retroactive tag for release 1.1.0"
# 列出标签 git tag -l git tag -l "v1."
# 推送标签 git push origin v1.2.0 # 单个标签 git push origin --tags # 所有标签
# 删除标签 git tag -d v1.2.0 # 本地 git push origin --delete v1.2.0 # 远程
技巧
git rebase -i是最有用的高级 Git 命令。优先学习它。- 永远不要对已推送到共享分支的提交进行变基。只变基你的本地/功能分支。
git reflog是你的安全网。如果丢失提交,几乎总能在 90 天内恢复。git bisect run配合自动化测试比手动二分搜索更快,且消除人为错误。- Worktree 比多次克隆更轻量——它们共享
.git存储。 - 除非有特定原因,优先使用
git subtree而非git submodule。Subtree 对协作者更简单。 - 全局启用
rerere。它会记住冲突解决方案,让你永远不用解决相同的冲突两次。 git stash push -m "描述"比裸git stash好得多。当你有 5 个暂存时会感谢自己。git log -S "字符串"(镐头)是查找函数或变量何时被添加或删除的最快方法。