开发向 · 第 2 篇

一个仓库、两台机器:并行开发不弄乱的秩序

碎片时间开发、还换机器——真正会出乱子的不是"冲突",是另外三件事。

START

你怕的不是学不会命令,是把自己的活弄乱

上一篇结尾那条铁律你已经知道了:开工前先 pull,收工前必 push。可真按自己的节奏干起来,问题立刻变具体——

我时间很碎,晚上在这台机器上写一半,第二天换另一台接着写;手里还不止一件事在推进。我怕的不是学不会命令,是怕把自己的活弄乱——怕覆盖掉前一天写的东西,怕两边各写各的对不上,怕哪天一拉下来发现前面的白干了。

于是很多人退回到最原始的做法:改完压缩打包传一份、文件名后面缀日期、或者干脆只在一台机器上干,另一台闲着。

这类担心的根,是把托管代码的地方当成了网盘。网盘是"同一份文件两边抢着覆盖",所以你怕;而档案馆是"每次改动都留底、谁改了哪张图纸的哪一行都记着"——它天生就是给多人多机同时干活准备的。这一篇要把三件事讲透:为什么它不会乱、真正该管的到底是什么、以及一套照着做就不出事的纪律。

MODEL

先卸下焦虑:它天生就是干这个的

两台机器往同一个仓库推送,不会造成混乱。这不是它的边界情况,而是它被发明出来要解决的核心问题——世界上最大的开源项目有上万人往同一个仓库提交;你两台机器,是最轻量的场景。

合并的粒度是"图纸上的行",不是"整座档案馆"

原因就一句话:它比对和合并的单位,是文件里的行,不是整个仓库。

机器 A 改了  site/pages/home.tsx     的第 20–30 行
机器 B 改了  admin/modules/list.ts   的第 5–8 行
        ↓
两处互不相干 → 自动合并,零冲突

用词典里的比喻说:档案馆管理员并的是图纸上的行。你改了这张图的这一段、另一台机器改了那张图的那一段,他直接两份都收,不用问你。只有当两台机器改了同一个文件的同一片区域,他才会喊一声"这里两个版本我不敢替你定",让你来裁决——这就是所谓的"冲突(Merge Conflict)"。你一个动对外站点、一个动内部后台,正常情况下永远撞不上。

承上一篇的关键区分(接词典「提交与推送」):提交 ≠ 推送。提交只是把施工日志放进自己抽屉;没送到档案馆之前,另一台机器看不到,也没有备份。

那个"推送被拒绝"不是出错,是在保护你

你一定会遇到这个:

! [rejected]  main -> main (fetch first)
error: failed to push some refs to ...

它不是故障。意思是"档案馆里已经有你还没见过的新记录(另一台机器送来的),你先取回来对一遍再送"。处理方式固定三步:

git pull        # 取回对方的提交,并与你的合并
                # 无冲突 → 自动合并完成;有冲突 → 解决后 git add + git commit
git push        # 再送一次

这个循环你会走成千上万次,它就是日常。看到 rejected 别慌、别删了重来、更别另存一份新文件——先 pull。

RISK

真正的风险不在"冲突",在这三件事

冲突是会主动报警的,反而不可怕。以下三个才是静悄悄埋雷的——它们是这一篇真正要管的东西。

改了共用的那部分,把另一条线弄崩了(最高危)

你在机器 A 上为了对外站点方便,把两边共用的一个日期格式化函数的参数改了。这里不会报冲突——因为机器 B 根本没动这个文件。但机器 B 一拉下来,内部后台里几十处调用全挂。这是它管不了的问题:它只认文本,不理解代码的意思。

→ 见纪律 4:动共用区只增不改

把跑不通的半成品送上了主线

你写到一半要收工,为了在另一台机器上接着写,只能推上去。可推上去的是坏的,另一条线一拉下来,连带着起不来了。解法:把没做完的活放在草案分册里,正式图纸永远保持能照着施工。

→ 见纪律 2:草案分册

改动历史混成一锅粥

日志里两条工作线的记录交替出现,三个月后想查"内部后台那块是什么时候改的",翻不出来。留底的价值在于查得回来;查不回来,等于没留。

→ 见纪律 3:一行成本的前缀

CHOICE

先定一件事:两条线放一个仓库,还是拆开?

这是"秩序"背后真正要先定的架构问题。

单仓库(Monorepo)多仓库(Polyrepo)
共用的那部分直接引用,改完立即生效要打包发布、还要管版本号
一次改动跨两条线一次提交搞定,历史完整提交两次,还要对齐版本
仓库体积 / 首次下载较大各自较小
权限隔离做不到(有权限就能看全部)可以分别授权
适合谁少数人、两条线之间有共用多团队、彼此独立

给一个人/小团队的建议:保持单仓库,先不要拆。

