0

I’m using Git 2.8 on Mac Sierra. I’m having an issue when I try and push my local repository to the remote. It is taking a very long time to do so. I think this is because it is trying to push a very big file that I must have inadvertently checked in. This is what happens when I try and push the content. It just hangs, and then I have to Ctrl + C out of it.

On branch master
Your branch is ahead of 'origin/master' by 62 commits.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean
Counting objects: 609, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (608/608), done.
Writing objects:  20% (124/609), 33.04 MiB | 1.03 MiB/s 
localhost:myproject nataliab$ Killed by signal 2.

How do I figure out what the file/files are that are causing the hang up? I have tried “git status”, but it tells me nothing …

localhost:myproject nataliab$ git status
On branch master
Your branch is ahead of 'origin/master' by 62 commits.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean

Thanks for any help, -

2 Answers2

0

Sounds like you've added the built binaries to your Git repository. In macOS you can set advanced filtering options while you're searching in Finder:

1.) Open up Finder and go to your repository

2.) Click Search or press Command+F, then change the Search location from "This Mac" to your actual folder

3.) Click on "Kind" filter and select "Other", then select "File Size" from the attribute list

4.) Click on the second filter and choose "is greater than"

5.) In the third space, enter the size to search for anything greater than (ex: 500KB, or 1MB) and choose either KB or MB as the final filter

enter image description here

balazs630
  • 3,421
  • 29
  • 46
  • Sorry but I'm not understanding how I use your answer to solve my question. I deleted the files long ago from my file system, but somehow they are in my git repo and it is trying to push things over. –  Dec 30 '16 at 00:02
  • Sorry, I didn't know that you've already deleted the files from the file system. It was not included in the original question, please update it. By the way you can try to remove the local repository just by dragging it to the Trash, then download it again with "git clone url-to-your-repo"; If the size remains the same (~33MB) then it must be a big file is still in your repo... :) – balazs630 Dec 30 '16 at 12:35
0

TL;DR: do an interactive rebase and replace your bad commits with better ones, or use the BFG (see How to remove/delete a large file from commit history in Git repository?).

Git pushes commits, not files

In Git, every commit is permanent and unchangeable. Moreover, the commits are the history: your latest commit points back to your second-latest commit, which points back to your third-latest, and so on, all the way back to the very first commit.

Now suppose you have committed a large file (such as a DVD image, 4.7 GB or so). Later, you delete the file and commit again.

When you go to git push the resulting commit, Git will—must—push not only the new commit, that deletes the file, but also the older commit that creates the file.

If Git failed to do this, you would not be able to recall the commit that contains the big file. The whole point of Git is to be able to recall every commit ever, so this would bethe opposite of version control. If Git only sent your latest, that would be uncontrolled unversion.

The files are a side effect of the commits. Git is all about commits. Files are just sort of an accidental bonus. Of course, the files are the purpose of the commits in the first place, but Git is still about commits.

What this means to you

Your big files are somewhere in the commits that you have, that they don't:

localhost:myproject nataliab$ git status
On branch master
Your branch is ahead of 'origin/master' by 62 commits.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean

Somewhere in these 62 (probably1) commits, you added some big files. Somewhere later, you presumably deleted them—but Git has to push all the commits.

Moreover, commits are permanent and unchangeable. You cannot change the older commits that add the file. This leaves only one possible solution: don't push these commits at all.

You might—and should, really—object. Presumably you do want to push (at least some of) these commits. But what I am telling you is that you don't want to push these commits. You want to push, instead, some slightly altered, better commits.


1"Probably", because origin/master is your Git's memory of what is under the name master on the other Git repository over at origin. This memory is not always up to date. You can run git fetch origin to pick up the latest commits from them, and thus have your Git update its memory. But if you are the only one using the other repository, your Git's memory will be accurate enough.


Copy the "bad" commits to new, different, "better" commits

Use git log to view the commits you are currently pushing:

$ git log --name-status origin/master..master

The --name-status argument tells Git to compare each commit to the previous commit (as usual), but then instead of showing a full git diff, just show which files were added, modified, and deleted.

You will have one commit that deletes some big file(s), and then an earlier commit that adds those same big file(s). Your job now is to correct the earlier commit, so that it does not add those files at all.

You can't actually change that earlier commit! But you can copy it, to a commit that is very similar: make a commit that is almost exactly the same, except that it doesn't add the big file(s). The new commit you make will have the same parent ID—this is how Git keeps track of which commits go before which other commits. It will have the same author (you), the same committer (you), the same log message, perhaps even the same date ... but it won't have the big file(s).

As a side effect of copying this particular bad commit to a new, better commit, you will be forced to copy every subsequent commit. The reason is that every commit records its previous (parent) commit ID, and your new-and-improved copy commit will have a different parent. So now you need to copy its child. The new "child copy" is the same as the previous child, except for two things: the parent ID, and the fact that the big file is gone.

This repeats for every commit up to the one that deletes the big file(s). Now, if that particular commit just deletes the big files, you can just discard that commit at this point: every copy you've been making so far lacks those files anyway, so there will be nothing to do. If that commit does something besides just deleting the big files, though, you will presumably want to keep the other parts of it.

After that point, you probably just want to copy each remaining commit, changing only its parent ID.

There are two Git commands that do this kind of commit copying: git filter-branch and git rebase -i. The former is somewhat difficult to use, so if you are going to stick with things that come with Git, I generally recommend using rebase, unless you have merge commits in those commits you need to copy (any such merges will show up in the git log output).

The instructions for using both filter-branch and rebase -i are in Greg Bacon's linked answer to the above-linked question.

Although I have never used BFG, its operation is reported to be much simpler. It does not do nearly as many things as filter-branch and interactive rebase, so it does not have such complicated controls. It still copies the commits, though.

Once the commits are all copied, you simply "forget" the bad ones

The way Git's branches work is that the name, master, simply points to the latest commit on branch master. Each commit points to its earlier counterpart. So once you have copied the "bad" commits to a "better" ones, your master will point to the newest copied commit. That commit points back to its parent, and so on, for however many—maybe 61, now—commits that it takes to get to where origin/master points.

The other Git repository, over on origin, already has that commit and every earlier commit. But now you can git push origin master, and your Git will call up their Git, find the commits to push, and start pushing—and the ones to push will be the new, better copies, not the originals.

(What happens to the originals? Eventually, they age out and get expired and deleted. If you want them back, you have at least 30 days to get them back.)

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • This is so much harder than I thought it would be. Let me start by asking how do a copy a commit? You said if I identify the commits that have the big files (which I have), I could copy them, but I can't tell what commadns I need to do that. –  Dec 30 '16 at 03:08
  • Git is very strong on not letting you lose anything you've committed. :-) Which is what makes this such a pain (it's much easier if there are fewer commits). Anyway, see that linked answer: you generally want `git rebase -i`, rebasing onto the commit *just before* the bad one. That brings up an editor with `pick` commands for each commit. Change the bad one from `pick` to `edit`, and ... well, see the linked answer ("interactive rebase" section). – torek Dec 30 '16 at 03:48
  • Thanks -- after following (I think) the instructions from teh link you sent I can now commit without things taking an extraordinary length of time. I seem to be unable to push to my remote repository so I'll investigate the causes of that before I come back to accept. –  Dec 30 '16 at 20:14