Assumed Audience: Hackers, Git users, code archeologists, and anyone who would need to do forensics on Yzena repositories.
Epistemic Status: Very sorry and repentant.
Forgive me, for I have sinned.
I had to create a new GPG key, separate from my personal one, and I had to re-sign every single commit in a few repos with the new key.
This is rebase on a whole new level.
These were my requirements:
- The author date needs to be the same.
- The committer date (yes, it’s different from the author date) needs to be the same.
- It has to deal with a repo that is a bunch of combined repos.
It turns out that the first is easy, but the second is nigh impossible, and the third needs some manual work.
For obvious reasons, do NOT try this at home!
If you must do this, do it on a full new clone of the repo like I did. I had to try a couple of times to get it right, and I never had the possibility of losing data.
The resulting command:
$ git rebase --exec \
'GIT_COMMITTER_DATE="$(git show --format=tformat:%aD XXXXXXXXX | head -n1)" \
git commit --amend --author="Gavin D. Howard <email_redacted>" --no-edit
--no-verify -S' --committer-date-is-author-date -i --root --rebase-merges
Here’s the explanation:
git rebase
I said that this is rebase on a whole new level, but it is still a rebase.
--exec '...'
git
rebase “recommits” all of the target commits, changing things that the user asks to be changed, and it can execute a command on each recommit.GIT_COMMITTER_DATE="$(...)"
This sets
GIT_COMMITTER_DATE
, which is the only way I could find to set the committer date. What this does is puts the result ofgit show ...
into the variable.git show --format=tformat:%aD XXXXXXXXX ...
This shows a commit with the short hash of
XXXXXXXXX
. This is a placeholder for later. The given format string just tellsgit
to show the author date of the commit.... head -n1
This takes the first line of the
git show
because thegit show
will show what you tell it to, as well as the patch. This filters out the patch.
git commit --amend ...
This is what does the recommit. It does an
--amend
to change the commit I’m trying to rebase.... --author="Gavin D. Howard <email_redacted>" ...
This sets the author to the new commit. I needed to set this for legal reasons.
... --no-edit --no-verify -S
These are generic options to ensure the commit isn’t wrongly edited or have useless hooks run. And also sign.
--committer-date-is-author-date
This was an attempt to change the committer date to the author date. It didn’t work, but I just copied and pasted, so this ended up in the final command.
-i
This makes the rebase interactive. This isn’t important because
--exec
will make the rebase interactive.--root
The option makes the rebase happen from the start commit, so I wouldn’t have to find the start commit.
--rebase-merges
This is the option that made the rebase work in the presence of multiple histories. It tells the rebase to also rebase merges instead of just regular commits.
But even that isn’t everything.
When you run the command, git
does nothing you expect. Instead, it pulls up
your $EDITOR
. For me, it’s Neovim.
In the editor is a file. There are two lines for every commit, one to allow the user to pick the commit or reject it, and one saying what command will be executed when rebasing that commit.
There are a few more lines, but you don’t need to worry about them unless there are multiple branches. Those lines basically select the branch.
The fact that each commit has its own “exec” line means that the user can set a different command for each commit. I used this to my advantage.
This was the reason for the XXXXXXXXX
in each command. I used that to set a
Vim macro to grab the hash for the macro from the first line for the commit and
then replace the placeholder with the commit hash. This is what made the
GIT_COMMITTER_DATE
trick work.
And then, once you save the file and quit the editor, git
starts running. This
is the actual rebase process.
Because of the repo with multiple histories, there were a few merge conflicts. This is the manual part, but it turned out to not be a problem for me.
Anyway, that’s the full explanation of how I did it.
It wasn’t fun; I had one repo with over 4000 commits to re-sign. It was quite tedious to use the Vim macro because I wanted to make sure that the macro worked on every line.
In conclusion, don’t do this. Remember, it’s a sin.
But it was…enlightening.