git 多人协作这件事很多团队一开始都是这样走过来的所有人都在 main 分支上直接提交谁 push 得晚谁就撞车合并靠吼回滚靠猜。等团队从三五个人涨到十几个人的时候这条路上基本就只剩一片红红的冲突标记了。我自己的团队也是在那个阶段被迫切换思路开始认认真真地做“不同分支下”的协作把 main 保护起来让每个人在独立的分支上并行推进再通过合并流程把成果收拢回来。效果立竿见影但过程里踩的坑也不少。这篇文章就是把我这段时间的实战经验整理出来讲的不是 Git 的底层原理而是一整套可落地的分支协作方案环境怎么准备、分支怎么命名和划分、日常操作怎么做、冲突怎么排解、操作翻车了怎么救。无论你是刚开始带团队接触 Git还是自己用得挺熟但总在多人协作时被分支合并和冲突折磨都可以把这篇文章当成一份参照。我尽量把命令背后的逻辑和场景也讲清楚方便你拿去就能用而不是背一串咒语。1. 从同一分支的混乱到不同分支的秩序1.1 同一分支协作为什么必然失控想象一下一条主干道上来回跑几百辆车没有红绿灯会是什么样子。所有人在同一个分支上提交代码本质上就是把整个团队的车都赶到同一条车道上。早期人少还行提交频率不高大家互相迁就一下就能过。可一旦并行开发的需求变多两个人同时改同一个文件几乎成了每天的日常git pull 时动不动就蹦出 CONFLICT后面提交的人只能硬着头皮解冲突还要担心自己是不是把别人的改动给覆盖了。更麻烦的是同分支协作下你没法控制“什么代码进入主干”。某个功能明明还在半成品状态因为你提交到了 main别人拉下来之后直接被半成品代码影响了整个项目的可运行性。这种情况发生几次之后团队里就会滋生出一种下意识能不在 main 上动就不在 main 上动改什么先攒着。结果就是历史上一大堆挂着WIP的提交谁也说不清哪个版本是能跑的。1.2 分支协作到底解决了什么不同分支协作的本质是把“共享一份代码”改成“各自维护一份代码副本在确定的节点相互合并”。每个人从主分支拉出一条功能分支在自己的分支上随便提交、随时推送都不会影响别人。这就好比每个人都有自己的工作间工作间里怎么折腾都行完工之后再走统一的质检流程把成果搬进公共仓库。它能解决几个非常实际的问题一是隔离你不会在开发过程中被别人的半成品绊倒二是并行多人可以同时推进不同功能而不互相覆盖三是可控只要主分支被保护起来任何进入主分支的代码都要经过合并请求和检查代码质量就有了一个把关口。这也是为什么稍微正规一点的团队都会采用分支协作不是因为它时髦而是因为它能显著降低协作成本。1.3 要转换的是工作习惯不是命令记忆很多人一听到分支协作第一反应是“我要记住好多新命令”。其实真正需要学的命令就那么几条难的是把原来的工作习惯改过来。比如你不能再一上来就git add . git commit git push你得先确认自己在哪个分支上、要推到哪个分支、和谁合并、合并出问题了怎么回头。这些习惯的转变比命令本身更重要后面几节我会把标准动作一步步拆开。2. 开工前的环境准备安装、配置与SSH免密2.1 各平台的 Git 安装Windows 是最省事的直接去 Git for Windows 官网下载安装包一路下一步就行。安装的时候有一步会让你选默认编辑器、PATH 环境变量这些保持默认就好。装完打开一个新的 CMD 或 PowerShell敲git --version能看到版本号就说明没问题。如果官网下载慢国内的开源镜像站一般也都有同步下载体验会好一些。macOS 上如果你装了 Homebrew一条命令就能搞定brew install git。Linux 视发行版不同可能是sudo apt install git或sudo yum install git。装完之后同样用git --version验证。这里有个小细节很多系统会自带一个旧版本 Git如果你发现命令行为和你学的不一样优先确认版本是不是太老太老的话直接升级省得后面被各种兼容性问题折腾。2.2 身份信息配置Git 每次提交都会记录两样东西用户名和邮箱。这俩不是随便填的它直接关联到你在远程仓库平台上的身份最后会跟着你的提交历史展示给团队所有人看。配置命令是git config --global user.name 你的名字 git config --global user.email 你常用的邮箱加上--global表示这些配置对当前用户的所有仓库生效。你可以在项目目录下运行git config --list查看当前配置确认没写错。如果某个仓库想用不同的身份去掉--global在仓库内重新设置即可。2.3 SSH 密钥与免密登录日常协作中你总要往远程仓库 push 代码每次都要输密码会很影响心情更别提有些平台对密码验证本身就有限制。更稳的做法是配 SSH 密钥一次性配置之后push、pull、clone都走密钥验证不需要再输密码。生成密钥很简单ssh-keygen -t rsa -b 4096 -C 你的邮箱回车之后会让你确认保存位置和设置口令直接一路回车就好。生成的密钥默认在~/.ssh/id_rsa.pub这是公钥可以放心复制给别人同目录下的id_rsa是私钥打死也不能外传。查看公钥内容cat ~/.ssh/id_rsa.pub然后登录你使用的 Git 平台Gitee、GitHub、GitLab 都类似在个人设置的“SSH 公钥”页面把这段内容粘贴进去保存。验证是否配好以 Gitee 为例ssh -T gitgitee.com如果返回一段欢迎信息说明 SSH 通道已经打通从此告别密码输入。2.4 SSH 认证失败的常见排查思路有不少人卡在第二步就跑不过去常见报错是ssh: connect to host ... port 22: Connection refused或者Permission denied (publickey)。前者一般是网络或端口问题后者基本就是公钥没配对。我建议按这个顺序排查先跑一次ssh -T gitgitee.com看返回信息再确认你当前用户目录下确实存在id_rsa.pub最后回平台检查公钥是不是粘贴完整复制时不要带多余空格和换行。还有一个容易忽视的点如果你电脑上有多个 Git 平台账号默认的id_rsa可能已经被别的平台占用这时候需要在~/.ssh/config里对不同域名指定不同的密钥文件属于进阶玩法先用单平台的标准流程跑通就够日常用了。3. 分支模型设计哪些分支常驻哪些分支用完即弃3.1 一张分支角色清单聊分支协作第一步不是学命令而是定好规矩。没有规矩的分支最后会变成巨大的垃圾场谁也不知道哪些分支还活着、哪些已经废弃。我比较推荐一套轻量化的分支模型不重但够用main或master主分支永远是能跑能发布的稳定代码受保护不允许直接 push。develop可选集成分支所有功能分支完成之后先合到这里做联调和集成测试。feature/*功能分支从一个需求拉出来开发完合并回去后即删除。hotfix/*紧急修复分支从main直接拉出来修复线上问题后合回main和develop。对于十来个人的团队我甚至建议连develop都可以先省掉直接走mainfeature/*的简化模式。分支层级越多合并链路越长管理成本越高。先跑通最简单可靠的模型再根据团队节奏调整比一开始就搞一套庞大的 Git Flow 要务实得多。3.2 分支命名规范分支名是团队沟通的一部分。好的分支名让人一眼就知道这个分支在做什么、对应什么需求。我常用的格式是feature/需求编号-功能简述例如feature/20240715-login-page表示一个登录页功能。hotfix同理比如hotfix/order-amount-error。别小看这一条当你的远程仓库里躺着几十个分支时一个规范的名字能帮你快速定位目标而不是点开每一个分支看提交记录猜它是干嘛的。命名规范不一定完美但一定要有而且最好写进团队文档里新成员来了直接照抄。3.3 主分支保护与合并权限在我的实践里main分支的写权限必须收回来。做法是在 Git 平台的项目设置里开启“保护分支”把允许推送的成员设为空或仅限维护者。这样一来任何人想把自己的代码合入main都必须走合并请求PR/MR在平台上经过代码评审之后再点击合并。这是整个分支协作模型里最关键的一环也是很多小团队容易忽略的分支拉了一堆最后所有人还是往main上硬推那等于白搭。合并请求的好处不光是代码评审它还会把改动集中成一个清晰可见的提交历史和讨论记录下次出问题排查时你能快速知道“这块代码是哪个合并请求、哪个功能带进来的”。这一步看起来是流程上的事实际省掉的是未来大量的排查成本。4. 分支协作的标准动作创建、提交、推送与合并4.1 创建功能分支的标准起手式无论你用的是命令还是图形工具开始一个功能的流程应该是一样的。先切到main并拉取最新代码保证自己的起点是最新的稳定版本git switch main git pull origin main然后基于最新的main创建并切换到功能分支git switch -c feature/20240715-login-page-c是--create的简写表示创建并切换。老版本 Git 可能不支持git switch命令那就用等价的git checkout -b feature/20240715-login-page效果一样。创建完分支后用git branch --show-current确认一下当前分支别稀里糊涂在main上改了半天代码才发现没建分支。4.2 提交与推送的正确姿势在分支上做开发的时候提交频率要高于你单机开发的时候。我见过有人攒了一周的改动一次性 commit到了合并时发现和别人的功能大量重叠解冲突解到怀疑人生。正确的姿势是小步提交一个功能点完成、一个文件修改稳定就提交一次。提交信息也要有实质内容别写update、fix这种谁都看不懂的话我给团队推荐的格式是feat: 添加登录页表单校验、fix: 修复订单金额计算错误前缀标明类型后面做啥一目了然。提交的命令是git add src/pages/Login.vue git commit -m feat: 添加登录页表单校验注意我刻意没有用git add .。按文件添加能避免你把临时文件、调试代码、本不该提交的配置一并带进仓库。推送功能分支到远程git push -u origin feature/20240715-login-page加上-u是为了将本地分支和远程分支建立跟踪关系之后在该分支上直接输入git push就能推送不需要每次指定远程名和分支名。4.3 merge 还是 rebase合并方式怎么选这是团队协作里最容易吵起来的话题。我的立场很明确对于大多数团队日常合并用merge就好别一上来就 rebase。merge会保留每个人的独立提交历史形成一个分叉再合并的图形虽然看起来不够“线性”但胜在真实、安全冲突解决也是基于三方合并心智负担低。rebase是把你的提交“搬”到目标分支的最新提交之后历史变成一条直线看起来非常清爽。但代价是你的提交哈希全部改变如果这期间有人已经拉过你的分支代码他会遇到一堆强行更新的问题。用 rebase 要遵守一条铁律只 rebase 自己还没推出去的本地提交。团队里如果每个人都频繁 rebase 共享分支那基本就是互相制造麻烦。如果你是自己管理本地提交历史想整理成一串干净的提交再推出去rebase 可以放手用如果是把功能分支合回团队主分支我推荐直接在平台合并请求里选merge或squash merge。squash merge会把功能分支上的所有提交压缩成一个提交合入目标分支历史非常干净适合一个功能对应一次合并的场景。4.4 把一个分支合并到另一个分支的实战命令工作中经常有这种需求同事要求把 A 分支的功能合并到你的 B 分支一起联调或者把 B 分支合到 A 分支验证效果。操作本身不复杂# 切到目标分支 git switch B # 拉取最新代码 git pull origin B # 把 A 分支合并进来 git merge A如果是把已经推送到远程的分支合进来可以先拉取远程分支再合并也可以直接指定git pull origin A这条命令相当于git fetch origin A git merge origin/A适合快速把别人的分支拉进来联调。合并完成后如果有冲突就着手处理没冲突就git push推送到远程 B 分支。这里提醒一句往别人的功能分支里合并代码之前先和对方打声招呼不然对方下一次 push 时又是一堆合并和解冲突容易引发不愉快。4.5 cherry-pick只要某几个提交不要整个分支有些时候你不需要把一个分支整体合并过来只想把其中某几个提交搬到另一个分支。比如你在feature-A上写了三个提交其中只有一个修复严重 bug 的提交需要立刻上main剩下的功能还在开发中。这时用git cherry-pick最合适。先查看目标提交的哈希git log --oneline找到那串哈希值后切换到要搬入的分支执行git switch main git pull origin main git cherry-pick a1b2c3d如果只想搬连续的几个提交可以用范围写法git cherry-pick A..E。cherry-pick 的过程同样可能冲突解决方式和普通合并一样解决后git cherry-pick --continue提交即可。这个命令特别适合“从旁支迁移稳定修复”的场景不用因为一个提交就拉出一大堆无关代码。5. 代码冲突的完整排解链路5.1 冲突是怎么产生的为什么躲不掉合并冲突是多人协作里绕不开的坎本质上它不丑陋反而是 Git 对你代码的一种保护。冲突产生的唯一原因就是两个分支修改了同一个文件的同一个区域而 Git 无法判断哪个版本才是“正确”的只能把选择权交给你。你可以想象两个人同时在一张纸上改同一行字各自存了一个版本现在要把两份合起来总得有个人裁定谁的字压过谁。5.2 从冲突标记到解决一个完整实例假设两个分支都改了一个README.md我来演示一次完整的解决过程。执行git merge feature/login-page后 Git 报冲突Auto-merging README.md CONFLICT (content): Merge conflict in README.md Automatic merge failed; fix conflicts and then commit the result.打开README.md你会看到冲突标记 HEAD 这是 main 分支上的描述 这是 feature/login-page 分支上的描述 feature/login-page和之间是当前分支HEAD的内容和之间是合并进来的分支内容。这一步你是裁判把不要的部分删掉保留正确的描述最后把三个标记行也一起删掉。保存文件后git add README.md git commit -m merge: 合并 README 描述merge 引起的冲突解决之后直接commit就能完成合并如果是 rebase 或 cherry-pick 途中遇到的冲突解决之后不能直接commit而是用git rebase --continue或git cherry-pick --continue这个区别不少新手会卡一下。5.3 减少冲突的实战技巧冲突虽然躲不掉但频率完全可以降下来。我总结了三条实际有效的经验一是按模块分工尽量让不同的人负责不同目录和文件从源头降低交集的概率二是频繁从main同步代码到自己的功能分支积攒的差异越小合并时越不容易大面积碰撞三是一次性别干太长时间一两天的分支还好一两周的长寿分支合并时几乎必然迎来大规模冲突。如果功能确实很长可以切分成多个小功能分支依次合并别一个分支拖到天荒地老。6. 分支操作翻车后的急救手册6.1 commit 信息写错了git commit --amend刚提交完就发现 commit message 写了个错别字或者漏掉了一个文件没提交这个时候不用新建一个“补救”提交用git commit --amend就能修复上一次提交。改 message 是这样的git commit --amend -m feat: 添加登录页表单校验这个命令会用新的提交替换上一次提交相当于给上一个 commit 改了名字。如果你只是想补一个漏掉的文件可以git add 忘掉的文件 git commit --amend --no-edit加上--no-edit是告诉 Git 不要改动 message保持原样。这里有一条非常重要的红线amend只能用在还没有推送到远程的提交上。如果这个提交已经被别人拉走你再 amend 就等于把一个“篡改”过的历史强行推送出去所有人都会遇到合并问题。已经 push 的提交不要 amend老老实实新增一个提交说明修正即可。6.2 删掉本地 commit 但没 push 的代码热搜里有个问题特别典型在 IDEA 或命令行里提交了一个 commit但还没有 push 到远程现在后悔了想把这个提交连同改动一起删掉。这可以用git reset解决关键是选对模式命令效果适用场景git reset --soft HEAD~1撤销提交改动保留在暂存区想重新整理 commitgit reset --mixed HEAD~1撤销提交改动保留在工作区想重新修改文件后再提交git reset --hard HEAD~1撤销提交改动全部丢弃确定这些改动不要了HEAD~1表示上一个提交。如果你想删掉最近两个提交就用HEAD~2。用--hard的时候要格外谨慎因为改动会彻底消失没有任何后悔药确定不要了再用。如果提交已经推送到远程那就不能用 reset 硬删了正确做法是用git revert生成一个反向提交来“撤销”这种方式不会篡改历史对协作方也最安全。6.3 删除本地和远程分支的完整姿势功能合并完分支就没用了该删就删不然远程仓库会膨胀成一片分支坟场。删除本地分支git branch -d feature/old-d会检查该分支是否已经被合并如果没合并会报错不让你删防止误删工作成果。如果你确定这个分支不要了就改用git branch -D feature/old强制删除。删除远程分支git push origin --delete feature/old远程分支被人删了之后其他同事本地的分支列表里还会残留引用执行一次git remote prune origin可以清理掉远程已经不存在的分支记录。VSCode 里删除分支后偶尔还会在源代码管理栏看到旧分支名刷新或关闭重开窗口就能解决本质问题还是本地引用没清理干净。6.4 同一项目多分支同时开发git worktree这是我最想安利给团队的小技巧之一。有时候你需要在多个分支间并行操作比如当前在 A 分支开发新功能同时要去看一眼 B 分支上的一个紧急问题。常规做法是先 commit 或 stash 当前改动再切分支来回折腾很烦。git worktree可以让你在一个仓库里同时检出多个分支到不同目录完美解决场景冲突。# 在项目根目录相邻位置创建一个新目录检出 feature-B 分支 git worktree add ../myproject-b feature-B之后你可以打开../myproject-b这个目录里面就是feature-B分支的完整工作副本本地和另一个目录的 A 分支互不干扰。处理完不用了再移除git worktree remove ../myproject-b对于“VSCode 同项目多分支同时开发”这类需求worktree 就是标准答案比复制整个项目文件夹要正确得多因为它保持同一个 Git 版本库的元数据提交历史完全连贯。7. 图形化工具里的分支操作IDEA、VSCode与小乌龟7.1 IDEA 里的分支切换、合并与新建很多读者日常开发工具是 IDEA没必要硬要求所有人背命令行。IDEA 的分支入口在右下角显示当前分支名的小按钮点开之后能看到所有本地分支和远程分支。切换分支直接双击目标分支名就行新建分支在分支列表顶部选择New Branch填名字后点创建并切换合并分支时先切到目标分支再从列表里选择要合并的分支点击Merge Selected into Current。IDEA 里遇到冲突会弹出一个合并对话框左边是本分支的版本右边是合并进来的版本中间可以手动编辑最终内容处理完标记为已解决。还有人问“IDEA 复制了一个主项目复制的项目怎么切换分支”这个问题的核心往往是复制后的项目 Git 远程地址还是指向原仓库需要在Git Manage Remotes里把 remote 地址改掉再执行git fetch分支列表就会更新成自己仓库的分支。另外一个常见场景是新建项目时从 Git 拉取代码直接在欢迎页选Get from VCS粘贴仓库地址就能 clone 下来关联的分支会同步加载。7.2 VSCode 的源码管理与分支清理VSCode 的 Git 能力全靠左侧的“源代码管理”图标。分支切换在左下角的源代码管理面板里点击分支名就能看到分支列表并切换。合并分支的操作也比较顺手先切到目标分支打开命令面板输入Git: Merge Branch选择要合并进来的分支即可。VSCode 解决冲突用的是可视化编辑器冲突文件里能看到Accept Current Change、Accept Incoming Change、Accept Both Changes三个按钮点哪边就保留哪边比我手工删标记要直观不少。清理删除的分支在 VSCode 里有两种常见情况本地分支没有删除干净用命令面板输入Git: Delete Branch选择删除远程分支被别人删掉了但本地依然显示需要在源代码管理面板的刷新按钮旁执行Git: Fetch (Prune)把已经不存在的远程分支引用清掉。记住这一点就不会再遇到“明明删了怎么还在列表里”的困惑。7.3 小乌龟TortoiseGit的日常操作Windows 上有一批老用户比较习惯用左右键选菜单里的小乌龟图标操作 Git。小乌龟最常用的是右键菜单里的Pull、Switch/Checkout、Merge整体逻辑和命令行一致。切换分支时选择Switch/Checkout在弹出的窗口里选中目标分支即可合并时选Merge填入要合并的分支有冲突会弹出冲突文件列表双击文件手动解决保存后再Add标记为已解决。小乌龟有一点比命令行直观每一次操作都会弹出一个进度窗口过程是成功还是失败、哪个文件冲突都看得特别清楚适合不习惯黑窗口的开发者。不过它本质还是 Git 命令的图形化封装理解了分支协作的整体流程之后用哪个工具只是习惯问题。团队协作时我的建议是负责人统一推荐一种主流方式但不要强制所有人都用同一款工具因为分支模型和合并流程才是协作的核心工具只是手感差异。最后的体会是不同分支下的多人协作真正的难点从来不在于记住几条 Git 命令而在于把团队的协作秩序建立起来。分支再干净、命令再熟练如果每个人都随心所欲地推代码、合并、删分支最后还是乱成一锅粥。我自己的经验是先把分支命名规则、主分支保护、小步提交这三件事立起来团队的协作效率就能比在 main 上直接堆代码高出一大截。再往后遇到冲突、误操作就用上面这些方法从容处理用顺手了你甚至会开始享受这种各做各的、收工统一合并的节奏。
阅读完成 · 觉得有帮助?