Reputation: 77137
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
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.
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
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