The wonders of Git Rebase

One of the tools I find more useful on `git`, and which I love the most

is `git-rebase(1)`. It works around the premise that originally bought git to

control version systems: that you *can and should* rework your repository

history, to make it more organized and readable.

A toot praising git-rebase on my fediverse account

Around the world, each project stablish its one convention of how to work with

git, some of them use techniques like git-flow, others, just a branch and tags,

others divide between `master/main` and a `production/release` branch. The

company I work for convention is to always create a specific branch to work on a

new feature, bug fix or whatever. Having done that, I adopt the discipline to

(1) keep it up to date with the parent branch using `git pull --rebase

origin/parent_branch` and (2) as I work on it, I try to keep it tidy using `git

rebase -i HEAD~N`.

Rebase

The rebase command has a very simple behavior, essencially it does not execute a

merge (or does not generate a commit that introduces the changes upstream),

rebase temporally remove all your work, pull the newer commits unto your branch

and then sequencially applies your patches on this updated branch. The benefit

being: the branch ends up only with a list of your newer commits, on a very

coherent and readable way.

The `-i` parameter on the command, is a shortcut for git start a interactive

rebase, starting from the commit I have asked for [^1]. For example, `git rebase

-i HEAD~5` will start a rebase up until my last 5 commits. It will proceed to

show you this:

[^1]: `HEAD~N`, means essencially from my HEAD commit up until N commits before

this.

pick 9fdd140 hooks: Flesh out the Hook::Destroy service test
pick ecb296e resource: Flesh out the resource test
pick bd115a0 hooks: Generate token when subscribing
pick fcaba9d hooks: Flesh out the handler test
pick 3926d4b doc: Add document about testing

# Rebase bed2ff9..3926d4b onto bed2ff9 (5 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

That means, it will show my last five commits, and ask me what I want to do with

them, presenting me the options to pick, remove or edit (`edit` or `reword`) or

join them (`fixup`, `squash`).

Discipline

My work plan, usually is the following: as I keep advancing on my work, I commit

them locally, in a very vague way so I can understand what sort of milestone

that represents. Whenever I feel I can present this work upstream I use _rebase_

to rewrite, split or join commits forming a narrative of coherent and readable

sequential steps.

Seeking to write this narrative, I strive to write atomic changes that themself

are capable of answer the question of _what has been done?_ and _will this break

the tests that exists?_ ¹

A experience that has helped me a lot on this purpose, was to contribute to

projects that utilize a public developer mailing list², sending patches

instead of creating Pull Requests. Specially because the work logic changes with

that. The way to work presented by tools like Github, GitLab and other similar

derivations is to create a PR and keep adding commits _ad infinitum_ until the

work on it is ready to merge. Yet, the logic behind git via email is of trying

to be assertive as possible, avoiding sending giant patchsets³ with changes

all over the place. One of the ways to deal with patchsets is that is possible

to apply only a subset of the overall changes presented, and ask revision for

specific patches that need some more work.

¹ That means no `fix tests` or `fix typo` commits.

² Which is the way git has been designed to work, notable mailing list

driven developments examples are the Linux Kernel, git, cgit, dwm and other

suckless tools, Que é o formato de contribuição do qual o git foi feito para

funcionar originalmente, coreutils, musl-libc, freedesktop, vim, emacs, *BSD

and many others.

³ A patch is a commit, and a patchset is a collection of patches, somewhat

akin to a PR.

Applying this logic, I strive to break a long patchset into several different

patchsets. A example here could be organize _includes_, remove white spaces or

any other cosmetic changes on the source code that hasn't a direct connection

to what I was originally writing. Each one with the most possible atomic

patches, with a good description, organized by modules or systems. I generally

like to use the following format for my commits: `system: subsystem,

description of what this commit does` and describe why this change is necessary

and my approach on the commit message.

Concluding: git is fantastic. Having discipline to utilize it and learn how it

works is a must for any programmer. One of the main differences between git and

other VCS before him is that the history of the repository is not written in

stone, is not read-only. Besides that, git has an extensive offline

documentation on its manpages available to you to study it. I would strong

recommend you to read git-rebase(1), giteveryday(7) and gitrevisions(7).

--

The post "The Wonders of Git Rebase" was published in April 29, 2021.

Back to the home page

The content of this site is under the terms of Creative Commons CC-BY-SA. The

code is available under GPL-3.0.