78

I did a commit and reverted with

git revert HEAD^

just git log

➜  git:(master) git log
commit 45a0b1371e4705c4f875141232d7a97351f0ed8b
Author: Daniel Palacio <danpal@gmail.com>
Date:   Tue Jan 17 16:32:15 2012 -0800

    Production explanation

But if I do git log --all it still show up. I need to remove it from the history as it has sensitive information

git log --all
commit 5d44355080500ee6518f157c084f519da47b9391
Author: Daniel Palacio
Date:   Tue Jan 17 16:40:48 2012 -0800

    This commit has to be reset

commit 45a0b1371e4705c4f875141232d7a97351f0ed8b
Author: Daniel Palacio 
Date:   Tue Jan 17 16:32:15 2012 -0800

    Production explanation

How do I remove the commit 5d44355080500ee6518f157c084f519da47b9391 from the history too?

Tim Randall
  • 4,040
  • 1
  • 17
  • 39
daniel
  • 9,732
  • 7
  • 42
  • 57
  • possible duplicate of [Git undo last commit](http://stackoverflow.com/questions/927358/git-undo-last-commit) – Adrian Cornish Jan 18 '12 at 01:16
  • @AdrianCornish: That's a good start, but it doesn't help with the "sensitive information" bit. – Lily Ballard Jan 18 '12 at 01:29
  • @KevinBallard - a git reset --hard will? Assuming it has not been pushed anywhere - if its been pushed all bets are off – Adrian Cornish Jan 18 '12 at 01:31
  • @AdrianCornish: Yes, but also assuming that he's ok with the sensitive information staying in his local copy. Which he *should* be, but I can't speak for him. – Lily Ballard Jan 18 '12 at 01:33
  • Why would a hard reset leave the data after DB cleanup? – Adrian Cornish Jan 18 '12 at 01:36
  • @AdrianCornish: It wouldn't, but it takes about 1.5 months after you make a commit unreachable before it actually gets deleted from disk (and that's assuming regular garbage collections). – Lily Ballard Jan 18 '12 at 05:17
  • @KevinBallard 1.5 month is a pissing in the wind estimate really - common - really! – Adrian Cornish Jan 18 '12 at 05:25
  • @AdrianCornish: With the default settings, `git gc` will expire unreachable commits from reflogs after 1 month. Additionally, once unreachable from *any* ref (including reflogs), `git gc` will unpack objects into loose files. And again with the default settings, `git gc` will delete any unreachable loose objects once they're at least 2 weeks old. Therefore, if you `git gc` regularly, once a commit has been unreachable for 1 month + 2 weeks (e.g. 1.5 months), it can reasonably expect to have been deleted. – Lily Ballard Jan 18 '12 at 06:17
  • I couldn't get the sensitive info out unfortunatelly....I had to safe delete the repository completely. There just doesn't seem to be a "safe" way to remove a commit so oyu are sure if someone get's a copy there is just no way they can get a file you commited back. – daniel Jan 18 '12 at 06:30

5 Answers5

78

First off, git revert is the wrong command here. That creates a new commit that reverts an older one. That's not what you're asking for. Secondly, it looks like you want to revert HEAD instead of HEAD^.

If you haven't pushed this anywhere, you can use git reset --hard HEAD^ to throw away the latest commit (this also throws away any uncommitted changes, so be sure you don't have any you want to save). Assuming you're ok with the sensitive information being present in your copy and nobody else's, you're done. You can continue to work and a subsequent git push won't push your bad commit.

If that's not a safe assumption (though if not I'd love to hear why), then you need to expire your reflogs and force a garbage collection that collects all outstanding objects right now. You can do that with

git reflog expire --expire=now --expire-unreachable=now --all
git gc --prune=now

though this should only be done if you really absolutely need to do it.


