scares a lot of people…
…but the benefit is a pretty commit history!
… but only if everyone does it :(
In short: rewriting commit history.
Longer explanation:
your own commits from a branch are temporarily rolled back
the commits of the rebase target (=develop) that you didn’t have
yet are applied
your own commits are replayed on top
Cleaning up your commit history in feature branches provides a clear overview of what has been done for which feature
Because we’re human and make mistakes. This is fine on feature branches, however there’s no need to bother your colleagues with them
Let’s say - a feature branch is started.
develop
A -- B -- C -- D
\
E // start of feature
So, you happily implement features and commit without worry…
develop
A -- B -- C -- D
\
E -- F -- G -- ... // much fun is had
But, so did others, and they had easy features that were simple to review and merge, causing develop to have moved forward, while you were doing all the hard stuff!
develop
A -- B -- C -- D -- H -- I
\
E -- F -- G -- ... // much fun is had
So, you finish your feature, the build passes, code is the prettiest thing ever to be seen by mankind… but the commit history is a bit ugly:
3 commits? this can actually be condensed into a single commit, because of raisins.
there are a couple of merge conflicts because of commit H…
We want to solve two problems:
our feature should merge cleanly, and after commit I
.
we want to combine commits E
, F
and G
.
So, when we start the rebase, our commits are rewinded:
develop
A -- B -- C -- D -- H -- I
\
// we're here, sort of
And then, the other commits are applied:
develop
A -- B -- C -- D -- H -- I
\
// now we're here
develop
A -- B -- C -- D -- H -- I
\
// and now we're at the HEAD of develop!
Then, we apply our own commits:
develop
A -- B -- C -- D -- H -- I
\
E // still fine
develop
A -- B -- C -- D -- H -- I
\
E -- F // oops, this one has a merge conflict!
… you resolve the merge conflict, and continue
develop
A -- B -- C -- D -- H -- I
\
E -- F -- G
Bam, rebase done, conflicts resolved.
Now, we’ll clean up our stuff - squash E, F and G together:
develop
A -- B -- C -- D -- H -- I
\
J
There, single commit for our feature, that references the develop HEAD, without merge conflicts. PR ready to be merged!
Make sure your local develop
is up to date:
git checkout develop && git pull origin develop
Start the rebase: git rebase -i develop
, which prompts you with a ‘commit plan’:
pick 34836972 Start automating production dumps for testing/development purposes
pick 5abe11b0 wip
# Rebase 4aa66062..5abe11b0 onto 4aa66062 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# 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.
#
# Note that empty commits are commented out
the WIP commit is fine, I change pick
into fixup
, :wq
and rebase is ‘done’
output of git status
:
git:(feature/dev-db-dump) git status
On branch feature/dev-db-dump
Your branch and 'origin/feature/dev-db-dump' have diverged,
and have 17 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
now push to the remote, with force, since we rewrote history:
git push --force-with-lease
done, feel good about yourself
maaaaaassive PR, with a lot of merge conflicts -> takes a long time to resolve them commit by commit, and it’s possible ‘in-between’ commits cancel each other out.
Solution: rebase on current branch: git rebase -i HEAD~4
and squash commits before rebasing on develop.
Or, if that also complicates things too much, git merge develop --no-ff
conflict resolution results into ‘empty commits’ later on. You can skip these with git rebase --skip
.
git says branches have diverted -> this is expected, you should push with --force-with-lease
. If you pull
now (as it suggests), you’ll introduce nasty merge commits and duplicate commits. Ew.
when in doubt, create a backup branch from your feature branch: git branch backup
, so you can recover using that branch if you mess up.
configure git globally to pull with rebase (= git pull --rebase
):
# ~/.gitconfig
[pull]
rebase = true # <-- THIS
Web APIs The Business Process Task Library in Common Ground