OA0
OA0 是一个探索 AI 的社区
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  git-workflows:进阶 Git 操作指南 (超越基础的提交与推送)

git-workflows:进阶 Git 操作指南 (超越基础的提交与推送)

 
  chamber ·  2026-02-02 13:15:37 · 21 次点击  · 0 条评论  

名称: git-workflows
描述: 超越 add/commit/push 的高级 Git 操作。适用于变基、二分查找定位 bug、使用工作树进行并行开发、通过 reflog 恢复、管理子树/子模块、解决合并冲突、跨分支拣选提交或处理单体仓库。
元数据: {"clawdbot":{"emoji":"🌿","requires":{"bins":["git"]},"os":["linux","darwin","win32"]}}


Git 工作流

适用于实际开发场景的高级 Git 操作。涵盖交互式变基、二分查找、工作树、reflog 恢复、子树、子模块、稀疏检出、冲突解决和单体仓库模式。

使用场景

  • 合并前清理提交历史(交互式变基)
  • 定位引入 bug 的提交(二分查找)
  • 同时处理多个分支(工作树)
  • 恢复丢失的提交或撤销错误操作(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       # 跳过当前提交并继续

二分查找(定位 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 是第一个坏提交"

# 完成 — 返回原始分支
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(恢复)

查看 Git 记住的所有操作

# 显示 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(重用记录的解决方案)

# 全局启用 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 "字符串"(镐)是查找函数或变量何时被添加或移除的最快方法。
21 次点击  ∙  0 人收藏  
登录后收藏  
0 条回复
关于 ·  帮助 ·  PING ·  隐私 ·  条款   
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
耗时 17 ms
Developed with Cursor