smudge filters do not round trip through `git diff` / `git apply`

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



this is boiled down from a larger problem outlined here:
https://github.com/pre-commit/pre-commit/issues/776

I've had some time to sit down and poke at this today -- here's my
minimal reproduction using just `git` and `git-crypt` -- though my
users have reported this also seems to affect other smudge filters.

I understand `git-crypt` is not associated with the git project,
however it was an easy, readily-available smudge filter to demonstrate
the problem.  I'm using git-crypt 0.6.0 (from ubuntu 22.04).

(the key material below is not sensitive -- I generated it afresh in a
docker container)

```bash
#!/usr/bin/env bash
set -euxo pipefail

rm -rf repo

git --version
git init --quiet repo -b main
cd repo
git commit --allow-empty -m 'Initial empty commit'

# determinstic git-crypt key so the output is stable
base64 -d > keyfile <<EOF
AEdJVENSWVBUS0VZAAAAAgAAAAAAAAABAAAABAAAAAAAAAADAAAAIIBi0O4iuCHghpYj4Teb6F72
KjTRHePBTf/6XC6fiVqvAAAABQAAAECTwWTDHfx0/Ytw3IZrVhonb5IPTr7kio27u0prnb8X25ui
9k4UqrdRQy8ZtBERv6wnHwC8A6q7CamRZ22L4q7UAAAAAA==
EOF
git-crypt unlock keyfile

echo 'f filter=git-crypt diff=git-crypt' > .gitattributes
git add .gitattributes
echo 'hello world' > f
git add f
rm f && touch f

tree="$(git write-tree)"
! git diff-index \
    --ignore-submodules \
    --binary \
    --exit-code \
    --no-color \
    --no-ext-diff \
    --no-textconv \
    "$tree" -- > patch

git checkout -- .
git apply patch || (echo FAILED && cat patch && exit 1)
```

here's my output:

```console
$ bash t.sh
+ rm -rf repo
+ git --version
git version 2.38.1.381.gc03801e19c
+ git init --quiet repo -b main
+ cd repo
+ git commit --allow-empty -m 'Initial empty commit'
[main (root-commit) 97d9520] Initial empty commit
+ base64 -d
+ git-crypt unlock keyfile
+ echo 'f filter=git-crypt diff=git-crypt'
+ git add .gitattributes
+ echo 'hello world'
+ git add f
+ rm f
+ touch f
++ git write-tree
+ tree=beca08f8b3c0774060f3e28e081ac69a80a1a10d
+ git diff-index --ignore-submodules --binary --exit-code --no-color
--no-ext-diff --no-textconv beca08f8b3c0774060f3e28e081ac69a80a1a10d
--
+ git checkout -- .
+ git apply patch
error: binary patch to 'f' creates incorrect result (expecting
2f89279ce748725a41cec60d5025b22efc863b42, got
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
error: f: patch does not apply
+ echo FAILED
FAILED
+ cat patch
diff --git a/f b/f
index ee7d1b67cd31482ae9bc772a0b2d016c81e1c613..2f89279ce748725a41cec60d5025b22efc863b42
100644
GIT binary patch
literal 0
HcmV?d00001

literal 34
qcmZQ@_Y83kiVO&0IB9<_f2(d}=e+;CXFaaIzZ@c>%yE8!<X-^jJPz&v

+ exit 1
```

I traced through the execution and it appears that smudge filters are
maybe still running despite the `--no-textconv` setting which may
explain this?

```
+ GIT_TRACE=2
+ git diff-index --ignore-submodules --binary --exit-code --no-color
--no-ext-diff --no-textconv beca08f8b3c0774060f3e28e081ac69a80a1a10d
--
20:40:21.551771 git.c:455               trace: built-in: git
diff-index --ignore-submodules --binary --exit-code --no-color
--no-ext-diff --no-textconv beca08f8b3c0774060f3e28e081ac69a80a1a10d
--
20:40:21.552190 run-command.c:668       trace: run_command: '"git-crypt" clean'
20:40:21.555249 git.c:455               trace: built-in: git rev-parse --git-dir
```

as shown in the output I'm using the current primary branch revision
of git -- though I usually use 2.34.1 (ubuntu 22.04)

oddly enough, using `--textconv` instead of `--no-textconv` "fixes" --
but is unsatisfactory for my use case (I don't want to rely on the
state of filters installed, etc.)

the error message seems to occur due to the comparison of the hash in
the `index a...b` line above the patch hunk

`e69de29bb2d1d6434b8b29ae775ad8c2e48c5391` is the hash of an empty file:

```console
$ git hash-object -w /dev/null
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
```

`2f89279ce748725a41cec60d5025b22efc863b42` appears to be the hash of
the smudged empty file (I used base64 here since it's binary nonsense
-- I got the contents of this blob by committing the empty file and
then fished the object out of the git database instead of trying to do
the patch dance):

```console
$ base64 -d <<< 'AEdJVENSWVBUAGoDwWO5GXWQ4B1kIQ==' | git hash-object
-w /dev/stdin
2f89279ce748725a41cec60d5025b22efc863b42
```

I *believe* the fix here is to avoid smudging in `git diff
--no-textconv` -- I started a patch where I added a `HASH_NO_TEXTCONV`
flag to `cache.h` but wasn't super sure on where to go from there and
decided I should ask first whether this is the right approach to take!

anthony



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux