kojiro
kojiro

Reputation: 77137

What happens when you create a git branch and then remove the previous commit?

I created a new file (with quite a bit of code and hard work) and committed it to the master branch of my git repo. A moment later I realized that I probably should've created a branch for the file. I thought I could revoke the commit from master and commit again on a branch, but I think I did things in a silly order. For some reason I created the branch first, then went to revoke the commit, using is it possible to revoke commits? as a guide.

Here's essentially what I did:

echo "Lots of code and hard work" > newfile.txt
git commit -m "thoughtless commit" newfile.txt
# Oh wait, I should've branched!
git branch thoughtful
git checkout thoughtful
# Hmm, I didn't mean for that last commit to be in master
git checkout master
# get the previous commit's hash
oldhead=$(git log | awk 'BEGIN{commitcount=0; } /^commit/{ if (commitcount++ == 1) { print $2 }; }')
git reset --hard "$oldhead"
git checkout thoughtful

So now I've apparently trashed a commit on master (indeed, 'newfile.txt' no longer exists on master), but it still exists in thoughtful (and I guess reflog).

Would someone be so kind as to explain what I did here, and why no commit on thoughtful is required?

Upvotes: 2

Views: 399

Answers (2)

user229044
user229044

Reputation: 239362

When you created your branch thoughtful, it started from the current state of master, which already contained the commit. From that point onwards, the two branches are unrelated. Whatever you do to master, it won't affect thoughtful. The commit that introduces the file will still be part of thoughtful regardless.

You should try git log thoughtful and git log master to review the history of commits. On thoughtful you'll still see the commit you tried to remove from master.

A more complete breakdown follows

Assuming we're currently on master...

echo "Lots of code and hard work" > newfile.txt
git commit -m "thoughtless commit" newfile.txt

You've created a commit on master, containing newfile.txt.

# Oh wait, I should've branched!
git branch thoughtful

You've created a branch thoughtful, which is at the current state master, but is otherwise completely unrelated to master. The branch's starting point is master, because you didn't explicitly specify a starting point and master is currently checked out.

git checkout thoughtful

Now you're on thoughtful; you don't have to checkout new branches to make them real, this step has absolutely no effect since you do the following:

# Hmm, I didn't mean for that last commit to be in master
git checkout master

Now you're on master.

# get the previous commit's hash
oldhead=$(git log | awk 'BEGIN{commitcount=0; } /^commit/{ if (commitcount++ == 1) { print $2 }; }')

You've jumped through a ton of hoops to get the previous commit, when you could have just used master~ or HEAD~. That is how you get the "previous" commit, not by inspecting git log. In the future, to undo the last commit, please use git reset --hard HEAD~.

git reset --hard "$oldhead"

You've changed the commit master points to. Now it's whatever commit the above command discovered, presumably the commit before newfile.txt was introduced. Using --hard causes Git to update the working directory as well, so newfile.txt goes away; had you omitted --hard, newfile.txt would still be in the directory, but listed as a new file in git status.

git checkout thoughtful

You've gone back to thoughtful, which contains newfile.txt, updating your working directory and reintroducing newfile.txt.

Upvotes: 3

manojlds
manojlds

Reputation: 301297

The basis to understanding this is that you have to visualize branches as pointers to a commit which is the HEAD of the branch. And every commit points to one or more of its parents. A branch ( master etc.) is a pointer. No more, no less.

When you created thoughtful, you created another pointer to the same commit as master. Then you moved the master pointer to the previous commit. You are not removing a commit or something like that, you moved the master pointer to the previous commit. And your thoughtful branch / pointer is still pointing to the commit you want. You are in the state you wanted to be in!

PS: To get old HEAD, use git reflog or ref names like HEAD@{1}. Also, in this case, a HEAD~ would give you the right one as well.

Upvotes: 1

Related Questions