任务分支 主分支master是日常开发里最常用也最该用好的一套协同方式。但不少人到了命令行跟前还是会卡壳分支建完忘记切开发到一半 master 已经往前走了一截合并时一看到冲突就发慌merge 完再看历史记录已经乱成一团。这篇文章就用纯 git 命令把“创建任务分支 → 开发 → 合并回 master”这条路径完整走一遍每个命令为什么这样用、执行前后要注意什么都会拆开讲。适合刚接触 Git 想理清分支逻辑的开发者也适合一直依赖图形界面、遇到问题没法下手排查的同学。1. 分支开发整体思路任务分支和master到底是怎样的关系1.1 直接改master会遇到什么麻烦很多刚接触 Git 的开发者最初都经历过“所有人在 master 上直接开发”的阶段。我自己刚入行那年也是这么干的拉下来代码开改git add .git commitgit push一气呵成。单个任务这么玩完全没问题可一旦出现两个并行任务痛点立刻冒出来。任务 A 要改接口返回结构任务 B 要重构页面渲染逻辑两个人同时在 master 上提交互相之间根本不知道对方改到哪个文件。A 把代码推到远端后B 拉下来发现接口返回的字段不对排查半天才发现不是自己代码的问题而是把对方没改完的半成品代码一起拉下来了。更难受的是master 上只要有一个任务处于“改到一半失败退出”的状态整个分支就处于不可发布、不可交付的尴尬局面。1.2 任务分支开发到底解决了什么问题任务分支的核心逻辑其实很朴素把“客厅”和“私人书房”分开。master 是客厅任何时刻都应该保持干净、整洁、可招待客人任务分支是你的书房可以在里面随便折腾东西乱一点没关系书桌堆满再收拾也不影响客厅里的人。任务是任务 A拉一条feature/task-a任务是任务 B拉一条feature/task-b。两个人各干各的互不碰面。谁的代码完成了、经过验证了再把分支合并回 master。合并之前还可以在分支上做 review看到的就是一次完整任务的 diff而不是“这个提交是 A 的、那个提交是 B 的”搅成一锅粥。从历史记录的角度看任务分支的价值更大。一次任务对应一次合并master 的历史里能清楚看到“某一次合并引入了某个功能”。后面如果线上出了问题想知道这个功能是哪次改动带来的git log --oneline --graph扫一眼就能定位。如果当时还是直接在 master 上东一榔头西一棒子地提交想定位问题只能逐个 commit 翻效率低到让人绝望。1.3 不是所有项目都需要任务分支任务分支很好但也不是银弹。我自己带过的项目里就见过有人为了一个只需要改一行文案的改动硬是拉分支、开发、合并、删除分支走了全套流程结果五分钟能完成的活折腾了半小时。什么时候应该用任务分支我的判断标准很简单任务是否涉及多个文件、是否需要多天开发、是否有第二个协作者参与。三个条件只要满足一个就走任务分支流程。如果是十分钟就能改完的小修、单文件改动、也没有别人在动同一套代码直接在 master 上提交完全没问题。分支策略是用来保护协作节奏的不是用来表演流程完整性的。1.4 一个完整任务的生命周期一次标准的任务分支流程通常是这样一条链路拉取最新 master → 基于最新 master 创建任务分支 → 在分支上开发并多次提交 → 同步 master 上的新改动merge 或 rebase→ 验证无误 → 合并回 master → 删除任务分支。后面几章的实操命令就是围绕这条链路展开的。先把链路记在脑子里再去看命令就不会觉得每一步是孤立的。链路比命令重要命令只是实现链路的工具。2. 创建任务分支前的准备三个检查 一条标准创建命令2.1 创建分支前必须确认的三件事很多人的任务分支从第一步就建歪了没有基于最新的 master或者当前工作区还有一堆没提交的改动结果分支创建完“新鲜”代码和“陈年旧账”混在了一起。在创建分支之前我至少会执行三条命令git status git branch -a git log --oneline -5git status看工作区是否干净。如果显示有未提交的改动而你又正好想基于当前这棵“半成品树”来建新分支那新分支里就带着这些半成品后续排查问题会非常痛苦。处理方式有两种如果这些改动本来就是当前任务的一部分直接先提交掉再在干净的工作区上建新分支如果这些改动是当前任务之外的临时改动暂时不想提交那就先git stash暂存起来等后续需要再git stash pop拿出来用。git branch -a看本地和远端现有的分支列表确认没有已经存在的同名任务分支。如果同名分支已经存在尤其是远端已经有一个同名的那你创建的就不是新分支而是基于旧分支的一棵特殊历史树很容易带来混乱。git log --oneline -5看当前分支最近几条提交帮助确认当前所在位置是不是最新状态。2.2 基于最新master创建任务分支的标准命令确认当前在 master 分支后执行git checkout master git pull origin master第一条命令把当前分支切到 master第二条命令把远端最新的 master 拉下来。这两步很关键很多人会跳过git pull直接建分支结果分支基于过期 master开发完合并回 master 时才发现差异巨大冲突一大堆。做完两步之后就可以创建任务分支了。假设这个任务是“登录页表单校验优化”我会执行git checkout -b feature/login-validationgit checkout -b是“创建并切换”的合并命令等价于先git branch feature/login-validation再git checkout feature/login-validation。Git 2.23 之后新推出的git switch -c feature/login-validation也可以达到同样的效果语义上更清晰。我自己现在更习惯用git switch -c但老项目里团队成员用checkout的还很多两种写法并存没有谁对谁错。分支命名上我建议按照“分支类型/任务名”的约定来常见前缀有这几个前缀适用场景feature/新功能开发bugfix/缺陷修复hotfix/线上紧急修复chore/构建、配置、依赖调整等杂项refactor/代码重构不改变外部行为任务名尽量简短用英文短横线连接一眼能看出这个分支对应的任务。feature/login-validation比feature/new-login-code-2024这种名字好太多前者三个月后回来看仍然一目了然后者只体现“有个人在某个时间改了登录代码”。2.3 新分支上第一次push要做什么分支创建完成开发了一段代码之后第一次推送到远端需要带上-u参数git add . git commit -m feat: 登录表单增加邮箱格式校验 git push -u origin feature/login-validation-u的全称是--set-upstream作用是建立本地分支与远端分支的跟踪关系。设置完之后后续再执行git push就不再需要指定远端和分支名了Git 会自动知道要把本地分支推送到远端对应的分支。我第一次用 Git 时总是不明白为什么第一次 push 之后第二次 push 直接写git push就行。后来理解了-u的语义才恍然第一次 push 不只是把代码推到远端更是在本地分支和远端分支之间“绑定了关系”。如果第一次 push 忘了加-u后续git push会报错提示没有配置上游分支这时候补一条git push -u origin feature/login-validation就能重新建立关联。3. 分支开发期间的核心循环提交、同步、解决冲突3.1 不要攒着改动一并提交我见过太多开发者有一个坏习惯开发两三天攒了几十个文件的改动临到合并前一次性git add .再git commit提交信息随手写一句“代码更新”。这种提交方式在任务分支上虽然不至于立刻出大问题但回顾这个分支时你完全说不清每一步改了什么、为什么改。更好的做法是把任务拆成几个逻辑块每个逻辑块完成时就提交一次。比如这个登录页表单校验任务可以拆成这样第 1 次提交新增邮箱格式校验规则。 第 2 次提交新增密码复杂度校验规则。 第 3 次提交修复校验失败时的错误提示文案。每次提交前用git status和git diff确认要提交的内容范围。git diff不带任何参数时显示的是工作区文件和暂存区之间的差异也就是“我改了但还没 add 的内容”。git status git diff git add src/pages/login.js git commit -m feat: 登录表单增加邮箱格式校验git add后面跟的是具体文件路径我会尽量少用git add .这个无差别全加命令。原因很简单目录里很可能有临时生成的日志文件、本地调试用的配置文件这些本不该出现在版本控制里git add .会把它们一股脑拖进去。如果你的项目.gitignore配置得足够完善用git add .倒也没问题但为了稳妥尤其是团队协作时我建议用git add指定文件。提交信息上推荐用约定式提交的格式feat:表示新功能fix:表示修复docs:表示文档变更refactor:表示重构。这样从提交历史里扫一眼就能看出每个提交的类型和意图比乱写“更新”“修改”这种信息专业得多。3.2 开发到一半如何同步master上的新改动任务分支开发了几天master 上其他同事的功能已经合并进去好几轮了你的分支还是基于几天前的旧 master。这种情况下上线前或者继续开发前需要把 master 上的新改动同步到你的任务分支上。有两种姿势merge 和 rebase。先看 merge 的方式git fetch origin git merge origin/master执行后Git 会把 master 上的新提交和你的任务分支上的提交做一个三方合并如果两边改了不同文件合并会自动完成如果改了同一个文件的同一区域就会出现冲突需要手动解决。merge 的优点是安全、直观冲突解决一次就记录一次后续可以完整回溯。缺点是任务分支的历史上会出现“Merge branch master into feature/xxx”这样的节点如果频繁同步分支历史会被这些 merge 节点弄得很乱。再看 rebase 的方式git fetch origin git rebase origin/masterrebase 做的事情是把任务分支上那几条提交“拔下来”放到最新的 master 节点之后重新应用。效果上是你的任务分支历史变成一条直线所有提交都基于最新的 master完全看不到同步痕迹。rebase 的优点是历史非常漂亮像你一直基于最新 master 开发一样。缺点也很明显它会重写任务分支上提交的 hash 值。如果这个任务分支是你一个人用重写无所谓如果团队成员已经把这个分支拉下去一起开发了rebase 一执行其他人本地历史和你远端历史就对不上了再 push 会一片混乱。我的实践原则很简单任务分支如果是个人独享用什么看你心情如果是多人共用的协作分支一律用 merge别用 rebase。这个选择无关对错而是避免在错误的时间使用重写历史的操作。3.3 冲突是怎么产生的以及如何安全地解决冲突的本质是两个人改了同一处代码Git 无法判断谁对谁错只能把决定权交给你。遇到冲突先别慌那说明 Git 成功保护了双方的改动而不是把谁的代码悄悄覆盖掉了。merge 场景下的冲突处理流程git statusGit 会列出冲突文件比如both modified: src/pages/login.js。打开这个文件会看到冲突标记 HEAD 当前分支任务分支上的代码 master上的代码 origin/master手动编辑这段代码把、、这些标记行删掉留下真正要保留的内容。保存文件后git add src/pages/login.js git commitmerge 冲突的解决结果会形成一次合并提交。注意这里的git commit不需要再写-m因为 Git 已经预填了Merge branch origin/master into ...这样的提交信息直接确认即可。rebase 场景下的冲突处理流程类似但细节不同git status # 找到冲突文件 # 手动编辑解决 git add src/pages/login.js git rebase --continuerebase 冲突解决后不是直接 commit而是git rebase --continueGit 会继续把后面剩下的提交一个个重新应用。如果中途发现不对想反悔执行git rebase --abort可以回到 rebase 之前的状态相当于整个操作没发生过。这个“安全逃生门”是 rebase 和 merge 相比更需要注意的地方。我第一次处理 rebase 冲突时就犯过一个经典错误解决完一个冲突后直接执行git commit结果 Git 报错说没有变更需要提交我不明所以又执行了git rebase --continue这才发现 rebase 有自己独立的继续流程跟普通提交是两套逻辑。多踩几次坑后我现在的习惯是处理任何冲突之前先在心里默念一遍当前是 merge 还是 rebase 模式对应的后续命令完全不同。4. 合并回master的纯git命令完整流程与三种合并方式4.1 合并前必须确认的三件事开发完成、任务分支上的代码已经验收通过接下来就是最关键的一步合并回 master。我见过不少人在合并这一步翻车要么合完发现 master 上有别人新推的代码冲突了要么发现自己把调试代码也一起合了进去。所以合并且执行前我至少会检查以下三项第一工作区必须干净git status如果有未提交的改动先提交掉或暂存。带着脏工作区切到 master 再合并既容易把自己搞晕也容易把无关改动混进合并结果。第二确认要带进 master 的提交清单git log --oneline master..feature/login-validation这条命令显示的是“master 上不存在、但任务分支上存在”的提交也就是这次合并将会引入的改动清单。逐条核对一下确认清单里没有“临时修复”“调试输出”这类不应该上线的提交。第三再快速看一眼文件级差异git diff master...feature/login-validation --stat注意这里用了三个点表示对比的是“master 和任务分支分叉之后的最新状态”而不是两个分支当前状态的全部差异。如果写两个点master..feature对比的是两个分支直接所有差异反而会混入 master 上已经存在的内容。三点的语义我在初学阶段总是记混后来把它记成“只看我这边多出来的东西”就再也没用错过。4.2 三种合并方式merge、no-ff、squash各适用什么场景合并回 master 之前先决定用哪种合并方式。很多新人不知道git merge默认行为下Fast-forward 合并不会产生合并提交任务分支的历史会直接并到 master 后面表面上看起来就像“没有经历过分支一样”。这在某些场景下并不是好事。三种方式的对比方式命令历史表现适合场景默认合并git merge feature/xxx若可 Fast-forward 则历史直线不保留分支痕迹个人项目、单线历史保留合并节点git merge --no-ff feature/xxx强制产生一次合并提交历史清晰呈现出任务边界团队协作、需要回溯任务边界压缩提交git merge --squash feature/xxx把分支所有提交压成一个提交不保留分支内的提交粒度小任务分支内提交太多但没必要保留在团队协作的项目里我强烈建议使用--no-ff理由只有一个它保留了“任务边界”。默认 merge 在可以 Fast-forward 时提交历史是一根直线你说不清这批提交是哪个任务的。用--no-ff后每次任务合并都会产生一个明确的合并节点git log --graph里看过去非常清晰“这里合入了一个任务”“那里合入了一个修复”。后面如果某个功能出问题定位到是哪次合并引入的退回到那个提交的时刻去分析效率完全不同。至于--squash我平时用得不多。如果一个任务分支里有十几次粒度很小的提交直接 merge 会把这十几次提交全带进 master历史显得零碎--squash把它们压成一次提交master 历史很干净代价是分支上的原始提交粒度全丢了。适合那种“分支只是个零碎草稿最终交付只需要一个完整结果”的任务。4.3 一条完整的合并命令序列确认好合并方式和前序检查之后完整的合并流程如下git checkout master git pull origin master git merge --no-ff feature/login-validation -m Merge feature/login-validation: 登录页表单校验优化 git push origin master逐条解释一下。git checkout master先把当前分支切回 master。这一步是最容易被忽略的有人忘了切分支在任务分支上直接执行git merge合并完才发现合反了方向。git pull origin master同步 master 的最新状态。这里的价值在于即使你几小时前刚拉过 master这期间别人也可能又合并了新功能。这一步确保你的合并基于远端最新 master避免基于过期代码合并后推不上去。git merge --no-ff feature/login-validation -m ...执行真正的合并。-m参数可以为这次合并提交指定提交信息要不然 Git 会默认生成Merge branch feature/login-validation也能用但没有我自定义的信息直观。git push origin master把合并结果推送到远端。如果在合并阶段遇到冲突处理方式和 3.3 节完全一致打开冲突文件、手动解决、git add、git commit然后继续。4.4 合并后的清理动作删除分支和打标签合并完成、推送到远端之后任务分支已经完成了它的使命剩下的就是清理现场。本地删除git branch -d feature/login-validation-d是小写表示删除已合并过的分支。Git 会先检查该分支是否已经完全合并到 master如果还有未合并的提交会拒绝删除并提醒你。如果你确认这个分支不要了可以用-D强制删除但建议先确认没有遗漏毕竟未合并的提交一旦删除内容不会自动出现在 master 上。远端删除git push origin --delete feature/login-validation远端分支的删除常用命令是--delete也支持简写git push origin :feature/login-validation。我这里用的是前者语义更明确也推荐团队里统一用这个写法。如果团队有版本发布节奏合并后还可以顺手打个标签git tag -a v1.2.0 -m v1.2.0: 登录页表单校验优化上线 git push origin v1.2.0-a表示附注标签带标签说明和创建者信息比轻量标签更适合发布版本使用。打了标签之后万一这个版本线上出了严重问题可以快速切回标签对应的状态。5. 开发中的高频问题与排查技巧5.1 分支删错了怎么办开发任务结束后误删分支是很常见的事。尤其是有的人习惯用-D强制删除明明分支还有未合并提交也照样删掉了。遇到这种情况先别急着哭Git 的本地引用日志 backup 里通常还留着线索。git refloggit reflog显示的是本地 HEAD 指针的移动记录包括创建分支、切换分支、提交、合并、重置等操作。每一条记录都有对应的 hash 和操作说明。找到删除分支前最后一条操作记录的 hash然后基于这个 hash 重新创建分支git checkout -b feature/login-validation hash这个办法在大部分情况下都能救回来前提是那个提交还没有被 Git 的垃圾回收机制清理掉。我在本地测试时就故意误删过几次分支来验证这个恢复流程实测下来十分可靠。需要特别提醒的是恢复出来的分支是“那个 commit 当时的状态”它之后的提交记录并不会跟着回来所以恢复后要检查一下恢复到的节点是否真的包含你要找的内容。5.2 合并到master之后发现有问题怎么办任务分支合并回 master 并推送之后发现代码有问题这是每个人都会经历的尴尬时刻。处理办法取决于一个问题这个合并是否已经推送到了远端。如果还没推送本地直接回退git reset --hard HEAD~1其中HEAD~1表示回到倒数第二个提交也就是合并提交之前的状态。注意--hard会把工作区也重置掉执行之前确保没有未提交的改动。如果已经推送到了远端就不要用reset了。因为你本地重置后需要强推覆盖远端记录而在团队共享仓库里做强制推送会把别人基于这个历史拉下来的分支全部搞乱是很危险的操作。这时候应该用 revert生成一个反操作提交来抵消合并的结果git revert -m 1 merge提交的hash这里-m 1的含义是“保留主分支这一侧”即保留 master 原状撤销从任务分支合并进来的内容。revert 的好处是不重写历史多了一个“撤销某次合并”的提交记录所有协作者都能安全拉取。不过这里有个容易踩的坑如果这个任务需要重新上线因为历史里已经有它的提交和 revert 提交直接再合并任务分支会非常绕。我遇到这种情况宁可新开一个修复分支来修正问题也不去尝试“revert 的 revert”。前者逻辑清晰后者很容易把自己绕进去。5.3 发现临时代码混进了master本地调试输出、测试开关、临时写死的配置这些东西一不小心就会顺着任务分支合并到 master。我在过去的工作里遇到过不少次有个同事把console.log留在登录校验函数里上线后在浏览器控制台里打出了一堆内部状态排查了半天才发现是这么回事。防止这种事情发生我会在合并前做几道检查用git diff查看具体的代码改动git diff master...feature/login-validation用git grep搜索调试代码git grep -n console.log\|debugger feature/login-validationgit grep可以在指定分支上搜索文本比切到那个分支再搜索要快得多。如果搜出来的console.log是业务上需要保留的日志就检查一下打印内容是否合规如果是忘了删的调试代码果断删掉再提交。更前提的预防手段是配置好.gitignore把日志文件、临时文件、本地配置全部挡在版本控制之外。.gitignore不是万能的但至少能避免“无意间 add 了不该 add 的文件”这类低级别的失误。5.4 push被拒绝了怎么办另一个高频问题是 push 被拒绝具体表现是一堆英文报错大意是“远端包含了本地不存在的提交”。本质很简单远端 master 更新了你本地 master 落后了。解决办法是先把远端更新合并到本地再尝试 pushgit pull --rebase origin master git push origin master这里我更推荐使用--rebase而不是普通的git pull。因为普通 pull 会生成一次合并提交如果团队对历史整洁度有要求这并不受欢迎。--rebase会把你本地的提交“放到”远端最新提交之后历史是一条干净的直线。注意只有在没有冲突时这个操作才会顺滑如果有冲突走 3.3 节的 rebase 冲突处理流程。之前见过不少新手在 push 被拒后第一反应是直接git push -f强推。这是最忌讳的操作。强推会把自己本地的历史直接覆盖掉远端历史如果远端有别人新提交的代码会全部被冲掉。强推要用但必须是经过团队确认、且明确知道自己会覆盖掉什么内容时才用。5.5 任务分支长期不合并怎么办最后聊聊一种很普遍但没人主动提的场景任务分支建了开发了一半然后任务被搁置分支就一直躺在那里。时间一长分支上的代码和 master 渐行渐远最后变成一个没人敢碰的“死分支”。我见过一些项目里躺着几个月甚至一年没人管的旧分支。对于这种分支我的建议是如果功能确定不做了直接在远端删除分支即可本地保留或删除都无所谓。删除分支不等于删除提交记录如果后续想找回通过 reflog 或远端仓库的对象记录还是能找到线索。如果功能只是暂时搁置但未来可能继续做建议定期做一次同步git merge origin/master保持分支与 master 的相对差距不要太大避免等真正回来继续开发时面对一场几百个文件的冲突大战。如果分支已经乱到自己也看不懂了那就果断在 master 最新代码上重新拉一条新分支把有价值的代码手工挑过来。这看起来损失了“历史连续性”实际上比在一团乱麻上继续叠加轻松得多。6. 最后分享几点个人实践习惯聊了这么多命令和流程最后说几条我这么多年实际踩坑踩出来的体感。第一条任务分支一定要短小。理想状态是一条分支对应一个明确的、能在几天内完成的任务。分支生命周期越长同步成本越高冲突风险越大最后合并时的心智负担也越重。如果发现一个任务分支连续几周都合不回去先怀疑是不是任务本身拆得太大而不是一味硬撑着把更多代码堆上去。第二条一条任务一个分支分支合并后就删除。很多人心疼自己辛苦写的代码舍不得删分支。实际上 Git 的提交对象还在并不会因为删除分支而让代码消失。保留分支只会让仓库分支列表越来越杂乱每次git branch -a都像在翻一个没整理的抽屉。第三条多用命令理解机制而不是只依赖图形界面。我不反对使用可视化工具但建议至少把status、log、diff、merge、rebase这五个命令熟练用起来。图形工具在大多数时候确实方便可一旦遇到冲突、rebase 卡住、push 被拒这类复杂场景理解命令行背后发生了什么比盲目点按钮可靠得多。理解了底层的引用、提交树、HEAD 这些概念你就会发现所谓 Git 难题本质上就那么几类完全可以靠逻辑推导出解法。任务分支开发不是一套复杂的流程它真正需要的是对“为什么要这么做”的理解。把这个理解抓在手里再去看那些命令每一步就都顺理成章了。