Hide Your Fat Fingers With Git
May 21, 2020
Git used to freak me out. When I started using it to collaborate with other developers, it was a really great tool to make me feel like an idiot. For the longest time, I would fill myself with dread and a sense of impending doom every time I’d type git
into my console. But the whole point of git and online repositories is to provide a reliable backup of code for collaboration with others. I started at FinanceIt a couple of months ago, and here are a few things that I’ve learned (mostly the hard way) that might aid you in preventing a git related freakout.
- Before you
git commit
anything to prepare it for being pushed up to a remote repository, always run agit diff
and make sure that the changes you’re preparing to push are the changes you want. Look for any errantbinding.pry
ordebugger
statements, trailing whitespace, bad formatting or any other easy to fix low-hanging fruit. Do you really want other developers to know about your fat fingers? If you aren’t seeing your changes, chances are you’ve already committed them (staged them to be pushed), so pass a flag to see them:git diff --staged
. Use the commandgit status
to show the current status of your working tree, and look for any files you didn’t mean to change. I usually use the VSCode version control tool called Source Control to highlight all the changes I’ve made since the last commit, looking for accidental deletions (they happen!), messy additions (like whitespace cruft), or even just to reflect on exactly what changes have stuck. The git desktop client provides similar functionality. - When you start a new feature branch, make sure that you’re branching off of the starting branch that you intend to. It’s pretty easy to be on branch
bugfix-1
, finish work on it and then forgetfully start work on branchfeature-A
without first moving back tomaster
. It might not cause any issues to branch your feature off of the bug fix branch, but when someone goes to review your code they’ll likely be confused about the unrelated commits. You don’t always want to branch off ofmaster
, but you really need to be aware of your starting point when you build a new branch withgit branch -b <name of new branch>
. But it’s much more fun to learn things the hard way. To fix this mistake, the easiest solution is probably to do an interactive rebase withgit rebase -i <branch name>
and delete the “pick” lines from the unwanted branch. It will be your secret. - If you accidentally change a file while poking around in a bunch of different files trying to uncover the source of your troubles, it’s a really easy fix. From the command line, all you need to do is
git checkout master -- path/to/file
and the file will be reset to whatever it is inmaster
. You can choose any commit hash, it doesn’t have to be master. In VSCode source control, you can use “discard changes” (the bent arrow icon) to revert all changes to the file.git reset --hard <commit hash>
is how you abandon all your changes and go “back in time” to a commit hash (i.e.git reset —-hard master
). It is destructive, so you’ll lose all your local changes, but that’s the point right?HEAD
is the last commit in the currently checked out branch, and this is resettingHEAD
to a previous commit. - If you’re working on a team, remember to merge
master
(or whatever branch you’re merging into) back into your feature branch before you try to issue a pull request to merge it in. If there are any conflicts, you should be able to resolve them yourself.git fetch origin
, thengit merge origin/<branch you're merging into>
. - Take a second to make your commit messages have meaning. If you can’t easily summarize the changes you’ve made in the commit, your commit is probably too large and you should be committing more often. Keep in mind that other people will actually read those messages!
How to Revert A File That You’ve Already Pushed
If you’re like me and can write an article of what not to do and then still do a bunch of that stuff, read on. It’s a technique I recently learned on how to secretly revert a file that got accidentally changed several commits ago. Essentially what you do is create a new commit to revert the file, then rebase
your branch to re-arrange the new “fix commit” to happen right after the accidental commit. Then you squash
the two commits together. Don’t rebase and force push if other people are working on the branch, because that is just mean. Force pushing will overwrite any remote changes with your local copy, and that’s not how we make friends. But if you are the only person contributing to the branch, it should be fine.
The reason why you would want to do this kind of cleanup is because if you modify a file accidentally in a PR that has nothing to do with that file, it clutters up the git history of the file, which makes it confusing and annoying for anyone looking at the history of the file. Also, anybody reviewing your pull request will be pretty happy if they don’t have to wonder why you made changes to unrelated files.
Instructions:
- In your PR where the other developer really politely said “Hey, can you revert this nonsense change?”, look at the files changed tab, locate and copy the filename of your mistake.
- From the terminal on a fresh commit, use
git checkout master path/to/file
to revert those nasty changes. Run a quickgit status
to confirm that the file (and only that file) has changed. git commit -am 'revert unwanted changes'
to add the commit and give it a nice message (we’ll just delete later).- Open the github desktop app and count how many commits back you want to rebase to. Count from the top, and include the commit you want to rebase to.
git log
works too if you don’t like pretty things. - Back on the command line, if your number from the above step is 3, use
git rebase -i HEAD~3
to interactively rebaseHEAD
that many steps. Your code editor will open up. - Move the new “revert unwanted changes” commit up to just below the commit with the unwanted change, and change it from
pick
tosquash
(on the left). Save and close your editor. - Your editor will pop up again. Delete the lovely ‘revert unwanted changes’ message you just wrote, save and close. You got into this mess by not being careful, so now’s a good time to open github desktop and double check that this new commit is the one you actually want.
- Run
git push --force-with-lease
to force push your changes up to the remote repository somewhat safely, which will overwrite the remote branch with your local branch and hopefully not make you any new enemies in your organization. Don’t justforce
your local branch onto the remote… I’ve never done that… This article explains why you should use—-force-with-lease
, but basically it’s because this way your push will (imperfectly) be rejected if anyone else has pushed to the remote branch since you pulled it, while plainforce
is like a honey badger.
Because you will inevitably still have yourself a git freakout:
- For a deeper dive into git, read this book (free).
- This page has a nice explanation of
git HEAD
. - It’s a page called Oh Shit, Git!?! so you’re probably going to click it.
Thanks to S.Blondal for the pictures to plunder and display, and to dubroe for the lesson on rebasing.