引言:从“普通 Git 推送”到“AOSP 提交”的困惑

如果你之前用过 GitHub、GitLab 这类平台,一定会对 git push origin main 这种简洁的推送命令非常熟悉——本地代码写完,addcommit 后一键推送,代码就直接合并到远程分支了。

但当你投身 AOSP(安卓开源项目) 或其他基于 Gerrit 的代码库时,突然发现:

git push aosp master  # 直接推送?权限被拒!

取而代之的是这种“看不懂”的命令:

git push aosp master:refs/for/master

为什么必须加 refs/for/?直接推送不行吗?今天咱们就用最通俗的方式,彻底拆解背后的原理和实操逻辑!

一、核心差异:Gerrit 的“审核铁律”

普通 Git 平台(GitHub/GitLab):代码合并 = 信任开发者
你有仓库写权限时,push 就是“直接合并”,代码立刻生效(除非开了保护分支)。

Gerrit 平台(AOSP/LineageOS 等):代码合并 = 必须审核
Gerrit 是专为大型开源项目设计的代码托管系统,核心规则是:任何代码在合并前,必须经过至少 1 位 Reviewer(审核人)的批准

为了强制执行这条规则,Gerrit “阉割”了直接推送的权限——你无法像普通 Git 那样,用 git push 直接修改远程分支的代码。

取而代之的是 refs/for/ 这个“审核投递口”:

  • 推送格式:git push <远程名> <本地分支>:refs/for/<目标分支>
  • 作用:把你的代码“投递”到审核队列,不会直接合并,而是生成一个“代码审核任务(Change)”,等 Reviewer 网页端审核通过后,才自动合并。

二、实战:为什么你必须用 refs/for/

场景 1:普通 Git(GitHub)的“直推”逻辑

假设你在 GitHub 有一个仓库,远程名是 origin,本地分支是 main

推送命令:

git push origin main

执行后:

  1. GitHub 直接把你的 main 分支代码合并到远程 main 分支。
  2. 代码立刻生效,其他协作者拉取时,会直接获取你的新代码。
场景 2:Gerrit(AOSP)的“审核”逻辑

假设你在 AOSP 有一个远程仓库 aosp,本地分支是 master

错误操作(直接推送):

git push aosp master

执行后大概率报错:

remote: error: denied: You are not allowed to push code directly to this branch.

原因:Gerrit 禁止直接推送 refs/heads/*(远程分支的存储路径),必须走审核流程。

正确操作(用 refs/for/ 投递审核):

git push aosp master:refs/for/master

执行后:

  1. Gerrit 拦截请求,把你的提交包装成一个 “Change(审核任务)”
  2. 生成一个审核页面链接(如 http://gerrit.example.com/c/12345),你和 Reviewer 可以在网页端评论、打分。
  3. 只有审核通过(+2 分)并点击“Submit”后,代码才会真正合并到远程 master 分支。

三、深入:refs/for/ 到底是什么?

1. 引用(Ref)的底层逻辑

Git 的分支本质上是 “引用(Ref)” —— 一个指向某次提交的指针。

  • 普通分支:refs/heads/main(本地分支) → 远程分支是 refs/remotes/origin/main
  • Gerrit 的审核分支:refs/for/main → 这不是一个真正的“分支”,而是 Gerrit 定义的 “审核队列”
2. 推送时的“隐秘逻辑”

当你执行:

git push aosp master:refs/for/master

Git 实际做的是:

  1. 把本地 master 分支的提交,推送到远程的 refs/for/master 引用。
  2. Gerrit 检测到 refs/for/ 开头的引用,触发 “审核流程”
    • 创建一个新的 Change(审核任务)。
    • 把你的提交作为这个 Change 的“初始补丁集(Patch Set)”。
    • 等待 Reviewer 处理。

四、实操:从“本地提交”到“Gerrit 审核”的完整流程

假设你已经完成了本地修改、addcommit,现在要把代码推送到 Gerrit 审核:

步骤 1:确认远程仓库名

先查看远程仓库配置:

git remote -v

输出示例:

aosp  http://gerrit.example.com/platform/Customer.git (fetch)
aosp  http://gerrit.example.com/platform/Customer.git (push)

这里的 aosp 就是远程名(对应你之前的 git push aosp ...)。

步骤 2:推送至 refs/for/

假设你的本地分支是 master,目标远程分支也是 master,推送命令为:

git push aosp master:refs/for/master

如果本地分支是 my-feature,目标远程分支是 feature-branch

git push aosp my-feature:refs/for/feature-branch
步骤 3:处理推送结果

推送成功后,终端会输出类似内容:

remote: SUCCESS
remote: http://gerrit.example.com/c/12345  # 这是你的审核任务链接!

打开这个链接,你就能看到自己的代码审核页面,等待 Reviewer 批准即可。

五、常见问题 & 解决方案

1. 推送时提示“权限被拒”?
  • 原因:你没有 aosp 远程仓库的写权限。
  • 解决:联系项目管理员,申请对应的推送权限(通常需要加入项目的“Contributors”组)。
2. 推送后看不到审核任务?
  • 原因:可能推送的分支名错误(如 refs/for/xxx 不存在)。
  • 解决:检查目标分支是否存在(可询问同事或查看仓库分支列表),确保 refs/for/ 后的分支名与远程一致。
3. 想修改已推送的代码?

Gerrit 的设计是 “审核未通过时,可更新补丁集”

  1. 本地修改代码,addcommit --amend(修改上次提交)。
  2. 重新推送:git push aosp master:refs/for/master
  3. Gerrit 会自动把新提交作为 “新的补丁集(Patch Set)”,附加到原来的审核任务下。

六、总结:Gerrit 的“审核思维”

用一句话概括:Gerrit 的 refs/for/ 是“审核流程”的入口,直接推送是“绕过审核”的违规操作

记住这个核心逻辑,你就能理解所有基于 Gerrit 的项目(如 AOSP、LineageOS、CyanogenMod 等)的提交规则。

下次再遇到“为什么必须加 refs/for/”,你可以自信地回答:因为代码世界里,“审核”是质量的护城河,而 refs/for/ 是通往护城河的唯一桥梁

延伸阅读

希望这篇指南能帮你彻底搞懂 Gerrit 的提交逻辑!如果觉得有用,欢迎分享给更多被“refs/for/”困扰的开发者~

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