Whats the difference between git reset --mixed, --soft, and --hard?

git reset soft vs mixed

When you want to undo a commit in Git, you can use the git reset command. This command allows you to move the current branch pointer and the HEAD pointer to a specified commit.

The git reset command can be used with different options, each of which changes the way the reset operation works. Two of the most commonly used options are:

  • --soft: This option moves the current branch pointer to the specified commit, but leaves the changes that were made in the later commits in the staging area. This means that the changes can be committed again later.

  • --mixed: This option is the default behavior of git reset when no option is specified. It moves the current branch pointer to the specified commit and removes the changes made in the later commits from the staging area. This means that the changes will still be in the working directory and can be modified and committed again.

In summary, git reset --soft moves the branch pointer and HEAD pointer to the specified commit, but leaves the changes in the staging area so they can be committed again later. git reset --mixed does the same, but also removes the changes from the staging area and places them in the working directory.

 

What's the staging area? What's the meaning of leave the changes and remove changes from staging area?

The staging area (also known as the "index") is a feature in Git that allows you to prepare changes for a commit. It's essentially a snapshot of the current state of your working directory that you have explicitly marked as being ready to be committed.

When you make changes to your code, those changes are initially only present in your working directory. To commit those changes, you first need to add them to the staging area using the git add command. This allows you to selectively choose which changes to commit, and which to leave out. Once you have added the changes to the staging area, you can then create a commit with those changes using the git commit command.

If you have made changes to your code and added them to the staging area, but then decide you don't want to commit those changes after all, you can use the git reset command to remove the changes from the staging area. The git reset command has several options that allow you to choose which changes to remove and how to handle the changes in your working directory.

The git reset --soft option will remove the changes from the staging area, but leave the changes in your working directory. This means that the changes will still be present in your files, and you can re-add them to the staging area and commit them later.

