pzavolinsky

Pato Z

Posted on March 23, 2022

Brutal wisdom

A kid and an old man sit by a campfire by the side of the road in the middle of nowhere. There's something quite odd about the old man.

Perhaps it's the glint of mischief in his one good eye (the one not covered by the eye patch). Perhaps it's his sly smile or the reflection of the fire light in his teeth that look like diamonds*. Perhaps it's his huge sword that rests by his cane or his fashion choice wearing full barbarian attire (leather underwear and all).

* or the fact they actually ARE diamonds.

The old man is telling a story the kid has heard a million times, but it's a great story, one that once started, should be told all the way to the end, it's a story of...

Brutal wisdom

Listen kid, if you want to make it in this profession*, you'd better have a few tricks up your sleeve. The fact that our uniform is pretty much leather underwear and boots makes the whole "sleeve" situation all the more complicated...

* that is, to survive.

I'll tell you a few basic tips, the ones I store in my metaphorical suitcase of knowledge**.

** or "luggage" if you would.

Let's start with the basics...

Getting your bearings

Some distance away a bunch of elderly warriors are fast asleep. They call themselves "The Silver Horde". One of them is snoring loudly, the rest don't seem to mind.

When you go adventurin' it's very important to know where you're goin'. See Mad Hamish over there, the old man points to a senior barbarian snoring loudly amongst the Horde, when he was younger he spent months on an uneventful and boring quest only to later realize he was questin' in the wrong direction.

You need to learn to read a map, and the best map out there it's called git log, but not just any git log, a very specific one:

git log --decorate --graph --oneline
Enter fullscreen mode Exit fullscreen mode

This shows a nice view of your commit history like this one (newest commit first):

* 85535bb493 (HEAD -> your/branch) Sit in your throne and mope in boredom
* b0e7ec39d5 Keep fighting while looking awesome
* ef95ab8b04 Trip, fall and lose dentures
* c5059d4b97 Swing axe in style
* d3a023c22d Crack some skulls
Enter fullscreen mode Exit fullscreen mode

Of course, no barbarian would ever type that long git log command. Instead accomplished barbarians would just:

git config --global alias.ll "log --decorate --graph --oneline"
Enter fullscreen mode Exit fullscreen mode

And then use git ll instead.

Now let's talk about something else...

A small step goes a long way

When you put your hip at risk with every roundhouse kick, you learn to take it slowly. If there's a lesson for you to learn here, this is it: write wee tiny commits!

You renamed a function or variable: commit!

You changed a function signature: commit!

You moved a file: commit!

You swapped the execution order of some functions*: commit!

You extracted a function: commit!

You inlined a function: commit!

You changed some logic: commit!

You refactored some imperative logic into an expression: commit!

If in doubt: commit!

* hopefully pure functions.

There is a catch though: every commit must build. Every commit should be something you'd be proud to put in production**.

** even more importantly, every commit should be one that your mum would be proud to pin on her fridge, whatever that is.

No broken commits. No "fix the mess I coded two commits ago" commits. No "let me revert all this nonsensical attempt at solving this problem" commits. No "...and now I remove all those debug logging functions from the code" commits. No "fix code review comments" commits.

Keep your history tidy and you will be regarded as the hero you are. Your legend will live on forever.

But what if you already messed up? You need...

Savegames

For most adventures, having no plan is the best plan. When that plan fails, having a plan B is the bestest of plans.

The kid could swear the sleeping seniors snore in approval of that statement.

Every now and then even the most accomplished heroes take a wrong turn and find themselves surrounded by evil, outnumbered and outcrossbowed.

Before you know things will get nasty, so it's always useful to have a checkpoint to go back to if things get too messy*.

* too messy by barbarian standards can by worse than debugging hardware with a shaky pulse.

To create a save game all you need to do is run:

git branch <save-game-name>
# for example: git branch checkpoint
Enter fullscreen mode Exit fullscreen mode

To load a save game you just run:

git reset --hard <save-game-name>
# for example: git reset --hard checkpoint
Enter fullscreen mode Exit fullscreen mode

As with any save game out there, loading a save game means losing all your unsaved progress so be careful.

But what if you're in a pickle** and forgot to save?

** poor Old Vincent, GNU.

Don't worry, life also has...

Autosaves

Manual savegames are always handy, but in case you messed it up big and monsters are in hot pursuit, you can always fish for an autosave that can magically get you out of this situation*.

* it's a million to one chance, so you know the odds are in your favor.

You can check the autosaves by running:

git reflog
Enter fullscreen mode Exit fullscreen mode

That will show a list of commit ids, surely one of those is a safe enough state to go back to (newest changes first):

