一个人开发时,Git 只是备份工具;两个人以上开发时,Git 才真正显示威力。但协作必然带来一个问题:你和同事同时改了同一个文件,怎么办?本文用一个双人模拟项目走完完整协作流程:分支策略、先拉后推、制造并解决冲突、merge 与 rebase 的选择,最后过一遍 Code Review。
1. 协作的基本模型
多人协作最常见的是共享仓库模式:所有人都有同一个远程仓库的推送权限,各自在功能分支上开发,完成后合并回主分支。流程是:
git checkout -b feature/login
# ……开发、提交……
git push -u origin feature/login
git checkout -b 创建并切换到新分支;git push -u 把新分支推到远程并建立追踪关系。功能分支的名字要能说明意图,比如 feature/login、fix/typo,这样别人一眼就知道你在干什么。
推荐的分支策略很简单:主分支 main 永远保持可发布状态,开发都在功能分支上做。这样即使某人的功能做了一半,也不会污染别人的代码。
2. 先拉后推:协作的交通规则
两个人都基于同一份代码开发,必然出现"你推的时候,远程已经有别人的新提交"的情况。此时直接 git push 会被拒绝:
git push
# ! [rejected] main -> main (fetch first)
# 别慌,这是 Git 在保护你
git pull --rebase
git push
看到 rejected 不是错误:直接覆盖会把同事的提交弄丢。正确的动作是先拉后推:git pull --rebase 先把远程的新提交拉下来,再把你的本地提交"重放"到最新代码之上,历史是一条干净的直线,然后再 push 就顺理成章了。
3. 制造一场冲突
冲突的根源是两个人改了同一个文件的同一处。Git 很聪明,能自动合并不同位置的修改;只有改到同一行时,它才不知道听谁的,把决定权交给你。来亲手制造一个,先建一个文件推上去:
echo "print('你好')" > hello.py
git add hello.py && git commit -m "add hello"
git push
# —— 终端 A:改了问候语并推送 ——
echo "print('早上好')" > hello.py
git commit -am "A 改的问候语"
git push
# —— 终端 B:晚一步,也改了同一行 ——
echo "print('晚上好')" > hello.py
git commit -am "B 改的问候语"
git pull --rebase
第二个终端(终端 B)的 git pull --rebase 会停下来,告诉你 CONFLICT:两个提交都改了 print('你好') 这一行,Git 不知道该保留哪个。注意 git commit -am 中的 -a 会自动暂存已跟踪文件的修改,但前提是文件已经被跟踪过。
4. 解决冲突
冲突发生时,Git 会在文件里插入冲突标记。打开 hello.py 会看到:
<<<<<<< HEAD
print('早上好')
=======
print('晚上好')
>>>>>>> B 改的问候语
<<<<<<< HEAD 和 ======= 之间是当前分支(你的)版本,======= 和 >>>>>>> 之间是对方分支的版本。你的任务是手动决定最终内容——可以选 A、选 B,或者两行都留。把冲突标记删掉,写成想要的样子:
print('早上好')
print('晚上好')
改完后告诉 Git"我解决了":
git add hello.py
git rebase --continue
git add 标记冲突已解决,git rebase --continue 让 rebase 继续把后面的提交重放完,然后正常 git push 即可。小技巧:冲突越少越好,方法就是勤 git pull,别让本地分支落后远程太久。
5. merge 还是 rebase
合并分支有两条路线,风格截然不同:
| 方式 | 历史形态 | 特点 |
|---|---|---|
git merge | 保留分叉,多一个合并提交 | 忠实记录"发生过什么",适合大型团队 |
git rebase | 线性历史,没有分叉 | 历史清爽,但会改写提交 |
实际项目里两者常配合使用:功能分支开发时,定期 git rebase main 把主分支的新代码合进来,保持分支不落后;功能完成合并回 main 时,用 git merge 保留一次合并记录:
git checkout main
git pull
git merge feature/login
规则只有一条:rebase 只用于本地未推送的分支。已经推送出去的分支被 rebase,等于改写公共历史,队友会崩溃。
6. Code Review 与 Pull Request
在 GitHub 或 GitLab 上,功能分支通常不直接合并,而是走 Pull Request(MR)流程:推分支 → 网页上发起 PR → 同事审查代码、留言 → 通过后合并。这套流程叫 Code Review,是团队质量的第一道防线:
git push -u origin feature/login
# 在 GitHub 上点击 Compare & pull request
# 填写描述,指派 reviewer,等待审查
PR 里可以直接对某一行代码留言讨论,修改后重新 push,PR 会自动更新。审查通过后,可以选择 Merge(保留合并提交)或 Squash and merge(把整个分支压成一个提交,历史最干净)。
7. 总结与练习
多人协作的三个关键词:分支隔离、先拉后推、冲突可控。功能分支开发、git pull --rebase 保持同步、冲突时手动裁决、rebase 只碰本地分支、合并走 PR 审查,这套流程足够支撑一个十人团队。练习:
- 开两个终端模拟两位同事,按第 3 节步骤制造一次真实冲突,完整走一遍解决流程。
- 在 GitHub 上建一个仓库,邀请一位朋友,两人同时改同一个文件再互相 pull,体验
rejected和冲突。 - 分别用
git merge和git rebase合并同一个功能分支,用git log --graph --oneline对比两种历史形态。
💡 冲突不是坏事,说明大家在认真改代码。关键是解决冲突前先理解对方为什么这么改——必要时直接问,比闷头删标记靠谱得多。