I was following the interactive LearnGitBranching tutorial and came across two ways of moving around a branch. I want to be sure that I am not missing anything.
Is there a difference between:
git branch -f master master^
and, supposing HEAD
points to master
,
git reset HEAD^
And, if they do the same thing, why have the reset
command at all? Would it mean it's simply a shorthand for git branch -f
?
Thank you kindly!
As genisage noted in a comment, git reset
has multiple modes.
Ignoring all of the modes and what they do, the answer to the first question is: if HEAD
refers to master
and you move master
to another commit with git branch
, then yes, this achieves the same thing as indirectly moving master
with git reset
. But:
- We can't simply ignore the options to
reset
. - We also started the answer with "if
HEAD
refers tomaster
". What if it doesn't?
In particular, for point #1, the default mode for git reset
is --mixed
. The reset
command can, and in this case does, do more than just move the branch-tip. It can also update git's index (aka "staging area").
You can—and in fact, probably should—think of git's index / staging area as "what git is ready to commit the next time you run git commit
". When you git add
a file to make git pick up changes for the next commit, git reads the current work-tree version of that file, then writes that1 into the index.
What git reset
does (or can do, and does with --mixed
) is undo this kind of index-modification, by changing the index contents to match the commit you reset-to. That is, git reset --mixed HEAD^
not only backs the branch up one commit, but also resets all the index contents to the backed-up-by-one commit. The git branch
command does not touch the index.
You can make git reset
not touch the index, by using the --soft
option. In this case, it really does do the same thing as your git branch -f
—although, as noted in point #2, this only works if HEAD
refers to master
. If you want to do a soft reset without using git reset
, your first step is to find out what branch, if any, HEAD
points to. Only then can you update the branch (and then only if you're not in "detached HEAD" mode).
For completeness, it's worth noting here that git reset --hard
does even more than git reset --mixed
: it not only updates the branch-tip and resets the index, it also "resets" your work tree, making it look like the new target commit.
It's also worth adding that several common uses of git reset
deliberately only make use of some of its actions:
git reset --soft HEAD^
backs you up one commit, without changing the index or work-tree, so that a newgit commit
provides a new branch-tip with the same contents as the commit you just backed-up-over. This lets you change the commit message.2 This is exactly the same asgit commit --amend
, except that the latter is actually easier; so this usage is not common any more (it was how one did this beforegit commit
had the--amend
option).git reset --mixed -- <path>
"moves" you to your current commit—that is, it rewrites the current branch to point to where it already points, which is a no-op—but resets the index, without altering the work-tree. This lets you "undo" agit add
orgit rm --cached
.3 Usually it's just spelledgit reset <path>
, since--mixed
is the default.git reset --hard
orgit reset --hard <path>
again "moves" you not at all, but resets the index and restores the work-tree version.
The reset
command has several extra options (--merge
and --keep
; and also -p
) that don't quite fit this pattern. I'm just going to ignore them here, though, to keep this answer from getting way too long.
1Actually, git writes the file (or "blob") to the repository, rather than to the index. In the process of writing the blob, git also calculates the resulting SHA-1: the "true name" of the object. (All repository objects are found by their "true name" SHA-1s. The repository acts as a simple key-value store, with the key being the SHA-1, and the value being the object: the file aka "blob", or tree, or commit, or annotated-tag-object.) The SHA-1 goes into the index, along with the file's name and execute-permission bit. The index is later turned into one or more git "tree" objects at commit time, as needed; those tree objects contain the various file names, modse/execute-permissions, and SHA-1s.
2More precisely, you make a new, different commit with the corrected message. This is also what git commit --amend
does. The old (pre-amendment) commit is still in the repository, with its same SHA-1; the new commit, with its different message and different time-stamp, has a different SHA-1, even though it starts from the same index, and hence has the same tree attached.
3A regular (not --cached
) git rm
has removed the file from the work-tree, so git reset --mixed
won't do. git reset --hard <path>
will do, or you can git checkout HEAD -- <path>
to get the file back. The latter "writes through" the index, so again these wind up being pretty much redundant command-wise, like git reset --soft
vs git branch -f
.
来源:https://stackoverflow.com/questions/25478633/is-there-a-difference-between-these-two-ways-of-moving-a-branch