remove single git reflog entry by hash/id

843 views Asked by At

I committed sensitive data to a single file on my main branch. I never pushed it. I can change HEAD to point to the previous commit with git reset HEAD^, but the commit is still hanging around as git reflog shows.

How can I use the reflog hash/ID, to COMPLETELY remove/delete that commit?

2

There are 2 answers

0
TTT On

To remove a specific reflog entry, you can use:

git reflog delete HEAD@{X} # set X to the entry number listed in reflog

This is documented here. For removing all reflog history, or history older than a certain date, see this question and answer.

Note, this removes the reflog entry only. For more information about completely removing the commit, see torek's answer.

1
torek On

You can't.1 There is no Git command that removes commits by hash ID or reflog entry.

You can however rest assured that if you use:

git push origin somebranch

and the set of commits reachable by traversing somebranch's tip commit on backwards does not reach any of the reflog-entry-only commits, git push won't send those commits (the unwanted ones). It will send only the commits listed by git rev-list somebranch, minus any commits that the receiving Git already has in the repository to which you're pushing. So the fact that you still have the commits is not important.

The other things you can do—useful for the paranoid, a group in which I count myself sometimes—include:

  • Clone your clone. The resulting clone won't have the commits, so you can now safely run, in that clone:

    git remote add github ssh://[email protected]/myname/myrepo.git
    git push github origin/somebranch:somebranch
    

    to send the commits from your new clone's origin/somebranch (cloned from your old clone's somebranch) to your GitHub repository over at github (assuming your final target repository here is on GitHub; use other names and URLs as appropriate).

  • Use git reflog expire --expire-unreachable=all2 or git reflog delete --rewrite --updateref to delete specific, or all unreachable, entries from some (specified) reflog or, with --all, all reflogs. Note, however, that this does not delete the commit.

  • Once the reflog entries are deleted, run git gc --prune=now. Be sure nothing else is active in the repository while this runs. If you have packed objects in a .keep-kept file, though, this won't get rid of them (but also note that .keep is something you would have had to manually create, so you probably do not have keep-files).

To verify that the commits are really gone, use git show <hash-id> or git cat-file -t <hash-id> for each hash ID. Note that you will need to save the hash IDs somewhere, since there won't be any in-Git references to them (that's what allows git gc to remove them).


1If—this is a strong-ish condition but it may be met in your case—the commit has never been packed, you may be able to use rm .git/objects/xx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx where the xxs are the hash ID, split up into first-two and remaining hexadecimal characters. That's not a Git command and you have to be very careful with this; it's a good idea to copy or otherwise back up the repository first. The reflog entries will still exist: they'll just become useless in that Git will barf up an error if you try to use them.

2This all should logically be now, and I believe now works, but it's what's listed in the documentation.