{"thread":{"id":"56574","subject":"Bug: Documentation and behavior mismatch in and between git-archive and gitattributes/git-check-attr.","startedAt":"2021-09-23T14:52:34Z","lastAt":"2021-09-23T14:52:34Z","messageCount":1,"participants":["David Hurka"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"436858","messageId":"22364178.6Emhk5qWAg@doro","threadId":"56574","inReplyTo":null,"subject":"Bug: Documentation and behavior mismatch in and between git-archive and gitattributes/git-check-attr.","fromName":"David Hurka","fromEmail":"david.hurka@mailbox.org","sentAt":"2021-09-23T14:52:22Z","receivedAt":"2021-09-23T14:52:34Z","isPatch":false,"sender":{"key":"david.hurka@mailbox.org","avatar":null},"body":"Hello all,\n\nin https://github.com/Kentzo/git-archive-all/issues/87 I reported a problem \nwhich turns out to be more likely a problem in git.\n\nThe documentation of `gitattributes` says:\n\n> The rules [mostly follow .gitignore], with a few exceptions:\n> * [...]\n> * patterns that match a directory do not recursively match paths\n>   inside that directory (so using the trailing-slash path/ syntax\n>   is pointless in an attributes file; use path/** instead)\n\nThis means to me, that patterns may match _directories_ instead of the files \nin them.\n\nSo, if I have a /.gitattributes file like this:\n\n> LICENSES/ export-ignore\n\nI expect the whole directory /LICENSES/ to be matched, so a tool like \n`git-archive` skips it when creating an archive.\n\nHowever, if I have a /.gitattributes file like this:\n\n> LICENSES/** export-ignore\n\nI expect the directory itself not to be matched, but all files inside it, so a \ntool like `git-archive` adds an empty /LICENSES/ directory to the archive.\n\n`git-archive` actually behaves like this.\n\nHowever, `git-check-attr` does not behave like this. In the second example, it \nwill list `export-ignore` matching on every file inside /LICENSES/. But in the \nfirst example, it will list `export-ignore` matching nowhere. This leads to \nthe problem where `git-archive-all` wrongly exports the whole /LICENSES/ \ndirectory.\n\nThe documentation for `gitattributes` says for `export-ignore`:\n\n> `export-ignore`\n>   Files and directories with the attribute `export-ignore`\n>   won’t be added to archive files.\n\nFor most other attributes it does not include directories, because they do not \nmake sense for directories.\n\n---\n\nI think the solution is like this:\n\n1. Change the documentation of `gitattributes` to:\n\n> The rules [mostly follow .gitignore], with a few exceptions:\n> * [...]\n> * patterns that match a directory do not recursively match paths\n>   inside that directory. (Since most attributes are only useful for\n>   files, the path/ syntax is probably incorrect; use path/** instead.)\n\n2. Change git-check-attr to make patterns like path/ match directories. At \nleast, for attributes like `export-ignore` this should be changed, but I think \nthe correct solution is to change it for all attributes.\n\n---\n\nBelow is a shell script to reproduce the problem.\n\n```\nmkdir test_attributes\ncd test_attributes\ngit init -b master\nmkdir include dont-include-1 dont-include-2 dont-include-3\ntouch include/a.txt dont-include-1/b.txt dont-include-2/c.txt dont-include-3/\nd.txt\necho dont-include-1/ export-ignore >>.gitattributes\necho dont-include-2/** export-ignore >> .gitattributes\necho dont-include-3 export-ignore >>.gitattributes\ngit add -A\ngit commit -m \"Test gitattributes\"\n\ngit archive --output ../test_attributes.zip HEAD\nzipinfo ../test_attributes.zip\n\ngit check-attr export-ignore * */*\n```\n\nThis is the output which I get with git version 2.33.0.752.gd22421fcc6:\n\n> $ git archive --output ../test_attributes.zip HEAD\n> $ zipinfo ../test_attributes.zip\n> Archive:  ../test_attributes.zip\n> Zip file size: 584 bytes, number of entries: 4\n> -rw----     0.0 fat       94 tx defN 21-Sep-23 16:37 .gitattributes\n> drwx---     0.0 fat        0 bx stor 21-Sep-23 16:37 dont-include-2/\n> drwx---     0.0 fat        0 bx stor 21-Sep-23 16:37 include/\n> -rw----     0.0 fat        0 tx stor 21-Sep-23 16:37 include/a.txt\n> 4 files, 94 bytes uncompressed, 46 bytes compressed:  51.1%\n> \n> $ git check-attr export-ignore * */*\n> dont-include-1: export-ignore: unspecified\n> dont-include-2: export-ignore: unspecified\n> dont-include-3: export-ignore: set\n> include: export-ignore: unspecified\n> dont-include-1/b.txt: export-ignore: unspecified\n> dont-include-2/c.txt: export-ignore: set\n> dont-include-3/d.txt: export-ignore: unspecified\n> include/a.txt: export-ignore: unspecified\n\nNote how `dont-include-2/** export-ignore` caused an empty directory to be \ncreated. This is problematic for my workflow.\n\n`dont-include-3 export-ignore` yields the desired results here, but I think it \nis not always an alternative, since the missing trailing slash makes the rule \napply to files too. For example in this repository structure:\n\n> - a/\n>   - dont-include-3/\n>     - a.txt\n> - b/\n>   - dont-include-3/\n>     - b.txt\n> - dont-include-3\n\nI could not use the glob syntax `**/dont-include-3 export-ignore` to skip only \nthe two _directories_ with a.txt and b.txt, because the _file_ dont-include-3 \nwould also be skipped.\n\n---\n\nI am generally available for submitting patches to fix this issue. Let me know \nwhat you think. :)\n\nCheers, David\n\n\n\n"}]}