6篇文章讲清楚Git:Git 团队开发实践:从 Pull Request 到版本发布 (6/6)

前面五篇讲的都是单人在自己电脑上操作,这一篇讲的是一群人怎么在同一个项目上协作,以及代码怎么从大家的电脑汇总成一个对外发布的版本。

Fork、Clone、Pull Request

这三个词经常一起出现,先把关系理清楚:

动作 做什么 在哪操作
Fork 把别人的仓库复制一份到我们自己的账号下 GitHub 网页上点一下
Clone 把仓库下载到本地 git clone <url>
Pull Request 请求把我们的改动合进原仓库 GitHub 网页上发起

Fork 和 Clone 的区别在于目标位置:Fork 是复制到我们的 GitHub 账号(还是远程),Clone 是下载到我们自己的电脑(本地)。

什么时候用 Fork

开源项目:我们没有原仓库的写权限,只能 Fork 一份到自己账号下,改完发 PR 请求维护者合并。

公司内部项目:通常大家都有权限,直接在同一个仓库上建分支就行,不需要 Fork。两种流程的差别大概是:

text 复制代码
Fork 模式(开源)                     分支模式(团队内部)
Fork 原仓库 → 你的账号                在有权限的仓库直接建分支
   ↓ clone                              ↓ clone
本地改 → push 到你的 Fork             本地改 → push 到仓库的分支
   ↓                                    ↓
发 PR:你的分支 → 原仓库的 main        发 PR:你的分支 → main

一次完整的协作流程

以团队内部的分支模式为例,从接到任务到代码合入主干:

shell 复制代码
# 1. 同步最新的主干
git switch main
git pull

# 2. 建一个功能分支(从最新的 main 上分)
git switch -c feature/user-validation

# 3. 开发,随时提交
git add .
git commit -m "添加用户名格式校验"

# 4. 期间主干有更新,同步一下(保持自己的分支不落后)
git fetch origin
git rebase origin/main

# 5. 推送到远程(第一次要 -u)
git push -u origin feature/user-validation

# 6. 在平台上发起 PR / MR
#    选好源分支和目标分支,写清说明,指定 reviewer

# 7. 根据 review 意见改代码(如果改的是同一个提交,用 amend)
git add .
git commit --amend --no-edit
git push --force-with-lease
# 如果是新增的改动,就正常提交再 push,不用强推

# 8. 合并之后再清理分支
git switch main
git pull
git branch -d feature/user-validation
git push origin --delete feature/user-validation

几个实践上的注意点:

  • 功能分支要短命。活了一周以上的分支,rebase 和合并都会很痛苦,尽量拆小、尽快合
  • PR 别太大。几百行的 PR 大家愿意认真看,几千行的 PR 一般只会点个 approve
  • 合并前先同步主干 ,让自己的分支包含最新的 main,CI 才会跑在真实状态上
  • 第 7 步用的是 --force-with-lease 而不是 --force,原因在第 4 篇的强推边界里讲过

四种工作流的对比

Git Flow

分支最多,规矩也最严:

text 复制代码
main        只放已发布的版本
hotfix/*    紧急修复,从 main 分,修完合回 main 和 develop
release/*   发布准备,从 develop 分
develop     集成分支,功能都往这里合
feature/*   功能分支,从 develop 分

好处是发布流程很清晰,适合有明确版本号和发布周期的产品;坏处是复杂,日常开发要在 5 种分支之间来回切,在持续交付的场景下这套流程太重了。所以它现在基本只在传统软件、或者需要同时维护多个历史版本的项目里用。

GitHub Flow

极简,只有一条规则:main 永远可发布,其它都是短命的功能分支。

text 复制代码
main        随时可部署
feature/*   从 main 分,改完发 PR,合并回 main

合并之后立刻部署,适合持续部署的 Web 服务。

  • 优点:简单,分支少,和自动化部署天然契合
  • 缺点:没有处理"多版本并行维护"的机制,也不区分发布和开发

GitLab Flow

可以看成 GitHub Flow 的加强版,引入了环境分支来解决部署问题:

text 复制代码
main        → pre-production → production

代码先合进 main,再往上游的环境分支合并,每个环境分支对应一个部署环境。也可以配合 release 分支做版本管理。

它比 GitHub Flow 多了一层环境管控,又比 Git Flow 简单,适合需要区分多套环境的团队。

主干开发(Trunk Based)

所有人直接在主干上开发,或者只开存活不超过一天的超短分支。

它的前提是有完善的自动化测试和 CI,否则主干随时可能是坏的。通常还会配合特性开关(feature flag):代码合了但功能没开,等真正要上线才打开开关。Google、Facebook 这类公司用的就是这套。

怎么选

工作流 分支数量 复杂度 适合
Git Flow 5 种 高 有明确版本发布周期的产品
GitHub Flow 2 种 低 持续部署的 Web 服务
GitLab Flow 2~3 种 中 需要多环境管控
主干开发 1 种 低(但对 CI 要求高) 测试覆盖率高、追求快速迭代

一个粗略的判断标准:部署越频繁,分支就该越少。每天发好几次的团队用 Git Flow 会把自己累死,一年发两次版的软件用主干开发也不现实。

tag 和版本发布

tag 是给某个提交打的固定标记,常用来标记版本。

轻量标签和附注标签

shell 复制代码
git tag v1.0.0                          # 轻量标签
git tag -a v1.0.0 -m "第一个正式版本"     # 附注标签
轻量标签 附注标签
是什么 就是一个指向提交的指针 一个独立的对象,含打标签的人、时间、说明
能签名吗 不能 能,-s 参数可以做 GPG 签名
命令 git tag <名字> git tag -a <名字> -m "说明"
建议 临时标记可以用 正式发版用这个
shell 复制代码
git tag                       # 列出所有标签
git tag -l "v1.*"             # 按模式过滤
git show v1.0.0               # 看某个标签的信息
git tag -d v1.0.0             # 删除本地标签

推送标签

git push 默认不会推送标签,这一条很容易踩:

shell 复制代码
git push origin v1.0.0        # 推一个
git push origin --tags        # 推所有
git push origin --follow-tags # 只推附注标签(更安全)

删除远程标签:

shell 复制代码
git push origin --delete v1.0.0

检出某个标签

tag 指向的是固定提交,不能像分支那样移动,所以检出标签会进入游离 HEAD:

shell 复制代码
git switch --detach v1.0.0

想基于某个版本修 bug,应该从标签建一个分支出来:

shell 复制代码
git switch -c hotfix/1.0.1 v1.0.0

语义化版本

约定是 MAJOR.MINOR.PATCH:

位 什么时候加
MAJOR 有不兼容的改动
MINOR 新增功能,向后兼容
PATCH 修 bug,向后兼容

比如 1.4.2 修个 bug 就是 1.4.3,加功能是 1.5.0,改了不兼容的接口才是 2.0.0。加上预发布标识就是 2.0.0-beta.1 这种形式。

.gitignore 的常见坑

已经被跟踪的文件,加进 .gitignore 没用

这是最常见的困惑:明明把 config.local 加进 .gitignore 了,git status 里还是能看到它。

原因是 .gitignore 只对"未被跟踪的文件"生效 。一个文件一旦被 git add 过,Git 就一直在跟踪它,后面写进 .gitignore 也管不着。

正确的做法是先把它从跟踪里移除(保留本地文件):

shell 复制代码
git rm --cached config.local      # 只从 Git 里移除,磁盘上的文件还在
git commit -m "停止跟踪本地配置文件"

注意别漏了 --cached,直接 git rm config.local 会把本地文件也一起删掉。

几个写法上的细节

text 复制代码
node_modules/           # 目录要带斜杠
*.log                   # 通配符
!important.log          # ! 表示不忽略(取反),必须写在忽略规则后面才生效
/build                  # 开头的斜杠表示只匹配仓库根目录下的 build
**/temp                 # 任意层级的 temp 目录

