(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 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:
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:
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:
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!