4

I know about git reset (including --hard), but that doesn't completely remove the commit, it just adjusts the tree. I've explored git gc, but that seemingly never permanently removes a commit, either. I know about filter-branch, but that only removes files from commits, but not commits themselves.

There are a lot of stack overflow questions that circle around this topic, but I can't find a real answer to the question: How to really delete a local commit (or series of commits), so that it no longer exists anywhere in the database, as though it was never done at all? If it was a method limited to a branch tip, that would be fine.

EDIT: It was a mistake on my part. I was using gitk and used F5 to view the entire tree. But F5 does an "update" and shift-F5 does a "reload". And the deletes don't actually show up actually unless you fully reload by doing the shift-F5.

Nairebis
  • 283
  • 1
  • 8
  • Do a `git reset --hard origin/master` – Sid Mar 25 '16 at 17:21
  • Follow this article and perform the steps. https://help.github.com/articles/remove-sensitive-data/ – Som Bhattacharyya Mar 25 '16 at 17:22
  • 1
    First, origin/master is for remote repositories. Second, get reset --hard does NOT remove commits, contrary to popular belief. If you look at the entire repository tree, they'll still be there. – Nairebis Mar 25 '16 at 17:23
  • Som: That article is about filter-branch, which does not remove commits. It only removes files. – Nairebis Mar 25 '16 at 17:24
  • 2
    Possible duplicate of [How to permanently delete a commit from Git's history?](http://stackoverflow.com/questions/18010048/how-to-permanently-delete-a-commit-from-gits-history) – Andrew C Mar 25 '16 at 17:36
  • Ahh i am sorry. You could do the reflog, lookup the commit SHA and delete it from history there. Have a look here. http://gitready.com/intermediate/2009/02/09/reflog-your-safety-net.html – Som Bhattacharyya Mar 25 '16 at 17:38
  • Do you know `git rebase -i`? Would that help? – PerlDuck Mar 25 '16 at 17:42
  • Andrew: Nope, that's fliter-branch. "it won't remove those two commits, but it will rewrite history to get rid of the bulky files from your history" – Nairebis Mar 25 '16 at 17:51
  • Som: I've tried delete the reflog and then doing a git gc. The commit stays. Perl Dog: That doesn't do it, it just creates another branch (tried that too) – Nairebis Mar 25 '16 at 17:53

2 Answers2

3
  1. Make the commit unreachable from any ref or tag, using reset, rebase, or even filter-branch. (How to use filter-branch to remove the file from all branches).
  2. Expire the reflog so that the commit is not reachable from there. git reflog expire --expire=now --all
  3. git prune to remove unreachable objects from the database. (git gc --prune=all would also have done the trick, but git gc plain keeps loose objects that are younger than two weeks old).
  4. You can verify it's really gone by manually inspecting the git object store. A git object is stored according to its SHA1 hash. THe first two letters of the hash are a directory and the final 38 are the filename inside of that directory.

So if the SHA1 hash of your problem commit is 12345ABCDEF... then it will live at .git/objects/12/345ABCDEF...

To be really paranoid you can find out the SHA1 hash of the specific blob (file contents) that you're worried about and hash it with git hash-object <filename>, and then check the DB for that hash.

masonk
  • 9,176
  • 2
  • 47
  • 58
2

If your branch looks like this:

A   <-- HEAD
|
B
|
C   <-- to be removed
|
D

and you want to remove commit C, the following command might help you:

git rebase -i D     # if you have D's sha1 handy
# OR
git rebase -i C^    # parent of C (which is the same as D in this simple example)

Once issued, an editor pops up with all the commits starting from D's children, i.e. A, B, and C in a todo list. Simply remove the line with commit C in that editor window and save the file.

PerlDuck
  • 5,610
  • 3
  • 20
  • 39
  • That doesn't delete the original commits. They'll still be in there. I tried that exact scenario. The rebase just becomes another branch, but the old commits are still there if you look at the whole tree. – Nairebis Mar 25 '16 at 17:55
  • 2
    @Nairebis Where did you look? Gitk? Hit SHIFT-F5 (reload). You told about _local_ commits. Have you already pushed them? Then `push -f` – PerlDuck Mar 25 '16 at 17:58
  • GAH! You hit it exactly right... I was using Gitk and used F5 instead of shift-F5. I did a shift-F5 and they vanished. Dammit. – Nairebis Mar 25 '16 at 18:25
  • 1
    :-) Nice to hear. But be aware: The commits are still there but you cannot reach them easily if you don't know their SHA1s. They are subject to garbage collection. So run `git gc --prune=now` to get rid of all their traces (or wait for 30 days or so until they are auto-gc'ed). – PerlDuck Mar 25 '16 at 18:44