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 real
A lot of the existing answers don't seem to answer the actual question. They are about what the commands do, not about what you (the user) want — the use case. But that is what the OP asked about!
It might be more helpful to couch the description in terms of what it is precisely that you regret at the time you give a git reset
command. Let's say we have this:
A - B - C - D <- HEAD
Here are some possible regrets and what to do about them:
git reset --soft A
. I can now immediately commit and presto, all the changes since A are one commit.
git reset --mixed A
. The commits are gone and the index is back at A, but the work area still looks as it did after D. So now I can add-and-commit in a whole different grouping.
Make a new branch otherbranch
, and then git reset --hard A
. The current branch now ends at A, with otherbranch
stemming from it.
(Of course you could also use a hard reset because you wish B, C, and D had never happened at all.)
In the simplest terms:
--soft
: uncommit changes, changes are left staged (index).--mixed
(default): uncommit + unstage changes, changes are left in working tree.--hard
: uncommit + unstage + delete changes, nothing left.Please be aware, this is a simplified explanation intended as a first step in seeking to understand this complex functionality.
May be helpful for visual learners who want to visualise what their project state looks like after each of these commands:
Given: - A - B - C (master)
For those who use Terminal with colour turned on (git config --global color.ui auto):
git reset --soft A
and you will see B and C's stuff in green (staged and ready to commit)
git reset --mixed A
(or git reset A
) and you will see B and C's stuff in red (unstaged and ready to be staged (green) and then committed)
git reset --hard A
and you will no longer see B and C's changes anywhere (will be as if they never existed)
Or for those who use a GUI program like 'Tower' or 'SourceTree'
git reset --soft A
and you will see B and C's stuff in the 'staged files' area ready to commit
git reset --mixed A
(or git reset A
) and you will see B and C's stuff in the 'unstaged files' area ready to be moved to staged and then committed
git reset --hard A
and you will no longer see B and C's changes anywhere (will be as if they never existed)
There are a number of answers here with a misconception about git reset --soft
. While there is a specific condition in which git reset --soft
will only change HEAD
(starting from a detached head state), typically (and for the intended use), it moves the branch reference you currently have checked out. Of course it can't do this if you don't have a branch checked out (hence the specific condition where git reset --soft
will only change HEAD
).
I've found this to be the best way to think about git reset
. You're not just moving HEAD
(everything does that), you're also moving the branch ref, e.g., master
. This is similar to what happens when you run git commit
(the current branch moves along with HEAD
), except instead of creating (and moving to) a new commit, you move to a prior commit.
This is the point of reset
, changing a branch to something other than a new commit, not changing HEAD
. You can see this in the documentation example:
Undo a commit, making it a topic branch
$ git branch topic/wip (1) $ git reset --hard HEAD~3 (2) $ git checkout topic/wip (3)
- You have made some commits, but realize they were premature to be in the "master" branch. You want to continue polishing them in a topic branch, so create "topic/wip" branch off of the current HEAD.
- Rewind the master branch to get rid of those three commits.
- Switch to "topic/wip" branch and keep working.
What's the point of this series of commands? You want to move a branch, here master
, so while you have master
checked out, you run git reset
.
The top voted answer here is generally good, but I thought I'd add this to correct the several answers with misconceptions.
git reset --soft <ref>
: resets the branch pointer for the currently checked out branch to the commit at the specified reference, <ref>
. Files in your working directory and index are not changed. Committing from this stage will take you right back to where you were before the git reset
command.
git reset --mixed <ref>
or equivalently
git reset <ref>
:
Does what --soft
does AND also resets the index to the match the commit at the specified reference. While git reset --soft HEAD
does nothing (because it says move the checked out branch to the checked out branch), git reset --mixed HEAD
, or equivalently git reset HEAD
, is a common and useful command because it resets the index to the state of your last commit.
git reset --hard <ref>
: does what --mixed
does AND also overwrites your working directory. This command is similar to git checkout <ref>
, except that (and this is the crucial point about reset
) all forms of git reset
move the branch ref HEAD
is pointing to.
It is not useful to say a command moves the HEAD
. Any command that changes where you are in your commit history moves the HEAD
. That's what the HEAD
is, a pointer to wherever you are. HEADis you, and so will move whenever you do.
In these cases I like a visual that can hopefully explain this:
git reset --[hard/mixed/soft]
:
So each effect different scopes
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: