名称: git-workflows
描述: 超越 add/commit/push 的高级 Git 操作。适用于变基、二分查找定位 bug、使用工作树进行并行开发、通过 reflog 恢复、管理子树/子模块、解决合并冲突、跨分支拣选提交或处理单体仓库。
元数据: {"clawdbot":{"emoji":"🌿","requires":{"bins":["git"]},"os":["linux","darwin","win32"]}}
适用于实际开发场景的高级 Git 操作。涵盖交互式变基、二分查找、工作树、reflog 恢复、子树、子模块、稀疏检出、冲突解决和单体仓库模式。
# 交互式变基最近 5 个提交
git rebase -i HEAD~5
# 变基到 main 分支(自分支点以来的所有提交)
git rebase -i main
编辑器会打开一个选择列表:
pick a1b2c3d 添加用户模型
pick e4f5g6h 修复用户模型中的拼写错误
pick i7j8k9l 添加用户控制器
pick m0n1o2p 添加用户路由
pick q3r4s5t 修复控制器中的导入
可用命令:
pick = 原样使用提交
reword = 使用提交但编辑提交信息
edit = 在此提交后暂停以修改它
squash = 合并到前一个提交中(保留两条提交信息)
fixup = 合并到前一个提交中(丢弃此提交信息)
drop = 完全移除该提交
# 将修复提交压缩到其父提交中
# 将修复提交的 "pick" 改为 "fixup":
pick a1b2c3d 添加用户模型
fixup e4f5g6h 修复用户模型中的拼写错误
pick i7j8k9l 添加用户控制器
fixup q3r4s5t 修复控制器中的导入
pick m0n1o2p 添加用户路由
# 重排提交(只需移动行)
pick i7j8k9l 添加用户控制器
pick m0n1o2p 添加用户路由
pick a1b2c3d 添加用户模型
# 将一个提交拆分为两个
# 标记为 "edit",然后在暂停时:
git reset HEAD~
git add src/model.ts
git commit -m "添加用户模型"
git add src/controller.ts
git commit -m "添加用户控制器"
git rebase --continue
# 提交修复时,引用要压缩到的提交
git commit --fixup=a1b2c3d -m "修复拼写错误"
# 或
git commit --squash=a1b2c3d -m "额外更改"
# 之后,使用自动压缩进行变基
git rebase -i --autosquash main
# fixup/squash 提交会自动放置在其目标提交之后
git rebase --abort # 取消并恢复原始状态
git rebase --continue # 解决冲突或编辑后继续
git rebase --skip # 跳过当前提交并继续
# 开始二分查找
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 是第一个坏提交"
# 完成 — 返回原始分支
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,存在则返回 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
# 为另一个分支添加工作树
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
# 在功能分支上开发时,在 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(所有 HEAD 移动记录)
git reflog
# 输出:
# abc123 HEAD@{0}: commit: 添加功能
# 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 条目
# 恢复丢弃的储藏
git fsck --unreachable | grep commit
# 或
git stash list # 如果它还在
git log --walk-reflogs --all -- stash # 查找丢弃的储藏提交
# 拣选单个提交
git cherry-pick abc123
# 拣选多个提交
git cherry-pick abc123 def456 ghi789
# 拣选一个范围(起始提交不包含,结束提交包含)
git cherry-pick abc123..ghi789
# 拣选但不提交(仅暂存更改)
git cherry-pick --no-commit abc123
# 从另一个远程/复刻拣选
git remote add upstream https://github.com/other/repo.git
git fetch upstream
git cherry-pick upstream/main~3 # 来自上游 main 分支的第 3 个提交
# 如果出现冲突:
# 1. 在文件中解决冲突
# 2. 暂存已解决的文件
git add resolved-file.ts
# 3. 继续
git cherry-pick --continue
# 或中止
git cherry-pick --abort
# 添加子树
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
# 将子树拆分到自己的分支中(用于提取)
git subtree split --prefix=lib/shared -b shared-lib-standalone
# 添加子模块
git submodule add https://github.com/org/shared-lib.git lib/shared
# 克隆包含子模块的仓库
git clone --recurse-submodules https://github.com/org/main-repo.git
# 克隆后初始化子模块(如果忘记 --recurse)
git submodule update --init --recursive
# 更新子模块到最新版本
git submodule update --remote
# 移除子模块
git rm lib/shared
rm -rf .git/modules/lib/shared
# 如果 .gitmodules 中仍有条目,请移除
子树:更简单,克隆者无需特殊命令,代码存在于你的仓库中。
适用于:共享库、供应商代码、上游更新不频繁。
子模块:指向确切提交的指针,仓库更小,分离清晰。
适用于:大型依赖项、独立的发布周期、贡献者众多。
# 启用稀疏检出
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
git config --global rerere.enabled true
# Git 会记住你如何解决冲突
# 下次出现相同冲突时,它会自动解决
# 查看记录的解决方案
ls .git/rr-cache/
# 忘记一个错误的解决方案
git rerere forget path/to/file.ts
# 带信息储藏
git stash push -m "WIP: 重构认证流程"
# 储藏特定文件
git stash push -m "部分储藏" -- src/auth.ts src/login.ts
# 储藏包含未跟踪文件
git stash push -u -m "包含未跟踪文件"
# 列出储藏
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
# 谁更改了每一行(带日期)
git blame src/auth.ts
# 追溯特定行范围
git blame -L 50,70 src/auth.ts
# 在追溯中忽略空白更改
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 "发布 1.2.0:添加认证模块"
# 创建轻量标签
git tag v1.2.0
# 为过去的提交打标签
git tag -a v1.1.0 abc123 -m "为发布 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 比手动二分查找更快,并能消除人为错误。.git 存储。git subtree 而非 git submodule。子树对协作者来说更简单。rerere。它会记住冲突解决方案,让你永远不会重复解决相同的冲突。git stash push -m "描述" 比单纯的 git stash 好得多。当你有 5 个储藏时,你会感谢自己。git log -S "字符串"(镐)是查找函数或变量何时被添加或移除的最快方法。