git reset
git reset is one of the commands I use less frequently, which means it is easier to forget when and how to use it to achieve the desired effect.
Let's say I have just commited something and then I decided that what I commited should not be commited. Basically, I want to get rid of the very last commit, no matter the consequences.
$ mkdir repo $ cd repo $ git init Initialized empty Git repository in /Users/mczenko/Projects/learning/test_git/repo/.git/ $ echo "file1" > file1.txt $ git add file1.txt $ git commit -am 'Initial commit.' [master (root-commit) c9859f4] Initial commit. 1 file changed, 1 insertion(+) create mode 100644 file1.txt $ git log --oneline c9859f4 Initial commit.
Now, I add another file and I commit it to the repository:
$ echo "file2" > file2.txt $ git add file2.txt $ git commit -am "Added file2.txt" [master 11d160b] Added file2.txt 1 file changed, 1 insertion(+) create mode 100644 file2.txt $ git log --oneline 11d160b Added file2.txt c9859f4 Initial commit.
This will be our invariant, from which we will investigate the impact of variuos forms of git reset.
git reset --hard
Let's say I decided that I actually do not need file2.txt:
$ git reset --hard c9859f4 HEAD is now at c9859f4 Initial commit. $ git log --oneline c9859f4 Initial commit. $ ls file1.txt
If after that you realized that there was somathing important in file2.txt and you want to get it back, there is a good news: git actually did not remove your file:
$ git reflog c9859f4 HEAD@{0}: reset: moving to c9859f4 11d160b HEAD@{1}: commit: Added file2.txt c9859f4 HEAD@{2}: commit (initial): Initial commit. $ git checkout -b recovered 11d160b Switched to a new branch 'recovered' $ ls file1.txt file2.txt $ cat file2.txt file2 $ git checkout master Switched to branch 'master' $ git merge recovered Updating c9859f4..11d160b Fast-forward file2.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 file2.txt $ git branch -d recovered Deleted branch recovered (was 11d160b). $ git reflog 11d160b HEAD@{0}: merge recovered: Fast-forward c9859f4 HEAD@{1}: checkout: moving from recovered to master 11d160b HEAD@{2}: checkout: moving from master to recovered c9859f4 HEAD@{3}: reset: moving to c9859f4 11d160b HEAD@{4}: commit: Added file2.txt c9859f4 HEAD@{5}: commit (initial): Initial commit.
We see that git reflog records a lot of useful information about the repository, not only the commits.
What's intersting here is that the recovered commit preserves its original commit id:
Original:
$ git log --oneline 11d160b Added file2.txt c9859f4 Initial commit.
Recovered
$ git log --oneline 11d160b Added file2.txt c9859f4 Initial commit.
We are now back with the original content. Let's try another version of git reset: soft reset.
git reset --soft
git reset --soft leaves the content in the staged area. Handy if you simply want to revise last commit:
$ git reset --soft c9859f4 $ ls file1.txt file2.txt $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: file2.txt
I use git reset --soft when I want to move the last commit to a new branch:
$ git reset HEAD . $ git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) file2.txt
Now I can easily move the changes I've done to a new branch:
$ git checkout -b new_branch Switched to a new branch 'new_branch' $ git status On branch new_branch Untracked files: (use "git add <file>..." to include in what will be committed) file2.txt nothing added to commit but untracked files present (use "git add" to track)
We can now commit file2.txt and then merge it with master:
$ git add file2.txt $ git status On branch new_branch Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: file2.txt $ git commit -m "file2.txt added again." [new_branch 14a6c4b] file2.txt added again. 1 file changed, 1 insertion(+) create mode 100644 file2.txt $ git status On branch new_branch nothing to commit, working directory clean $ git log --oneline 14a6c4b file2.txt added again. c9859f4 Initial commit. $ git checkout master Switched to branch 'master' $ git merge new_branch Updating c9859f4..14a6c4b Fast-forward file2.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 file2.txt $ git log --oneline 14a6c4b file2.txt added again. c9859f4 Initial commit.
Of course, in this case the new commit has new commit id.
git reset
Finally, what does simple git reset do?
$ git reset c9859f4 $ git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) file2.txt nothing added to commit but untracked files present (use "git add" to track)
As we see, ordinary git reset marks the changes from the last commit as untracked without going through the staging area.
Do you see a mistake in this post, or you think something should written in a less ambiguous way? Please let me know. It is not my intention to have anyone learing gits the wrong way because of my incompetence.
External references:
[1] reflog, your safety net








