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?
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.1git resetchanges, at minimum, where the current branch2 is pointing. The difference between--mixedand--softis whether or not your index is also modified. So, if we’re on branchmasterwith this series of commits:HEAD(the current commit) isC3 and the index matchesC(assuming we haven’t staged any changes withgit add).When we run
git reset --soft B,master(and thusHEAD, indirectly) now points toB, but the index still has the changes fromC;git statuswill show them as staged. So if we rungit commitat this point, we’ll get a new commit with the same changes asC.(If we did have staged changes before the reset, those will still be in the index and
git commitwould commit those changes in addition to the changes fromC.)Okay, so starting from here again:
Now let’s do
git reset --mixed B. (--mixedis the default option, so this is equivalent togit reset B). Once again,masterandHEADpoint toB, but this time the index is also modified to matchB. If we rungit commitat this point, nothing will happen since the index matchesHEAD. We still have the changes in the working directory, but since they’re not in the index,git statusshows them as unstaged. To commit them, you wouldgit addand 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,
--hardis the same as--mixed(it changes yourHEADand index), except that--hardalso modifies your working directory. If we’re atCand rungit reset --hard B, then the changes added inC, as well as any uncommitted changes you’ve made, will be removed, and the files in your working copy will match commitB. Since you can permanently lose changes this way, you should always rungit statusbefore 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 usesHEADas the default.git reset --softwith 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 justgit 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 --harddoes 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 adda file, any changes you made to that file are copied to the index, and when yougit commit, the contents of the index are turned into a commit and added to the current branch.2
HEADis 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
HEADpoints tomasterandmasterpoints toC. Whenever we add or remove a commit on the current branch, the commit thatHEADrefers to changes not becauseHEADitself changed, but because the branch thatHEADpoints to was updated to point to that commit.