Every time a file has been staged, Git offers helpful instructions in the event you needed to unstage a file:
(use "git reset HEAD <file>..." to unstage)
However the decent Git Tutorials by Atlassian simply say:
git reset <file>
This seems more straightforward, so why the difference?
The git reset HEAD~2 command moves the current branch backward by two commits, effectively removing the two snapshots we just created from the project history. Remember that this kind of reset should only be used on unpublished commits.
git reset --soft , which will keep your files, and stage all changes back automatically. git reset --hard , which will completely destroy any changes and remove them from the local directory. Only use this if you know what you're doing.
The easiest way to undo the last Git commit is to execute the “git reset” command with the “–soft” option that will preserve changes done to your files. You have to specify the commit to undo which is “HEAD~1” in this case. The last commit will be removed from your Git history.
When using git reset --soft HEAD~1 you will remove the last commit from the current branch, but the file changes will stay in your working tree. Also the changes will stay on your index, so following with a git commit will create a commit with the exact same changes as the commit you "removed" before.
No difference (from git reset
man page) in term of default parameter:
The
<tree-ish>/<commit>
defaults toHEAD
in all forms.
That message initially did not include HEAD: commit 3c1eb9c, Jan. 2007, git 1.5.0-rc1, but since the default is not always known, the help message makes it clear to which commit you are supposed to reset.
HEAD
appears in commit 367c988, Nov. 2007, Git 1.5.4:
# On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage)
torek points out an actual difference in the comments:
By specifying
HEAD
, you guarantee that the first word afterHEAD
is taken as a path name.
For instance, suppose you rungit reset zorg
. Iszorg
a tree-ish, such as a tag name, or is it a path name,./zorg
?
Git's answer is: it's a tree-ish ifgit rev-parse
can turn it into a tree ID, otherwise it's a path.
You can either writegit reset -- zorg
orgit reset HEAD zorg
to make sure that git treats it as a path.
See more on the double hyphen syntax ( --
) in "Deleting a badly named git branch".
The OP skube adds in the comments:
As an aside, they do suggest it for discarding changes in working directory
(i.egit checkout -- <file>
).
It just seems inconsistent withgit reset HEAD <file>
.
While git reset
man page clearly indicates the lack of tree-ish in git reset <tree-ish> -- <paths>
means HEAD, it is not so for git checkout <tree-ish> -- <paths>
.
git checkout <tree-ish> -- <pathspec>
When
<paths>
are given,git checkout
does not switch branches.
It updates the named paths in the working tree from the index file or from a named<tree-ish>
(most often a commit).
That means git checkout -- path
will override the working tree with what has already been staged (git add
'ed).
While git reset -- PATH
(being the mixed form of git reset) will reset the index with what HEAD contains (effectively un-staging what was added)
git reset
and git checkout
don't use the same default, and:
git reset <tree-ish> <file>
: HEAD
.git reset HEAD <file>
;git checkout
: it is the index.git checkout -- file
.The --
has to be used in the git checkout
case, since there is only one parameter, and it needs to be clear that parameter represents files.
Note that git checkout HEAD files
is different: torek mentions in the comments
git checkout HEAD path
copies from theHEAD
commit (the tree-ish) to the index and then on to the working dir.
Note: with Git 2.23+, August 2019, you might use git restore
instead
See the examples:
To restore a file in the index to match the version in HEAD (this is the same as using
git-reset
)$ git restore --staged hello.c
man page:
git restore --staged hello.c
does not specify a source, and restore the index only (--staged
): it does so (by default) using HEAD as source.
By default, the restore sources for working tree and the index are the index and HEAD respectively.
--source
could be used to specify a commit as the restore source.
Other examples:
You can restore both the index and the working tree (this the same as using
git-checkout
)$ git restore --source=HEAD --staged --worktree hello.c
or the short form which is more practical but less readable:
$ git restore -s@ -SW hello.c
git restore
is a more natural command name, and has no ambiguity.
By default, git reset
is equivalent to git reset HEAD
Quoting the man page (my emphasis):
git-reset - Reset current HEAD to the specified state.
git reset [-q] [<tree-ish>] [--] <paths>… git reset (--patch | -p) [<tree-ish>] [--] [<paths>…] git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
In the first and second form, copy entries from <tree-ish> to the index. In the third form, set the current branch head (HEAD) to <commit>, optionally modifying index and working tree to match. The <tree-ish>/<commit> defaults to HEAD in all forms.
[...]
git reset [-q] [<tree-ish>] [--] <paths>…
This form resets the index entries for all <paths> to their state at <tree-ish>. (It does not affect the working tree or the current branch.)
This means that
git reset <paths>
is the opposite ofgit add <paths>
.
From this you see that there's no actual difference in behavior.
This seems more straightforward, so why the difference?
Since they're both the same, you might as well use the shortest version of the two.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With