
1. 分支查看这件事远比你想的更重要很多人刚接触 Git 的时候觉得git branch不就是列一下分支名嘛有什么好讲的。但我带了这么多新人发现一个规律凡是分支查看命令用不利索的人后面合并冲突、误删分支、推错远程的事故概率至少翻三倍。原因很简单你连自己现在站在哪、周围有哪些分支、本地和远程差了多少都搞不清楚后面的操作基本就是盲人摸象。这篇文章我打算把git branch这一族命令彻底讲透。从最基础的本地分支查看到远程分支、所有分支的列举再到排序、过滤、追踪关系、已合并未合并筛选以及和git branch -r、git branch -a、git branch -vv这些高频参数的配合使用。适合刚学 Git 的新手也适合用了两三年但一直靠 IDE 图形界面、命令行不熟练的开发者。看完你至少能做到在任何仓库里三秒内判断出当前分支状态、本地与远程的差异、哪些分支可以安全清理。我下面讲的所有内容都是基于 Git 2.30 以上版本在 Windows、macOS、Linux 上的通用行为命令本身跨平台一致差异只在终端显示上。文中涉及的操作步骤和参数解释一部分来自官方文档一部分来自我这些年踩坑总结出来的实际经验我会明确区分哪些是标准行为、哪些是我个人的使用习惯。2. 先搞清楚 Git 分支的本质模型2.1 分支就是一个指向提交的指针要理解git branch的输出得先明白 Git 里分支到底是什么。很多人以为分支是一份代码拷贝其实完全不是。Git 的分支本质上就是一个包含 41 个字节的文件里面存的是一个 40 位的 SHA-1 哈希值加一个换行符指向某个 commit 对象。这个文件放在.git/refs/heads/目录下文件名就是分支名。你可以自己验证一下。随便进一个仓库执行cat .git/refs/heads/main输出就是类似a3f5c8e9b2d1...这样一串哈希。这就是main分支的全部内容。所以创建分支在 Git 里是极轻量的操作本质上就是写一个文件这也是为什么 Git 鼓励你频繁开分支。理解这一点之后git branch的输出就好解释了它做的事情就是去.git/refs/heads/目录下把所有文件名列出来然后对照 HEAD 指针判断哪个是当前分支。远程分支则存在.git/refs/remotes/目录下结构是远程名/分支名。2.2 HEAD、本地分支、远程跟踪分支三者的关系这里有个概念必须掰清楚否则后面看-a的输出会晕。Git 里有三种分支本地分支存在.git/refs/heads/你可以直接切换、提交。远程跟踪分支存在.git/refs/remotes/origin/这类目录下它是本地对远程仓库状态的缓存你不能直接在上面提交只能通过 fetch/pull 更新。远程分支真正存在于远程服务器上的分支本地看不到实体只能通过远程跟踪分支间接感知。git branch默认只显示本地分支git branch -r显示远程跟踪分支git branch -a显示两者。这个设计逻辑是日常开发你 90% 的时间只关心本地分支远程分支是参考信息所以默认不显示避免干扰。注意远程跟踪分支的名字里带origin/前缀这个origin是远程仓库的别名不是固定值。你可以用git remote -v查看当前配置了哪些远程别名。3. 本地分支查看git branch 的基础与进阶3.1 最基础的 git branch 输出解读在任意仓库里敲下git branch你会看到类似这样的输出develop * main feature/login hotfix/order-bug星号*标记的就是当前 HEAD 指向的分支也就是你正在工作的分支。没有星号的是其他本地分支。默认按字母顺序排列这个顺序在分支多的时候其实不太好用后面我会讲怎么按时间排序。这里有个细节如果你处于detached HEAD状态比如 checkout 了某个具体的 commit 哈希git branch的输出里不会有任何分支带星号而是会提示* (HEAD detached at a3f5c8e)。这个状态很危险你在此状态下的提交不属于任何分支切走之后很容易丢失。看到这个提示就要警觉。3.2 用 -v 和 -vv 看清分支背后的提交光看分支名信息量太少。加上-vgit branch -v输出变成develop a3f5c8e [ahead 2] 修复订单导出问题 * main b7d2e1f [ahead 1, behind 3] 合并支付模块 feature/login c9a4b2d 登录页样式调整每一行多了三块信息该分支最新 commit 的短哈希、与上游分支的领先/落后情况、commit message 首行。这个[ahead 2]表示本地比远程多 2 个提交[behind 3]表示远程比本地多 3 个提交。这个信息在推送前判断是否需要先 pull 极其有用。再加一个-v变成-vvgit branch -vv多出来的是上游分支的完整名称develop a3f5c8e [origin/develop: ahead 2] 修复订单导出问题 * main b7d2e1f [origin/main: ahead 1, behind 3] 合并支付模块 feature/login c9a4b2d [origin/feature/login] 登录页样式调整方括号里origin/develop就是这个本地分支追踪的远程分支。如果某个分支没有方括号内容说明它没有设置上游追踪分支你在这个分支上直接git push会报错需要git push -u origin 分支名来建立追踪关系。我个人的习惯是每次开始工作前先git branch -vv扫一眼确认当前分支的上游是否正确、有没有落后远程。这个习惯帮我避免过好几次在错误分支上开发然后推错地方的事故。3.3 按提交时间排序快速找到最近在动的分支分支一多字母序就废了。比如你有二十几个 feature 分支想找上周刚动过的那个字母序里翻半天。这时候用git branch --sort-committerdate-committerdate表示按提交者日期排序前面的-表示降序也就是最近提交的排最前面。输出大概是这样* feature/payment-v2 2小时前 main 3天前 develop 1周前 feature/login 2周前 hotfix/order-bug 1个月前这个视图在清理陈旧分支时特别好用一眼就能看出哪些分支已经很久没动了。我一般会配合--format自定义输出把日期也带上git branch --sort-committerdate --format%(committerdate:short) %(refname:short) %(subject)输出2024-06-15 feature/payment-v2 支付流程重构 2024-06-12 main 合并支付模块 2024-06-05 develop 修复订单导出问题%(committerdate:short)是短日期格式%(refname:short)是分支短名%(subject)是 commit 标题。这个 format 语法是 Git 的 for-each-ref 格式git branch底层就是调它所以可以直接用。实操心得我建议把git branch --sort-committerdate --format...做成一个 alias比如git recent这样每天开工敲一下整个仓库的活跃分支一目了然。配置命令是git config --global alias.recent branch --sort-committerdate --format%(committerdate:short) %(refname:short) %(subject)。3.4 用 --merged 和 --no-merged 判断分支能否安全删除这是我认为git branch里最被低估的两个参数。当你合并完一个 feature 分支后想删掉它但又怕里面还有没合并的提交怎么办git branch --merged列出所有已经合并到当前分支的分支。这些分支的提交都已经在当前分支的历史里了删掉它们不会丢失任何代码。输出里通常会包含main、develop以及刚合并完的 feature 分支。git branch --no-merged列出所有尚未合并到当前分支的分支。这些分支里有当前分支没有的提交删掉会丢代码要谨慎。这两个命令还可以指定基准分支git branch --merged main git branch --no-merged develop意思是哪些分支已经/尚未合并到 main或 develop。在清理分支时我通常先切到 maingit branch --merged看哪些能删再git branch --no-merged看哪些还得留着。这个流程比凭记忆删分支安全得多。注意--merged判断的是该分支的 tip commit 是否是当前分支的祖先。如果分支被 squash merge 过它的原始提交不在当前分支历史里--merged会认为它没合并。这是 squash merge 的一个副作用清理时要注意甄别。4. 远程分支查看git branch -r 的正确用法4.1 git branch -r 显示的是什么git branch -r输出origin/HEAD - origin/main origin/develop origin/main origin/feature/login origin/feature/payment-v2这里列出的不是远程服务器上的实时分支而是你本地缓存的远程跟踪分支。它们的更新时机是你执行git fetch、git pull、git push的时候。如果你很久没 fetch这个列表可能是过时的远程新建或删除的分支不会反映出来。origin/HEAD - origin/main这一行表示远程仓库的默认分支是main。这个符号引用在你git clone时自动建立方便你知道远程的默认分支是哪个。4.2 先 fetch 再看避免看到过时信息因为-r显示的是缓存所以看之前最好先同步一下git fetch --prune git branch -r--prune的作用是删除本地那些远程已经删掉的跟踪分支。比如同事在远程删了feature/old你本地还留着origin/feature/old--prune会把它清掉。不加--prune的话这些幽灵分支会一直堆着越积越多。我见过最夸张的一个仓库git branch -r列出来两百多个远程分支其中一半是早就删掉的。当事人一直以为远程有这么多分支其实是本地缓存没清理。所以养成 fetch 带 --prune 的习惯能省很多困惑。你也可以把 prune 设成默认行为git config --global fetch.prune true这样以后每次git fetch都自动清理不用手动加参数。4.3 查看远程分支的详细信息和本地分支一样-r也能配合-vgit branch -rv输出origin/develop a3f5c8e 修复订单导出问题 origin/main b7d2e1f 合并支付模块 origin/feature/login c9a4b2d 登录页样式调整这里显示的是每个远程跟踪分支最新 commit 的信息。注意远程跟踪分支没有上游概念所以-vv对-r没有额外效果。如果你想知道某个远程分支和本地对应分支差多少用git rev-list --left-right --count main...origin/main输出两个数字比如1 3表示本地领先 1 个提交、落后 3 个提交。这个在git branch -vv里已经用[ahead 1, behind 3]展示过了但单独用 rev-list 可以在脚本里做判断。5. 所有分支查看git branch -a 与远程本地对照5.1 git branch -a 的完整输出git branch -a输出* main develop feature/login remotes/origin/HEAD - origin/main remotes/origin/develop remotes/origin/main remotes/origin/feature/login remotes/origin/feature/payment-v2本地分支在前远程跟踪分支在后用remotes/前缀区分。这个视图适合做本地与远程的对照哪些分支本地有远程也有、哪些只有本地、哪些只有远程。比如上面这个输出里feature/payment-v2只有远程有本地没有说明同事推上去了但你还没拉下来。develop本地远程都有。这种对照在多人协作时特别有用能快速发现我是不是漏拉了某个分支。5.2 用 --format 定制 -a 的输出-a的默认输出信息量有限配合--format可以做出很实用的对照表git branch -a --format%(refname:short) | %(committerdate:short) | %(subject)输出main | 2024-06-12 | 合并支付模块 develop | 2024-06-05 | 修复订单导出问题 feature/login | 2024-05-28 | 登录页样式调整 origin/main | 2024-06-12 | 合并支付模块 origin/develop | 2024-06-05 | 修复订单导出问题 origin/feature/login | 2024-05-28 | 登录页样式调整 origin/feature/payment-v2 | 2024-06-15 | 支付流程重构这样一眼就能看出本地和远程的日期差异。如果origin/main的日期比main新说明远程有更新你还没拉。5.3 过滤掉 origin/HEAD 这类符号引用-a输出里的remotes/origin/HEAD - origin/main有时候会干扰脚本处理。可以用--format配合 grep 过滤或者直接用for-each-refgit for-each-ref --format%(refname:short) refs/heads refs/remotes这个命令只列出实际的引用不包含 HEAD 符号引用。refs/heads是本地分支refs/remotes是远程跟踪分支。输出更干净适合喂给脚本。6. 分支追踪关系与上游配置6.1 什么是上游分支为什么重要上游分支upstream branch是本地分支追踪的远程分支。设置好之后你在本地分支上执行git status会告诉你领先/落后远程多少git pull和git push不用带参数就知道推拉到哪里。查看当前分支的上游git branch -vv或者单独查git rev-parse --abbrev-ref main{upstream}输出origin/main。如果没设置上游这条命令会报错。6.2 设置和修改上游分支新建本地分支并推送到远程时建立追踪git push -u origin feature/login-u就是--set-upstream的简写。之后这个分支的git push和git pull都不用带参数了。如果分支已经存在想补设上游git branch --set-upstream-toorigin/develop develop或者简写git branch -u origin/develop develop如果当前就在 develop 分支上可以省略最后的 developgit branch -u origin/develop想取消上游追踪git branch --unset-upstream这个在你想让某个分支不再自动推送到远程时有用。6.3 上游分支丢失时的处理有时候远程分支被删了本地分支的上游就变成了gone状态。git branch -vv会显示feature/old a3f5c8e [origin/feature/old: gone] 旧功能开发gone表示上游分支不存在了。这时候你有两个选择如果这个分支的工作已经合并直接删掉如果还要继续重新推送并设置上游git push -u origin feature/old或者换一个上游git branch -u origin/feature/new feature/old实操心得我习惯定期跑git branch -vv | grep gone来找出所有上游丢失的分支集中处理。这些分支往往是历史遗留不清掉会越积越多最后自己都忘了哪些还有用。7. 常见问题与排查技巧实录7.1 常见问题速查表问题现象可能原因解决方法git branch -a看不到远程新分支本地缓存未更新git fetch --prune后再看远程已删的分支本地还在未启用 prunegit fetch --prune或配置fetch.prune true分支没有上游push 报错未设置追踪git push -u origin 分支名-vv显示[gone]远程分支被删删除本地分支或重设上游detached HEAD 状态checkout 了 commit 哈希git switch -c 新分支名保存工作--merged漏掉 squash 合并的分支squash 改变了提交历史手动确认后删除分支太多找不到最近的默认字母序--sort-committerdategit branch输出乱码终端编码问题设置core.quotepath false7.2 分支名乱码的处理中文分支名在某些终端下会显示成\344\270\255\346\226\207这样的八进制转义。这是 Git 默认对非 ASCII 字符做 quote 处理导致的。解决git config --global core.quotepath false设置后中文分支名正常显示。这个配置对git status、git log里的中文文件名同样有效建议所有中文环境都配上。7.3 误删分支后的找回如果不小心删了本地分支只要 commit 还在 reflog 里就能找回。先看 refloggit reflog找到被删分支最后的 commit 哈希然后git branch 恢复的分支名 commit哈希如果连 reflog 都找不到比如超过了默认 90 天那就只能从远程重新拉了。所以删除分支前用--merged确认一下能避免绝大多数误删。7.4 分支查看在脚本里的注意事项如果你在 CI 脚本或自动化工具里用git branch要注意它的输出格式可能随版本变化。更稳定的做法是用git for-each-ref配合--format输出可控。另外git branch默认会分页如果输出超过一屏在脚本里要加--no-pager或者设置GIT_PAGERcat否则脚本会卡在分页器上。git --no-pager branch -a这个细节我在写部署脚本时踩过坑脚本在本地跑没事到 CI 环境就挂起排查半天才发现是分页器的问题。8. 我个人的分支管理习惯讲了这么多命令最后分享几个我实际工作中形成的习惯都是踩坑换来的。第一每天开工先git fetch --prune再git branch -vv。这两条命令加起来不到两秒但能让我清楚知道远程有什么变化、本地哪些分支落后了、哪些上游没了。这个习惯帮我避免过好几次在过时代码上开发的问题。第二分支名带日期或工单号。比如feature/20240615-payment或feature/JIRA-1234-login。配合--sort-committerdate找分支时特别快。纯英文单词的分支名在分支多了之后根本分不清。第三合并完的分支立刻删。本地用git branch -d远程用git push origin --delete 分支名。不要留着以后可能还要看有需要从 reflog 或远程历史里找。分支列表干净git branch的输出才有参考价值。第四定期检查--no-merged。每隔一两周跑一次git branch --no-merged main看看有哪些分支还没合并到主干。这些分支要么赶紧合并要么确认废弃后删掉。放任不管的话半年后你会面对几十个不知道还有没有用的分支。第五善用 alias。我把常用的几个分支查看命令都配了 alias比如git recent、git branches、git unmerged。配置一次长期受益。alias 的配置命令前面都给了直接抄就行。分支查看看起来是 Git 里最基础的操作但基础的东西用扎实了后面复杂操作才有底气。我见过太多人 merge 出问题、push 错分支根子都在于对自己仓库的分支状态没有清晰的认知。把git branch这一族命令用熟是每个开发者都该过的关。