de42f4a (HEAD -> master) HEAD@{0}: checkout: moving from cd7a1bb60f3c6eacba7e78cb5c665c75da19686f to master
cd7a1bb HEAD@{1}: checkout: moving from master to cd7a1bb
de42f4a (HEAD -> master) HEAD@{2}: reset: moving to HEAD~1
cd7a1bb HEAD@{3}: Swing axe like in style
de42f4a HEAD@{4}: Crack some skulls

Enter fullscreen mode Exit fullscreen mode

The reflog is append-only so you're pretty safe moving between those commits, but beware of some spells like gc and prune that might mess up with this.

Never making mistakes

You don't get to be my age in this line of work without bending a few rules. In this case, I'm talking about the rules of space and time.

You just tripped mid fight and lost your dentures? It happens. And when this happens you wish you could go back and change the past.

So that's exactly what you do!

You concentrate really hard and do a:

git rebase -i <base-branch>
# usually: git rebase -i origin/master
Enter fullscreen mode Exit fullscreen mode

That move it's called "the interactive rebase" and it helps you overcome your mistakes and always look like a legend.

It usually shows something like this (oldest commit first):

pick d3a023c22d Crack some skulls
pick c5059d4b97 Swing axe in style
pick ef95ab8b04 Trip, fall and lose dentures
pick b0e7ec39d5 Keep fighting while looking awesome
pick 85535bb493 Sit in your throne and mope in boredom
Enter fullscreen mode Exit fullscreen mode

Just find the broken commit, replace the pick with an edit, save and exit your editor*.

* as you grow old like me, trivial tasks like tying your boot laces and exiting your text editor can become impossibly difficult.

Now watch where you step, fix the issue, git add your files, then git rebase --continue.

Type the new commit message and carry on.

A quick git ll shows (newest commit first):

* 4ff8e57a4d (HEAD -> your/branch) Sit in your throne and mope in boredom
* 6a998b0893 Keep fighting while looking awesome
* 804cfb513c Pretend to fall and dodge attack
* c5059d4b97 Swing axe in style
* d3a023c22d Crack some skulls
Enter fullscreen mode Exit fullscreen mode

Better than standing up is never falling down

But what if you tripped, fell, fought on the floor for a while, then stood up and kept fighting, is there a way to avoid this mess?*

* surely your sciatica would appreciate it.

Of course there is, let's say you are here (git ll, newest commit first):

* 4ff8e57a4d (HEAD -> your/branch) Stand up and recover dentures
* 6a998b0893 Fight on the floor, kick evil in the groin
* ef95ab8b04 Trip, fall and lose dentures (**)
* c5059d4b97 Swing axe in style
* d3a023c22d (origin/master) Crack some skulls
Enter fullscreen mode Exit fullscreen mode

** coding barbarians usually break tests instead of losing dentures, but the pain is more or less the same.

You can git rebase -i origin/master (oldest commit first):

pick c5059d4b97 Swing axe in style
pick ef95ab8b04 Trip, fall and lose dentures
pick 6a998b0893 Fight on the floor, kick evil in the groin
pick 4ff8e57a4d Stand up and recover dentures
Enter fullscreen mode Exit fullscreen mode

You can move the "stand up" commit right after the "trip and fall" commit like this:

pick c5059d4b97 Swing axe in style
pick ef95ab8b04 Trip, fall and lose dentures
pick 4ff8e57a4d Stand up and recover dentures
pick 6a998b0893 Fight on the floor, kick evil in the groin
Enter fullscreen mode Exit fullscreen mode

And then change the pick to a squash, save and exit:

pick c5059d4b97 Swing axe in style
pick ef95ab8b04 Trip, fall and lose dentures
squash 4ff8e57a4d Stand up and recover dentures
pick 6a998b0893 Fight on the floor, kick evil in the groin
Enter fullscreen mode Exit fullscreen mode

