You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何配置Git以保留所有推送至服务器的提交、树、Blob和标签,同时仍允许通过git repack优化存储?

如何配置Git以保留所有推送至服务器的提交、树、Blob和标签,同时仍允许通过git repack优化存储?

这个需求我太懂了!管理上千个仓库,部分还有百万级提交,既要保住所有推上来的东西不丢,又得让git repack正常优化存储,还怕搞一堆refs把ls-remote搞炸——完全是生产环境里的真实痛点,我之前在维护类似规模的Git集群时踩过不少坑,给你分享几个靠谱的方案:

方案一:用隐藏的永久引用锁死所有对象(最可靠)

Git的GC机制只会删除没有任何引用指向的对象——不管是分支、标签,还是你自己建的自定义引用,只要有个ref指着它,GC就动不了它。那我们的思路就是:每次推送后,把所有新提交(包括标签)都塞进一个隐藏的引用空间,既不会被ls-remote显示,又能给每个对象上"保险"。

具体操作步骤

  1. 添加服务器端推送钩子
    在服务器端仓库的post-receive钩子(Gitea里可以在仓库「设置-钩子」中添加服务器端钩子)里写脚本,每次推送后自动给新增的提交创建隐藏引用:

    #!/bin/bash
    # 处理本次推送的所有ref更新
    while read oldrev newrev refname; do
        # 跳过删除ref的操作
        if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
            continue
        fi
    
        # 提取本次推送新增的所有提交哈希
        git rev-list $oldrev..$newrev | while read commit_hash; do
            # 创建隐藏引用:refs/keep/commits/[哈希]
            git update-ref refs/keep/commits/$commit_hash $commit_hash
        done
    
        # 如果推送的是标签,同步到隐藏空间
        if [[ $refname == refs/tags/* ]]; then
            tag_name=$(basename $refname)
            git update-ref refs/keep/tags/$tag_name $newrev
        fi
    done
    exit 0
    

    (不用给Blob和树单独加引用,因为提交会引用树,树会引用Blob,只要保住提交,底下的对象自然不会丢)

  2. 配置Git隐藏这些引用
    在仓库的.git/config里添加以下配置,让git ls-remote不显示这些隐藏引用:

    [transfer]
        hideRefs = refs/keep/
    

    这样别人用git ls-remote时,完全看不到这些refs/keep/下的引用,不会污染输出,完美解决你担心的ls-remote膨胀问题。

  3. 保留基础GC配置
    继续保留gc.pruneExpire = never,现在加上隐藏引用的双重保险,哪怕Gitea跑GC,也绝对删不掉任何有引用的对象。

方案优势

  • 100%可靠:所有推送的提交都有专属引用,GC碰不了
  • 无副作用:隐藏引用不会影响ls-remotegit branch等日常命令
  • 低开销:每次推送只处理新增提交,对于百万提交的仓库,首次初始化后日常开销可以忽略

方案二:拆分GC逻辑,只做打包不做清理(更简单)

如果你觉得写钩子脚本太折腾,还有个更轻量化的配置方案——直接把Git的GC拆成两半:允许它做repack优化存储,但彻底阻止它删除任何对象。

具体操作步骤

  1. 修改仓库Git配置
    编辑仓库的.git/config,改成以下内容:

    [gc]
        pruneExpire = forever  # 哪怕无引用对象也永远不删
        auto = 0  # 禁用Git自动GC
    [repack]
        useDelta = true  # 保留delta压缩(repack优化核心)
        packedGitLimit = 512m  # 可根据你的存储容量调整
        packedGitWindowSize = 100m
    
  2. 接管GC触发逻辑
    去Gitea的app.ini配置文件里,找到[git]部分的GCInterval,把它改成0或者直接注释掉,禁止Gitea自动跑git gc。然后自己写个定时任务(比如用Cron),定期给所有仓库跑:

    git repack -a -d -f
    

    这个命令只会把所有松散对象重新打包成更高效的压缩包,删除旧的无用打包文件,但绝对不会删除任何对象——包括无引用的。

方案优势

  • 配置简单:不用写钩子脚本,改几个配置就行
  • 完全可控:自己掌握GC节奏,避免Gitea误操作
  • 不影响存储优化:repack的所有压缩、打包优化都能正常运行

针对Gitea的额外注意事项

  • 一定要检查Gitea的app.ini配置,确保它不会强制触发git gc——比如[git]下的GCInterval要设为0,或者直接关闭自动GC功能
  • 对于上千个仓库的批量配置,可以用脚本遍历所有仓库路径,批量修改.git/config和添加钩子脚本,比如用find命令配合xargs批量操作

最后总结

  • 追求绝对可靠选方案一:隐藏引用+钩子的组合,哪怕Gitea乱搞GC也不怕,是生产环境的首选
  • 图省事选方案二:拆分GC逻辑,用定时任务跑repack,适合不想折腾脚本的场景
  • 不管用哪个方案,git repack都能正常运行——它只是做存储打包优化,和对象保留完全不冲突,放心用就行!

火山引擎 最新活动