Selectively Removing Parts of a Commit (git reset -p)
In the previous lesson we looked at
git add -p to see how we could selectively add 'hunks' of code to our staged commit.
In this lesson we are going to selectively unstage parts of an already staged file in our current git branch.
Well, let's start with a command you are probably already familiar with:
git reset HEAD --hard
This is one of the first commands you commit (excuse the pun) to memory when using git. Usually, you use it to get yourself out a bad place.
Like Pushing The Reset Button When You Are The Last Bowsers Castle On SNES Mario 1
The thing is,
git reset HEAD --hard is quite a brutal command.
It is going to completely wipe out any change you have done since you checked out the current branch.
Let's say we happily do a
git init on a brand new folder, and then spend the following 10 hours working. We never create a single commit in that time. Unlikely, but for the sake of argument, let us pretend that's exactly what we did.
Then, something goes wrong - you end up with nasty errors blowing up all over your application. Boo! Boo and such.
Ok, no danger, your friendly colleague tells you to run
git reset HEAD --hard and you dutifully follow instruction.
Bad times. Literally every file will be lost. We reset our folder structure to the
HEAD in this case is our initial commit, where we had no files.
In most cases, it's not possible to get all those files back. I say most cases - you may have local snapshots, or an OSX time machine backup within 10 hours, but you hopefully get where I am coming from.
Even if you had staged the files but not yet actually done the commit, the staged files (the current Working Tree) would also be removed thanks to the use of
Selectively Unstaging Using
git reset -p
Of course, we are likley to create lots of little commits as the shape of our code ebs and flows, as is natural through development.
A common workflow I see is:
git add --all git commit -m "some commit message" git push origin master # or other branch
The thing with this is, it adds in all kinds of wild stuff. If you get your line encodings wrong for example, every time you do a commit, git is going to think you have changed the entire file.
This is very likely to make your code reviewer reject your merge request. If you don't do code reviews, it's still likely to bite you hard in the future, when you go to review your commit history to find when something changed, and find yourself having to trawl through the entire file line by line. Painful.
We learned about mitigating this with
git add -p.
The good news is that the same functionality exists - but in reverse - with
git reset -p.
Now, the naming of this command is not inituitive at all.
However, the functionality is incredibly useful.
git reset -p allows us to step through our staged file and selectively remove some, or all, of the changed 'hunks' of code.
This won't remove the change completely. It will only remove the change from the current list of staged staged files. The file will remain in your list of changes not staged for commit.
This allows us to compose a commit where only some of the changes in a given file are to be saved / committed.
Once the staged files are fully committed, we will still have our modifications, and they will remain unstaged.
At this point, should we have chosen all the bits we want, we could then do a
git reset HEAD --hard to remove any changes / differences between our new
HEAD and the local staged and unstaged changes area (the Working Tree).
Or you could continue on, and create another commit containing some or all of the changes you didn't commit.