Enforcing logical order of chronologically incoherent commits in Github

Hugues Alary

2019/04/04

The problem

Github’s pull request interface has the somewhat unexpected tendency to show your commit history in a different order than what otherwise appears when running git log.

Imagine the following master and feature branches.

The master branch has 1 commit that adds 1 file named file 0 as shown in the following output:

(master)$ git log --pretty=format:"%h %d %cd %s" master
8ad092a (origin/master, master) Tue Apr 4 00:00:00 2019 -0700 Adding file #0

The commit was made Tue Apr 4 2019 at 00:00:00.

The feature branch, based off of master and subject of our upcoming PR, adds 4 more files, starting with file 3, followed by file 1, file 2, file 4 and finally file 5:

(feature)$ git log --pretty=format:"%h %d %cd %s" feature

a34d3c1 (HEAD -> feature) Tue Apr 4 05:00:00 2019 -0700 Adding file #5
376448b Tue Apr 4 04:00:00 2019 -0700 Adding file #2
412e275 Tue Apr 4 03:00:00 2019 -0700 Adding file #4
a11c180 Tue Apr 4 02:00:00 2019 -0700 Adding file #1
62de4c8 Tue Apr 4 01:00:00 2019 -0700 Adding file #3
8ad092a (origin/master, master) Tue Apr 4 00:00:00 2019 -0700 Adding file #0

We can see that each commit was made 1 hour appart from each other, starting at 1am with file #3 and finishing at 5am with file #5.

Now imagine we wish to organize our commits such that our file #0 through file #5 are commited in ascending order.

We can achieves this using an interactive rebase of our feature branch:

(feature)$ git rebase -i master

This command opens up our text editor for us to re-organize our commits:

pick 62de4c8 Adding file #3
pick a11c180 Adding file #1
pick 412e275 Adding file #4
pick 376448b Adding file #2
pick a34d3c1 Adding file #5

# Rebase 8ad092a..a34d3c1 onto 8ad092a (5 commands)
...some output removed...

Which we do, in the following order:

pick a11c180 Adding file #1
pick 376448b Adding file #2
pick 62de4c8 Adding file #3
pick 412e275 Adding file #4
pick a34d3c1 Adding file #5

# Rebase 8ad092a..a34d3c1 onto 8ad092a (5 commands)
...some output removed...

After applying our rebase we can check the final result and see that our commits are now in a more logical order, with file #1 through file #5 committed in order:

(feature)$ git log --pretty=format:"%h %d %cd %s" feature

2c00db3 (HEAD -> feature) Tue Apr 4 05:00:00 2019 -0700 Adding file #5
6c383ed Tue Apr 4 03:00:00 2019 -0700 Adding file #4
ccd5cbb Tue Apr 4 01:00:00 2019 -0700 Adding file #3
3320005 Tue Apr 4 04:00:00 2019 -0700 Adding file #2
916a257 Tue Apr 4 02:00:00 2019 -0700 Adding file #1
8ad092a (origin/master, master) Tue Apr 4 00:00:00 2019 -0700 Adding file #0

After pushing our feature branch to Github, we can create a pull request:

createPR

And, as one would expect, our commits show up in ascending order, just like we saw with git log.

However, upon submitting the PR, the commits are back in the original, chronological, order:

submittedPR

This wasn’t expected.

From a reviewer’s perspective, the chronology, the time and date a specific feature was born, matter less than its logical, intuitive ordering. Not only that, but, a pull request doesn’t only pertain to the code itself. It is also about its organization in the VCS and how it affects the repository tree.

Github, by showing commits in their chronological order, which isn’t what git log utimately output, which also isn’t what Github’s pull request submission interface itself shows, complicates the life of both the developer and the reviewer.

They do, however, address this issue in their FAQ; unfortunately, the proposed solution isn’t of much help:

"" If you always want to see commits in order, we recommend not using git rebase. ""

Well, thanks Github, but that is not an option!

The exercise is left to us to figure out.

Solution

So here’s a solution: change the authoring date of each of your commits such that the chronological authoring matches the logical ordering.

That is, so that each commit’s authoring date is always in the future relative to its parent authoring date.

One way to achieve this in an automated manner is to use the --exec parameter of git rebase:

(feature)$ git rebase -i master \
--exec 'git commit --amend --no-edit --date="now" && sleep 1'

Which will open your text editor and execute the following rebase:

pick 916a257 Adding file #1
exec git commit --amend --no-edit --date="now" && sleep 1
pick 3320005 Adding file #2
exec git commit --amend --no-edit --date="now" && sleep 1
pick ccd5cbb Adding file #3
exec git commit --amend --no-edit --date="now" && sleep 1
pick 6c383ed Adding file #4
exec git commit --amend --no-edit --date="now" && sleep 1
pick 2c00db3 Adding file #5
exec git commit --amend --no-edit --date="now" && sleep 1

# Rebase 8ad092a..2c00db3 onto 8ad092a (10 commands)

Each exec line will execute git commit --amend --no-edit --date="now" && sleep 1 after each commit rebase.

git commit --amend --no-edit --date="now" effectively changes the authoring date of the commit to now.

sleep 1 is my way of ensuring that no commit ends up with the same date and time. git rebase being really fast, multiple commits would end up with the same authoring date, leading to Github showing the commits in yet another random order.

We can verify the dates correctly changed:

(feature)$ git log --pretty=format:"%h %d %cd %s" feature

(HEAD -> feature, origin/feature) Thu Apr 4 22:00:49 2019 -0700 Adding file #5
Thu Apr 4 22:00:48 2019 -0700 Adding file #4
Thu Apr 4 22:00:47 2019 -0700 Adding file #3
Thu Apr 4 22:00:46 2019 -0700 Adding file #2
Thu Apr 4 22:00:45 2019 -0700 Adding file #1
(origin/master, master) Tue Apr 4 00:00:00 2019 -0700 Adding file #0

Once the rebase is applied, you can push your branch and submit a PR.

If you already pushed your branch, force pushing it will also work and reloading Github’s interface will show the commits in the right order.

Happy reviewing!