On the other hand, the git reset --mixed option (which is the default behavior if you don't specify a mode) will remove the changes from the staging area and also from the index, but it will leave the changes in your working directory. This means that the changes will still be present in your files, but they will no longer be tracked by Git. You will need to add the changes back to the staging area using git add before you can commit them.

Note that using git reset can be a powerful and potentially dangerous command, since it can discard changes that you have made. It's important to use this command with care, and to always make sure you have a backup of your code before using it.

 

How can I check if there is anything in staging area?

You can use the git status command to check the status of the files in the staging area.

If there are changes in the staging area, git status will show them under the "Changes to be committed" section, with the path of the file(s) and their status. For example:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   file1.txt
        new file:   file2.txt
If there are no changes in the staging area, git status will show a message like:
nothing to commit, working tree clean
This means that there are no changes to commit or stage.
 
 

What's status  of files that are new added or modified, but not add to staging area?

The "Changes to be committed" status in git status output refers to changes that have been added to the staging area and are ready to be committed to the local repository. These changes may be new files, modified files, or deleted files that have been staged with the git add command.

On the other hand, if you have modified or added files that have not yet been staged with git add, their status will typically be listed as "Changes not staged for commit" in the git status output. These changes are not yet ready to be committed to the repository, as they have not been added to the staging area.

 

Yes, you're correct. The main difference between git reset --soft and git reset --mixed is the state of the changes in the staging area (index) and the working directory after the reset.

  • git reset --soft preserves the changes from the reset commit in the staging area. The commit itself is effectively undone, but the changes remain staged. This allows you to easily make further modifications to the staged changes and create a new commit.

  • git reset --mixed (or the default behavior) updates the staging area to match the reset commit and leaves the changes from the reset commit as unstaged in the working directory. The changes are preserved in your working directory, but they are not staged for a new commit. You can review the changes, modify them if necessary, and selectively stage them before creating a new commit.

In both cases, the changes made in the commit are not lost. The commit itself is effectively removed from the branch's history, but the changes are retained either in the staging area (git reset --soft) or as unstaged changes in the working directory (git reset --mixed).

It's worth noting that if you want to completely discard the changes made in the reset commit, including both staged and unstaged changes, you can use git reset --hard. However, be cautious when using --hard as it irreversibly discards all changes and resets the branch pointer.

 
 

What's the difference between git reset --mixed, --soft, and --hard?

问题

I'm looking to split a commit up and not sure which reset option to use.

I was looking at the page In plain English, what does "git reset" do?, but I realized I don't really understand what the git index or staging area is and thus the explanations didn't help.

Also, the use cases for --mixed and --soft look the same to me in that answer (when you want to fix and recommit). Can someone break it down even more? I realize --mixed is probably the option to go with, but I want to know why. Lastly, what about --hard?

Can someone give me a workflow example of how selecting the 3 options would happen?

 

评论

Note to self: In general, soft: stage everything, mixed: unstage everything, hard: ignore everything up to the commit I'm resetting from.

Oct 22, 2016 at 10:37  

 

回答1

When you modify a file in your repository, the change is initially unstaged. In order to commit it, you must stage it—that is, add it to the index—using git add. When you make a commit, the changes that are committed are those that have been added to the index.

git reset changes, at minimum, where the current branch (HEAD) is pointing. The difference between --mixed and --soft is whether or not your index is also modified. So, if we're on branch master with this series of commits:

- A - B - C (master)

HEAD points to C and the index matches C.

When we run git reset --soft B, master (and thus HEAD) now points to B, but the index still has the changes from C; git status will show them as staged. So if we run git commit at this point, we'll get a new commit with the same changes as C.


Okay, so starting from here again:

- A - B - C (master)

Now let's do git reset --mixed B. (Note: --mixed is the default option). Once again, master and HEAD point to B, but this time the index is also modified to match B. If we run git commit at this point, nothing will happen since the index matches HEAD. We still have the changes in the working directory, but since they're not in the index, git status shows them as unstaged. To commit them, you would git add and then commit as usual.


And finally, --hard is the same as --mixed (it changes your HEAD and index), except that --hard also modifies your working directory. If we're at C and run git reset --hard B, then the changes added in C, as well as any uncommitted changes you have, will be removed, and the files in your working copy will match commit B. Since you can permanently lose changes this way, you should always run git status before doing a hard reset to make sure your working directory is clean or that you're okay with losing your uncommitted changes.

And finally, a visualization: 

总结:

假设有A<--B<--C 三个commit,当前分支master指向C【先确保本地是clean的状态】

执行git reset --soft B,HEAD指向commit B,本地文件变成修改状态,并且是B到C的变化。

执行 git reset --mixed B,master指向B,并且文件work directory的文件内容会切换到B所在的状态

 

--mixed不会stage文件,但是--soft会stage文件。举个例子,如果commitC新增了一个文件。那么做了--mixed操作,git status会显示那个文件是untracked。而--soft会显示那个文件是已经添加到index(已经staged的状态)

 

想到了关于soft的使用,如果在回滚之后,进行commit。其实就相当于一次squash

 

Practical uses of git reset --soft?

git reset is all about moving HEAD, and generally the branch ref.
Question: what about the working tree and index?
When employed with --soft, moves HEAD, most often updating the branch ref, and only the HEAD.
This differ from commit --amend as:

  • it doesn't create a new commit.
  • it can actually move HEAD to any commit (as commit --amend is only about not moving HEAD, while allowing to redo the current commit)

Just found this example of combining:

  • a classic merge
  • a subtree merge

all into one (octopus, since there is more than two branches merged) commit merge.

Tomas "wereHamster" Carnecky explains in his "Subtree Octopus merge" article:

  • The subtree merge strategy can be used if you want to merge one project into a subdirectory of another project, and the subsequently keep the subproject up to date. It is an alternative to git submodules.
  • The octopus merge strategy can be used to merge three or more branches. The normal strategy can merge only two branches and if you try to merge more than that, git automatically falls back to the octopus strategy.

The problem is that you can choose only one strategy. But I wanted to combine the two in order to get a clean history in which the whole repository is atomically updated to a new version.

I have a superproject, let's call it projectA, and a subproject, projectB, that I merged into a subdirectory of projectA.

(that's the subtree merge part)

I'm also maintaining a few local commits.
ProjectA is regularly updated, projectB has a new version every couple days or weeks and usually depends on a particular version of projectA.

When I decide to update both projects, I don't simply pull from projectA and projectB as that would create two commits for what should be an atomic update of the whole project.
Instead, I create a single merge commit which combines projectA, projectB and my local commits.
The tricky part here is that this is an octopus merge (three heads), but projectB needs to be merged with the subtree strategy. So this is what I do:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

Here the author used a reset --hard, and then read-tree to restore what the first two merges had done to the working tree and index, but that is where reset --soft can help:
How to I redo those two merges, which have worked, i.e. my working tree and index are fine, but without having to record those two commits?

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

Now, we can resume Tomas's solution:

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

So, each time:

  • you are satisfied with what you end up with (in term of working tree and index)
  • you are not satisfied with all the commits that took you to get there:

git reset --soft is the answer.

 

How to revert multiple git commits?

有了abcd四个commit之后,需要再提交一个commit E,确保E这个commit时,repository的内容和commitA保持一致

A <-- B  <-- C <-- D  

The solution by Jeff Ferland, modified by Charles Bailey builds upon the same idea, but uses git reset:

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit

 

posted @ 2017-06-22 18:17  ChuckLu  阅读(278)  评论(0编辑  收藏  举报