工作四年了,我最怕的就是遇到合并冲突。我们团队 8 个人做商城项目,三天两头因为 Git 问题整个下午都没了,那时候真的觉得肯定有别的办法。折腾了几周后,我终于整出了一套新的玩法,合并冲突直接减少了 94%。
记得那个周二早上,我刚到公司就看到同事们都在那儿抓头发。订单管理那个分支和 main 主分支合并的时候,一下子冒出来 47 个冲突,三个人整整忙了 4 个小时才搅定。那时候我心里就明白了,我们这套功能分支的玩法不行。
我跟项目经理说:老大,你说我们能不能一开始就别让分支跑太远?这样不就不会合并冲突了吗?
我研究了一下主干开发,发现可以整个混合的方案。这样既能保留功能分支的好处,又能像主干开发那样频繁地合并代码。
Main Branch (始终可部署)
│
├── micro/product-list (最多 6 小时前创建)
│ └── → 通过 CI 自动合并
│
├── micro/cart-logic (最多 4 小时前创建)
│ └── → 通过 CI 自动合并
│
└── micro/order-payment (最多 2 小时前创建)
└── → 通过 CI 自动合并
第一个规则:6 小时必须合并 一个分支最多活 6 个小时,超时就得合并。如果 6 小时做不完,那就是你的任务切得不够细。
# 我的日常工作流脚本
git checkout main
git pull origin main
git checkout -b micro/$(date +%H%M)-feature-name
# 用小而专注的提交来工作
git add components/ProductCard.jsx
git commit -m "add product card component"
# 立即推送并创建 PR
git push -u origin HEAD
gh pr create --fill --draft
第二个规则:提交前先同步 我加了个钩子,每次提交代码前先和 main 分支同步一下:
#!/bin/bash
# .git/hooks/pre-commit
echo "正在与 main 同步..."
git fetch origin main
# 检查是否需要 rebase
BEHIND_COUNT=$(git rev-list --count HEAD..origin/main)
if [ $BEHIND_COUNT -gt 0 ]; then
echo "落后 $BEHIND_COUNT 个提交。正在自动 rebase..."
git rebase origin/main
fi
第三个规则:文件"占座"制度 用 GitHub Issues 做了个简单的文件锁定,谁在改哪个文件都要登记:
# 我们的协调系统 (.github/file-locks.yml)
file_assignments:
- path: 'src/api/products.js'
assignee: '@小李-dev'
issue: '#1247'
expires: '2025-08-30T14:00:00Z'
- path: 'src/components/ShoppingCart.jsx'
assignee: '@小王-frontend'
issue: '#1248'
expires: '2025-08-30T16:00:00Z'
项目经理听了直摇头:"这不是增加工作量吗?"我心想光说不练假把式,就和另一个同事先在支付功能上试了两周。
第1周(传统工作流):
├── 合并冲突:12 个
├── 解决耗时:6.5 小时
├── 合并失败:3 次
└── 开发者压力水平:高
第2周(微集成工作流):
├── 合并冲突:0 个
├── 解决耗时:0 小时
├── 合并失败:0 次
└── 开发者压力水平:低
数据这么一对比,项目经理立马改口:"行,全团队都试试这个。"
要让这套方法跑起来,得先写点自动化的工具:
// conflict-predictor.js - 我的冲突检测工具
const { execSync } = require('child_process')
function analyzeConflictRisk(branchName) {
const changedFiles = execSync(`git diff --name-only origin/main...${branchName}`).toString().split('\n').filter(Boolean)
const recentMainChanges = execSync('git diff --name-only HEAD~10..HEAD').toString().split('\n').filter(Boolean)
const overlappingFiles = changedFiles.filter((file) => recentMainChanges.includes(file))
return {
riskScore: overlappingFiles.length,
conflictFiles: overlappingFiles,
recommendation: overlappingFiles.length > 2 ? 'IMMEDIATE_MERGE' : 'SAFE'
}
}
不能靠人工去记这些规则,得用 CI 来自动检查:
# .github/workflows/branch-lifecycle.yml
name: Branch Lifecycle Management
on:
push:
branches-ignore: [main]
jobs:
check-branch-age:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check branch age
run: |
BRANCH_AGE=$(git log --format="%ct" -n 1)
CURRENT_TIME=$(date +%s)
AGE_HOURS=$(( (CURRENT_TIME - BRANCH_AGE) / 3600 ))
if [ $AGE_HOURS -gt 6 ]; then
echo "分支已存在 $AGE_HOURS 小时(最大限制:6小时)"
exit 1
fi
还写了个脚本,能按照最优的顺序来合并分支:
#!/bin/bash
# smart-merge-order.sh
# 找出所有待合并的 PR,按文件数量排序
gh pr list --json number,files,headRefName | \
jq -r '.[] | "\(.files | length) \(.number) \(.headRefName)"' | \
sort -n | \
while read fileCount prNumber branch; do
echo "合并 PR #$prNumber,共 $fileCount 个文件"
# 先看看有没有冲突
CONFLICTS=$(gh pr checks $prNumber --required | grep -c "failure")
if [ $CONFLICTS -eq 0 ]; then
gh pr merge $prNumber --squash --delete-branch
else
echo "PR #$prNumber 有问题,先跳过"
fi
done
全团队用了 3 个月下来,数据变化明显:
传统工作流(基准线):
├── 每周冲突:23 个
├── 平均解决时间:45 分钟
├── 开发者挫败事件:12 次
└── main 分支损坏事件:4 次
微集成工作流:
├── 每周冲突:1.4 个
├── 平均解决时间:3 分钟
├── 开发者挫败事件:0.2 次
└── main 分支损坏事件:0 次
后来发现,把相关的文件放在一个提交里能减少冲突:
# 相关的文件一起提交
git add src/components/ProductDetail.jsx src/hooks/useProduct.js src/types/product.ts
git commit -m "feat: complete product detail functionality"
分支名字里加了些标记,提醒可能的问题:
git checkout -b micro/ui-product-solo # 无依赖
git checkout -b micro/api-order-conflicts # 预期有冲突
git checkout -b micro/db-schema-breaking # 破坏性变更
让同事们学会把功能切成 6 小时的小块比较困难,我做了个模板:
## 功能:商品管理系统
### 微功能:
1. 商品列表组件(2 小时)
2. 商品详情 API 接口(3 小时)
3. 购物车添加逻辑(4 小时)
4. 商品搜索功能(1 小时)
5. 商品分类筛选(2 小时)
一开始同事们都觉得文件锁定太麻烦了。后来我们接了 Slack 通知,自动提醒:
// 文件被锁定时自动通知
const webhook = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
fetch(webhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `🔒 ${fileName} 被 ${developer} 占用到 ${expiry}`
})
})
这套方法之所以好用,就是因为它直接解决了问题的根源:分支跑太远。每次只改一点点东西,并且快速合并,冲突的概率就大幅下降。
冲突概率 = (分支存在时间 × 文件变更数 × 团队规模) / 集成频率
以前的方式: (168 小时 × 25 个文件 × 8 个人) / 168 小时 = 25
现在的方式: (6 小时 × 3 个文件 × 8 个人) / 6 小时 = 3
# 可以用我的模板开始
git clone https://github.com/your-username/micro-git-workflow
cd micro-git-workflow
./setup.sh
从不重要的功能开始练手,重点学会把工作切成 2-4 小时的小块。
把自动化工具都启用起来,开始统计冲突减少的数据。
用了半年之后,我们最大的烦恼不再是合并冲突了,而是商城功能做得太快,测试都跟不上节奏。现在发布更快,心情更好,Git 也不再是噩梦。
这套方法不一定适合所有团队,但如果你也被合并冲突折磨得不行,不妨试试这个微集成的做法。前期多花点功夫做工具和规范,几周之后就能看出明显的效果。