Consider the following example.
mkdir pgp-git-test
cd pgp-git-test
git init
touch a.txt
git add a.txt
git commit -m "Add a.txt" -S
touch b.txt
git add b.txt
git commit -m "Add b.txt" -S
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a.txt' --prune-empty HEAD
git log --oneline --decorate
b4efdf0 (HEAD -> master) iQIcBAABCgAGBQJVrvqHAAoJEGuo23L9/VuyntUQAIBD0g03rTKRkOd9eM4bJgUV jJezu7R4J0U+zVLrsrSl8oTrYrKPL5QAIqqaB9978qSx5WsmCJj8EfIZ2lwFj7kI sWWcqjAWcRjWrte/v7ehUyTpJF6h5mWJPbC31BueZ3qlVvvfI03NbMUGocm1VOvE KZakYkbhrA4ucA0K0YH9RKFo59cLS48SB7DQK4dBfdJSOnBC0Ga9pgBp8wnF2TQG znRA3MnGRPJMRxZsend5P6gyeGl3wo0J/yk8HDFZXudTRS3SLB+um3NcTXRLIE9Z Whud2oERKE9CuHU8Y64prbKKA27vWgaVQOC44ujaCqbYXuq+4Ozs34PBlf3CjJqW 19GdVBlqFfiyCPULwyxoPWkRk2kPQyEejt+sJIXG9QgefvoqFF5oLW/YA7AOrDSE luLbC8uxmTARCWGZVGINL7NmPmEVFDZVj9EyYOjxE/+0wm4cGNBHrL6/JMkVOgpT pWlgZWR3rX4IjzYtN6DMqKYNWVkVawZQUPh5n5jteuripWtnu5IG8vvvK2mtlkQ3 1OZIdQNAv0HYhBO0vHlV0o2TlVL5x9WfPFn+1XJepJUcoN3MdzXLxN27njdW5Bti olEqyHrTRxYJNZgSwpQ7WITheIFDqpdcoUV2h4hNjGcXfc0DfaevtCA/oQir+3L4 JlFB35Le9Yby9htVhlu2 =RJi9 -----END PGP SIGNATURE-----
9f82e63 iQIcBAABCgAGBQJVrvp7AAoJEGuo23L9/Vuy/3QP/itaNGwRtlPB4uajGGHMUPxn gnzd5k5gaWvWlE0Cn/v+CFqE9zVfNiqfsEwJ5YUycUNtEuF9rsiqeQWaWdHWquqr kOEwRx/9mliK7iC2MZBn/biY5wBE2VcO2m281SnCKxslCjHJxlBo9rglq1t0wybT CX7C7ScKAtYkos4c1vL0D5Bam2panVXs/KT/YDgWZT9kTg+lEd7NaIwxxNn7HNs7 sOvI2zLyca2FepahF99ZRGg1kKXarVh9nW4mZ1GfeAUuCNSwgwBv+NO3wFC1Blcp uzKAo1F/cN0rAOr0bMdIZ0qJEVMBBpaqodqRqCOcmYTm5CoKFmcNJvixPl+hYsQx mPFxM0yVsjlLAnIckNqos/T3i6T8zrb5X4g5ZwuQZzzNKy1xx809v9erb2HHK+d1 +MqdzwEcMyGqfyhz9s1BGrwpBk5CAg2MXbtPpoMTBIG7hmke1al89jvgBiuir06E kEN6jl/2yAfsj7k5ryjFQNSPJ+HYEyvYBCx3u+xXdA5IBH6CU2S44RqugwztbVKz /Viel4wIHJ8UCA85ZiprRWJE+nz1RXKlBZc/37W4vcSUSTELXEkhaybOM/eBKACR sDHOKq5MG9VmZXcu0Zs0cyEvuqljSnZggbDasXHj68b86rB5VRGIO10ad1xKPnFZ PTUmKKtz1NZkMmjIX4vR =Mnxi -----END PGP SIGNATURE-----
I have two questions. First, why does this not remove the first commit from the log? And, second, why is the PGP signature now at the top of the commit message and is there a way to avoid this behavior when using git filter-branch
?
(In case it matters, I'm using git v. 2.4.6.)
I don't have an answer as to why the first commit has not been removed from the history in this toy example.
However, as for the second question, it seems that this is status by design. The PGP signature is deliberately broken when using git filter-branch
so as to prevent someone from modifying the contents of a signed commit and making it seem like the person who signed the commit has signed off on the modified contents of the commit.
Presumably the broken signature is also placed at the top of the commit message to draw attention to this fact.
In my case, since all of the signatures start with something like iQIcBA
, I was able to remove the broken signatures by doing the following.
git filter-branch --msg-filter 'sed "/iQIcBA.*/,/.*END PGP SIGNATURE.*/d"' HEAD
If you want to resign the commits, you could do something like the following.
git filter-branch --commit-filter 'git commit-tree -S "$@"' HEAD
However, it seems that signing a tag rather than retroactively signing all of the commits is preferred (see, e.g., here).
This answer is based in large part on help from Jacob Keller on the git
mailing list.
In response to your first question. The documentation says:
Though, this switch only applies for commits that have one, and only one parent
https://git-scm.com/docs/git-filter-branch
This is essentially two statements.
The commit adding a.txt fails the first check.
The code (as poorly documented and written as it is) confirms this.
First the parent string is passed as arguments to the command which is run for each commit: https://github.com/git/git/blob/master/git-filter-branch.sh#L410
Because there are no parents for your root commit this is empty. So then when the code later checks that we have three arguments test $# = 3
it finds this is not the case:
https://github.com/git/git/blob/master/git-filter-branch.sh#L47
So it moves onto the else branch where the commit is added.
In cases where there is a parent commit, the code goes onto the second check test "$1" = $(git rev-parse "$3^{tree}")
which does a comparison of the sha of the tree for the previous commit and the sha of the tree for this commit, if they are the same then it skips making that commit.
As a side note, I really wish this code was a lot more readable. It's pretty sad that such a major project allows submissions like this. Then again, maybe it's just my bias against SHELL.
I have no idea why the PGP signature is added. It's certainly not documented so I'm guessing it's a bug, which isn't surprising given the difficulty of reading the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With