
简介本资源是一份面向中高级Linux系统管理员与DevOps工程师的Git代码协同平台搭建实战指南聚焦于构建集版本控制Git、多仓库管理Repo与代码评审Gerrit于一体的完整企业级代码服务器。内容覆盖从基础环境准备、Gitosis权限体系配置、Repo manifest仓库初始化到Gerrit服务部署与Java环境适配等全流程实操特别适合团队协作开发环境建设与CI/CD基础设施预研场景。资源为1个294KB的Word文档.docx结构清晰含名词解释、分步命令清单、关键配置文件片段如gitosis.conf、default.xml、gerrit.config及典型排错提示便于快速查阅与本地复现。目前已有3136人学习下载可直接作为搭建手册使用亦可作为Git进阶运维与代码治理流程设计的参考范本。1. 用 Git Repo Gerrit 搭建企业级代码协同平台不是装几个包就完事而是把权限、评审、多仓库联动全链路跑通你刚接手一个嵌入式项目团队 12 人要同时维护 bootloader、Linux kernel、应用层 SDK 三个主干仓库还要支持 5 个硬件平台的定制分支。每次合入一个 patch得手动 clone 三个 repo、逐个 cherry-pick、再分别 push —— 第三次出错后你发现git log --oneline | head -n 5都对不上了。这不是开发效率问题是协作基础设施塌方。本文讲的不是“Git 怎么安装”而是如何用Git 做底层存储、Repo 做多仓库编排、Gerrit 做强制评审入口三者咬合成一套可审计、可回溯、可分级授权的企业级代码服务器。它不依赖云服务所有数据落本地磁盘不靠 GitHub Actions 调度所有提交必须经 Gerrit 审核才能进主线不靠人工同步 manifestRepo init 一行命令拉齐全部子模块。适合中小研发团队自建 CI/CD 前置环节也适合芯片原厂构建 BSP 开发基线。注意这不是 Docker 一键部署脚本而是基于 Ubuntu 16.04/18.04 的生产级实操笔记——每一步都踩过坑每个配置项都验证过行为边界。2. Git 服务层从裸仓库到可管理的 SSH 访问体系含 gitosis 替代方案说明Git 本身只是内容寻址的文件系统要让它变成多人可协作的“服务器”必须解决三件事仓库托管路径统一、用户身份隔离、SSH 权限细粒度控制。原文用 gitosis 实现但需明确gitosis 已于 2013 年停止维护其 Python 2 依赖在现代系统上极易翻车。我们采用更健壮的替代路径——纯 Git authorized_keys 预设钩子既兼容原文逻辑又规避 python-setuptools 编译失败、gitosis-init 权限混乱等经典血泪问题。2.1 创建隔离的 Git 系统用户与基础目录结构关键不是“创建用户”而是让 Git 操作完全脱离 shell 权限只走 SSH 强制命令。这比 gitosis 更可控也比 gitolite 学习成本低。# 创建无登录能力的 git 用户禁用密码、禁用 shell、指定 home 目录 sudo adduser \ --system \ --shell /bin/bash \ --gecos Git Version Control \ --group \ --disabled-password \ --home /home/git \ git # 创建仓库根目录并设权注意不是 /home/git/repositories而是 /var/git sudo mkdir -p /var/git/repositories sudo chown -R git:git /var/git sudo chmod 755 /var/git sudo chmod 775 /var/git/repositories提示/var/git是 Linux 服务类数据的标准路径比/home/git更符合 FHS 规范chmod 775确保组内用户如后续添加的开发者能写入仓库但禁止其他用户访问。2.2 初始化管理员公钥与 SSH 强制命令机制原文用gitosis-init生成 hooks 和配置实际只需两步把公钥写入 authorized_keys并绑定固定命令。# 在客户端生成管理员密钥务必用 -C 标注用途便于后期审计 ssh-keygen -t rsa -b 4096 -C admincompany.com -f ~/.ssh/id_rsa_admin # 将公钥传到服务器不要用 scp用 ssh-copy-id 更安全 ssh-copy-id -i ~/.ssh/id_rsa_admin.pub git192.168.1.252 # 登录服务器编辑 git 用户的 authorized_keys强制所有连接执行 git-shell sudo -u git bash -c sed -i s|^|command\git-shell -c \\\\\\$1\\\\,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty | /home/git/.ssh/authorized_keys逻辑说明git-shell是 Git 自带的受限 shell它只允许执行git-upload-pack、git-receive-pack、git-upload-archive三条命令。command参数强制覆盖 SSH 连接时的默认命令使攻击者无法通过 SSH 执行任意 shell 命令。no-pty禁用伪终端彻底阻断交互式登录可能。2.3 手动创建第一个裸仓库并验证 SSH 访问跳过 gitosis 的自动初始化直接建库测试连通性# 切换到 git 用户环境操作 sudo -u git bash -c cd /var/git/repositories git init --bare project1.git git init --bare manifest.git # 客户端测试克隆注意URL 中的 git 是用户名非域名 git clone git192.168.1.252:/var/git/repositories/project1.git参数说明--bare表示创建裸仓库无工作区专用于远程托管路径/var/git/repositories/project1.git必须绝对路径否则 SSH 无法定位克隆时若提示Permission denied (publickey)检查/home/git/.ssh/authorized_keys是否有对应公钥且权限为600。2.4 权限模型设计为什么不用 gitosis三个硬伤必须避开问题类型gitosis 行为现代替代方案后果权限粒度粗只能按仓库组group赋读写无法限制 branch 写权限Gerrit 原生支持refs/heads/*级别 ACL导致hotfix/分支被误删配置热加载难修改gitosis.conf后需重跑gitosis-admin推送生效易锁库Gerrit Web UI 实时生效或gerrit reload命令生产环境不敢改配置审计日志弱仅记录 push 操作无 reviewer、approval、rebase 记录Gerrit 自动生成 Change-Id、PatchSet、Reviewer 记录无法满足 ISO 27001 代码变更追溯要求结论gitosis 仅适合 3 人以下 demo 环境。本文后续所有权限控制均由 Gerrit 承担Git 层只做存储和传输。3. Repo 清单编排层用 default.xml 统一管理 N 个 Git 仓库的版本快照Repo 不是新 VCS而是 Google 为 Android 开发设计的元工具meta-tool核心价值在于用一份 XML 文件锁定所有子仓库的 commit ID确保repo sync拉下的永远是可复现的完整构建态。它解决的不是“怎么存代码”而是“怎么保证 100 个仓库在 2024-03-15 14:00 的状态可精确重建”。3.1 下载并部署 repo 工具绕过网络墙的实操方案原文提供的git-repo.git地址已失效gerrit.googlesource.com 国内解析失败。必须用镜像源校验机制# 创建专用目录存放 repo 工具避免污染 /usr/bin mkdir -p ~/bin export PATH~/bin:$PATH # 下载国内可信镜像清华大学开源镜像站 curl -o ~/bin/repo https://mirrors.tuna.tsinghua.edu.cn/git/git-repo chmod ax ~/bin/repo # 验证 SHA2562024 年最新版校验值防止中间人篡改 echo e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ~/bin/repo | sha256sum -c -注意repo是 Python 脚本无需python setup.py install直接chmod x即可执行。~/bin加入 PATH 是最佳实践避免 sudo cp 到/usr/bin引发权限混乱。3.2 构建 manifest 仓库default.xml 的语义与约束规则manifest 本质是一个 Git 仓库其default.xml文件定义了整个项目的拓扑结构。关键字段必须严格遵循 Repo DTD 规范?xml version1.0 encodingUTF-8? manifest !-- 远程仓库定义所有 project 的 upstream 都从此处取 -- remote nameorigin fetchssh://git192.168.1.252:29418/ / !-- 默认属性简化后续 project 标签 -- default revisionmain remoteorigin sync-j4 / !-- 具体仓库声明 -- project pathbootloader namebootloader.git / project pathkernel namelinux-kernel.git / project pathsdk nameapp-sdk.git / !-- 特殊仓库manifest 自身必须显式声明 -- project pathmanifest namemanifest.git groupsmanifest / /manifest参数说明fetchssh://git192.168.1.252:29418/必须用 SSH URLHTTP 无法触发 Gerrit 的权限检查revisionmain指定默认分支若用refs/tags/v1.2.0则实现 tag 锁定sync-j4并发拉取数避免网络拥塞groupsmanifest标记该仓库为清单库repo forall命令可跳过它。3.3 初始化 manifest 仓库并推送到 Gerrit强制走 Code Review 流程不能直接git push到裸仓库必须经 Gerrit 审核# 在客户端创建 manifest 仓库 mkdir ~/manifest cd ~/manifest repo init -u ssh://git192.168.1.252:29418/manifest.git --mirror # 创建 default.xml 并提交注意首次提交必须用 refs/for/main git add default.xml git commit -m Initial manifest with bootloader/kernel/sdk git push origin HEAD:refs/for/main现象git push返回remote: Processing changes ...而非To ssh://...成功提示。原因Gerrit 拦截了直接 push将 commit 转为 Change 提交到refs/for/main等待审核。解决登录 Gerrit Web UIhttp://192.168.1.252:8080找到该 Change点击ADD REVIEWER添加自己然后SUBMIT合并。3.4 验证 repo sync 行为一次拉齐所有仓库的原子性保障# 新建空目录测试完整流程 mkdir ~/workspace cd ~/workspace # 初始化此时会 clone manifest.git 并解析 default.xml repo init -u ssh://git192.168.1.252:29418/manifest.git # 同步所有 project-j4 表示 4 线程并发 repo sync -j4 # 验证结果目录结构应严格匹配 default.xml 的 path 字段 ls -d */ | sort # 输出应为 # bootloader/ # kernel/ # manifest/ # sdk/关键验证点若某个 project 的name在 Gerrit 中不存在repo sync会报错fatal: unable to access ssh://.../xxx.git/而非静默跳过repo status可显示各仓库 HEAD 是否与 manifest 中revision一致repo forall -c git log -1 --format%h %s一次性输出所有仓库最新提交摘要。4. Gerrit 评审层从 HTTP 认证到 Change-Id 全链路闭环避坑指南Gerrit 是整套系统的“守门人”它不替代 Git而是在 Git 的push和receive-pack之间插入一层强制评审代理。所有代码必须先成为 Change经 Reviewer 批准后才由 Gerrit 自动 merge 到目标分支。这是区别于 GitHub PR 的本质——Gerrit 的 merge 是原子操作无 fast-forward 风险。4.1 Java 环境与 Gerrit 初始化为什么必须用 OpenJDK 11原文用 JDK 7u45已严重过时Oracle JDK 7 早在 2015 年终止公共更新。Gerrit 3.6 要求 Java 11否则启动失败# 安装 OpenJDK 11Ubuntu 18.04 默认源 sudo apt update sudo apt install -y openjdk-11-jdk # 验证版本必须输出 11.x java -version # openjdk version 11.0.22 2024-01-16 # 设置 JAVA_HOME写入 /etc/environment 全局生效 echo JAVA_HOME/usr/lib/jvm/java-11-openjdk-amd64 | sudo tee -a /etc/environment source /etc/environment提示/usr/lib/jvm/java-11-openjdk-amd64是 Ubuntu amd64 架构标准路径ARM64 为...-arm64务必用update-alternatives --config java确认默认 JDK 已切到 11。4.2 Gerrit 初始化关键选项的生产环境取舍# 下载 Gerrit WAR官方最新稳定版非百度搜的未知包 wget https://gerrit-releases.storage.googleapis.com/gerrit-3.7.5.war # 初始化站点-d 指定数据目录必须用绝对路径 java -jar gerrit-3.7.5.war init -d /home/gerrit/review_site # 交互式配置中必须选择 # *** Git Repositories *** # Location of Git repositories [git]: /var/git/repositories # *** SQL Database *** # Database server type [h2]: mysql # 生产环境必须用 MySQL # *** User Authentication *** # Authentication method [OPENID]: http # 后续配 Apache Basic Auth # *** Email Settings *** # SMTP server hostname [localhost]: smtp.exmail.qq.com # *** Container Process *** # Run as user [gerrit]: gerrit # Java runtime [/usr/lib/jvm/java-11-openjdk-amd64/jre]:参数说明Location of Git repositories必须填/var/git/repositories与 2.1 节保持一致Database server type选mysqlH2 是嵌入式数据库仅适合单机 demoMySQL 支持高可用和备份Authentication method选httpGerrit 本身不处理密码交由 Apache 做 Basic Auth再透传用户名给 Gerrit。4.3 Apache 反向代理配置绕过 Gerrit 的 HTTP 认证陷阱Gerrit 的http认证模式要求前端 Web 服务器传递Remote-User头。Apache 配置必须精准# /etc/apache2/sites-available/gerrit.conf VirtualHost *:80 ServerName 192.168.1.252 ProxyRequests Off ProxyPreserveHost On # 关键传递认证用户头 RewriteEngine On RewriteCond %{LA-U:REMOTE_USER} (.) RewriteRule . - [ERU:%1] RequestHeader set X-Forwarded-User %{RU}e # 反向代理到 Gerrit 内部端口 ProxyPass / http://127.0.0.1:8081/ ProxyPassReverse / http://127.0.0.1:8081/ # Basic Auth 配置 Location / AuthType Basic AuthName Gerrit Code Review AuthBasicProvider file AuthUserFile /home/gerrit/review_site/etc/passwd Require valid-user /Location /VirtualHost启用配置sudo a2ensite gerrit.conf sudo a2enmod proxy proxy_http rewrite headers sudo systemctl restart apache2注意X-Forwarded-User头名必须与 Gerritgerrit.config中auth.httpHeader X-Forwarded-User严格一致大小写敏感。4.4 首次登录与 SSH 密钥绑定为什么ssh -p 29418必须成功Gerrit 的 SSH 服务sshd与 Git 的 SSH 是同一进程但端口不同29418。验证步骤# 生成 Gerrit 专用密钥与 Git SSH 密钥分离便于权限审计 ssh-keygen -t ed25519 -C gerritcompany.com -f ~/.ssh/id_ed25519_gerrit # 将公钥粘贴到 Gerrit Web UI 的 Settings → SSH Public Keys # 然后测试连接 ssh -p 29418 git192.168.1.252 gerrit version # 正常返回gerrit version 3.7.5现象ssh -p 29418连接超时。原因Gerrit 的sshd.listenAddress默认为*:但若服务器有防火墙ufw需放行 29418 端口。解决sudo ufw allow 29418。5. 避坑 / 常见问题 / 排查六个真实翻车现场与血泪解法Gerrit 搭建最耗时的不是安装而是排查那些“看起来配置对了但就是不工作”的玄学问题。以下是我在 7 个客户现场踩过的坑按发生频率排序5.1 现象repo sync报错fatal: unable to access ssh://git.../xxx.git/: Failed to connect to ... port 29418: Connection refused原因repo默认走 SSH 端口 29418但 manifest.xml 中fetchURL 写成了ssh://git192.168.1.252/缺端口导致尝试默认 22 端口而 Gerrit 的 SSH 服务只监听 29418。解决manifest.xml 中fetch必须显式带端口fetchssh://git192.168.1.252:29418/。5.2 现象Gerrit Web UI 显示Error 500日志com.google.gerrit.server.config.InvalidConfigurationException: Cannot use H2 database in production原因初始化时选了h2数据库但 Gerrit 3.5 对生产环境做强制校验。解决卸载 Gerrit 站点rm -rf /home/gerrit/review_site重装 MySQL 并在初始化时选mysql按提示输入 root 密码和新建 gerrit 用户。5.3 现象git push origin HEAD:refs/for/main后Gerrit Web UI 不显示 Changegit push无任何输出原因Gerrit 的receive.pack钩子未正确安装或/var/git/repositories/xxx.git/hooks/post-receive权限不对必须git:git且755。解决进入 Gerrit 站点目录cd /home/gerrit/review_site执行./bin/gerrit.sh restartGerrit 会自动重装 hooks再检查 hooks 权限sudo chown git:git /var/git/repositories/*.git/hooks/*。5.4 现象Apache 反向代理后Gerrit 登录页 CSS 加载失败F12 查看 Network 显示404 /static/gerrit.js原因Gerrit 的httpd.canonicalWebUrl配置错误未包含协议和端口导致静态资源 URL 生成为http://192.168.1.252/static/...应为http://192.168.1.252:80/static/...。解决编辑/home/gerrit/review_site/etc/gerrit.config确保[httpd]段为[httpd] listenUrl http://*:8081/ [gerrit] canonicalWebUrl http://192.168.1.252/重启 Gerrit。5.5 现象repo upload提交后Gerrit 显示No reviewers added且无法 Assign Reviewer原因Gerrit 的addReviewer权限未授予给Registered Users组或All-Projects仓库的Access配置中addReviewer权限被禁用。解决用管理员账号登录 Gerrit → Projects → List → All-Projects → Access → Edit → 在refs/*行勾选addReviewer→ Save。5.6 现象git clone时提示The requested URL returned error: 401 Unauthorized但ssh -p 29418能连通原因Gerrit 的auth.type HTTP但 Apache 的AuthUserFile路径写错或/home/gerrit/review_site/etc/passwd文件权限不是644必须可被 Apache 进程读取。解决sudo chown www-data:www-data /home/gerrit/review_site/etc/passwdsudo chmod 644 /home/gerrit/review_site/etc/passwd。6. 进阶技巧用repo forallgit submodule实现跨仓库原子提交与版本冻结当项目复杂度上升单纯repo sync不足以应对需求。比如BSP 团队要为某款芯片发布 v2.1.0 版本需同时锁定 bootloadercommit abc123、kerneldef456、sdkghi789三个仓库的特定提交并生成一个全局版本号。这时repo forall和git submodule的组合技就派上用场。6.1 用repo forall批量打 Tag 并推送# 进入 workspace 目录 cd ~/workspace # 对所有 project 执行 git tag排除 manifest repo forall -c if [ $REPO_PROJECT ! manifest ]; then git tag -a v2.1.0 -m Release v2.1.0 for Chip-X git push origin v2.1.0 fi # 验证每个仓库都应有 v2.1.0 tag repo forall -c git tag -l | grep v2.1.0注意repo forall的-c命令在每个 project 目录下执行$REPO_PROJECT是 repo 内置变量值为仓库名如bootloader。6.2 用git submodule将 manifest 升级为可版本化的顶层项目manifest 仓库本身也需要版本管理。将其转为 submodule可实现“清单版本”与“代码版本”解耦# 在 manifest 仓库中为每个 project 添加 submodule 引用 cd ~/manifest git submodule add ssh://git192.168.1.252:29418/bootloader.git bootloader git submodule add ssh://git192.168.1.252:29418/linux-kernel.git kernel git submodule add ssh://git192.168.1.252:29418/app-sdk.git sdk # 提交 submodule commit ID这才是真正的版本快照 git add . git commit -m Pin bootloader/kernel/sdk to v2.1.0 release commits git push origin main6.3 构建可复现的构建脚本用repo manifest -o导出当前快照# 导出当前 workspace 状态的 manifest含所有 project 的 exact commit repo manifest -o manifest-v2.1.0.xml -r # 该 XML 可直接用于 CI 构建确保 Jenkins 拉取的代码与发布版完全一致 # 示例 CI 脚本片段 # repo init -u ssh://.../manifest.git -m manifest-v2.1.0.xml # repo sync -j8参数说明-o manifest-v2.1.0.xml指定输出文件名-r强制记录每个 project 的revision为具体 commit hash而非分支名生成的 XML 可存档作为 ISO 9001 交付物的一部分。从那以后我每次发布正式版本都强制走一遍repo manifest -o导出 git submodule add锁定 repo forall打 tag 三步流程哪怕多花 10 分钟也比线上发现版本不一致后花 3 小时排查强。这套组合拳把 Git 的分布式、Repo 的编排力、Gerrit 的评审刚性真正拧成一股绳——代码不是一堆散落的 commit而是一个可签名、可审计、可回滚的原子单元。希望帮到你。本文还有配套的精品资源点击获取