如果发现规则没生效,别自己猜,用这条命令直接问 Git:

shell 复制代码
git check-ignore -v config.local     # 看是哪条规则把它忽略了

它会明确告诉我们匹配到的是哪个文件的哪一行,比自己排查快得多。

另外两个容易忽略的点:.gitignore 本身应该被提交 ,这样团队所有人共享同一套规则;而个人的偏好(比如 .idea/、.vscode/)不该写进项目的 .gitignore,应该放到全局配置里:

shell 复制代码
git config --global core.excludesFile ~/.gitignore_global

敏感信息误提交了怎么办

分三步,而且顺序不能反。

第一步:立刻作废这个凭据

这一步比清理历史重要得多。

只要提交推送到了远程,就默认它已经泄露了。哪怕是私有仓库、哪怕我们一分钟内就删掉了,都要当作泄露处理,因为平台可能已经缓存、索引、备份过,CI/CD 系统可能已经拉取过,有权限的同事可能已经拉到了本地,甚至可能已经有人看到了通知邮件。

所以正确的第一反应是:去对应的平台把密钥、密码、token 作废并重新生成。数据库密码就改密码,云服务的 AK 就去控制台禁用旧的。清理历史是第二步的事。

第二步:从历史里移除

作废之后,如果还想把内容从 Git 历史里清掉(比如开源仓库不希望留个把柄),可以用 git filter-repo:

shell 复制代码
# 安装:pip install git-filter-repo
git filter-repo --path config/secret.yml --invert-paths

或者替换文件里的某段文本:

shell 复制代码
git filter-repo --replace-text expressions.txt

expressions.txt 里写替换规则:

text 复制代码
password123==>REMOVED
sk-xxxxx==>REMOVED

早期用的是 git filter-branch,但它慢、容易出错,Git 官方已经明确不推荐使用 ,改用 git filter-repo。BFG Repo-Cleaner 是另一个选择,Java 写的,速度很快。

清理完历史之后,本地所有提交的哈希都变了,需要强推:

shell 复制代码
git push --force --all
git push --force --tags

第三步:通知协作者

强推之后,别人本地的仓库和历史就对不上了。标准做法是让所有人重新克隆:

shell 复制代码
git clone <url>   # 重新拉一份,最省事

不重新克隆的话,他们那些旧提交还在本地,一不小心又推回远程,等于白清理。

这也是为什么"清理历史"是一件很麻烦的事:它是一次影响全团队的操作。所以更省事的做法是从一开始就防住:

  • .gitignore 里提前写好 *.env、*.pem、config.local 这类模式
  • 用 git add -p 而不是 git add .,提交前过一眼到底加了什么
  • 装个 git-secrets,或者用平台的 secret scanning,让推送时直接被拦下来
相关推荐
DevUp1 小时前
老站往哪搬:织梦、帝国、PHPCMS 的出路
后端·php·cms
王中阳Go1 小时前
甲骨文裁3万、DeepSeek却招150人:后端程序员往哪走,我把这批JD拆了一遍
后端
打工仔折腾 AI2 小时前
蓝耘元生代实测:用WorkBuddy与TextIn xParse拆解43页建模论文
人工智能·后端·python·数学建模·langchain·ai agent 实战
隔窗听雨眠2 小时前
FunProxy用Rust构建跨平台全链路测试抓包代理工具
开发语言·后端·rust
geovindu2 小时前
rust: search
开发语言·后端·rust
the局外人2 小时前
读懂LangGraph 的分支执行逻辑
后端·langchain·llm
SimonKing2 小时前
Stream-Nexus:我的轻量级推送中间件(sse、websocket)终于定下来了
java·后端·程序员
打工仔折腾 AI2 小时前
网易UU远程实测:手机控电脑做Python开发和Agent调试的真实体验
人工智能·后端·python·智能手机·性能优化·电脑·ai agent 实战
YYYing.2 小时前
【设计模式系列 (四) 】建造者模式
c++·后端·设计模式·建造者模式·c/c++