79

I am new to Git and have a fairly large project that I want to push to a remote repo (Repo B) on Github. The original project was on Github as well but from a different repo (Repo A). I have to make some changes to files from Repo A before I can setup the project up on Repo B. I have setup the remotes, ssh keys etc. and I run into an issue when pushing the codebase to Repo B.

I get the following error all the time:

$ 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

I changed the following settings in my local gitconfig

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

... and ran git gc (which I see reorganized the packs so that each pack stayed within the packsize of 1GB). This did not work and I get the error seen above.

I tried to lower the size of each pack as well ....

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

... and ran git gc (which I see reorganized the packs so that each pack stayed within the packsize of 500MB). This did not work either and I ran into the same error.

I am not sure of what Github's default packsize limits are (if any). The account is a micro account if that matters.

seekinganswers
  • 791
  • 1
  • 5
  • 3
  • Are you on a Unix/Linux machine? If you are, type `du -sk .` into your working repository and add that to your question. [GitHub doesn't have a limit](https://help.github.com/articles/what-is-my-disk-quota), but I'm curious as to how large your binaries are. – Makoto Feb 28 '13 at 03:51
  • Consider [this answer](https://stackoverflow.com/a/64565533/) which discusses the use of `http.postBuffer`, etc. – Asclepius Oct 28 '20 at 11:36

4 Answers4

58

The packsize limit does not affect git protocol commands (your push).

From git-config under pack.packSizeLimit:

The maximum size of a pack. This setting only affects packing to a file when repacking, i.e. the git:// protocol is unaffected.

When executing a push git will always create exactly one pack no matter the size!

To fix this use two (or more) pushes:

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

These pushes will all have smaller packs and will succeed.

onionjake
  • 3,905
  • 27
  • 46
  • 19
    Great answer - but I ran into the issue described at http://stackoverflow.com/q/28417845/1304104. Basically since the remote was clean and did not have any branch setup, I had to be more specific, and the first push was of the following form: `git push remoteB :refs/heads/master` – mbargiel Jun 29 '15 at 19:59
  • 11
    push every 500 commits (reverse does not blend with skip): `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` – rurban Mar 18 '17 at 11:49
  • 3
    What if my initial commit causes this issue? My repo is 30GB in total size. – Martin Braun Oct 14 '20 at 20:02
  • I have @MartinBraun same question. This is in the initial commit / upload of the repo from my local server? – Tamer Ziady Oct 16 '20 at 09:48
  • @TamerZiady I found a solution that works for me. I use `git add` only on a few files / folders and then I do `git commit -m "Initial commit of "` and `git push -u origin master --verbose`. I repeat this process until all my files are uploaded. I use SSH to have a better connection. If you don't use any passphrase on your SSH pubkey, you can build yourself a script to run in background. When a `git push` fails, the next `git push` should catch up, again. You need a little luck on your side, but you will make it to your goal, eventually. – Martin Braun Oct 17 '20 at 15:17
  • @MartinBraun A repo with 30G sounds like a bad idea in general. It sounds like the very large files should go to some CDN-like solution, with GitHub, LFS is your friend, as it serves exactly that purpose. Check the information to see how versioning works with LFS, though. – Pavel Šimerda Jan 17 '21 at 14:46
  • 5
    @PavelŠimerda I know that this is not the best solution after all, but sometimes you need to find quick solutions for problems that you didn't create in the first place. Everything costs money and time plus my client is aware that this is an inconvenience that he needs to deal with, now. Thanks, nonetheless. And to clarify things, there are no "large" files in my repo, it's just massive of small files. I'm talking about over 300 sub projects that are too hard to split, because they are held together by software wrapper that has full integration with those sub projects. Splitting costs too much – Martin Braun Jan 17 '21 at 15:36
  • 2
    What if I want to push a bare repository using --mirror? – Ed Randall Jan 23 '21 at 08:49
  • So there is no solution if it's a single large file, then? Like 600 MB and the limit is 500 MB? – clel Jan 09 '23 at 17:28
50

As onionjake noted in his answer, the pack.packSizeLimit setting does not affect pushes. As he suggested, this can often be addressed by using multiple pushes with fewer commits each. rurban posted a comment on how to automatically push chunks of 500 commits. Following is a modified version of his comment, generalized to work correctly regardless of whether the branch on the remote does not exist or exists and contains some of the commits already. I also added the --first-parent argument to the git log calls to prevent errors when the repository contains multiple root commits. I also made some tweaks to improve effiency, and added an additional call to git push to push the final (partial) batch of commits:

# 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
Guillaume Jacquenot
  • 11,217
  • 6
  • 43
  • 49
Daniel Harding
  • 1,186
  • 11
  • 14
  • seq: needs positive increment – 0x11901 Jan 08 '19 at 09:09
  • 0x11901 - mind to explain? The code uses seq with a negative increment because we want git log to skip successively fewer and fewer commits in order to push each batch. – Daniel Harding Jan 16 '19 at 14:52
  • 4
    @0x11901 You're probably using macOS, right? `seq` bundled with macOS is slightly different. You can install `seq` from GNU core utils using Homebrew: `brew install coreutils`. Then replace `seq` by `gseq` in the script. – muenchdo Sep 06 '19 at 12:51
  • Hi @DanielHarding, what script is this? Could you help me to run this scripts on Mac OS? – llch Nov 13 '19 at 08:17
  • Hi @llch, unfortunately I don't have access to a macOS machine. But perhaps the above comment by muenchdo can help get things working for you. – Daniel Harding Nov 15 '19 at 18:42
  • It doesn't work for me. I'm getting a `error: failed to push some refs` – Gabrielizalo Jun 13 '20 at 15:35
  • 2
    I don't think this will work on a bare repository created using "--mirror"? (Trying to migrate between servers, having stripped out old and large files using git filter-repo). Now looking at https://stackoverflow.com/questions/40652831/how-do-i-push-a-git-mirror-in-chunks – Ed Randall Jan 23 '21 at 08:54
  • 1
    @EdRandall it may very well not work on bare repositories. I just looked back at the commands and don't see any which should require a worktree, but I've never tested it on a bare repo. – Daniel Harding Jan 24 '21 at 19:58
  • Thanks a lot. It's sad that developers of git haven't provided a solution like that – user1476860 Jul 06 '22 at 13:38
7

Well, limit commit count (for example 500) in each push is helpful in most time. But it can not resolve the error caused by a single large commit.

If a single large commit exceeds the limit size of git server, it won't help by limiting commit count(even to 1).

To fix a single large commit:

  1. if this commit contains multiple files, it can be resolved by create sub-commits and merge commits.
  2. if it's a single large file, then there's no good solution.

To fix a single large commit with multiple files ( say 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

banyudu
  • 1,046
  • 9
  • 19
  • i had a single large merge commit from a subtree folder repo and this resolved the issue. thanks! – harish Jun 27 '22 at 14:22
4

Here is a solution from @DanielHarding that you can put into your .gitconfig and then invoke it with git partial-push origin branchname (where origin is your desired remote)

[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'"

What it basically does is take the range of the commits it needs to push and then goes and pushes then one by one. It can take quite some time, but on the end it will do the job - automatically.

Here is above oneliner with some spacing for easier readability:

[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'
    "
Crt Mori
  • 242
  • 4
  • 11