Making Git Your Partner

My last post was about keeping an engineer’s notebook, which is probably the thing that has helped me to remember meetings, tasks, and be infinitely more dependable and start acting like the senior engineer I hope to soon become. Today, I’m writing about something that has helped me grow as a developer: learning more than just the basics of git (and perforce before that).

Learning to use git more effectively (as well as being a little more thoughtful about branch naming and commit messages) has helped me grow as a developer by allowing me to try things I might have thought of as too sweeping or too big of a change to make, as well as, allowing me to save those changes when they’re not quite working and go back to a working codebase to fix another bug that has come up, then easily rebase the new fix into my experimental code when I come back to it. A lot of these exploratory branches end up being deleted without being merged, of course, but sometimes I’m able to make some big change that happens to make things better. The other thing, I always learn something, even on the experiments that don’t get merged.

The Problem

Now, I know that a lot of (maybe most) developers tend to copy whole projects into new directories, or clone projects into separate directories from where they generally work, in order to avoid mishaps. They scroll through endless commits, looking for where a line of code was deleted (finding where it was last changed is usually fairly easy, and where it was introduced, as well, as long as it wasn’t moved from a different file). They do this for a host of reasons, including:

  • lost work because a destructive command didn’t do what they expected
  • confusing workflows, instructions, and training
  • obtuse implementations in tooling (IDE’s, source hosting, etc…​)
  • poor understanding of best practices and why they exist
  • only learning the bare minimum to get work done

I think pretty much all of those reasons stem from not learning to use git well enough to let it do the heavy lifting for them. Usually, I imagine they haven’t learned it more because we are all very busy. Our managers like to keep us chasing the next goal, driving to the next milestone. Also, learning to use your version control system a little better sounds less fun then learning a new language or programming paradigm.

Destructive Commands

Losing work is a hell of lesson…​ I’ve done it and it drove me nuts, but it wasn’t the end of the world. I figured it out again, probably a bit faster than the first time. It’s bound to happen now and then, regardless of your process. However, learning to use git effectively can probably actually help to reduce this (as well as help you test different solutions more effectively). By learning git, you will learn which commands are destructive and therefore, you should be a little more careful with. Rebase, merge, reset, and even checkout are some of the most common offenders.

Keep in mind, reflog could save you in the event that you do ever accidentally run a command deleting something you didn’t mean to. The most sure way to get something into the reflog is to commit it. This is why I, generally, checkout a new branch and commit my changes before I change back to the other branch and run the command (or if they’re already committed, checkout a new branch and run the command there). However, beware, the reflog will not save you if the changes never made it into the reflog, like if you accidentally discarded them with reset or checkout.

Reset

You probably already knew about the potential destructiveness of reset. The name itself seems to drip with malice. A reset --hard can and will delete code and whole files from your working directory. However, it can also be your ally, as long as you approach it, correctly, and maintain a good workflow (or at least create a new branch from your changes before running your potentially destructive command).

Have you ever accidentally checked into master, then had to make emergency changes before releasing the change you checked into master? This is where reset --hard can shine.

# Move your commit to a new branch
git checkout -b my-unfinished-feature
git checkout master
git reset HEAD~ --hard

Now you’re back in line with master and ready to make the change and push your hotfix.

How about a careless git add . staging changes that weren’t meant to be checked in. This is where a plain git reset -- dont-commit.txt will help.

Merge, Rebase, and Checkout

You know how the rest of these commands can be destructive. Just remember to cut a new branch and commit your changes and you avoid most of the pain. Branches are, pretty much, free, anyway. I’ve probably lost more files to a careless git checkout -- some-dumb-file.js than I care to remember.

Now, I remember to commit, even when I think I’ll never come back to it. I go through my branches every once in a while. If I come across something I haven’t touched in a while, I’ll git branch -D old-branch with reckless abandon. I’ve already moved on, those changes are now dead to me. But, I’ve also come back to those branches I never thought I’d touch again, occasionally, and I’m always glad I had them.

Log

Use git log. Seriously, it can change your life. It’s so much faster than most web interfaces…​ and insanely powerful! Ever heard of the pickaxe? I have to duck duck go it every time I need it, because I don’t use it often, but when I do…​ chef’s kiss. A quick check where my current branch is (have I told you like to have lots of branches)…​ git log --oneline. See the last couple of patches…​ git log -2 -p. Want to see a list of the files changed in a commit, how many lines were added and removed in each file…​ git log <commit-hash> --numstat -1.

Commit

Git commits have a very loose structure. A lot of times, you’ll see things can get pretty crazy in the log because of this. If you follow a few simple tips, it can save you from future headaches, as well as keep your log orderly bringing you even greater benefit.

The first line is the subject. It starts with an imperative: Add, Merge, Revert, Refactor, Remove, etc. It should be fairly short, around 50 characters or 10 words. The second line should be blank, to separate the header from the body.

After the blank line is the body. Don’t list the files you changed (those are in the patch, remember --numstat). Don’t rehash what changes you made…​ Tell your team, future you, or whoever might be working on this codebase in the future why you’re making this change. Yea, a lot of these will be, “Adding a new feature,” but bug fixes aren’t. Make a note of the bug, how it presented. Link to the ticket if you can. Make it very clear what was fixed by this, so when you come back in six months and try to clean it up, you can look at the commit and think, “Oh yea!”

Here’s a couple good articles about this:

Keep Learning

There’s a lot of good resources on the web to help you get the most out of git (or any VCS you happen to be using). I seriously recommend reading the git book. Skim it if you must, but there’s a lot to learn from that book. I read it after using git for years. I read it cover to (almost) cover. I think I stopped at the appendices, but, now that I’m looking at them again, I think I’m going to go back and read some of those, too.

Atlassian has a decent tutorial. Github has a lot of good resources, too. Do what works best for you (I think you might have noticed, I like reading the official documentation, but I know that’s not for everybody).

The trick is, don’t count on the web to give you the answer for everything, right when you need it. So often, you don’t even know what to search for, when things go wrong, if you are only using the bare minimum of the tool. Take a look at the docs, or do some more advanced tutorials. Get to know your tools and you’ll find them much more valuable.

@phillipwills #GitYourPartner

Published