Github 远程推送包大小超限

82
我刚开始使用Git,有一个相当大的项目想要推送到Github上的远程仓库(Repo B)。原始项目也在Github上,但来自另一个仓库(Repo A)。在将项目设置到Repo B之前,我必须对来自Repo A的文件进行一些更改。我已经设置了远程仓库、ssh密钥等等,但在向Repo B推送代码库时遇到了问题。
我一直收到以下错误提示:
$ git push <remote_repo_name> master
Enter passphrase for key '/c/ssh/.ssh/id_rsa':
Counting objects: 146106, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (35519/35519), done.
fatal: pack exceeds maximum allowed size00 GiB | 154 KiB/s
fatal: sha1 file '<stdout>' write error: Invalid arguments
error: failed to push some refs to 'git@github.com:<repo>.git

我在本地的gitconfig文件中更改了以下设置

git config pack.packSizeLimit 1g
git config pack.windowMemory 1g

我运行了git gc(我看到它重新组织了包,使每个包保持在1GB的大小限制内),但这并没有起作用,我收到了上面看到的错误。

我也尝试降低每个包的大小....

git config pack.packSizeLimit 500m
git config pack.windowMemory 500m

我运行了git gc(我看到它重新组织了包,使每个包保持在500MB的包大小内)。但这也没有起作用,我遇到了同样的错误。

我不确定Github的默认包大小限制是多少(如果有的话)。如果有影响的话,该账户是微型账户。


你正在使用Unix/Linux机器吗?如果是的话,请在您的工作目录中键入“du -sk .”,并将其添加到您的问题中。GitHub没有限制,但我很想知道您的二进制文件有多大。 - Makoto
请考虑此答案,其中讨论了http.postBuffer的使用等问题。 - Asclepius
4个回答

58

包大小限制不会影响git协议命令(您的推送)。

来自pack.packSizeLimit下的git-config:

打包的最大大小。此设置仅在重新打包时将打包写入文件受到影响,即git://协议不受影响

执行推送时,git将始终创建一个包,无论其大小!

要解决此问题,请使用两个(或更多)推送:

git push remoteB <some previous commit on master>:master
...
git push remoteB <some previous commit after the last one>:master
git push remoteB master

这些推动都会有更小的包装,并且会成功。


19
回答很好,但我遇到了在https://dev59.com/Ll4c5IYBdhLWcg3wAWTv描述的问题。基本上由于远程是干净的且没有任何分支设置,我必须更具体,第一次推送采用以下形式:git push remoteB <主分支上的某个先前提交>:refs/heads/master - mbargiel
11
每500次提交就推送一次(跳过不混合反向):max=$(git log --oneline|wc -l); for i in $(seq $max -500 1); do echo $i; g=$(git log --reverse --oneline --skip $i -n1|perl -alne'print $F[0]'); git push gh $g:refs/heads/master; done注:该代码用于 Git 版本控制系统,意为将最新的代码提交推送到指定分支。 - rurban
3
如果我的初始提交引起了这个问题怎么办?我的存储库总大小为30GB。 - Martin Braun
5
@PavelŠimerda 我知道这毕竟不是最好的解决方案,但有时候你需要为那些起初并非由你创造出来的问题找到快速解决方案。所有事情都需要花费金钱和时间,而且我的客户意识到这是他现在需要处理的一个不便之处。还是谢谢您。为了澄清问题,我的仓库里没有“大”的文件,只是数量巨大的小文件。我是指超过300个子项目,它们很难拆分,因为它们被软件包装器紧密地集成在一起。拆分的代价太高了。 - Martin Braun
2
如果我想使用--mirror推送一个裸仓库怎么办? - Ed Randall
显示剩余5条评论

51

正如onionjake在他的答案中指出的那样,pack.packSizeLimit设置不影响推送。正如他建议的那样,这通常可以通过将较少的提交拆分为多个推送来解决。rurban在如何自动推送500个提交块的评论中发布了一条评论。以下是他评论的修改版本,可以正确地处理远程分支不存在或已包含某些提交的情况。我还添加了--first-parent参数到git log调用中,以防止存储库包含多个根提交时出现错误。我还进行了一些调整以提高效率,并添加了对git push的额外调用来推送最后(部分)批次的提交:

# Adjust the following variables as necessary
REMOTE=origin
BRANCH=$(git rev-parse --abbrev-ref HEAD)
BATCH_SIZE=500

# check if the branch exists on the remote
if git show-ref --quiet --verify refs/remotes/$REMOTE/$BRANCH; then
    # if so, only push the commits that are not on the remote already
    range=$REMOTE/$BRANCH..HEAD
