First of all, the man page for git hash-object doesn't specify whether --path should be relative to the root of the tree or relative to the current directory after git -C. Current behavior seems to be the latter, which is sensible enough and usually works. While .. components seem to be handled correctly, . components seem to get matched literally in both gitattributes patterns and git hash-object --path arguments. On a related note, the man page for git hash-object doesn't specify how a leading --path argument with a leading ./ should be treated. Given current behavior, I think a leading ./ should indicate the current directory. I can try to work up a patch, but I'd like some confirmation that this is not actually intended behavior. Cheers, Oliver #!/bin/sh git init --quiet git-bug-test cd git-bug-test git config filter.line1.clean 'sed -e 1cclean' git config filter.line1.smudge 'sed -e 1cdirty' mkdir sub cd sub printf '# prefix\n' git rev-parse --show-prefix printf '# reference dirty object\n' printf 'dirty\n' | git hash-object -w --stdin printf '# reference clean object\n' printf 'clean\n' | git hash-object -w --stdin printf 'sub/* filter=line1\n' > ../.gitattributes printf '# case 1: sub/* filter; --path=file\n' printf 'dirty\n' | git hash-object -w --stdin --path=file printf '# case 2: sub/* filter; --path=./file\n' printf 'dirty\n' | git hash-object -w --stdin --path=./file printf '# case 3: sub/* filter; --path=sub/file\n' printf 'dirty\n' | git hash-object -w --stdin --path=sub/file printf '# case 4: sub/* filter; --path=./sub/file\n' printf 'dirty\n' | git hash-object -w --stdin --path=./sub/file printf '# case 5: sub/* filter; --path=../sub/file\n' printf 'dirty\n' | git hash-object -w --stdin --path=../sub/file printf '# case 6: sub/* filter; --path=../sub/./file\n' printf 'dirty\n' | git hash-object -w --stdin --path=../sub/./file printf 'sub/./* filter=line1\n' > ../.gitattributes printf '# case 7: sub/./* filter; --path=file\n' printf 'dirty\n' | git hash-object -w --stdin --path=file printf '# case 8: sub/./* filter; --path=./file\n' printf 'dirty\n' | git hash-object -w --stdin --path=./file printf '# case 9: sub/./* filter; --path=sub/file\n' printf 'dirty\n' | git hash-object -w --stdin --path=sub/file printf '# case 10: sub/./* filter; --path=./sub/file\n' printf 'dirty\n' | git hash-object -w --stdin --path=./sub/file printf '# case 11: sub/./* filter; --path=../sub/file\n' printf 'dirty\n' | git hash-object -w --stdin --path=../sub/file printf '# case 12: sub/./* filter; --path=../sub/./file\n' printf 'dirty\n' | git hash-object -w --stdin --path=../sub/./file # EOF Observed output: $ git --version git version 2.22.GIT $ ./git-bug-test.sh # prefix sub/ # reference dirty object 6e5aa7ceca195942768e6e0997c3a0271b07fffc # reference clean object 83126302079c10762b29692dc322e430472a5360 # case 1: sub/* filter; --path=file 83126302079c10762b29692dc322e430472a5360 # case 2: sub/* filter; --path=./file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 3: sub/* filter; --path=sub/file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 4: sub/* filter; --path=./sub/file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 5: sub/* filter; --path=../sub/file 83126302079c10762b29692dc322e430472a5360 # case 6: sub/* filter; --path=../sub/./file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 7: sub/./* filter; --path=file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 8: sub/./* filter; --path=./file 83126302079c10762b29692dc322e430472a5360 # case 9: sub/./* filter; --path=sub/file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 10: sub/./* filter; --path=./sub/file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 11: sub/./* filter; --path=../sub/file 6e5aa7ceca195942768e6e0997c3a0271b07fffc # case 12: sub/./* filter; --path=../sub/./file 83126302079c10762b29692dc322e430472a5360