涉及 add/commit/push 之外的进阶 Git 操作。适用于变基、二分查找 Bug、使用工作树并行开发、利用 reflog 恢复、管理子树/子模块、解决合并冲突、跨分支拣选提交或处理单体仓库等场景。
Git Workflows. 高级Git操作,用于现实世界的发展.是一项面向实际任务的技能,主要用于Covers 交互式重置基础,双节虫,工作树,reflog恢复,子树,子模块,稀疏的退货,冲突解决,以及。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。
执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
面向真实开发场景的高级 Git 操作。涵盖交互式变基(interactive rebase)、二分查找(bisect)、工作树(worktree)、引用日志恢复(reflog recovery)、子树(subtree)、子模块(submodule)、稀疏检出(sparse checkout)、冲突解决,以及单体仓库(monorepo)模式。
# 交互式变基最近 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" # 后续执行带 autosquash 的变基 git rebase -i --autosquash main # fixup / squash 类提交将被自动置于其目标提交之后
git rebase --abort # 取消变基,恢复原始状态 git rebase --continue # 解决冲突或完成编辑后继续 git rebase --skip # 跳过当前提交并继续
# 启动 bisect git bisect start # 将当前提交标记为“坏”(含缺陷) git bisect bad # 将已知“好”的提交(缺陷出现前)标记为“好” git bisect good v1.2.0 # 或:git bisect good abc123 # Git 将检出中间提交。测试后执行: git bisect good # 若该提交无缺陷 git bisect bad # 若该提交存在缺陷 # 重复上述步骤,直至 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 # 若缺陷未出现则返回 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 # 在 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(所有 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
# 在执行了 git reset --hard 后恢复
git reflog
git reset --hard HEAD@{2} # 回退至 reflog 中倒数第二个条目
# 恢复被丢弃的 stash
git fsck --unreachable | grep commit
# 或
git stash list # 若 stash 仍存在
git log --walk-reflogs --all -- stash # 查找被丢弃的 stash 提交
# 挑选单个提交 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 主干倒数第 3 个提交
# 若发生冲突: # 1. 在文件中手动解决冲突 # 2. 将已解决的文件加入暂存区 git add resolved-file.ts # 3. 继续 cherry-pick 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 # 当前分支版本(ours) git show :3:path/to/file.ts # 待合并分支版本(theirs)
# 变基期间,冲突会逐个提交出现 # 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
# 带描述信息保存 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"
# 列出所有 stash
git stash list
# 应用最近一次 stash(保留在 stash 列表中)
git stash apply
# 应用并从 stash 列表中移除
git stash pop
# 应用指定 stash
git stash apply stash@{2}
# 查看某次 stash 的具体内容
git stash show -p stash@{0}
# 从 stash 创建新分支
git stash branch new-feature stash@{0}
# 删除指定 stash
git stash drop stash@{1}
# 清空所有 stash
git stash clear
# 查看每行代码由谁在何时修改 git blame src/auth.ts # 追溯指定行范围 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,比手动二分查找更快,且可避免人为失误。.git 存储。git subtree 而非 git submodule。子树对协作者更友好。rerere。它能记住冲突解决方案,让你永远不必重复解决同一冲突。git stash push -m "description" 而非裸用 git stash。当你拥有 5 个 stash 时,你会感激自己。git log -S "string"(pickaxe 功能)是查找函数或变量被添加/删除时间的最快方式。