本节摘要:分支操作的核心命令是 git branch(列/建/删)和 git switch(切换,推荐替代 checkout)。本节逐个演示列出、创建、切换、创建并切换、安全删除与强制删除六类操作,穿插 git branch -v、--merged、--no-merged 等查看技巧,并提醒分离 HEAD 与删除未合并分支的风险。
阅读完本节,你应当能够:
学会开分支之后,下一个现实问题是:分支开了一堆,怎么管? 每天面对 git branch 列出的十几条分支,你需要快速知道三件事:
本节把这三件事对应的命令全部讲清。你会发现,分支管理其实是一套"低风险、高频率"的操作——每一步都不难,难的是养成"切分支前先确认"的习惯。很多团队事故(改错分支、误删分支)都不是命令不会,而是没确认。
先说一个"确认"的例子。有一次我在仓库里工作,本想切到 feature 分支改代码,结果一敲 git switch feature,工作区文件唰地全变了,才发现自己刚才其实在 main 上改了一下午。幸好改动不冲突,Git 帮我把它们带了过去。这事给我的教训是:切分支前看一秒钟 status,比事后慌乱半小时便宜得多。本节反复强调的各种"先确认",都是这类事故换来的经验。
再补充一个认知:分支操作的命令不多,但组合场景不少。真正的高手不是背命令,而是"看一眼现状就知道该做什么、下一步会发生什么"。比如看到 git branch --no-merged 列出一堆分支,就知道这些分支删不得;看到 git branch -v 里某分支的哈希和 main 一样,就知道它俩内容相同可以放心删。命令是死的,判断力是活的——本节所有小节都在帮你建立这种判断力。
不带参数的 git branch 列出所有本地分支,当前分支带星号标记:
$ git branch develop * main feature/new-feature
三个常用变体:
git branch -r # 只列远程跟踪分支(origin/main 之类) git branch -a # 列出本地 + 远程全部 git branch -v # 附带每个分支指向的最新提交哈希与消息
两个过滤选项格外实用:
git branch --merged # 已合并到当前分支的(通常是"可清理"的) git branch --no-merged # 尚未合并的(有独有提交,删除要谨慎)
--merged 是清理分支的利器——列出的通常都是可以安全删掉的。
git branch 新分支名 在当前提交上创建新指针,但不切换——你仍留在原分支。基于其他提交创建:
git branch feature develop # 基于 develop 分支的最新提交 git branch bugfix/v1.0 a1b2c3d # 基于某个特定提交
注意创建后 HEAD 没动,当前分支不变。很多人以为 git branch 会"跳过去",实际不会——创建与切换是两个独立动作。
创建分支后 HEAD 不动,仍在原分支;要切换得再执行 git switch。
git switch 分支名 把 HEAD 指向目标分支,并把工作区更新成该分支的内容:
git switch feature Switched to branch 'feature'
创建并切换一步完成(日常最高频操作):
git switch -c feature/login Switched to a new branch 'feature/login'
-c(--create)等价于 git branch 名字 + git switch 名字。99% 的开分支场景都用这一条就够了。
为什么推荐 switch 而不是 checkout? checkout 是老命令,一身兼数职(切分支、恢复文件、创建分支),功能多到让人混淆——git checkout 文件 是恢复文件,git checkout 分支 是切分支,两个动作写在同一条命令里,意图经常分不清。Git 2.23 起把切分支拆给 switch、恢复文件拆给 restore,各自意图明确。新项目请习惯用 switch + restore。
安全删除 -d:只有当分支的提交都已合并到当前分支(或其上游)时才允许删除:
git branch -d feature/login Deleted branch feature/login (was a1b2c3d).
如果分支还有未合并的提交,-d 会拒绝并提示用 -D:
error: The branch 'feature/login' is not fully merged. If you are sure you want to delete it, run 'git branch -D feature/login'.
强制删除 -D:无条件删除,即使有未合并提交。用前想清楚:这条分支上的独有提交如果没被任何其他分支引用,就会"失去入口",只能靠 reflog 或记下的哈希找回。删"实验失败"的分支,-D 是常态;删正常功能分支前,先确认已经合并过。如果拿不准,还有个折中方案:先把分支的关键提交哈希记下来,再删除,真后悔了还能靠哈希找回。
把本节所有命令串成一个完整剧本,跟着走一遍,分支管理就齐活了:
# 初始化并提交 git init branch-life && cd branch-life echo a > a.txt && git add . && git commit -m "初始提交" # 创建并切换 git switch -c feature/user-login echo login > login.txt && git add . && git commit -m "添加登录功能" # 回主干看分支情况 git switch main git branch # main 在前,feature/user-login 在后 git branch -v # 能看到每个分支的最新提交 # 合回主干并删除 git merge feature/user-login git branch -d feature/user-login git branch # 只剩 main,干净 # 演示 -D:创建一条"烂尾"分支 git switch -c experiment/fail echo junk > junk.txt && git add . && git commit -m "半成品" git switch main git branch -d experiment/fail # 报错:未合并 git branch -D experiment/fail # 强制删除成功
这个剧本把"建、切、看、合、删"全部演了一遍。特别是最后 -d 报错、-D 成功的那一段,亲身体会一次,以后就不会把两条命令搞混了。
一个值得反复体会的细节:git switch main 之后,你工作区里的文件内容会跟着变——这正是指针模型的实际效果。假设 main 里 a.txt 是"v1",feature 里 a.txt 是"v2",切换时 a.txt 的内容就会在 v1 和 v2 之间来回变。这不是"文件被改坏了",而是"工作区被更新成了目标分支的状态"。
由此引出一个必须记住的规则:有未提交改动时切分支要小心。如果改动在 main 和 feature 之间不冲突,Git 会帮你带着改动切过去;如果冲突,Git 会拒绝切换(阻止你把改动弄丢)。想彻底理解这条规则,可以亲手试一次:在 main 上改 a.txt 不提交,然后 switch 到 feature——观察 Git 的反应。
切换分支会改变工作区内容,如果有未提交的改动,Git 可能阻止切换或带着改动一起切。安全的流程是:
git status:确认工作区状态。git stash(第 5 章)或先提交。git switch 目标分支。建议每完成一个功能就清理一次,别攒:
git switch main # 回主干 git merge feature/login # 合入 git branch -d feature/login # 删掉已合并的功能分支 git branch --merged # 复查还有没有漏网的
有个常见现象值得说透:为什么分支要"即用即删"?除了保持列表干净,还有个实际原因——分支不删,它和主干的差异就会越拉越大,等你想起来再合并时,冲突已经堆积成山。趁热打铁合并删除,是把"合并的痛苦"控制在最小。这也解释了为什么大厂团队规范里都有"feature 分支生命周期不超过 X 天"这类约定:不是仪式感,是工程上的务实选择。
单条命令学完后,把它们拼成策略才有威力。三种最常见的策略:
策略没有绝对对错,只有适不适合团队规模。本节先把"操作层"打牢,第 4 章讲远程协作时,会自然带出"策略在多人场景下怎么落地"。记住:分支策略是对操作的编排,操作学不扎实,策略就是空中楼阁。
| 想做什么 | 命令 |
|---|---|
| 看当前分支 | git branch 或 git status |
| 创建分支 | git branch 名字 |
| 创建并切换 | git switch -c 名字 |
| 切换分支 | git switch 名字 |
| 安全删除 | git branch -d 名字 |
| 强制删除 | git branch -D 名字 |
| 看已合并分支 | git branch --merged |
| 看未合并分支 | git branch --no-merged |
| 看每个分支的最新提交 | git branch -v |
⚠️ 常见坑:删分支时人在要删的分支上,或删除后才发现忘了合并。删除前先用 --merged 确认,再切到别的分支删。
💡 关键直觉:把分支列表想成"任务看板"。git branch 看板上有哪些任务卡,--merged 是"已完成待归档"的卡,--no-merged 是"还在推进"的卡。-D 就是"把这张卡从板上撕掉"——撕之前确认工作都留过底。
"我人在 feature 上,能删掉 main 吗?" 不能删当前所在的分支,Git 会拒绝。想删 main,先切到其他分支。
"切分支时未提交的改动会被带到新分支吗?" 默认会带过去(Git 尝试保留你的改动),但可能造成混乱或切换失败。想干净切换,先 stash 或提交。想强切,用 git switch -f 分支(会丢弃工作区改动,危险)。
"git checkout 还能用吗?" 能,完全兼容,老项目、老习惯都能继续用。只是新写法更推荐 switch/restore 分而治之。你自己喜欢哪个用哪个,团队统一即可。
"怎么给分支改名字?" git branch -m 旧名 新名。改名不改变指向,只是换标签。
"切到不存在的分支会怎样?" Git 会报错提示分支不存在。想基于当前状态创建并切换,用 git switch -c;想从远程拉一条本地没有的分支,用 git switch 远程分支名(Git 会自动创建跟踪的本地分支,第 4 章细讲)。
"我忘了我上次在哪条分支上,怎么办?" 用 git reflog 看 HEAD 的移动记录,能找到每次切换的痕迹;或者直接 git branch 看当前星号在哪。养成"开工先 git status"的习惯,这个问题自然消失。
"多个分支同时开发,会不会互相污染?" 不会。每个分支有自己的提交链和工作区状态,切换时互不影响。真正的"污染"只发生在两类场景:把未提交的改动带到别的分支、把没合并的分支误删——这两类都有对应的安全机制(Git 会阻止危险切换、-d 会拒绝未合并删除),理解机制比害怕事故更有用。
面对一条想删的分支,按顺序自问:
git branch --merged。git log 分支名 里有没有别的分支覆盖的提交。这套三步法把"删不删"从"凭感觉"变成"看事实"。刚开始可能觉得麻烦,但它是防止"误删独有提交"最实用的护栏。宁可多查两秒,不删错一条线。
补充一个很多人不知道的细节:删除分支不删除提交。被删分支指向的那个提交对象还留在对象库里,只要你还记得它的哈希(或能从 reflog 查到),随时能 git branch 分支名 哈希 把它"复活"。这也是"删分支"没有想象中那么可怕的原因——Git 的设计里,绝大多数操作都可逆,理解这个底层逻辑,操作时就少了无谓的恐惧。
下一节把分支合回来:git merge 的快进合并与三方合并,以及 --no-ff、--squash 的取舍。