threads / discuss / 38566

git ls-files -X option is relative to repo root

Subject: git ls-files -X option is relative to repo root

## tl;dr

5 messages between Feb 13, 2015 and Feb 13, 2015.

replies: 4people: 2as markdown or json

Daniel Finnie· Feb 13, 2015, 19:23 UTC · lore

I encountered some unexpected behavior with Git today and was hoping to either a) clear up my misconception or b) make a bug report.

My question deals with the --exclude-from option to git-ls-files. It appears that paths passed to this option are relative to the root of the repository, not your current working directory. I would have expected the opposite, that the paths are relative to the working directory. I would expect to put a colon at the beginning of the path to make it relative to the repository root. Here's an example:

$ tree -a -I .git
.
├── .gitignore
└── example_dir
    ├── .gitignore
    └── example_file

# (all of these files are checked in, including example_file which is also in .gitignore)

$ cat .gitignore # empty gitignore

$ cat example_dir/.gitignore example_file

$ (cd example_dir && git ls-files --ignore --exclude-from=.gitignore) # No output because this references the git ignore at the project root, not example_dir. I expected this to output "example_file".

$ (cd example_dir && git ls-files --ignore --exclude-from=example_dir/.gitignore) example_file # works for the reason above, but I expected this to break because example_dir/example_dir/.gitignore is not a file

So, what do you think? Am I missing a git/*nix convention explaining options would be specified relative to the repository root? Or is this a git bug?

Thanks for your time, Dan Finnie

Junio C Hamano· Feb 13, 2015, 20:42 UTC · re: Daniel Finnie · lore

Re: git ls-files -X option is relative to repo root

Daniel Finnie <dan@danfinnie.com> writes:
> My question deals with the --exclude-from option to git-ls-files.

You will be fine if you remember that these plumbing commands work by first cd'ing to the top-level of the working tree (and adjust the paths given from the command line by prefixing the prefix to them).

The input lines that come from --exclude-per-directory should go through the prefixing, but -X=<file> makes that single file affect the whole operation. It does not make sense to allow where you are to affect behaviour of the command, i.e. in these two invocations of ls-files:

	git ls-files -X /var/tmp/exclude -i
        cd example && git ls-files -X /var/tmp/exclude -i

if the same line in /var/tmp/exclude meant completely different things, it would be crazy.

Junio C Hamano· Feb 13, 2015, 20:54 UTC · re: Junio C Hamano · lore

Re: git ls-files -X option is relative to repo root

Junio C Hamano <gitster@pobox.com> writes:
Show 9 quoted lines
> ...  It does not make sense to allow where you are
> to affect behaviour of the command, i.e. in these two invocations of
> ls-files:
>
> 	git ls-files -X /var/tmp/exclude -i
>         cd example && git ls-files -X /var/tmp/exclude -i
>
> if the same line in /var/tmp/exclude meant completely different
> things, it would be crazy.

To put it another way, think of --exclude-from as a way to specify a replacement for .git/info/excludes, and --exclude-per-directory as a way to specify a replacement for the in-tree .gitignore files.

Historically, we did not have the --exclude-standard option from the beginning, and only after we gained experience with --exclude-from and --exclude-per-directory in our scripts, the --exclude-standard was added to codify the (then-) best-current-practice after the fact, and we used --exclude-from for exactly that purpose before then.

cf. 8e7b07c8 (git-ls-files: add --exclude-standard, 2007-11-15)
Daniel Finnie· Feb 13, 2015, 21:19 UTC · re: Junio C Hamano · lore

Re: git ls-files -X option is relative to repo root

Hi Junio,

Thanks for the info and backstory. I didn't realize that the paths in the file specified by --exclude-from would be relative to the project root. That makes my original use case kind of silly (it's a long story, but I was curious which files were ignored by a subset of my .gitignore files). This makes the example I gave before wrong in that the contents of example_dir/.gitignore should have been relative to the project root.

There are 2 sets of paths that can be relative to the working
directory or the project root with --exclude-from:
* The path to the file containing exclusion rules
* The paths of the exclusion rules themselves (the contents of the
file from the previous bullet point)

I now see why the second needs to be relative to the project root. I still think it's more intuitive for the first (the path to the file containing exclusions) to be relative to the current directory. Your example of `git ls-files -X /var/tmp/exclude -i` uses an absolute path, let's look at one with a relative path. Say you wanted to check what files were being ignored from your .git/info/exclude file from a subdirectory of your project. I would expect to run `cd subdirectory && git ls-files --ignore --other --exclude-from=../.git/info/exclude`. The correct command, though, is `cd subdirectory && git ls-files --ignore --other --exclude-from=.git/info/exclude`, even though I'm in a subdirectory.

Do you have any comments on why the path in --exclude-from=<path> is relative to the project root?

Thanks, Dan

On Fri, Feb 13, 2015 at 3:54 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 23 quoted lines
> Junio C Hamano <gitster@pobox.com> writes:
>
>> ...  It does not make sense to allow where you are
>> to affect behaviour of the command, i.e. in these two invocations of
>> ls-files:
>>
>>       git ls-files -X /var/tmp/exclude -i
>>         cd example && git ls-files -X /var/tmp/exclude -i
>>
>> if the same line in /var/tmp/exclude meant completely different
>> things, it would be crazy.
>
> To put it another way, think of --exclude-from as a way to specify a
> replacement for .git/info/excludes, and --exclude-per-directory as a
> way to specify a replacement for the in-tree .gitignore files.
>
> Historically, we did not have the --exclude-standard option from the
> beginning, and only after we gained experience with --exclude-from
> and --exclude-per-directory in our scripts, the --exclude-standard
> was added to codify the (then-) best-current-practice after the fact,
> and we used --exclude-from for exactly that purpose before then.
>
> cf. 8e7b07c8 (git-ls-files: add --exclude-standard, 2007-11-15)
Junio C Hamano· Feb 13, 2015, 22:02 UTC · re: Daniel Finnie · lore

Re: git ls-files -X option is relative to repo root

Daniel Finnie <dan@danfinnie.com> writes:
> Do you have any comments on why the path in --exclude-from=<path> is
> relative to the project root?
Not really.

Because ls-files was designed to be used by Porcelain scripts, and because the first thing Porcelain scripts are expected to do is to learn the prefix and then cd to the root level of the working tree before doing anything else, <path> that is relative to the root level of the working tree ends up to be not so unnatural thing to be used with --exclude-from=<path> (e.g. ".git/info/exclude").

If it were relative to whatever subdirectory the invoker of the Porcelain script happened to be, Porcelain would have to do a lot more (e.g. in "cd x/y && myPorcelain ../../.git/info/exclude", the myPorcelain script would first have to learn the prefix is x/y, go up two levels, and then strip two ../ from ../../.git/info/exclude to turn it into .git/info/exclude when it runs ls-files).

So that is a convenience explanation in retrospect, but "Why" is often a futile question to ask when talking about evolution, in which whatever works gets picked.

← back to recent threads