Git Workflows技能使用说明
2026-03-26
新闻来源:网淘吧
围观:87
电脑广告
手机广告
Git 工作流
适用于实际开发的高级 Git 操作。涵盖交互式变基、二分查找、工作树、引用日志恢复、子树、子模块、稀疏检出、冲突解决和单体仓库模式。
何时使用
- 在合并前清理提交历史(交互式变基)
- 查找哪个提交引入了错误(二分查找)
- 同时处理多个分支(工作树)
- 恢复丢失的提交或撤销错误操作(引用日志)
- 跨仓库管理共享代码(子树/子模块)
- 解决复杂的合并冲突
- 跨分支或派生仓库挑选提交
- 处理大型单体仓库(稀疏检出)
交互式变基
压缩、重新排序、编辑提交
# 交互式变基最近 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
恢复日志(Recovery)
查看Git记住的所有内容
# 显示恢复日志(所有HEAD移动记录)
git reflog
# 输出示例:
# abc123 HEAD@{0}: commit: 添加功能
# def456 HEAD@{1}: rebase: 移动到主分支
# ghi789 HEAD@{2}: checkout: 从功能分支切换到主分支
# 显示特定分支的恢复日志
git reflog show feature/my-branch
# 显示带时间戳的恢复日志
git reflog --date=relative
从错误中恢复
# 撤销错误的重设基底操作(在恢复日志中找到重设基底前的提交)
git reflog
# 找到:"ghi789 HEAD@{5}: checkout: 从功能分支切换到主分支"(重设基底前)
git reset --hard ghi789
# 恢复已删除的分支
git reflog
# 找到该分支上的最后一次提交
git branch recovered-branch abc123
# 在reset --hard操作后恢复
git reflog
git reset --hard HEAD@{2} # 回退到恢复日志中的前两个条目
# 恢复已丢弃的暂存
git fsck --unreachable | grep commit
# 或
git stash list # 如果它还存在
git log --walk-reflogs --all -- stash # 查找已丢弃的暂存提交
拣选提交(Cherry-Pick)
将特定提交复制到另一个分支
# 拣选单个提交
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 # 从上游主分支的第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 中仍有记录,请手动删除
使用场景对比
子树:更简单,克隆者无需特殊命令,代码直接存在于主仓库。
适用场景:共享库、供应商代码、上游更新不频繁的情况。
子模块:指向精确提交,仓库体积更小,分离清晰。
适用场景:大型依赖项、独立发布周期、多贡献者协作。
稀疏检出(Monorepo 单仓库模式)
仅检出所需目录
# 启用稀疏检出
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(或"我方")
当前分支上的修改内容
=======
来自合并分支的修改内容
>>>>>>> feature-branch(或"对方")
解决策略
# 接受我们所有的更改(当前分支胜出)
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 "字符串"(镐)是查找函数或变量何时添加或删除的最快方法。
文章底部电脑广告
手机广告位-内容正文底部


微信扫一扫,打赏作者吧~