简介:Gitblit 1.9.3 是一款面向团队协作的开源Git仓库管理工具,适合需要自建代码托管服务、对仓库进行访问控制的中小型团队与开发者。这一版本基于Java Web构建,既能独立运行也可嵌入现有应用,支持仓库创建、克隆、推送、拉取及用户/用户组权限配置。压缩包共321个文件,约41.31MB,其中75个jar为程序核心与依赖库,35个html、24个js、23个css构成Web管理界面,105个gitignore提供多语言忽略模板,另有8个cmd和4个exe用于Windows下启动、安装服务及日常运维,11个groovy脚本可用于自定义钩子与扩展。包内还包括配置模板、样式资源与说明文档,目录结构完整清晰;已有352人学习下载,适合需要快速搭建内部Git服务、集中管控代码权限的开发者。通过Gitblit可直观管理仓库状态、跟踪项目进度,并为不同成员分配只读或读写权限,有效保障代码安全与完整。
1. gitblit 1.9.3 是什么:一台纯 Java 的 Git 私服,能撑起小团队的日常
做开发这几年,我在内网搭过不少 Git 服务端,最后还是把 gitblit 1.9.3 这个版本固化成了自己的私服首选。它不像 GitLab 那样要占一台 4G 内存的机器,也不像 Gitea 那样依赖单独编译的二进制;一个 war 包或 tar 包,配一份 properties 文件,就能把仓库、用户、权限、Web 浏览、SSH 推送、邮件通知全部管起来。Gitblit 特别适合两种人:一是公司内网不允许数据出网、又不想为 Git 服务单独买服务器的小团队;二是自己维护个人代码仓库、想省掉折腾数据库的开发者。1.9.3 是这一系列里我在生产环境用得最久的一个版本,稳定性、插件接口和 Tickets 流程都成熟了,直到今天仍能在 Windows 和 Linux 上无差别跑起来。
2. 用 JDK 8 跑通 gitblit-1.9.3:standalone 与 Tomcat 两种部署方式
2.1 为什么选 1.9.3,而不是更高版本的替代品
先交代选型逻辑。Gitblit 1.9.3 之后这个项目基本进入维护周期,新版本更多是修兼容性问题,而 1.9.3 是功能集最全的一个稳定点:有 HTTP/SSH 双协议、有自带 Web 钩子脚本引擎、有 Tickets 评审流程、有 LDAP 集成。对绝大多数内网场景来说,它覆盖了 Git 服务端该有的全部能力,又没有引入复杂的前后端分离架构。
运行环境方面,1.9.3 要求 JDK 8。用 JDK 11 启动也不是不行,但会遇到 JGit 访问文件系统时的一些权限检查差异,比如对某些符号链接处理得不一样。我的建议是:生产环境直接用 JDK 8,且别用高版本 JDK 自带的 jre 目录,避免后续 Groovy 脚本里反射调用出问题。如果你的机器只有 JDK 17,最省事的做法是再装一个 JDK 8,单独给 Gitblit 用,不要改系统全局变量。
2.2 standalone 模式:下载解压后的最小启动配置
Gitblit 的发布包分两类:一类是可直接运行的 zip/tar 包,一类是给 Tomcat 用的 war。我先说 standalone,因为它在内网最快见效。
拿到安装包之后解压,目录结构里最重要的两个文件:gitblit.jar负责主程序,data/gitblit.properties是全部配置的入口。首次启动前,你至少要改三个参数,见下面的配置片段:
# data/gitblit.properties git.repositoriesFolder = /data/gitblit/git server.httpPort = 8080 server.httpBindInterface = 0.0.0.0 server.httpsPort = 8443 server.storePassword = yourstorepass参数含义我拆开解释:
git.repositoriesFolder决定裸仓库落在哪个目录。不要把仓库目录放在 home 下的隐藏目录里,否则后面做磁盘空间排查很痛苦。server.httpPort是 HTTP 访问端口,默认是 8080;如果这台机器还要跑别的 Web 服务,改成 8090 也行,后面访问路径会带端口号。server.httpBindInterface写成0.0.0.0表示监听所有网卡;如果只希望内网某个网段访问,就写具体 IP。server.storePassword是用来加密 cookies 和内部 token 的主密钥,务必改掉默认值。
改完配置后启动,Linux 下我用的是这条命令:
cd /opt/gitblit nohup java -jar gitblit.jar --baseFolder data --logFile logs/gitblit.log &注意点有三个:--baseFolder data必须显式指定,因为有的脚本版本会省略这个参数导致找不到配置;--logFile指定日志路径,方便出问题时tail -f;用nohup ... &而不是直接java -jar跑,避免终端断开后服务跟着死掉。启动完看到日志里[HttpServer] Listening on http://0.0.0.0:8080就算成了,浏览器直接访问服务器 IP 加端口即可。
2.3 Tomcat 部署:把 gitblit 塞进已有中间件
很多公司不允许独立起 Java 进程,只开放了现有的 Tomcat 容器。这时候就要用 war 模式。Gitblit 的 war 包名字一般是类似gitblit-1.9.3.war,部署到 Tomcat 的 webapps 目录后,需要额外告诉它数据目录在哪,否则它会试图把数据写到 Tomcat 临时目录里,重启就丢。
Tomcat 下最常见的做法是设置一个 JVM 系统属性,在bin/catalina.sh或setenv.sh里写一行:
export CATALINA_OPTS="-Dgitblit.home=/opt/gitblit-data $CATALINA_OPTS"然后建立数据目录并复制配置模板:
mkdir -p /opt/gitblit-data cp /opt/gitblit/WEB-INF/data/gitblit.properties /opt/gitblit-data/这里要特别说明:war 包里的WEB-INF/data只是默认配置模板,实际生效目录由gitblit.home指向的路径决定。如果你不设置该属性,服务能起来,但好像每次重置数据都在变,实际上是因为 Tomcat 运行时解压的临时目录每次重启都换位置。所以先确认gitblit.home指向一个稳定目录,再谈后续配置。
war 模式与 standalone 的端口冲突是一个高频问题:Tomcat 自己监听 8080,而 gitblit.properties 里的server.httpPort还是 8080,两者会打架。在 Tomcat 部署时,把server.httpPort设为0,表示不在容器内再启动独立 HTTP 服务。
2.4 启动后先做三件健康检查
部署完别急着建仓库,先花两分钟做三件事:
第一,查看日志,确认没有Address already in use和Failed to bind这类字样。第二,用浏览器打开首页,用默认管理员账号admin / admin登录,马上到用户管理里改密码。第三,点开「版本」页,确认版本显示是 1.9.3,并且 Java 版本显示为 1.8.x。如果显示的是 11 或 17,说明启动命令里指向了错误 JDK,后续 Groovy 脚本会出莫名奇妙的报错。
做完这三步,这个 Gitblit 实例才算进入可管理状态,接下来才是真正把它变成团队协作工具的阶段。
3. 把 gitblit 变成团队 Git 服务器:认证、权限与仓库管理
3.1 认证方式选型:file、LDAP 以及它们的组合
Gitblit 默认的认证源是它自己的用户文件users.conf,所有密码以哈希形式存在这个 utf-8 文本文件里。这种方式对十人以下团队足够,但一旦账号超过几十个,管理员维护起来就会想骂人。这时 LDAP 集成才是正路。
Gitblit 的认证配置不像某些系统那样是“选一个”,它允许你组合多个 realm。用 LDAP 做登录校验,同时本地文件做管理员兜底,是内网最稳的组合:
realm.gitblit = true realm.ldap = true realm.ldap.server = ldap://192.168.1.10:389 realm.ldap.username = cn=gitadmin,dc=example,dc=com realm.ldap.password = xxxxx realm.ldap.basedn = dc=example,dc=com realm.ldap.accountPattern = (&(objectClass=person)(uid=${username}))这段配置里,realm.gitblit = true表示继续启用本地用户文件;realm.ldap = true则开启 LDAP 登录。注意accountPattern里必须保留${username}占位符,它是用户在登录框输入的用户名,Gitblit 会把${username}替换成实际输入值去 LDAP 查询。如果你公司账号的用户名属性和uid不一致,比如用的是sAMAccountName,就改成(sAMAccountName=${username})。
很多人在这里踩坑后以为是 Gitblit 不支持 LDAP,其实是username属性选错了。另外,用 LDAP 认证通过的用户,默认没有管理员权限,它会被视为普通用户,仓库权限还是按用户在 Gitblit 里的权限设置来算。所以我的组合习惯是:本地文件里只放一两个管理员账号,日常开发账号全部走 LDAP。
3.2 创建一个团队可用仓库的完整流程
现在用一个具体例子说清仓库创建和权限分配路径。假设要建一个名为order-system的仓库,参与人员分成 开发组 和 运维组。
登录管理员账号后,点击顶部「仓库」标签,新建仓库时填写路径order-system.git。这里有几个关键选项要做说明:
- 名称格式:通常带
.git后缀,这样 clone 地址更符合开发者习惯;不带后缀也能用,但前端显示会显得乱。 - 初始分支:
master还是main看团队规范,我统一用master避免老脚本硬编码出问题。 - 允许创建分支:勾选后开发才能在网页上直接建分支;内网流程严的话可以关掉。
- 提交时自动创建 tickets:不勾选,我们稍后单独开启评审流程。
仓库建好后,下一步在「用户」标签里创建本地用户或者等 LDAP 用户首次登录后自动出现。然后把用户拉进一个团队,团队再和仓库权限绑定。这一步的要点是:不要直接给用户发仓库权限,而是用团队套仓库,后续加人只改团队,不用一个个调整仓库。
给团队授权时,权限级别按以下矩阵理解:
| 权限项 | 含义 | 场景 |
|---|---|---|
| 查看 | 可以浏览代码和 clone | 只读成员 |
| 创建 | 可以创建分支并 push | 开发人员 |
| 重写 | 可以 force push 和删除分支 | 仓库维护者 |
| 删除 | 可以删仓库 | 管理员专用 |
3.3 SSH 与 HTTP 两种协议接入:clone 地址怎么配
Gitblit 默认同时启用 HTTP 和 SSH,但 SSH 需要先在网页里配置公钥。很多新人在这里犯迷糊:在服务器上生成了密钥,但没在 Gitblit 界面里添加公钥,于是git clone ssh://git@host:29418/order-system.git一直要求输入密码。
正确的接入顺序是:
- 客户端生成密钥
ssh-keygen -t rsa -b 4096 -C "yourname"。 - 在 Gitblit 网页右上角个人菜单里找到「SSH Keys」,粘贴公钥保存。
- 客户端再用
ssh://git@host:29418/order-system.gitclone。
SSH 端口为什么是 29418?因为 Gitblit 默认的 SSH 服务端口是 29418,它故意避开 22 以免和系统 SSH 冲突。如果机器上有防火墙,记得放行这个端口。
HTTP 方式则更简单:直接http://host:8080/git/order-system.git,用网页账号密码拉取。HTTP clone 的账号密码和登录界面的账号密码一致,但它不会走 LDAP 的 SSO,每次推送都要输入密码。不想每次输密码,就配 credential helper 或继续用 SSH。
3.4 用户、团队、仓库三个维度下的权限边界
用久了你会发现 Gitblit 的权限模型其实只有三层:用户、团队、仓库。一个用户既可以直接挂仓库权限,也可以挂在团队下间接获得权限,取的是并集。这意味着什么?如果一个用户从团队移除后还能 push,先查他是不是被单独授权过。这类问题出现频率不低。
另外,Gitblit 支持正则表达式形式的仓库权限设置。比如仓库路径写group/.*,可以一次性授权一个目录下的所有仓库。维护成本很低,但正则写错会放权,我建议先拿不重要的仓库测试完再上线。
现在权限这块已经清楚,下一步是让 Gitblit 更像一个协作平台,而不只是一个存储中心。也就是把推送、通知、持续集成串起来。
4. 串起整个研发流程:HTTP 推送、Groovy Hook 与自动化通知
4.1 为什么说 1.9.3 自带的 Groovy Hook 比外部轮询更省事
Gitblit 1.9.3 最让我看中的功能之一,就是它内置了一套 Groovy Hook 引擎。所谓 Hook,就是在 Git 的 pre-receive、post-receive 等事件发生时,由服务器自动跑一段 Groovy 脚本。相比 Jenkins 定时轮询仓库,Hook 是事件驱动,推送一发生立即触发,不会延迟,也不会空转。
这些脚本放在 Gitblit 数据目录下的groovy文件夹里。每个仓库可以单独启用哪些脚本,在仓库编辑页的「Hook」选项里勾选。没有勾选的脚本不会被触发,这是很多人以为配置了脚本但始终不执行的原因。
4.2 写一个 post-receive 邮件通知脚本
真正的团队协作离不开提交通知。与其让开发者自己去网页上看更新,不如让 Gitblit 每次收到 push 后把变更摘要发送到邮件列表。下面是一个可用的脚本:
// send-email.groovy import com.gitblit.utils.EmailUtils def subject = "[gitblit] ${repository.name} 收到推送" def body = new StringBuilder() body.append("用户: ${user.username}\n") body.append("仓库: ${repository.name}\n") body.append("分支: ${receivedRefs.collect { it.refName }.join(', ')}\n") body.append("提交:\n") commitHashes = receivedCommits.values().collect { it.id.name().substring(0, 8) + " " + it.shortMessage } body.append(commitHashes.join("\n")) EmailUtils.send("git@example.com", ["dev@example.com"], subject, body)这段脚本的逻辑是:在 post-receive 事件触发后,Gitblit 内置变量receivedRefs和receivedCommits已经保存了本次推送到引用和提交信息。脚本把它们整理成文本后,调用EmailUtils.send发送邮件。
参数上要注意几个细节:
repository.name是仓库路径名,比如order-system.git。receivedCommits.values()返回的是一个列表,每个 commit 对象有id和shortMessage两个常见属性可用。- 发件人邮箱建议用真实存在的邮箱,否则对方服务器会拒收。
- 脚本文件保存的编码必须是 UTF-8,包含中文时尤其如此;Windows 下用记事本保存很可能会变成 GBK,导致邮件标题乱码。
4.3 用 Hook 触发 Jenkins 构建:省掉轮询的写法
很多团队的 Jenkins 任务还停留在每分钟轮询 Git 仓库的模式。仓库一多,Jenkins master 压力大,而且从代码 push 到触发构建可能延迟好几十秒。Gitblit 的 Hook 可以做到即时触发。
// trigger-jenkins.groovy def postReceiveHook = { params -> def branch = params.repository.name + ":" + params.receivedRefs.collect { it.refName }.join(",") if (branch.contains("refs/heads/master")) { def url = new URL("http://jenkins.example.com/job/order-system/build?token=mybuildtoken") url.openConnection().with { conn -> conn.requestMethod = "GET" conn.connectTimeout = 5000 conn.readTimeout = 5000 conn.responseCode } } }.asType(groovy.lang.Closure)注意这里我把脚本主体包成了一个闭包。Gitblit 对脚本有两种执行方式,一种是用顶层命令,一种是注册闭包。实际生产中我习惯用闭包,因为闭包可以接收一个params参数,里面包含仓库名和引用信息,逻辑更干净。
在 Jenkins 端需要开启「触发远程构建」并配置一个 token,否则 URL 里带 token 也没用。触发 URL 的格式以你的 Jenkins 实际配置为准,上面例子中order-system是任务名,mybuildtoken要和 Jenkins 任务里的 token 一致。脚本里responseCode被读取,是为了让网络请求真正发送出去,否则某些 JVM 实现下请求会被延迟到 GC 时才执行。
4.4 仓库里中文文件名显示成八进制转义的处理
内网研发经常遇到这类问题:浏览器里看代码目录,中文文件全部显示为\346\241\200\346\241\257之类的转义串。这个现象的本质是 Git 默认core.quotepath为 true,会把非 ASCII 路径转义。Gitblit 走的是 JGit 实现,它默认行为同样不友好。
解决方法是给仓库写一个.gitattributes文件,强制 Git 按 UTF-8 对待文本路径和内容:
* text=auto *.java text *.md text这个文件随你的代码一起提交到仓库根目录后,重新 clone 的客户端就会按 UTF-8 规范处理文件名。对于已经存在的历史乱码提交,Gitblit 网页端显示时照样会转义,因为那是提交对象里存储的原始字节。最彻底的办法是迁移仓库时用git filter-repo重写历史,但除非仓库还很小,否则不建议动历史。
脚本和显示问题都处理完后,开发流程已经跑通。不过别急着推广给全组,因为接下来要聊的这几个坑,几乎每个新人在前两周都会踩一遍。
5. gitblit 1.9.3 部署必踩的五个坑:现象、原因与解决办法
5.1 启动后端口被占用,服务起来了但页面打不开
现象:启动日志没有报错,但浏览器访问 IP:8080 一直转圈;用netstat -anp | grep 8080才发现端口被一个叫java的进程占着,再一看,是另一个应用在监听。
原因:这个服务器上同时跑着别的 Java Web 应用。Gitblit standalone 默认端口就是 8080,和 Tomcat、Spring Boot 的默认端口高度撞车。
解决:把 gitblit.properties 里的server.httpPort改成 8090 或 8081,重启后再访问一次。改端口后记得所有相关文档和快捷方式里的地址都要同步更新。这里补充一个容易忽略的检查点:如果服务器上装了 Elasticsearch 或 Kafka,它们也可能占用 8080,排查时别只盯着 Tomcat。
5.2 部署到 Tomcat 后一直 404,找不到 gitblit 页面
现象:把 war 包放到 webapps 后访问http://host:8080/gitblit/,返回 404 或者空白页。
原因:大概率和 gitblit.home 指向的目录权限有关。Tomcat 对/opt/gitblit-data没有写权限,导致 Gitblit 初始化失败,页面自然就出不来。另外,如果你访问的是http://host:8080/而不是http://host:8080/gitblit/,也会 404,因为 war 包的解压目录名决定访问路径。
解决:先chown -R tomcat:tomcat /opt/gitblit-data把数据目录所有权交给 Tomcat 运行用户;然后确认访问路径带/gitblit/前缀。如果页面还是空白,打开 Tomcat 的logs/catalina.out,定位有没有java.security.AccessControlException,那是安全策略拦截了文件读写,需要在catalina.policy里放开。
5.3 管理员密码忘了:不必重装,改配置文件就能恢复
现象:登录页输入 admin 的密码一直报错,找不回旧密码,准备卸载重装。
原因:Gitblit 的密码存储在data/users.conf里,保存方式是加盐哈希。Web 页面没有“找回密码”功能,但账户文件本身是文本格式。
解决:先停掉 Gitblit 服务,打开data/users.conf,找到 admin 那一行,把密码字段改成一个已知哈希值。最简单的做法是用代码生成一个新的 SHA-256 哈希,或者直接删掉 admin 行里的 password 字段,重启后让 Gitblit 重新初始化默认密码。不过删字段有风险,我一般用备用脚本重新生成:
# 前提:有一个可用的 JDK 环境 cat <<'EOF' > /tmp/genpass.java import java.security.MessageDigest; public class genpass { public static void main(String[] args) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] d = md.digest("yournewpassword".getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte b : d) sb.append(String.format("%02x", b)); System.out.println(sb.toString()); } } EOF javac /tmp/genpass.java -d /tmp && java -cp /tmp genpass把输出的十六进制哈希填回 users.conf 中 admin 的 password 字段,重启 Gitblit,旧密码立即作废。这个办法比重装省太多事,建议截图存进团队 Wiki。
5.4 直接复制 git 目录做备份,恢复后仓库打不开
现象:从服务器cp -r了整个 git 目录到新机器,启动 Gitblit 后能看到仓库列表,但一点进仓库就报Invalid object或Pack corrupt。
原因:仓库处于运行状态时,直接复制目录会连index.lock、packed-refs的中间状态一起复制过去,JGit 一校验就崩。这个问题在 Windows 上更容易出现,因为文件锁机制和 Linux 不一样。
解决:备份 Gitblit 仓库前,先在网页端或命令行触发一次git gc,等所有并发进程都退出,再停服复制。实际上更稳的是在服务器上用git bundle做备份,只备份打包文件,不复制零碎对象:
cd /opt/gitblit/git/order-system.git git bundle create /backup/order-system.bundle --all恢复时,用git clone --bare order-system.bundle order-system.git重新生成裸仓库即可。千万别再图省事直接cp -r正在等待写入的仓库。
5.5 LDAP 开启后,本地账号全部无法登录
现象:配好 LDAP realm 后,原有本地账号突然都提示密码错误,连 admin 都进不去。
原因:如果配置里写了realm.gitblit = false,那本地 realm 就彻底关闭了,所有账号只能走 LDAP。很多教程为了让 LDAP 生效会把本地 realm 关掉,却没告诉你 admin 账号并不在 LDAP 里,结果就锁死了。
解决:改配置,把realm.gitblit设回true。这里的顺序也有讲究:Gitblit 的认证器是按配置顺序逐个校验的,如果 LDAP 在配置中排在前面,用户名和密码同时匹配时会先走 LDAP;本地 admin 因为 LDAP 里没有,会兜底到本地文件认证。所以安全且灵活的组合是realm.gitblit = true放在 LDAP 前面,然后用 3.1 里的方式给固定几个账号单独加“管理员”标记。
6. 用 Tickets 模式把 Gitblit 变成轻量评审平台:从裸仓库平滑迁移
6.1 普通仓库和 Tickets 模式的最大差异
很多团队用 Gitblit 当网盘用了几个月,还是每人直接 push master。想转变流程但不想引入太重的问题跟踪系统,Gitblit 的 Tickets 模式就是为这个场景设计的。一个开启 Tickets 的仓库,不再允许对受保护分支直接 push,而是要求开发者把改动作为 ticket 提交,由维护者在网页上评审、合并。
迁移成本比我预想的低:仓库本身的数据格式不变,只是在页面设置里勾选「使用 Tickets 功能」,再设置分支保护规则即可。开发者的 clone 地址不变,只是推送命令变成了git push origin HEAD:refs/for/master这类语法,需要同步更新团队文档。
6.2 具体开启步骤与 merge 权限设置
先在仓库页面把 Tickets 开关打开,然后在分支保护里把master设为“仅通过合并请求接受修改”。此时需要给评审人单独授予仓库的「重写」或特殊合并权限,否则大家会看到按钮灰掉。
容易出现的一个理解偏差是:Tickets 模式不等于禁止 push。如果你希望保留直接 push 的能力,可以在保护规则里不勾选强制校验,但这样一来 Tickets 就没有强制约束价值了。我实践下来的折中方案是:主干分支强制走 Tickets,release 分支留给维护者直接 push。
6.3 一个让我少吃亏的习惯,希望对你有帮助
最后说个从 Gitblit 里学到的运维习惯:我每次改动gitblit.properties之前,都会先复制一份带日期的备份,例如gitblit.properties.20250115。这个动作在三年的时间里救了我三次,有两次是改 LDAP 参数把整组人锁在门外,一次是调了 SSH 端口后半天没想起来对应的防火墙规则。改完配置先备份,然后再kill进程重启,已经是我的条件反射了。Gitblit 的配置读取不算复杂,但 parameters 之间的依赖关系藏在文档细节里,没有后悔药时就只能对着日志叹气。这套 1.9.3 方案只要你照着第 2 章的步骤部署、按第 5 章的坑预判,内网 Git 服务应该能安安稳稳跑上两三年,希望帮到你。
本文还有配套的精品资源,点击获取