If you have pushed your commit, then you're pretty much out of luck. You can do a force-push to revert it remotely (though only if the remote side allows that), but you can't delete the commit itself from the remote side's database, so anyone who has access to that repository can find it if they know what to look for.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 3
    Of course, if *you* have filesystem access to the remote, you can simply clean it the same way you did the local one. – Cascabel Jan 18 '12 at 04:29
  • the git reflog expire didn't seem to work, I could still access that commit if I went throught the history manually. Anyway I just decided to safe delete the whole repository out, next time I will just deploy from an encrypted drive so I don't have to go through this. – daniel Jan 18 '12 at 06:28
  • And you don't know how to do the same, only delete the commit history in the middle of history ? – Denis Denisov Nov 20 '14 at 05:12
  • 2
    @Denji Assuming you haven't pushed it anywhere, you can use `git rebase` to trim out the commit from your history. There's lots of documentation on doing this, including in the manpage itself. Look specifically for the `--onto` flag. Note that if you have merge commits that are more recent than the commit you're trying to delete, things get a lot more complicated. – Lily Ballard Nov 20 '14 at 23:50
  • Kevin Ballard, The problem, however, in the middle of the story there is a commit that we want to move the branch-1 from the middle of history in the other branch-2 (with inheritance master) [ 1 commit changes for branch-2, inheriting master ] – Denis Denisov Nov 21 '14 at 01:47
  • @Denji: I'm sorry, I can't understand what you're trying to say. – Lily Ballard Nov 22 '14 at 02:23
  • This did not work for me, but the command below which uses a `~` instead of `^` did. I am on Windows; is that the difference? – Topher Fangio May 27 '15 at 02:49
  • @TopherFangio `HEAD~` and `HEAD^` should be identical. The latter is the preferred form (the former is generally used when going back multiple steps, like `HEAD~3`). If `HEAD^` isn't working for you, maybe your shell is interpreting `^` oddly? I'm not familiar with Windows so I don't know what quirks there might be there. Try escaping it? – Lily Ballard May 27 '15 at 21:24
50

If you don't care about the commit, just do:

git reset --hard HEAD~

to blow away the commit.

If you want the changes to be in working directory, do:

git reset HEAD~

Depending on what you have done with git revert, you might have to change the above commands. Revert creates a new commit that reverts the commit you wanted to revert. So there will be two commits. You might have to do HEAD~2 to remove them both.

Note that, usually, revert is the safer way to, well, revert changes. But here, since you want to remove sensitive data, reset is the best approach.

manojlds
  • 290,304
  • 63
  • 469
  • 417
25

There is a nice solution here. To delete the last (top) commit you can do

git push [remote] +[bad_commit]^:[branch]

where [bad_commit] is the commit that [branch] currently points to, or if the [branch] is checked out locally, you can also do

git reset HEAD^ --hard
git push [remote] -f
dyrssen
  • 465
  • 4
  • 5
9

If you have not pushed the commit yet, you can just:

git reset --hard HEAD~2

(HEAD~2 to remove your original commit and your "revert" commit).

This will reset your current branch to the point in history before the commit you want to remove. If that commit is not in any other branch, it will not be pushed to your origin.

Bruno Oliveira
  • 13,694
  • 5
  • 43
  • 41
  • That doesn't help with the history part. The commit still in the history. – daniel Jan 18 '12 at 06:31
  • 2
    Not really, from this point on that commit will not be part of your current branch, and will be eventually deleted by git gc. If you want to actually remove the commit right now, you can use git gc to prune that now, or clone the repository again; when cloning a repository, only commits reachable by some branch are clone. – Bruno Oliveira Jan 18 '12 at 08:35
7

Here is a simple working solution that delete the last commit from remote:

  1. clone the repo and find the last 'good' commit (....c407)
$ git clone git@host:PROJ/myrepo.git 
$ cd myrepo 
$ git log --pretty=oneline

234987fed789de97232ababababcef3234958723 bad_commit
e4a2ec4a80ef63e1e2e694051d9eb3951b14c407 v4.3
2f116449144dbe46e7260d5dac2304803751b5c2 v4.2
  1. checkout the last good commit to a new temp branch
$ git checkout e4a2ec4a80ef63e1e2e694051d9eb3951b14c407
$ git checkout -b temp_branch
  1. replace the remote branch ( by delete and push the temp )
git push origin --delete dev_branch
git push origin temp_branch:dev_branch
chenchuk
  • 5,324
  • 4
  • 34
  • 41