判断依据只有一条:两条线之间有没有共用的东西。有,就别拆。一旦拆开,那些共用的东西立刻变成难题:要么复制两份(从此两边慢慢漂移,改一处忘另一处,这是最常见的技术债),要么打包发版(现在还不需要背上版本管理这套复杂度)。

该拆的时机是:共用部分稳定到几乎不改了,且有另一拨人要单独维护其中一条,且权限需要隔离——三条同时满足再拆。

秩序的第一层:一个目录只有一个主人

your-project/
├── site/            ← 对外站点:机器 A 的主战场
├── admin/           ← 内部后台:机器 B 的主战场
├── shared/          ← 共用区(高危区,动它要打招呼)
│   ├── types/       ← 共用的数据结构定义
│   ├── utils/       ← 共用的工具函数
│   └── config/
├── docs/            ← 项目文档
├── .gitignore       ← 忽略清单:不该进档案馆的东西写在这
└── (给 AI 读的项目说明文件)

核心原则:一个目录只有一个"主人"。一条线的代码不往另一条线的目录里放;真要共用,就往 shared/ 里提。目录边界一清晰,两台机器天然错开,冲突概率趋近于零——秩序不是靠小心,是靠边界。

顺带说清一件常被问的事:两条线各自有自己的依赖清单,各领各的一套酱油香料(接词典「依赖」),互不打架。这些领来的零件体积大、更新快,从不进档案馆,写进忽略清单即可——档案馆里存的是菜谱,不是菜(接词典「源码与生成物」)。

RULES

四条运转纪律(这才是"秩序"的本体)

纪律 1 · 开工先拉,收工必送

git pull        # 每次坐到任何一台机器前,第一条命令

不是可选项。跳过它,你就是在一份过期的图纸上画新图,后面必然要付出合并的代价。

纪律 2 · 没做完的活放草案分册,主线永远可用

这条直接解决"半成品污染"。分支(Branch)=档案馆里另起的一册草案图纸:在里面怎么画、画多少版都不影响正式那套;确认能照着施工了,才并回主线。

git checkout -b feat/site-homepage        # 为本次任务另起一册
git push -u origin feat/site-homepage     # 随便提交多少次、随时推送,只是备份

git checkout main
git pull                                  # 先拿到最新,确保基于最新合
git merge feat/site-homepage
git push
git branch -d feat/site-homepage          # 删掉已并入的那册

另一台机器走自己的一册。两册各自演进,互不干扰,只在并回主线时才相遇。分册名带工作线前缀:feat/site-*、feat/admin-*、fix/shared-*,一眼看得出它属于哪条线。

这条纪律还有一个当下就用得上的好处:让 AI 大改之前先另起一册。AI 一次可能动十几个文件,改砸了直接把这册丢掉,正式图纸毫发无损——上一篇说"提交是 AI 编程的安全网",分支是这张网的加固层。

纪律 3 · 每条记录带工作线前缀

git commit -m "[site] 首页横幅改为轮播"
git commit -m "[admin] 列表页加分页"
git commit -m "[shared] 新增金额格式化工具函数"

一行成本,换来"按线筛历史"的能力(git log --oneline | grep admin)。三个月后的你会感谢现在的你。

纪律 4 · 动共用区,遵守"只增不改"

  1. 优先新增,而不是修改。需要那个日期函数支持新格式?加一个新函数,别去改原来那个的参数——老的调用方一行都不用动。
  2. 确实必须改(改名、改参数、删除)时三步走:记录里明确标出这是破坏性改动;在同一次提交里把两条线上的调用处一起改掉(这正是单仓库最大的好处);改完立刻推送,并到另一台机器上第一时间拉取 + 跑一遍。
  3. 心态上:共用区是公共设施,改它等于动别人家的地基,永远比改自己目录更谨慎。

日常照做(抄走即可)

# 开工
git checkout main
git pull                          # 拿到另一台机器的成果
git checkout -b feat/site-xxx     # 为本次任务另起一册

# 干活中
git status                        # 随时看改了什么
git add .
git commit -m "[site] 具体做了什么"
git push                          # 想备份/换机器时随时推,不影响别人

# 做完
git checkout main
git pull
git merge feat/site-xxx
git push                          # 合并前先跑一遍,确认两条线都正常
git branch -d feat/site-xxx

# 换到另一台机器
git pull                          # 第一条命令,永远是它

常被追问的三点

Q 两台机器能不能在同一册上工作?

A 能,但没必要,而且会频繁撞上"推送被拒绝"。既然天然是两条线,就各用一册,摩擦最小。

Q 另一台机器怎么看到我新起的那一册?

A git fetch 刷新远端分册列表 → git branch -a 查看所有分册 → git checkout feat/admin-xxx 直接切过去。

Q 会不会有一天仓库太大?

A 撑大仓库的不是代码,是图片、视频、生成物、领来的零件包。把它们写进忽略清单就行——这个体量,几年内不用担心。