1. 先把SourceTree里"分支"这件事说透
用 SourceTree 干了几年活,我发现一个挺普遍的现象:很多人装了 SourceTree,日常操作却还是 git 命令行那一套——打开 SourceTree 只是用来看提交图谱和 diff。问起来原因,答案基本一样:"图形界面的分支操作看着挺花哨,但不知道它背后到底在干什么,怕点错。"
这个顾虑不算多余。分支是 Git 里最核心也最容易被误操作的东西,删错一个分支、切错一个状态,轻则丢几行改动,重则把别人正在用的远程分支搞乱。所以这篇我就专门聊 SourceTree 里创建分支和删除分支这两件事,把每个按钮、每个复选框背后的逻辑拆开讲清楚。
需要说明一下,我用的版本是 SourceTree 3.x(Windows 和 macOS 都试过),不同小版本的界面按钮位置略有差异,但核心逻辑一致。如果你是刚用 SourceTree 的新手,跟着走一遍就能上手;如果你已经用了挺久,我在后面几节里写的那些坑,大概率你也踩过至少一个。
分支这东西,看起来只是"复制一份代码",实际它是 Git 版本管理的骨架。搞懂分支在 SourceTree 里的呈现方式,你才敢在团队协作里放心地开分支、合并、删分支,而不是每删一个分支都心里打鼓。
2. 分支在SourceTree里的真实样貌
2.1 分支本质上只是一个指向提交的指针
要把分支玩明白,得先破除一个误解:很多人以为"创建一个分支"是把整个代码库复制了一份出来,所以觉得开分支很"重"。真实情况是,Git 的分支只是一个 41 字节左右的文本文件,里面存着一个 commit 的哈希值。
打个比方,把代码提交历史想成一条时间线的相册,每个提交是一张照片。分支就像一个贴在相册某张照片上的便利贴,上面写着"当前最新在这"。你新建一个分支,Git 做的事情就是在当前那张照片上再贴一张便利贴,两张便利贴同时指向同一张照片,仅此而已。所以创建分支是毫秒级的,一点不占空间。
理解了这点,你就能明白为什么 SourceTree 里新建分支那么快、为什么删分支不会"删掉代码"。删分支删掉的只是那张便利贴,照片(提交)还在,只要还有别的分支或标签指得到它,它就永远存在。这也是后面讲"误删分支怎么找回来"的原理基础。
SourceTree 的价值在于,它把"便利贴现在贴在哪张照片上"这件事可视化了。你看图谱里那些彩色的线条和标签,就是各个分支指针的实时位置及其分叉合并历史,比命令行git log --graph输出看着直观得多。
2.2 本地分支与远程分支是两套独立的账本
新手最容易混的地方在于:SourceTree 左侧边栏里的"分支"和"远程"是两个不同的节点,里面列出来的东西不是一回事。
- 本地(BRANCHES):存在你自己电脑
.git目录里的分支,你能直接在上面提交、修改、回滚。 - 远程(REMOTES):比如
origin/master、origin/feature-login,这些是别人推送上去的分支在你本地的"快照缓存",你在它上面不能直接提交。
最要命的是本地记录会过期。同事昨天推了一个新分支上去,你本地不拉取,就永远看不到它。所以 SourceTree 有个习惯动作必须先养成:动手之前先点一次"拉取(Pull)"或"获取(Fetch)",让本地这份远程快照更新到最新。我在团队里见过太多次"这分支怎么没了"的惊慌,最后发现只是没 fetch,远程分支死活还在。
这两套账之间靠"跟踪关系(tracking)"连接。一个本地分支通常会跟踪一个远程同名分支,git status里那句 "Your branch is ahead of 'origin/xxx' by 2 commits" 说的就是这种关系。创建分支时如果没建立跟踪,第一次推送就得手动指定,这点我在第 3 节会细讲。
2.3 一个提交上可能同时贴着好几张便利贴
为了让你对图谱有个心理准备,这里提前说一个常见现象:一个提交上同时挂着多个分支标签,在 SourceTree 里就会看到好几个彩色小方块叠在一起。
这通常发生在几种情况:一是你从当前分支直接新建分支且没做任何新提交,此时两个分支指向同一个提交;二是合并之后,被合并的分支指针追上了目标分支;三是有人建了release-1.0和hotfix-1.0这类临时分支,短时间内重合。
看到标签重叠别慌,这完全正常。判断当前在哪个分支,看 SourceTree 工具栏最左侧那个加粗显示的分支名,或者看文件列表上方那行状态说明就行。搞清楚这个显示规律,你在图谱里读分支走向就顺畅多了。
3. 创建分支的四种入口与实操细节
3.1 从当前提交直接新建分支(用得最多的一条路)
这是日常开发 90% 的场景:你正在master上,想开个功能分支干活。SourceTree 里最快的做法是点顶部工具栏那个写着"分支"的按钮(图标是两条分叉的线),或者用菜单仓库 > 分支 > 新建分支。快捷键的话,Windows 上是Ctrl+Shift+B,macOS 是Cmd+Shift+B。
弹出的对话框里只有几项要填:
- 新分支名称:这里有个命名建议,团队最好统一规范,比如
feature/用户登录、bugfix/订单金额计算、hotfix/线上支付超时。带前缀的好处是 SourceTree 左侧分支列表能按名称排序分组,一眼能看出哪些是功能、哪些是紧急修复。 - 检出分支(Checkout branch):这个复选框默认勾选,意思是"创建完立刻切过去"。绝大多数情况保持勾选,你建分支本来就是为了在上面干活。
- 从哪个提交创建:默认是当前 HEAD 位置,一般不用改。
点"创建"之后,SourceTree 会在左侧"分支"列表里多出一项,并且加粗高亮显示当前分支已经切到新的上面。整个过程一两秒。
注意:SourceTree 有时会在新建分支名里默认带一段自动生成的文本(比如基于时间戳或之前的分支名),别直接点确认,先手动改成有意义的名字。我见过仓库里躺着一堆
test、new-branch、分支2的,半年后没人敢删。
3.2 从历史某次提交或标签拉出新分支
有些场景需要你回到过去某个节点开分支。比如线上发现一个老版本才有的 bug,你要从v1.2.0那个标签位置拉一个修复分支;或者要基于某个历史提交做个实验。
操作方式是:在 SourceTree 的提交图谱里,右键点那次目标提交,选择分支...,然后输入新分支名。这里有个细节——从历史提交建的分支,默认不会自动检出(因为 SourceTree 认为你可能只是标记一下),你要注意对话框里那个"检出分支"复选框的状态,需要切过去就手动勾上。
从标签建分支也类似。SourceTree 左侧有"标签(TAGS)"节点,右键某个标签同样能选"分支..."。标签和分支在 Git 里其实是一类东西(都是指向提交的引用),区别只是标签通常不移动,分支会随提交前移。
这条路径的实用价值在于"救火"和"实验"两件事。我个人的习惯是,任何要动线上代码的修复,都从对应的 tag 拉分支,而不是从 master 拉,避免把 master 上还没发布的新代码误带进修复包里。
3.3 把远程分支检出成本地分支
团队协作里,你的同事开了个feature/payment推上去了,你要接手一部分。这时候本地还没有这个分支,得从远程"检出"。
在 SourceTree 左侧展开远程 > origin,找到那个分支,双击它,或者右键选检出 origin/feature/payment...。SourceTree 会弹个小提示,大致意思是"这会创建一个跟踪origin/feature/payment的新本地分支",确认即可。
这一步背后其实做了两件事:一是创建本地分支feature/payment,二是把它和origin/feature/payment建立跟踪关系。有了跟踪关系,之后你git pull/git push不用每次指定远端的名字,SourceTree 会自动往正确的远端同名分支推。
注意:双击检出是最省事的,但如果你想要一个和远程不同名的本地分支(比如远程叫
feature/payment,你想本地叫payment-dev),那就得右键选那个更长的"检出...并创建新分支"选项,手动改名字。别嫌麻烦,名字对不上后面推代码时容易推错地方。
3.4 新建分支时的几个隐形选项与判断依据
SourceTree 新建分支对话框里还有一些不那么显眼的选项,值得单独说说判断逻辑。
"检出分支"到底勾不勾?判断标准很简单:你接下来是要在这个分支上写代码吗?是,就勾;只是想先建个名字占位、稍后再切,就不勾。
要不要立刻推送?新建分支后 SourceTree 不会自动推送到远程。你得点"推送",在推送对话框里确认这个新分支前面的复选框是勾选状态,然后推送。第一次推送时建议同时勾选"跟踪"或"设置上游",一劳永逸。团队协作里,功能分支越早推上去越好,别等到攒了几十个提交才推,那样一旦出问题回滚成本很高。
分支名能不能带中文?Git 技术上允许,SourceTree 也能显示,但我不建议。跨平台、跨工具链(比如有些 CI 系统、脚本处理)中文分支名容易出编码问题,feature/用户登录这种写法在一些环境里会变成乱码。用拼音或英文更稳。
4. 分支切换、贮藏与工作区状态处理
4.1 工作区有未提交改动时,能不能直接切分支
这是被问得最多的一个问题。答案是:能切,但有条件。
Git 切换分支时,会尝试把你当前工作区的改动"带过去"。如果目标分支上这些文件的内容和你当前分支一样,Git 就让你带过去,切换顺利;如果目标分支上这些文件的内容不同,Git 会阻止你切换,并提示类似 "Your local changes to the following files would be overwritten by checkout" 的错误。
SourceTree 里这个错误通常弹个对话框,告诉你哪些文件冲突。遇到这种情况,三个处理思路:
- 提交当前改动:如果改动是完整的、可以形成一次有意义的提交,那就先提交,再切分支。这是最干净的做法。
- 贮藏(Stash)改动:改动还没写完、不想形成正式提交,就用贮藏把它临时收起来,切过去干完活再切回来恢复。
- 丢弃改动:如果那些改动是测试用的、不要了,直接丢弃。
我个人的判断顺位是:能提交就提交,提交不成就贮藏,实在不要了才丢弃。永远不要在没搞清楚改了什么的情况下点"丢弃",那是真的找不回来。
4.2 贮藏功能的正确打开方式
SourceTree 的贮藏入口在工具栏(图标像个箱子或者往下压的箭头),或者菜单仓库 > 贮藏。点开后可以:
- 给这次贮藏起个消息(强烈建议起,比如"登录页表单校验做到一半"),否则过几天你看着一堆"WIP on master"根本分不清谁是谁。
- 选择是否保留暂存区内容(Keep staged changes)。一般不用勾。
贮藏后的改动会被从工作区移走,你可以放心切分支了。干完活切回原分支,打开仓库 > 贮藏列表,找到那条记录,右键选"应用贮藏(Apply stash)"或"弹出贮藏(Pop stash)"。
这两个的区别值得记一下:应用是把贮藏内容恢复到工作区但保留这条贮藏记录,弹出是恢复的同时删掉这条记录。如果你不确定这次恢复会不会有冲突,用"应用"更安全,确认没问题后再手动删掉那条贮藏。
注意:SourceTree 有个老问题,贮藏新增(未跟踪)的文件时,默认可能不带进去,需要在贮藏对话框里勾选相关选项,或者在设置里开启。具体路径是
工具 > 选项 > Git,里面有个关于 stash 包含未跟踪文件的选项。切分支前如果你新建了文件还没 add,最好单独确认一下它有没有被收进去,否则切过去它还在工作区晃荡。
4.3 切换分支后本地文件到底发生了什么
很多人对切换分支有点心理阴影,怕切完代码就乱了。理解下面这件事你就放心了:切换分支,Git 更新的是工作区文件的内容,让它们和目标任务分支的最新提交保持一致。你原来分支的文件不会被改,只是"看不见了",切回去就回来。
但有两个例外要记牢:
第一,未跟踪的文件(Untracked files)不属于任何分支,切来切去它都在。这就解释了为什么有时候你切了分支,发现桌面上还留着一堆test.py、debug.log之类的垃圾文件——它们从来没被 Git 管理过。
第二,.gitignore规则差异会造成错觉。如果两个分支的.gitignore不一样,A 分支忽略的某个文件在 B 分支可能被跟踪,切过去就会"凭空冒出来"或者"莫名消失"。
切换完分支,我的习惯动作是看一眼 SourceTree 的"文件状态"面板,确认工作区是干净的,再开始写代码。养成这个习惯,能避开后面一大堆"我明明没改,怎么有改动"的困惑。
5. 删除分支:本地删、远程删与恢复
5.1 删除本地分支的前提条件
现在说重点——删分支。SourceTree 里删本地分支的操作路径是:左侧分支列表里右键某个分支 > 删除分支(Delete Branch)。
看起来简单,但这里有个关键的阻拦机制。如果这个分支上还有没有被合并到其他分支的提交,SourceTree 会弹出一个警告,大意是"这个分支含有未合并的提交,确定要强制删除吗?" 这时候你面对的其实是一个判断题:
- 这个分支的代码已经合并进
master了,只是还没来得及删——放心删。 - 这个分支是废弃的实验分支,代码不要了——可以强制删。
- 这个分支是我刚提交完还没推上去的工作——千万别删,删了这些提交如果还没别的引用指着,就会变成"悬空提交",只能靠 reflog 那种方式捞,很折腾。
我给自己定的规矩是:删除分支前,先确认它的提交要么已经进了主线,要么已经推送到远程。推到远程的分支就算本地删了,重新 fetch + 检出随时能拿回来,等于多了一层保险。
注意:SourceTree 里不能删除"当前所在的分支"。想删当前分支,得先切到别的分支上去。这个限制是合理的,因为没必要在一个分支上工作还把它删掉。
5.2 删除远程分支的两种路径
删远程分支比删本地要谨慎得多,因为那会影响整个团队。SourceTree 提供了两条路。
路径一:右侧操作。在左侧展开远程 > origin,右键目标远程分支,选删除 origin/xxx。SourceTree 会把一条删除指令推送到远端,实际操作是git push origin --delete xxx。
路径二:推送时的删除。如果某次推送对话框里显示某个远程分支状态异常,也可以从那里处理,但不如路径一直接。
无论哪条路,SourceTree 一般会弹确认框,让你再点一次确认。这一步别嫌烦,认真读一眼分支名,确认删的是origin/feature/old-login而不是origin/master这种要命的东西。
这里有个团队协作的礼仪问题:别随手删别人负责的远程分支。远程分支一旦被删,它的最后一次提交位置如果没有单独记下来,找回是有点麻烦的。要删某个功能分支,通常是这个功能已经合并进主干、PR 已经关闭,且确认没有人还在上面挂着工作。我一般会先在团队群里问一句,或者看一眼该分支最近的提交者是谁。
5.3 误删分支之后怎么把提交找回来
前面说了,分支只是个便利贴,删掉便利贴,照片还在。所以误删分支不等于提交没了,前提是那个提交还能被某些东西找到。
场景一:本地删了,远程还有。最简单,直接重新检出远程分支(第 3.3 节的方法),本地分支就回来了。
场景二:远程也删了,但你知道那个提交的哈希。右键那次提交,选分支...,输入原来的分支名重建即可。
场景三:忘了哈希,什么都没有了。这时候就得靠git reflog。SourceTree 本身对 reflog 的可视化支持不太够,我的做法是点终端按钮打开命令行,或者在 SourceTree 的操作 > 打开终端里执行git reflog,找到那次删除前后的记录,拿到对应的 commit 哈希,再回到 SourceTree 里右键那个提交重建分支。
为了减少这种麻烦,我强烈建议:重要分支在合并后不要急着立刻删,等一两个发布周期再删。留着它占不了多少空间,但能省下你未来可能花的一两个小时去排查和恢复。
5.4 清理那些已经消失的远程分支
有个很隐蔽的坑:同事把远程分支origin/feature/xxx删了,但你本地 SourceTree 的"远程"列表里还是显示着它,点开也能看到旧的提交。你会以为它还在。
这是因为本地的远程跟踪快照没更新。解决办法是在 SourceTree 的拉取(Pull)对话框里,勾选那个**"修剪远程追踪分支(Prune tracking branches)"** 选项,再执行拉取。或者更省事:工具 > 选项 > Git里有一项可以开启"拉取时自动修剪"。开启后,每次拉取都会把远端已经删掉的分支从本地列表里同步清掉,界面清爽很多。
这个设置我建议尽早开。不开的话,长期项目里本地会积累几十个早已删除的远程分支引用,看着乱,也容易误判"这个功能分支还在"。
6. 高频问题速查与排查技巧
上面都是讲原理和操作,这一节我把实际用过、被问过最多的问题整理成表,方便你出问题时直接对号入座。
| 现象 | 常见原因 | 处理办法 |
|---|---|---|
| 新建分支后推送,提示没有上游分支 | 本地分支没建立跟踪关系 | 推送对话框里勾选该分支,并选择"跟踪/设置上游",或首推时加-u |
| 切换分支报 "would be overwritten by checkout" | 目标分支上这些文件内容不同,冲突 | 先提交或贮藏当前改动,再切换 |
| 删除分支时提示 "not fully merged" | 分支上有未合并的提交 | 确认代码是否需要,需要就先合并或推送,不要直接强制删 |
| 远程分支明明删了,本地还显示 | 本地远程快照未更新 | 拉取时勾选"修剪远程追踪分支",或开启自动修剪 |
| 双胞胎分支名,一个本地一个远程,看着乱 | 本地分支和它跟踪的远程分支同时显示 | 属于正常现象,靠左侧节点的层级区分:本地在"分支"下,远程在"远程 > origin"下 |
| 切分支后文件没变化,以为没切成 | 两个分支在这些文件上内容本就相同 | 看工具栏当前分支名确认,或对比提交历史 |
| 忘了自己现在在哪个分支 | 图谱标签密集看不清 | 看工具栏最左侧加粗的分支名,或看"文件状态"面板顶部提示 |
| 新建分支名带了一堆乱码/前缀 | SourceTree 默认填充或编码问题 | 手动改名,避免使用中文和特殊字符 |
除了这张表,我再补几个搜索里经常被提到的关联问题。
关于"当前分支未提交就 checkout 其他分支":这在 SourceTree 里就是上面第 2 行那情况。它的本质不是 SourceTree 的 bug,而是 Git 的保护机制。理解它是在保护你的改动,就不会觉得烦躁了。
关于"从主干检出代码后想切 dev":流程是一样的,先确认工作区干净,然后在左侧分支列表双击dev(如果本地没有,就从远程 > origin里双击origin/dev)。如果 SourceTree 提示找不到,先 Fetch 一下。
关于"清理删除的分支":这就是第 5.4 节说的修剪问题。顺带一提,SourceTree 左侧分支列表本身没有批量清理本地分支的功能,要批量删闲置分支,还是得靠命令行git branch -d循环,图形工具在批量操作上确实不如命令行灵活,这是它的事实短板,不用回避。
7. 几个没人写进文档的个人经验
写到最后,分享几个我用了这些年 SourceTree 攒下来的、文档里基本不会提的习惯。
第一,动手前先 Fetch。这条我已经强调过,但值得再说一遍。90% 的"分支不见了""代码怎么不对"都源于本地视图过期。把 Fetch 变成肌肉记忆,能省掉一半的困惑。
第二,分支名当文档用。与其在群里记"这个分支是干嘛的",不如把信息写进名字:bugfix/20240315-订单超时、feature/积分商城-v2。SourceTree 显示全名,一眼就懂,半年后回来也不会认不出来。
第三,删除分支的时机我一般放在合并之后。合并(Merge)完成的当天不删,等这个改动上一次测试环境或者发一次版,确认没问题了再删。少删一天不占什么成本,但多留一天多一份从容。
第四,出问题别硬刚,开终端。SourceTree 的图形操作覆盖了日常 95% 的场景,但 reflog、批量删分支、复杂 rebase 这些还是命令行利索。SourceTree 顶部有"终端"按钮,能直接在当前仓库目录打开命令行,两者配合着用是最舒服的姿势,不用二选一。
第五,也是最想说的:把分支当"临时工作区"而非"固定资产"。一个团队如果分支开得随性、删得犹豫,仓库会越来越臃肿。理想状态是每个功能分支合并即清理,保持主干和活跃分支的清爽。SourceTree 给你的是操作工具,真正的规范还是得团队约定,工具替代不了约定。
我实际用下来最稳的一套流程就三句:开分支前先 Fetch,切分支前先看工作区,删分支前先想清楚提交去哪了。这三句背下来,SourceTree 的分支操作基本不会再出乱子。