0

Question summary

I have tried git reset --hard <old_commit> but that doesn't help because I can still view commits made after <old_commit>, i.e. descendants of <old_commit> using the commands git show <newer_commit> or git log <newer_commit>.

I want to revert the state of the repo to an older commit such that there is absolutely no evidence or trace of any newer commits that are descendants of the commit I reverted to. Can this be done?

Detailed question

I will my explain my question after I explain how I have set up my repo to reproduce the situation.

First I create a new repo and make 4 commits.

Here are the steps to quickly create an example repo that demonstrates my question.

mkdir foo
cd foo
git init
echo a > a.txt; git add a.txt; git commit -m "Add a.txt"
echo b > b.txt; git add b.txt; git commit -m "Add b.txt"
echo c > c.txt; git add c.txt; git commit -m "Add c.txt"
echo d > d.txt; git add d.txt; git commit -m "Add d.txt"
git log

I can see all 4 commits in the log.

lone@debian:~/foo$ git log
commit ae39b5666628d87abda5b82dbf8ede093946ec6d
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:01:29 2015 +0530

    Add d.txt

commit cc1843682a5f73c382e902d3db50c06d7d738fb0
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:01:20 2015 +0530

    Add c.txt

commit 95ed593fc7ab8f23b0584d0f34a47aca28190f11
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:01:08 2015 +0530

    Add b.txt

commit 14bdb2d20c730a77fd413f6b577c1afa93c8d32d
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:00:58 2015 +0530

    Add a.txt

Now I realize that the 3rd and 4th commits were mistakes. They should never have been done. I don't want to share those commits with anyone. I haven't pushed these commits anywhere or not shared these commits with anyone by any means yet. I want to revert the state of the repo to what it used to be before c.txt was added. In other words, it should look like that the 3rd and 4th commit never happened at all.

This would have been easy if I had cloned the repo just after adding b.txt and kept that clone as a backup. The backup clone would be in the state that I desire now. But in reality, I haven't made a clone at that point and now I want to change the repo as if nothing ever happened after adding b.txt.

I tried hard reset to the 2nd commit, i.e. the commit where I added b.txt.

lone@debian:~/foo$ git reset --hard 95ed593fc7ab8f23b0584d0f34a47aca28190f11
HEAD is now at 95ed593 Add b.txt
lone@debian:~/foo$ git log
commit 95ed593fc7ab8f23b0584d0f34a47aca28190f11
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:01:08 2015 +0530

    Add b.txt

commit 14bdb2d20c730a77fd413f6b577c1afa93c8d32d
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:00:58 2015 +0530

    Add a.txt

But the 3rd and 4th commit are still accessible via their commit IDs. For example, here is how I can still see the 3rd commit where I added c.txt.

lone@debian:~/foo$ git show cc1843682a5f73c382e902d3db50c06d7d738fb0
commit cc1843682a5f73c382e902d3db50c06d7d738fb0
Author: Lone Learner <lone@example.com>
Date:   Thu Dec 10 19:01:20 2015 +0530

    Add c.txt

diff --git a/c.txt b/c.txt
new file mode 100644
index 0000000..f2ad6c7
--- /dev/null
+++ b/c.txt
@@ -0,0 +1 @@
+c

How can I change the repo to a state where it would look like the 3rd and 4th commit never happened? There should be absolutely no evidence of the commits I want to lose.

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • This should answer your question: http://stackoverflow.com/questions/4114095/revert-git-repo-to-a-previous-commit – TheEye Dec 10 '15 at 13:55
  • @TheEye Which one of the answers to the linked question answers my question here? I found only the `git reset` approach mentioned there relevant but as I explained in this question the `git reset --hard` method doesn't work for me because I can still access the old commits (that I wanted to lose) using `git show` or `git log`. – Lone Learner Dec 10 '15 at 13:58
  • git reset only throws away your local changes, you need something like git revert. Did you read through the proposed solutions? They are quite exhaustive. – TheEye Dec 10 '15 at 14:10
  • @TheEye I read all those solutions and I am aware of all those solutions. `git revert` definitely does not solve this problem because it does not rewrite history. The solution to my question would definitely require rewriting the history and rewrite in a manner that no trace of any newer commits are left. – Lone Learner Dec 10 '15 at 14:12
  • Dangerous works of course, maybe this helps: http://stackoverflow.com/questions/307828/completely-remove-file-from-all-git-repository-commit-history – TheEye Dec 10 '15 at 14:17
  • http://www.ducea.com/2012/02/07/howto-completely-remove-a-file-from-git-history/ – choroba Dec 10 '15 at 14:19

2 Answers2

2

You should perform the "git reset --hard" as mentionned previously by Joseph Silber. The commit that you do not want to see will eventually disappear if they are dangling commit when the garbage collector will run. If you want to force the clean up, use the command "git gc" (see git gc command and git prune command)

iclman
  • 1,218
  • 9
  • 21
  • I did `git reset --hard 95ed593fc7ab8f23b0584d0f34a47aca28190f11` followed by `git gc --prune=all` but I can still see `git show cc1843682a5f73c382e902d3db50c06d7d738fb0`. – Lone Learner Dec 10 '15 at 14:23
  • 2
    Try "git reflog expire --expire-unreachable=now --all" followed by a "git gc --prune=now" – iclman Dec 10 '15 at 14:27
  • Thanks! This worked. Do you know why my attempt to do `git gc --prune=all` did not work? – Lone Learner Dec 10 '15 at 14:32
  • That's because the objects are still referenced in the reflog. This is a safety net in Git so that the automatic pruning does not destroy objects too quickly. Normally, even though the objects were still referenced after a pure "git reset --hard" command, the commits would not appear anymore. That behaviour suits most people and is safer. – iclman Dec 10 '15 at 14:37
0

iclman's answer worked for me. I have marked it as the correct answer. For future reference, I am noting down the steps that worked for me after following his steps.

git reset --hard 95ed593fc7ab8f23b0584d0f34a47aca28190f11
git reflog expire --expire-unreachable=now --all
git gc --prune=now

Once this was done, trying to access any pruned commit led to bad object error. Example:

lone@debian:~/foo$ git show cc1843682a5f73c382e902d3db50c06d7d738fb0
fatal: bad object cc1843682a5f73c382e902d3db50c06d7d738fb0
Community
  • 1
  • 1
Lone Learner
  • 18,088
  • 20
  • 102
  • 200