Reputation: 18697
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?
Upvotes: 1204
Views: 448130
Reputation: 753
The three options --soft
, --mixed
and --hard
share common info like:
branch
and HEAD
simultaneously, not just the HEAD
to the specific commit hash
commit hash
is not specified, the HEAD
is being the defaultreflog
and reset
to return to the original commit
that the HEAD
was on beforeWorking Directory
Staging
HEAD
and commit hash
are moved to the Staging
Working Directory
Staging
are removed from the Staging
and moved to the Working Directory
git restore --staged
HEAD
and commit hash
are not moved to the Staging
--mixed
is the default value for git reset
We can say that --mixed
equals --soft
but:
git add
Staging
to the Working Directory
Fun fact: What you see in git status
after executing --mixed
, after we perform git add .
, we will get the same git status
as if we executed --soft
--hard
is dangerous and should be used with caution because:
HEAD
to the commit hash
and forced the changes on the Working Directory
Working Directory
without a way to recover themModified Files
Staging
Note: Beware of using --hard
as it will affect the Working Directory
and force it matchs the specified commit
And any Modified Files
in the Working Directory
will be discarded
So, before using git reset --hard
and if there are files currently being modified in the Working Directory
, you should move these modifications elsewhere before executing git reset --hard
. This is where git stash
comes in and help you
Selection | Working Directory | Staged Files | Unstaged Modified Files | Changes made between HEAD and the specified commit |
---|---|---|---|---|
soft | remain as is | remain as is | Moved to the Staging Area |
Moved to the Staging Area |
mixed | remain as is | Moved to the Working Directory |
They remain as they are in the Working Directory |
They remain as they are in the Working Directory |
hard | Forced to be replaced to make the Working Directory match the version it was in the specified commit |
Get rid of it | Get rid of it | Get rid of it |
Upvotes: 4
Reputation: 23349
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.1
git reset
changes, at minimum, where the current branch2 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
(the current commit) is C
3 and the index matches C
(assuming we haven't staged any changes with git add
).
When we run git reset --soft B
, master
(and thus HEAD
, indirectly) 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
.
(If we did have staged changes before the reset, those will still be in the index and git commit
would commit those changes in addition to the changes from C
.)
Okay, so starting from here again:
- A - B - C (master)
Now let's do git reset --mixed B
. (--mixed
is the default option, so this is equivalent to git reset B
). 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.
(This time, if we had staged changes before the reset, those changes will be removed from the index, but the files themselves will still contain any changes that were made.)
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've made, 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.
In each of the above cases, we specified a commit (B
) to reset to. If you don't provide one, it uses HEAD
as the default. git reset --soft
with no commit specified doesn't do anything (it makes the current branch point to where it was already pointing), but it can be useful for the other two modes. git reset --mixed
(or just git reset
) updates the index to match the current commit, which has the effect of unstaging any changes that have been added but not committed; git reset --hard
does the same, plus it removes any changes in your working directory so that all your files match the latest commit on your current branch. (Again, since those changes are permanently lost, you should be careful when doing a hard reset.)
1 The index is also referred to as the "staging area". You can think of it as another copy of all the files in the repository. When a branch is checked out, the index is updated so that all its files match the contents of that branch. When you git add
a file, any changes you made to that file are copied to the index, and when you git commit
, the contents of the index are turned into a commit and added to the current branch.
2 HEAD
is how Git refers to the commit that is currently checked out. HEAD
(in most cases) points to a specific branch, called the current branch, and that branch points to a specific commit.
3 Because HEAD
points to master
and master
points to C
. Whenever we add or remove a commit on the current branch, the commit that HEAD
refers to changes not because HEAD
itself changed, but because the branch that HEAD
points to was updated to point to that commit.
Upvotes: 2136
Reputation: 21397
git reset --soft <B>
moves the changes from commits removed from repo into the index, merging in any that were there already.git reset --hard <b>
loses the changes in the working tree and the index.Upvotes: 2
Reputation: 8499
mkarasek's Answer is great, in simple terms we can say...
git reset --soft
: set the HEAD
to the intended commit but keep your changes staged from last commitsgit reset --mixed
: it's same as git reset --soft
but the only difference is it un stage your changes from last commitsgit reset --hard
: set your HEAD
on the commit you specify and reset all your changes from last commits including un committed changes.
--soft
and--mixed
are a bit similar, the only difference is, if you want to keep your changes in staging area use--soft
, and if you don't want your changes in staging area use--mixed
instead.
Upvotes: 16
Reputation: 534895
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 and containing B, C, and D.
(Of course you could also use a hard reset because you wish B, C, and D had never happened at all.)
Upvotes: 133
Reputation: 10621
Mo Ali has put it in simplest terms and here's another simple explanation:
--soft
: reset HEAD pointer to previous commit
--mixed
:--soft
+ delete add
ed changes
--hard
: --mixed
+ recover working tree file changes (CAREFUL!)
Upvotes: 1
Reputation: 8886
In these cases I like a visual that can hopefully explain this:
git reset --[hard/mixed/soft]
:
So each one affects different scopes:
Upvotes: 61
Reputation: 245
--mixed vs --soft vs --hard:
--mixed:
Delete changes from the local repository and staging area.
It won't touch the working directory.
Possible to revert back changes by using the following commands.
- git add
- git commit
Working tree won't be clean.
--soft:
Deleted changes only from the local repository.
It won't touch the staging area and working directory.
Possible to revert back changes by using the following command.
- git commit.
Working tree won't be clean
--hard:
Deleted changes from everywhere.
Not possible to revert changes.
The working tree will be clean.
NOTE: If the commits are confirmed to the local repository and to discard those commits we can use:
`git reset command`.
But if the commits are confirmed to the remote repository then not recommended to use the reset command and we have to use the revert command
to discard the remote commits.
Upvotes: 11
Reputation: 309
I’m not a git expert and just arrived on this forum to understand it! Thus maybe my explanation is not perfect, sorry for that. I found all the other answer helpful and I will just try to give another perspective. I will modify a bit the question since I guess that it was maybe the intent of the author: “I’m new to git. Before using git, I was renaming my files like this: main.c, main_1.c, main_2.c when i was performing majors changes in order to be able to go back in case of trouble. Thus, if I decided to come back to main_1.c, it was easy and I also keep main_2.c and main_3.c since I could also need them later. How can I easily do the same thing using git?” For my answer, I mainly use the “regret number three” of the great answer of Matt above because I also think that the initial question is about “what do I do if I have regret when using git?”. At the beginning, the situation is like that:
A-B-C-D (master)
A-B-C-D (master and mynewbranch)
Git reset --hard thenumberofthecommitA
or
Git reset --hard master~3
Then one obtains: A (master) – B – C – D (mynewbranch)
Then, it’s possible to continue working and commit from A (master) but still can get an easy access to the other versions by checking out on the other branch: git checkout mynewbranch. Now, let’s imagine that one forgot to create a new branch before the command git reset --hard. Is the commit B, C, D are lost? No, but there are not stored in any branches. To find them again, one may use the command : git reflog that is consider as “a safety command”( “in case of trouble, keep calm and use git reflog”). This command will list all commits even those that not belong to any branches. Thus, it’s a convenient way to find the commit B, C or D.
Upvotes: 0
Reputation: 7451
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)
Upvotes: 104
Reputation: 2139
You don't have to force yourself to remember differences between them. Think of how you actually made a commit.
Make some changes.
git add .
git commit -m "I did Something"
Soft, Mixed and Hard is the way enabling you to give up the operations you did from 3 to 1.
git commit
.git add .
Upvotes: 42
Reputation: 7600
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. HEAD
is you, and so will move whenever you do.
Upvotes: 11
Reputation: 1098
All the other answers are great, but I find it best to understand them by breaking down files into three categories: unstaged
, staged
, commit
:
--hard
should be easy to understand, it restores everything--mixed
(default) :
unstaged
files: don't changestaged
files: move to unstaged
commit
files: move to unstaged
--soft
:
unstaged
files: don't changestaged
files: dont' changecommit
files: move to staged
In summary:
--soft
option will move everything (except unstaged
files) into staging area
--mixed
option will move everything into unstaged area
Upvotes: 63
Reputation: 7640
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.Upvotes: 732
Reputation: 6620
--soft
: Tells Git to reset HEAD to another commit, so index and the working directory will not be altered in any way. All of the files changed between the original HEAD and the commit will be staged.
--mixed
: Just like the soft, this will reset HEAD to another commit. It will also reset the index to match it while working directory will not be touched. All the changes will stay in the working directory and appear as modified, but not staged.
--hard
: This resets everything - it resets HEAD back to another commit, resets the index to match it, and resets the working directory to match it as well.
The main difference between --mixed
and --soft
is whether or not your index is also modified. Check more about this here.
Upvotes: 2
Reputation: 406
Basic difference between various options of git reset command are as below.
Upvotes: 2
Reputation: 1844
Before going into these three option one must understand 3 things.
1) History/HEAD
2) Stage/index
3) Working directory
reset --soft : History changed, HEAD changed, Working directory is not changed.
reset --mixed : History changed, HEAD changed, Working directory changed with unstaged data.
reset --hard : History changed, HEAD changed, Working directory is changed with lost data.
It is always safe to go with Git --soft. One should use other option in complex requirement.
Upvotes: 11
Reputation: 6587
A short answer in what context the 3 options are used:
To keep the current changes in the code but to rewrite the commit history:
soft
: You can commit everything at once and create a new commit with a new description (if you use torotise git or any most other GUIs, this is the one to use, as you can still tick which files you want in the commit and make multiple commits that way with different files. In Sourcetree all files would be staged for commit.)mixed
: You will have to add the individual files again to the index before you make commits (in Sourcetree all the changed files would be unstaged)To actually lose your changes in the code as well:
hard
: you don't just rewrite history but also lose all your changes up to the point you resetUpvotes: 2
Reputation: 31337
Here is a basic explanation for TortoiseGit users:
git reset --soft
and --mixed
leave your files untouched.
git reset --hard
actually change your files to match the commit you reset to.
In TortoiseGit, The concept of the index is very hidden by the GUI. When you modify a file, you don't have to run git add
to add the change to the staging area/index. When simply dealing with modifications to existing files that are not changing file names, git reset --soft
and --mixed
are the same! You will only notice a difference if you added new files or renamed files. In this case, if you run git reset --mixed, you will have to re-add your file(s) from the Not Versioned Files list.
Upvotes: 26