Reorder git commit history by date
This is just an idea, I did not test it for this scenario but I used it (in a different way) to join two Git
repositories and keep the original commit dates.
If the history has branches and merges I think it's impossible to re-order them and keep the structure, even manual. The best you can get is a linear history.
Save the commit hashes and the timestamps (%ct
= commit date, %at
= author date) into a file, sort them by author date:
$ git log --pretty='%H %at %ct' --author-date-order --reverse > /tmp/hashlist
If the order provided by the command above does not satisfy you then force the order by sorting the output using its second field (the author date):
$ git log --pretty='%H %at %ct' | sort -k 2 > /tmp/hashlist
Create a new repository to hold the history ordered by author date. Create an initial commit setting the committer date in the past (before the oldest commit in your repository):
$ GIT_COMMITTER_DATE='2010-01-01 12:00:00' GIT_AUTHOR_DATE='2010-01-01 12:00:00' git commit --allow-empty
Put your own date into the command above.
Add the old repository as a remote into the new one, fetch all its commits. DO NOT set the master
branch of the new repo to track the one of the old repo.
Create a script that will cherry pick the provided commit and apply it on top of the current branch, keeping the original author date and commit date:
$ echo 'GIT_AUTHOR_DATE=@$2 GIT_COMMITTER_DATE=@$3 git cherry-pick $1' > /tmp/pick
$ chmod +x /tmp/pick
If you don't want to keep either the original author date or the committer date (or both) then just remove the corresponding assignment from the above command line.
Use the new script with xargs
to pick each commit in the selected order and commit it on top of your new master branch.
$ cat /tmp/hashlist | xargs -n 3 /tmp/pick
If everything went well then remove the temporary files created during the process.
$ rm /tmp/hashlist
$ rm /tmp/pick
Remarks:
- You will get a linear history. The original branches and merges won't be re-created into the new history timeline.
- The unmerged branches will not be copied at all. You can use
git rebase
to copy and attach them to the new commits. - Even if your repo does not have branches, there is still a high probability to get conflicts on cherry picks; it depends a lot of the changes introduced by the commits in the new order.
- If it doesn't work you can always remove the new repository and start over (or quit trying); the old repository is not changed.
reorder by interactive rebase
Assuming you would like to change the commit order only on one branch (master
).
Create a copy of the branch. This step is optional, you may do it directly on master
branch.
git checkout -b master2 master
Create a list of all commits. This command will sort them by Author time, oldest first.
git log --pretty='%H %at' | sort -k2 -n | awk '{ print "pick "$1 }' > /tmp/rebase.list
The output file looks like:
pick <hash of C1>
pick <hash of A1>
pick <hash of A2>
pick <hash of B1>
..
Run the interactive rebase going all the way back to the root of the repository
git rebase -i --root
You will be presented with the current list of commits. Delete everything and load the content of rebase.list
file (if your editor happens to be vi, issue commands :1,$d
and :r /tmp/rebase.list
). Save and exit.
After resolving the conflicts, you should end up with master2
branch with all commits reordered.