Git 实战指南:从分支管理到远程协作
本文从实际开发场景出发,梳理 Git 在日常工作中最常遇到的操作与决策。不讲花哨技巧,只聚焦「遇到这种情况该怎么办」。
一、分支:Git 的核心工作单元
在团队开发中,分支是隔离工作的基本手段。你不会直接在 main 上写代码——你会创建一个分支,在上面开发,完成后再合并回去。
创建与切换
# 创建并切换到新分支(推荐写法)git checkout -b feature
# 等价于两步操作git branch featuregit checkout featureHEAD 是什么?
HEAD 是一个指针,指向你当前所在的分支(或提交)。你做的每一次 checkout 都在移动它。
HEAD^— 当前提交的父节点HEAD~3— 往前数 3 个提交git branch -f main HEAD~2— 强制把 main 指针移到前 2 个提交
下面这张图展示了一个典型的分支工作流——从 main 创建 feature 和 bugfix 分支,独立开发后合并回主干:
二、merge vs rebase:最常见的选择困难
这是 Git 使用中最核心的决策之一。两者都能把代码合到一起,但适用场景完全不同。
merge:保留历史轨迹
# 在 main 上执行,合并 feature 分支git checkout maingit merge --no-ff feature--no-ff 强制生成一个合并提交,即使可以快进。这样在历史中能清楚看到「这个功能是从哪里合进来的」。
rebase:保持线性历史
# 在 feature 上执行,把自己的改动「垫」到 main 最新提交之上git checkout featuregit rebase mainrebase 的本质是:暂存你的提交 → 应用目标分支的更新 → 重新应用你的提交。注意,它修改的是当前分支,目标分支只是参照物。
黄金法则
- 个人分支用 rebase:保持干净的线性历史
- 公共分支用 merge:保留合并记录,不改写已推送的历史
- 已推送的分支禁止 rebase:会导致其他人的历史混乱
三、撤销操作:出了问题怎么办?
开发中难免会提交错误的代码。关键问题是:这个提交已经推送到远程了吗?
仅本地:git reset
# 撤销最近一次提交,保留改动在暂存区git reset --soft HEAD^
# 撤销最近一次提交,保留改动在工作区(默认行为)git reset HEAD^
# 彻底丢弃改动(不可恢复!)git reset --hard HEAD^已推送:git revert
# 生成一个新的「反向提交」来撤销指定提交git revert <commit-hash>revert 不会改写历史,而是创建一个新提交来抵消之前的改动。这在协作中是安全的。
cherry-pick:精确挑选
# 只把 C2 和 C4 这两个提交挪到当前分支git cherry-pick C2 C4区别于 rebase 拿整个分支,cherry-pick 让你精确选择需要的提交。
交互式 rebase:整理本地提交
git rebase -i HEAD~4可以重排、合并、删除、编辑最近 4 个提交。在推送前整理提交历史非常有用。
四、远程协作:本地与远程的同步
核心概念
origin/main(简写o/main)是本地存储的远程仓库快照- 它不会自动更新,需要手动
fetch
fetch、pull、push 的关系
# 只更新远程跟踪分支,不动本地分支(安全)git fetch origin
# 拉取并合并(= fetch + merge)git pull origin main
# 拉取并变基(= fetch + rebase,推荐)git pull --rebase
# 推送本地提交到远程git push origin mainRemote Tracking:分支跟踪
# 创建本地分支并跟踪远程分支git checkout -b feature o/main
# 为已有分支设置跟踪git branch -u o/main feature设置跟踪后,git push 和 git pull 就知道该推到哪里、从哪里拉了。
五、push / fetch / pull 参数详解
这三个命令都支持 origin <source>:<destination> 的参数格式,但方向相反:
- push:本地 → 远程(推送空 source = 删除远程分支)
- fetch:远程 → 本地(拉取空 source = 创建本地分支)
# push 的 source:destinationgit push origin main # 本地 main → 远程 maingit push origin local:remote # 本地 local → 远程 remotegit push origin :remote # 删除远程 remote 分支
# fetch 的 source:destination(方向相反)git fetch origin main # 远程 main → 本地 o/maingit fetch origin remote:local # 远程 remote → 本地 localgit fetch origin :local # 创建本地 local 分支
# pull 是 fetch + merge 的组合git pull origin foo # = fetch origin foo + merge o/foogit pull origin bar:bugFix # = fetch origin bar:bugFix + merge bugFix六、现代 Git 协作利器:worktree
为什么需要 worktree?
在实际开发中,你经常会遇到这样的场景:正在 feature 分支上写代码写到一半,突然线上出了 bug 需要紧急修复。传统做法是 git stash → 切分支 → 修 bug → 切回来 → git stash pop。这个流程有几个痛点:
- stash 恢复时可能产生冲突
- IDE 的构建缓存、运行状态全部丢失
- 频繁切换分支的心智负担很重
git worktree 从根本上解决了这个问题:它允许你从同一个仓库创建多个工作目录,每个目录检出不同的分支,彼此完全独立。
核心设计理念
worktree 的设计哲学是共享存储,隔离工作区:
- 所有工作树共享同一个
.git对象库(commits、blobs、trees) - 每个工作树有独立的
HEAD、index(暂存区)和工作目录 - 磁盘开销极小——只多了一份工作区文件,不会复制整个仓库
这意味着你在任何一个工作树中做的提交,其他工作树都能立即看到(通过 git log),因为底层数据是共享的。
实战场景对比
下面这张图对比了传统方式和 worktree 方式处理「开发中途需要修 bug」的流程差异:
常用命令速查
# 创建一个新的工作树,检出 hotfix 分支git worktree add ../project-hotfix hotfix
# 创建工作树的同时创建新分支git worktree add -b emergency-fix ../project-fix main
# 查看所有工作树git worktree list
# 完成后移除工作树(先删目录再清理引用)git worktree remove ../project-hotfix
# 清理已失效的工作树引用git worktree prune使用注意事项
- 同一分支不能同时被两个工作树检出——这是 Git 的硬性限制,防止两个工作区同时修改同一分支导致混乱
- 不要直接
rm -rf删除工作树目录——应该用git worktree remove,否则.git/worktrees中会残留失效引用 - 子模块在链接工作树中的支持有限,复杂项目需要测试验证
- worktree 非常适合 CI/CD 场景——可以同时构建多个分支而不互相干扰
什么时候该用 worktree?
| 场景 | 是否推荐 |
|---|---|
| 开发中途需要紧急修 bug | ✅ 最佳场景 |
| 需要同时对比两个分支的运行效果 | ✅ 非常适合 |
| 长期维护多个版本(v1、v2) | ✅ 比多个 clone 更轻量 |
| CI 并行构建多个分支 | ✅ 共享对象库,节省空间 |
| 只是简单切个分支看一眼 | ❌ 直接 checkout 更快 |
七、实战工作流总结
日常开发流程
git fetch origin— 先看看远程有什么变化git rebase o/main— 在个人分支上变基到最新- 解决可能的冲突
git push— 推送到远程- 在
main上merge --no-ff— 合并功能分支
版本标记
# 给特定提交打标签git tag v1.0.0 <commit-hash>
# 查看离当前位置最近的标签git describe main常见陷阱
| 陷阱 | 后果 | 正确做法 |
|---|---|---|
| 在公共分支上 rebase | 其他人的历史被打乱 | 只在个人分支 rebase |
| rebase 时搞反目标和参考分支 | 提交顺序错乱 | 仔细确认当前分支和目标分支 |
| reset —hard 后想恢复 | 改动永久丢失 | 先确认不需要,或用 reflog 抢救 |
| push 空 source 到远程 | 远程分支被删除 | 检查命令参数再执行 |
记住两条核心原则:个人分支随便折腾,公共分支只做加法。掌握了 merge/rebase 的选择和 reset/revert 的区分,日常 Git 操作就不会出大问题。
部分信息可能已经过时








