Is there a difference between these two ways of moving a branch?

纵饮孤独 提交于 2019-12-07 09:33:30
torek

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:

  1. We can't simply ignore the options to reset.
  2. We also started the answer with "if HEAD refers to master". 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 new git 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 as git commit --amend, except that the latter is actually easier; so this usage is not common any more (it was how one did this before git 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" a git add or git rm --cached.3 Usually it's just spelled git reset <path>, since --mixed is the default.

  • git reset --hard or git 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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!