CodyBugstein
CodyBugstein

Reputation: 23362

How can I delete commits that are after the current HEAD?

In my Git repository, I created three commits in a row: commit1, commit2, and commit3.

I then realized I messed up in commit2 and commit3, and decided to go back to commit1. To do that, I ran

git checkout commit1

Now I am in commit1. How do I delete commit2 and commit3?

Upvotes: 3

Views: 951

Answers (4)

jub0bs
jub0bs

Reputation: 66422

Check your branch out and then reset it

Based on your description and under the assumption you were on some branch called mybranch before checking out commit1 (C1 in my graphs below), you must be in the following situation:

C1 [HEAD]
 \
  C2 -- C3 [mybranch]

Commits C2 and C3 still appear in the output of git log because they're still reachable from the mybranch refererence. Also, note that HEAD is detached. What you should do is...

  1. Reattach HEAD to mybranch, by running

    git checkout mybranch
    

    This should put you in the following situation:

    C1
     \
      C2 -- C3 [HEAD -> mybranch]
    
  2. Reset the mybranch branch to its tip's grandparent, by running

    git reset --hard mybranch~2
    

    That should put you in the following situation:

    C1 [HEAD -> mybranch]
    

Because commits C2 and C3 have now become unreachable (i.e. "deleted"), they're not shown on this last graph.


Why resetting without first reattaching the HEAD won't work

This may be a bit cheeky, but here is an explanation of why the other two answers won't work. As correctly pointed out by cmbuckley in his comment,

git reset resets the state of the current branch you’re on (so you’d need to be on the branch to do that). If you’ve checked out commit1, you’re probably not on a branch (detached HEAD state).

Since the OP (Imray) is in detached HEAD state, running git-reset before reattaching HEAD to the branch will not move the branch reference in question. Here is a toy example illustrating this.

# set things up
$ mkdir test
$ cd test
$ git init
Initialized empty Git repository in /Users/jubobs/Desktop/test/.git/

# create a first commit
$ touch README
$ git add .
$ git commit -m "add README"
[master (root-commit) 85137ba] add README
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README

# create a second commit
$ printf "foo\n" > README
$ git commit -am "write 'foo' in README"
[master 3948e84] write 'foo' in README
 1 file changed, 1 insertion(+)

# inspect the log
$ git log --graph --decorate --oneline --all
* 3948e84 (HEAD, master) write 'foo' in README
* 85137ba add README

# check out the second commit (which detaches the HEAD)
$ git checkout 3948e84
Note: checking out '3948e84'.
# (boilerplate stdout is omitted...)
HEAD is now at 3948e84... write 'foo' in README

# reset to the first commit (equivalent to 'git reset --hard 85137ba')
$ git reset --hard HEAD^
HEAD is now at 85137ba add README
$ git log --graph --decorate --oneline --all
* 3948e84 (master) write 'foo' in README
* 85137ba (HEAD) add README

Note that the git reset command moved HEAD to the initial commit, but did not move the master branch whatsoever. The second commit is not "deleted", because it's still reachable from master; it is therefore listed in the output of git log.

Upvotes: 3

Joseph K. Strauss
Joseph K. Strauss

Reputation: 4913

Force Your Branch to Current HEAD and Checkout Branch

git branch -f mybranch
git checkout -

Checkout Branch and Force Your Branch to Current HEAD

git checkout -
git reset --hard HEAD@{1}

The second option is particular advantageous because you do not need to type the name of your branh or the identity of your current commit. You could even make it an alias.

EDIT: This assumes that you have not been jumping around, and your most recent checkout was from your branch.

Upvotes: 1

Mark Fisher
Mark Fisher

Reputation: 9886

I'm going to assume you're in the master branch in your repo for naming purposes, but any branch will do. This can be thought of as simply a pointer to a commit object. You can also think of HEAD as another pointer, which you can move around with git checkout

commit1  ->  commit2  ->  commit3
                             ^
                             |
                           master

If you want to change your master pointer to be at commit1, then you need to issue a git reset command, as others have indicated.

git reset --hard commit1

This moves the master pointer in the above diagram to same place as the commit1 object.

Note, you aren't actually deleting the commit2 and commit3 objects, it's just that within git there's no branches pointing to them, so git is free to clean them up if it wants to, or you can force it by running a garbage collect with something like:

git gc --aggressive --prune

Until it is actively cleared from your repo, you can still checkout both commit2 and commit3, so despite you moving the master pointer back to commit1 (with git reset), you have to be careful if (say) you accidentally commit passwords to the repository and are trying to revert back - they will still be in your local repo until pruned.

Upvotes: 1

nitishagar
nitishagar

Reputation: 9413

You want to nuke commit commit3 (Assuming you are currently on commit3 - as HEAD). you can do following:

git reset --hard HEAD~1

The result is:

commit1 -> commit2 
              ↑
             HEAD

You can follow similar process for moving back to commit1 (i.e. git reset --hard HEAD~2).

Upvotes: 0

Related Questions