0

When using git reset why would one prefer to use git reset --mixed over git reset --soft if the first option means re-adding files that --soft already takes care of ?

Also, regarding the difference between the 2 options - If I were not to use git reset --soft (e.g. as an exercise), is there a case where git reset --mixed && git add won't be able to mimic git reset --soft behavior ?


Edit: I know there are similar questions but they don't dive into edge cases where git reset --mixed together with git add might not be equivalent to git reset --soft and they mostly summarize known documentation.

caffein
  • 303
  • 1
  • 10
  • Does this answer your question? [What's the difference between git reset --mixed, --soft, and --hard?](https://stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard) – CodeCaster Apr 04 '20 at 10:52
  • @CodeCaster No. It states the difference but it doesn't show scenarios where one options if preferred over the other. More importantly, it doesn't really explain if the 2 options are equivalent or there are edge cases where the 2 options are not equivalent if we use "add" after "mixed" – caffein Apr 04 '20 at 10:57

2 Answers2

2

You are right that you can often mimic git reset --soft with git reset --mixed and git add.

However, I can imagine edge cases where it is not possible. Assume you edited a file, then added your changes to the index with git add, but after that you realized that it was wrong and you went back to the previous version using some editor. Now, you are in a situation where you think the file was not changed at all, but your changes still sit in the index. Here comes the difference: git reset --soft will keep your staged changes while with git reset --mixed they will be lost forever (git add will not help you, because you have nothing to add).

Those flags have different use cases: --mixed is often used to remove changes from the index which were added to it by an accident, and I have a feeling that --soft is used very rarely. I have used --soft flag only once, when I forgot to create a feature branch and made a commit on master.

szydra
  • 116
  • 6
  • I understand what you mean. Exactly the case I was looking for and also a great example that helps to better understand how the index interacts with git add and commit. Thanks ! – caffein Apr 04 '20 at 21:24
1

Consider what git reset (in its three modes, soft vs mixed vs hard, and not its other additional things that aren't one of these three modes at all) does. You run:

git reset --<mode> <commit-specifier>

and it does the following three things, or maybe 2, or 1:

  1. (--soft, --mixed, --hard) It changes the current branch name, as stored in HEAD, to point to the given commit. With --soft, it now stops.

  2. (--mixed and --hard) It replaces the existing index contents with the contents of the commit you selected in step 1. With --mixed (or the default), it now stops.

  3. (--hard only) It replaces the work-tree contents with the files as updated in step 2.

Now, you can, if you choose, select the current commit as the target in step 1, by running git reset HEAD for instance. If you do this, the updated current commit in step 1 is the current commit. That is, step 1 makes no actual change.

If you run git reset --soft HEAD, step 1 makes no change, and git reset then quits. So this does absolutely nothing.

If you run git reset without --soft, however—with no argument at all, which defaults to --mixed, or with --hard—it goes on to step 2: replace the index's contents with the files that are frozen for all time into the current commit. In effect, this un-does any git add that you did earlier.

If your goal is to re-do all the git add again afterward, this isn't useful. But if your goal is to not re-do all these git adds, it can be. Consider, for instance, this example—not terribly useful by itself, but meant to be illustrative:

git checkout somebranch
<edit for a while, test, `git add .`>

# think: wait, I don't want to commit everything
git reset      # index now matches HEAD

<edit README file to announce that the NEXT commit changes a lot of things>
git add README
git commit     # make a commit in which everything is the same except README

git add .
git commit     # make final commit in which everything is changed

To make sense out of all of this, remember that there are, at all times, three copies of each file:

  • There is a read-only, frozen-for-all-time copy of each file (README.md, main.py, etc., whatever files you have) in the current commit. You can change which commit is the current commit any time you like, using git checkout or git reset, but the commit itself is frozen for all time: none of its files will ever change at all, nor will its commit message, author, and so on.

  • There is a frozen-format copy of the file in Git's index.1 You can change this all you like: you can overwrite the copy of the file with a copy of another file or another version of the same file. You can even remove the file from the index entirely, using git rm. The index copy is in the frozen format, ready to go into a commit, but is not itself a commit, so it can be changed.

  • Last, there's a usable version of the file. It's not in some special Git-only format, and the rest of your computer programs can actually use this version of the file. That version resides in your work-tree.

It's very typical for the index copy of a file to match at least one other copy—the HEAD copy, the work-tree copy, or both—but in fact, you can have all three files be different. To do that, just:

  • edit the work-tree copy (be sure to save it so that git add can see it);
  • use git add to copy the work-tree copy to the index; and
  • edit the work-tree copy some more (and be sure to save again).

Now the file is both staged for commit, meaning that the HEAD and index copies differ, and not staged for commit, meaning that the index and work-tree copies differ.


1Technically, what's in the index is just a mode, file name, and reference to an internal Git blob object. The blob object represents the frozen-format copy of the file's content. Reset (git reset) copies the mode, name, and hash ID into the index. Adding (git add) compresses (makes ready-for-freezing) the work-tree file by making a new blob object, or finding the already-existing blob object, that will re-expand to the work-tree file later.

torek
  • 448,244
  • 59
  • 642
  • 775