Update the commit message (or don't***), save and exit again.

*** if you don't want to change the message, you can use fixup instead of squash.

And now the end result looks like this (git ll, newest commit first):

* 959eca46a8 (HEAD -> your/branch) Keep fighting while looking awesome
* 804cfb513c Pretend to fall and dodge attack
* c5059d4b97 Swing axe in style
* d3a023c22d (origin/master) Crack some skulls
Enter fullscreen mode Exit fullscreen mode

(you can use reword in git rebase -i to change a commit message, like 959eca46a8 above)

Refactoring quests

Every now and then you'll have to embark on a refactoring quest. Those quests are strange because if done right, after you're done no one will know they happened. And that's because they don't alter the (functional) world in any perceivable way.

A responsible barbarian would ensure that the code that needs changin' is properly covered by tests. The problem with this is that when you start refactorin' you probably don't know the code that needs changin'. So more often than not, you'll finish the refactor, then check that your refactored code is properly covered by tests and if not you'd add the missing tests.

Good barbarians go home satisfied with this. Great barbarians go one step further.

Let's say you are here (git ll, newest commit first):

* 6a998b0893 (HEAD -> your/branch) Check if the coast is clear (write tests)
* 742408667f Inline treasure in underwear
* ba001cea18 Extract treasure
* cd2d094a79 Refactor evil away
Enter fullscreen mode Exit fullscreen mode

Great barbarians would git rebase -i origin/master (oldest commit first):

pick cd2d094a79 Refactor evil away
pick ba001cea18 Extract treasure
pick 742408667f Inline treasure in underwear
pick 6a998b0893 Check if the coast is clear (write tests)
Enter fullscreen mode Exit fullscreen mode

They'd then move the last commit to the very top like this:

pick 6a998b0893 Check if the coast is clear (write tests)
pick cd2d094a79 Refactor evil away
pick ba001cea18 Extract treasure
pick 742408667f Inline treasure in underwear
Enter fullscreen mode Exit fullscreen mode

And then change the first pick to an edit (or a break), save and exit the editor:

edit 6a998b0893 Check if the coast is clear (write tests)
pick cd2d094a79 Refactor evil away
pick ba001cea18 Extract treasure
pick 742408667f Inline treasure in underwear
Enter fullscreen mode Exit fullscreen mode

The rebase will stop right after the test commit, but before any changes to other code.

This is the perfect time to run the tests. If they fail at this point you know that the refactor commits that follow did some functional change and this could very well be a bug.

After your done testing and the tests pass, you can just run git rebase --continue*.

* and then you run the tests again for good measure.

Your trusty companion

Some think we barbarians are solitary creatures, they're wrong. While you're cracking skulls over here, you need someone watching your back over there*.

* making sure you don't fall, swing your axe in style and take your pills.

And there is one companion that is by far the best companion to bring to any adventure. But there's a catch...

Remember when I was telling you to take it slowly and write wee tiny commits?

Well this companion will only join your party if you agree to make tiny commits and have all those commits pass the build. Annoying, I know, but the payoff is BIG.

This companion is called git bisect.

You've been cursed with an annoying bug? You've been poisoned by a strange venom? bisect is your friend!

bisect will go look for the solution, antidote or whatever while you go take a much needed nap.

This is how it works:

git bisect start <bad-commit> <good-commit>
# Usually git bisect start HEAD origin/master
Enter fullscreen mode Exit fullscreen mode

That will wake up bisect and will pick a commit for you to test. Of course no self-respecting barbarian would manually test the code when they can write a script to test it for them:

git bisect run <some-script-that-repros-the-bug>
# for example git bisect run ./run-tests.sh the-broken-test
Enter fullscreen mode Exit fullscreen mode

The script you pass to bisect run needs only return an exit status of 0 if the commit is good and anything else (except 125**) if the commit is bad.

** the best numbers are arbitrarily hardcoded into the fabric of the multiverse, like pi.

A typical bisect run script would build your code and run some repro tests.

After bisect starts running you can go take a nap and let it work. Once it finishes, it'll tell you which is the first commit that failed the script (usually the commit that has the bug).

Once you know where evil lurks you can make a note of the commit id and then git bisect --reset to go back to normal.

Finally, do a swift git rebase -i, edit that commit and nip the evil in the bud.

The final test

The old man stares intently in silence and then says:

Right, let's see what you've learned?

A cold shiver runs through the kid's spine remembering an inter-dimensional flashback of a high-school class. The kid shakes that nasty feeling while reluctantly handing over some notes.

The senior barbarian reads them out loud:

  • git config --global alias.ll "log --decorate --graph --oneline"
  • git ll to get your bearings
  • git branch <savegame-name> to create a savegame
  • git reset --hard <savegame-name> to load a savegame
  • git reflog to fish for an autosave
  • git rebase -i <base-branch> to change the past and always look like a legend
  • git bisect start HEAD <base-branch> and then git bisect run <some-script-that-repros-the-bug> to have bisect work while you take a nap
  • And most importantly: write wee tiny commits!

The old man cracks a wide smile and says:

Good, good, you've been paying attention.

Now for the final piece of advice, keep your eyes open, your knees warm and be on the lookout for the three most important things in life: hot water, good dentistry and soft lavatory paper.

💖 💪 🙅 🚩
pzavolinsky
Pato Z

Posted on March 23, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Brutal wisdom
git Brutal wisdom

March 23, 2022