Sunday, 20 March 2016

Rebase - Why merge pulls when you can commit lies and rewrite history

When I introduced a client to rebasing, they asked me: would it avoid our coders breaking wood?

Erm... not exactly. neither rebasing nor merging avoid conflicts. Conflicts arise from people changing the same file roughly at the same time. 

Both merge and rebase involve taking the bull by the horns; The more often you do this, the smaller the trouble.

The chief way to avoid "broken branches" (so to speak) is programmers don't commit files more than necessary and keep them (files) reasonably small.

Digression: one reason I like SourceTree is giving me a convenient interface to review and cancel (before committing) edits which I'd rather not push - log-fu, red herrings, blunders.

Interactive rebase is a popular way to pull changes from master while working on something. I suspect it's popular because it's also a swiss-army-knife letting you do semi-useful stuff like trash the inept commit you did 5 minutes ago, reword PG-rated commits and combine 25 puny commits into the 2001 Space Odyssey monolith.

WHAT IS it?

So, while Nono was fixing this and that on his branch Sassy has been committing wildly on master. What rebase does is, it rebuilds Nono's changes on top of master.
It is cleaner and morally satisfying in that:
- Keeps one's private changes in a sequence.
- Changes appear in the history in the order that they got committed to master.

A fun (huh) example

On Sassy's branch

14:00 Sassy: check garden
14:15 Sassy: investigate living room
14:30 Sassy: look in the bathroom
14:45 Sassy: report no intruders

Meanwhile

13:55 Nono: break into garden.
14:10 Nono: 'borrow' Sassy's Gameboy
14:29 Nono: hide in the bathroom.

What a merge would do:

13:55 Nono: break into the garden.
14:00 Sassy: check garden
14:10 Nono: 'borrow' Sassy's Gameboy
14:15 Sassy: investigate living room
14:29 Nono: hides in the bathroom.
14:30 Sassy: look in the bathroom
14:45 Sassy: report no intruders

Wacky eh. Now the rebase:

14:00 Sassy: check garden
14:15 Sassy: investigate living room
14:30 Sassy: look in the bathroom
14:45 Sassy: report no intruders
13:55 Nono: break into the garden.
14:10 Nono: 'borrow' Sassy's Gameboy
14:29 Nono: hides in the bathroom.

And you know what? it all makes sense because local branches are private, parallel universes. What was happening on Nono's branch indeed was perfectly hidden from Sassy until Nono committed to master.

I'm not expecting anybody to love it until they used it like, 4-5 times. It does have detractors after all. Still, it's massively popular.

In my humble opinion history rewriting (including rebase) is fundamentally wrong and I'll be happy to get rid of git... when something better comes along. In the meantime it offers great convenience at low cost (statistically) so it's a neat compromise.

TAKE ME TO THE DARK SIDE

Before we start.
Assuming you're not vimdictive the following git-fu needs the text editor setup I describe at the top of my cheat sheet.

1. commit

git commit -m "fix something" # commit changes locally, on your branch, first

2. pull master changes from remote

git checkout master # landed on master branch!
git pull # now master is up to date.

3. rebase

git checkout nono # get back to your branch
git rebase master -i # "i" for interactive.

Up, pops your text editor. Do read what it says in here truly fascinating - so many parlour tricks/substitutes for git-fu that no sane coder will ever remember.
Well. You can edit this file (hence interactive) to do nice touch-ups. Like fix a garbage comment slandering the web administrator, merge commits or forever delete spoof commits.
Now Save and Quit the Text Editor to continue the rebase. At which point, if conflicts are detected, the rebase will stall and...

git status # shows conflicted files among other things

4. Fix conflicts before continue

Just do it!

git add --all # when you solved ALL the conflicts

5. Rebase on and on

git rebase --continue

6. Push to your remote branch

git push -f # DANGEROUS don't ever -f(orce) except while doing this. But necessary if you already committed to your remote branch

7. If you change your mind

Too much for you to handle? Witchcraft afoot?

 # instead of rebase --continue
rebase --abort

INTERLUDE

I encourage everybody - even project owners - to work on their own branch (unless of course you are using feature branches but... ...alright, not today)

THE FINAL ACT

Once again without comments. First the non-screwy rebase:

git commit -m "fix something"
git checkout master
git pull
git checkout noha2
git rebase master -i
git push -f

Then the screwy rebase (notice the font change?)

git commit -m "fix something"
git checkout master
git pull
git checkout noha2
git rebase master -i
git status
git add --all
git rebase --continue
git push -f

No comments:

Post a Comment