10 Steps to Git Gud

I like Git. You should also like Git also. But do you get Git? Git gets you into trouble, but can you get out of Git trouble when the going gets tough?

Annoying wordplay aside, the power behind Git is amazing. All because it is simple, provided you get some core concepts and aren’t afraid of a reflog or two along the way.

Here are my 10 steps to become a little better at source control.

Ditch the Training Wheels

Git GUI applications (*cough* Sourcetree *cough) are great, and provide a great diving board with a familiar clicky-pointy interface for throwing source files around. But, after using one for years, I noticed I was getting lazy, and commit quality started to sag. This then led to frustration on merge, or conflict resolution, and in the end, more regressions.

Do yourself a favour. Ditch the UI. Yeah, its scary, but you will be amazed at how much your workflow, and attitude improves. Remember, Google is a thing (as if you didn’t know that already, being a developer and all) and will likely lead to a SO post or two with the CLI commands you used to have a convenient button for.

Here are the basics;

  • Heres all my shit!
git add .
  • No too much!
git reset
  • Just these things (more on this later)
git add -i
  • Looks #swag
git commit -m "SWAGGGIEST COMMIT"
  • Fuck this noise. *Smokebomb*
git push -u origin my-swag-branch # First time
git push # Here on out
  • Alright, Bob, Lets fix your shit
git branch -t origin/bobs-massive-ballsup
git add .
git commit -m "Fix xyz"
git push
  • Let me loose on that case!
git fetch && git checkout master
git pull
git checkout -b just-do-it

Use a Private Fork

Best practices in the OSS community usually mean that Merge Requests come from a private fork. Its a cool strategy, leaving the main repository clean, and fenced off.

But you too can use this in your workplace. Tools like Gitlab and GitHub Enterprise have the ability to create private forks, which really aren’t nothing more than a place for all your half-baked features, terrible commit messages and offensive tags to live. Think of it as the horrible desktop your client, or customer has, where all documents go to die. Maybe not that bad, but you get the point.

I usually keep my default upstream as origin, and point it to my private fork. Then I can keep it in sync with the main repository, and only push up, or request a merge when it is done. No more annoying Slack post-commit hook spam!

git remote add origin git@github.com:DanubeRS/Project.git
git remote add upstream git@github.com:Company/Project.git

git checkout master
git fetch --all
git merge upstream/master
git push

There we are, all synced up and just fetch, merge upstream, and push up to keep your fork in sync.

Atomic commits

Keepings your commits small, and atomic is a virtue obtained after too many “God Commits” and the pains introduced later on. Further on in the list I will talk about rewriting history, and small commits are a lifesaver. Trust me, do it.

  • Code changes one thing, or one aspect of the greater branch
  • Message explains change, ideally in present tense

Sometimes, you forget to step back and commit every time a brick for the house is laid, but fear not, interactive adding really helps here.

git add -i

This provides a great way to pick exactly what you want to add, largely with the patch (p) option, where each change chunk is reviewed, and staged.

Rebase every morning

My routine every morning

  1. Make coffee
  2. Rebase my branch on latest master
  3. Prepare for involuntary movements invoked by step 1

Why? It keeps your branch up to date as a part of keeping regular. By that i mean changes you have applied are updated to the current base branch, meaning when merge comes along, and the inevitable conflict(s) surface, the office pearl-clutchers won’t learn any new words. I’ll talk more about rebasing down below.

git checkout master
git pull
git checkout my-feature
git rebase master

Don’t compensate with a big branch

In similar tone to keeping your commits small, the same can be said about a branch. In an ideal world, code would never change, and we would all be out of a job. On the other end of the scale you’re stuck resolving conflicts all day for changes you made 6 months ago, and since the code has diverged so much between the two, you might as well end it all now.

The ideal place to be is somewhere in the middle, where you still keep your job, but don’t change so much that when the feature needs to be published the whole day isn’t lost trying to smash it all together. Rebasing frequently (see above) can mitigate this, but is a band aid on the festering wound of long-running branches.