else
    # else push all the commits
    range=HEAD
fi
# count the number of commits to push
n=$(git log --first-parent --format=format:x $range | wc -l)

# push each batch
for i in $(seq $n -$BATCH_SIZE 1); do
    # get the hash of the commit to push
    h=$(git log --first-parent --reverse --format=format:%H --skip $i -n1)
    echo "Pushing $h..."
    git push $REMOTE ${h}:refs/heads/$BRANCH
done
# push the final partial batch
git push $REMOTE HEAD:refs/heads/$BRANCH

0x11901 - 有解释吗?代码使用带负增量的seq,因为我们希望git log跳过逐渐减少的提交以便推送每个批次。 - Daniel Harding
4
你可能正在使用 macOS,对吗?macOS 中附带的 seq 命令略有不同。你可以使用 Homebrew 安装 GNU 核心工具中的 seqbrew install coreutils。然后在脚本中将 seq 替换为 gseq。请注意不要改变原来的意思。 - muenchdo
2
我认为这对使用“--mirror”创建的裸仓库将无法奏效?(试图在服务器之间迁移,使用git filter-repo剥离旧的和大文件)。现在看 https://dev59.com/PJzha4cB1Zd3GeqPHJOe - Ed Randall
1
@EdRandall,它很可能在裸仓库上无法正常工作。我刚刚回顾了一下命令,没有看到任何需要工作树的命令,但我从未在裸仓库上测试过它。 - Daniel Harding
非常感谢。很遗憾,Git的开发者们没有提供这样的解决方案。 - user1476860
显示剩余4条评论

7

通常情况下,限制每次 push 的提交计数(例如 500 次)对大部分情况都有帮助。但是它不能解决由单个大型提交引起的错误。

如果一个单独的大型提交超过了 git 服务器的限制大小,那么限制提交计数(即使为1)也无济于事。

修复单个大型提交的方法:

  1. 如果此提交包含多个文件,可以通过创建子提交和合并提交来解决。
  2. 如果它是一个单独的大文件,则没有好的解决方案。

要修复具有多个文件的单个大型提交(例如 file1、file2、...、file10)

git checkout -b tmp SINGLE_LARGE_COMMIT^
git add file1 file2 file3 file4  # add a sub-class of files inside SINGLE_LARGE_COMMIT
git commit -m 'sub-commit'
git push origin tmp
git merge master  # or any other branch which contains SINGLE_LARGE_COMMIT
git push origin tmp
git checkout master
git push origin master # success


我从一个子树文件夹仓库中得到了一个单一的大合并提交,这解决了问题。谢谢! - harish

6

这里有一个来自 @DanielHarding 的解决方案,你可以将其放入你的 .gitconfig 文件中,然后使用 git partial-push origin branchname 命令调用它(其中 origin 是你想要推送到的远程地址)。

[alias]
    partial-push = "!sh -c 'REMOTE=$0;BRANCH=$1;BATCH_SIZE=100; if git show-ref --quiet --verify refs/remotes/$REMOTE/$BRANCH; then range=$REMOTE/$BRANCH..HEAD; else range=HEAD; fi; n=$(git log --first-parent --format=format:x $range | wc -l); echo "Have to push $n packages in range of $range"; for i in $(seq $n -$BATCH_SIZE 1); do h=$(git log --first-parent --reverse --format=format:%H --skip $i -n1);  echo "Pushing $h..."; git push $REMOTE ${h}:refs/heads/$BRANCH; done; git push $REMOTE HEAD:refs/heads/$BRANCH'"

它的基本功能是获取需要推送的提交范围,然后逐个进行推送。这可能需要一些时间,但最终会自动完成任务。

以下是上述单行内容,添加了一些间距以便更容易阅读:

[alias]
    partial-push = "!sh -c 
        'REMOTE=$0;BRANCH=$1;BATCH_SIZE=100;
        if git show-ref --quiet --verify refs/remotes/$REMOTE/$BRANCH; then
            range=$REMOTE/$BRANCH..HEAD;
        else
            range=HEAD;
        fi;
        n=$(git log --first-parent --format=format:x $range | wc -l);
        echo "Have to push $n packages in range of $range";
        for i in $(seq $n -$BATCH_SIZE 1); do
            h=$(git log --first-parent --reverse --format=format:%H --skip $i -n1);
            echo "Pushing $h...";
            git push $REMOTE ${h}:refs/heads/$BRANCH;
        done;
        git push $REMOTE HEAD:refs/heads/$BRANCH'
    "

工作得很顺利!谢谢! - backpackerhh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接