Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected behavior with git filter-branch and signed commits

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.)

like image 640
Adam Liter Avatar asked Mar 15 '23 08:03

Adam Liter


2 Answers

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.

like image 127
Adam Liter Avatar answered Mar 18 '23 07:03

Adam Liter


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.

  1. The commit must have one parent.
  2. The commit must not have more than one parent.

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.

like image 33
Gerry Avatar answered Mar 18 '23 07:03

Gerry