We get told to break problems down into smaller and smaller parts, and the same can be applied to branching. Create a branch, patch a little part, merge in, and rinse-repeat.

The WIP Commit

Theres a fire. Get. Out. Now. But the code! Oh god, the code! Quick, commit and push and we can sort it out when the smoke clears. WIP IT! WIP IT GOOD!

There is a right way to create a WIP commit, and a wrong way.

The right way and wrong way start off the same;

git add .
git commit -m "WIP"

The wrong way is when the commit is left in history and becomes a part of the history. It breaks the rule of atomic commits, and good messages.

Instead, reset it.

git reset

There we go, everything is back before the building burnt down. Add interactively, and make sure to split things up where required.

Make messages great again

git log --oneline
e658f438c (HEAD -> 12345-a-feature) finished component
4b257786f fix bad css
27434dcb1 dan help pls
a1166a569 WIP
ad29c7fb6 WIP decorate stored POCO
a4f311933 moved vue to single file component

I punched the guy who wrote those messages.

Sure, I would not have judged if it was a private fork, in the same way that I don’t judge behind the closed doors of ones own castle. I would have kept things tidier personally, but each to their own, I guess.

But no, this was a merge request. And yes, GitLab lets a squash take place if requested to clean things up, but the messages left a bad taste in my mouth.

As explained above, commits are history, and it only works if it makes sense, both content, order, and message.

git log --oneline
 e658f438c (HEAD -> 12345-a-feature) add profile link to menu
 4b257786f fix bad alignment of icons in profile
 27434dcb1 query current user on profile component load
 a1166a569 generate TS API from OpenAPI
 ad29c7fb6 add API endpoint to query user data
 a4f311933 move user profile component to single file

Ahh, thats better. Says what it does on the box

Squash!

Rewriting history is a bit of a tricky topic. In some cases it’s magical, some not so much. Yeah, it can be great to renovate the house, but not when you’re living in it. Don’t pull out the carpet from under colleagues, thats not how to make friends.

So when do you squash? Well, never anything public, or used by other devs*.

Notice that asterisk? Well truth be told, your judgement should be used when doing anything like this, but generally, it should be done to bundle up a stretch of work that was a little messy, or when a feature is complete, and should be summed up in a single commit.

For example, imagine working on a branch, with 3 commits. The first commit is your initial implementation. It was flawed, but workable. The 2nd commit is a fix of the initial solution. It works well, but the codebase is a mess. The 3rd commit is a refactor, splitting files up, conforming to coding styles, etc.

Squishity squashity, and its as if you did it right the first time, with perfect styling and nobody is the wiser! Thats the power of rewriting history, but of course with great power, comes great potential to royally fuck things up.

Gitting –pretty

I love Git Log. And Reflog for that matter. Its the replacement for that classic tree view that we see in our graphical clients. But its better! Way better.

Out of the box, you get some neat stuff.

git log --graph --oneline

And theres you’re branch, looking suave.

git log --oneline upstream/master..

Hey look, everything since master!

git log -p -n 5

And theres every change in the last 5 commits, warts and all.

There are countless ways to use log, and come can get quite verbose in their commands. Consider writing an alias to help shorten the common ones to a single word or two.

Trust me, learn basic VI/VIM

For me, vim is an addiction. All my editors have support for it, and I’m helpless without it. Jumping around code without a mouse in the way simple VI commands let you is witchcraft to those around me.

But its also daunting for those that aren’t indoctrinated in it, or if they are, just know

:wq!

Git CLI uses inbuilt VI for navigation. And if not, less provides a great wrapper around output, and can fly around as need be. Even your merge conflicts can be done in vimdiff, which I admit, isn’t really all that great, coming from kdiff3, but hey, at least it can be done.

Heres my list of must-knows

  • h/j/k/l
  • Forward and Backwards search (/ and ?)
  • quantifiers (jump up 5 lines)
  • goto (G/gg) and line navigation
  • Saving!

There, you too can amaze your friends, family, colleagues, and hot dates!