'Difference between branch forcing and hard resetting
The title is pretty explicit. Say I'm on main with a few commits and I want to go back one commit on main. Both these commands work :
$ git branch -f main HEAD^
$ git reset --hard HEAD^
What differences will there be when using one or the other ? Is there one to favor ?
Solution 1:[1]
In terms of branch name movement, these would be the same, if git branch -f main HEAD^ were allowed while on branch main. But it's not:
$ cd ~/tmp && mkdir ttt && git init
Initialized empty Git repository in ...
$ echo test branch -f > README && git add README && git commit -m initial
[master (root-commit) 23903eb] initial
1 file changed, 1 insertion(+)
create mode 100644 README
$ git commit --allow-empty -m c2
[master 14290a1] c2
$ git branch other
$ git branch -m main
$ git branch
* main
other
$ git branch -f main HEAD^
fatal: Cannot force update the current branch.
So, as you can see, we're literally forced to use git reset, not git branch -f, when we're on the branch in question.1
That said, the git reset command—when used in this fashion—does up to three things:
- It moves the current branch name (if
HEADis attached; in detached-HEAD mode, it moves the detached HEAD instead). - Then, if not prevented with
--soft, it resets the index / staging-area: the current, i.e.,HEAD, commit's contents replace the old index contents. - Then, if told to do so with
--hard, it resets the working tree. This step actually happens "at the same time as" step 2 since what-to-reset is only known by the old index content, but aside from this implementation detail, it's usually best to think of this as the third optional step.
This all has to do with the rather peculiar fact that HEAD, in Git, is normally a symbolic name, "attached to" the current branch name, and that in the fresh checkout / switch state, there are three copies of each file:
- The
HEAD-commit copy exists, and is completely read-only. Nothing you do will ever change it. (You can change which commitHEADnames, of course.) - Git's index contains a copy of every file, copied out of the current commit.2
- Your working tree contains a usable (de-Git-ified, no longer compressed and read-only and hence unusable) copy of the file. This is the one you see and work with, despite the existence of the other copies (or "copies").
Note that the first two copies are in Git. They are therefore available forever, or at least, as long as you don't use git reset or git branch -f to make the commits themselves (eventually) die off. The third copy, however, is not in Git. If anything wrecks them, Git cannot help you with that.
Since, by definition, the branch name stored in HEAD is the current branch name, git reset can get us back to that happy state—all three copies matching—if we use --hard.3 But git branch -f can't, so we're forbidden from using it. If some bug slips up and does let us use it, we get the effect of git reset --soft, since git branch does not update index or working tree.
1This should apply to added work-trees as well, but historically there have been bugs in this area. Whether your Git version allows it may depend on your Git version.
2The index copy is in Git's internal, read-only, compressed and de-duplicated form. Since by definition a copy that was made from the copy that's in the commit is identical to the copy that is in the commit, everything in the index is de-duplicated, and therefore takes no space (apart from the index entry itself). So calling this a "copy" is slightly misleading—but unlike the committed version, the index copy can be replaced or removed, with a simple git add or git rm for instance.
The git add variant compresses a working tree copy, checks to see if it's a duplicate, and then either puts a new, never-before-seen version in as the index entry, or uses the existing duplicate version as the index entry. The git rm version simply removes the index entry entirely. As the index entry is really, internally, a reference to the Git-ified read-only-and-de-duplicated copy, the Git-ified copy itself remains intact if necessary (if existing commits are sharing it).
3While I call it a "happy state" here, if you had unsaved changes in your working tree, that you spent a long time working on, you might be rather un-happy. The happiness here may be Git's alone.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | torek |
