2

I am trying to list all untracked files in all submodules recursively:

git submodule foreach --recursive git ls-files --others --exclude-standard --full-name

But this does not list the absolute path, it gives the relative path to the submodule's folder.

How should I modify my command?

Porcupine
  • 5,885
  • 2
  • 19
  • 28

2 Answers2

4

(Note: this is a "tool" answer, not a "solution" answer. That is, this answer is meant to give you the tools you will need to construct the solution you want.)

Use git rev-parse --show-toplevel to get the absolute path to the current repository. Do this while in the superproject, of course. That gives you the first part of any path prefix—but now you'll need the remaining part.

As the git submodule documentation says, in a foreach, you have access to several variables, including $path:

$path is the name of the submodule directory relative to the superproject ...

What's not immediately clear (you will have to test this yourself) is whether $path is cumulative in the case of recursive submodules. That is, if superproject A has submodule B under path top/subB, $path will be top/subB when you're in submodule B. But suppose submodule B acts as a superproject to submodule C, with B's own path being contained/C. Will $path be top/subB/contained/C when inside submodule C, or will it be only contained/C?1

In any case, once you have the appropriate prefix, just use something like sed to tack it on the front, e.g.,:

git ls-files --others --exclude-standard --full-name | sed "s,^,$prefix,"

where $prefix is set to the appropriate prefix, which might just be $path/. (This assumes none of your file names contain an embedded newline; if they do, your original command is in trouble anyway. As written, it also assumes none of your prefixes contains a comma—if they do, use a character other than ,, or make $prefix quote the commas with backslashes.)


1Logic suggests it will be the complete path; the relative path is not entirely usable without knowing how many submodules have been recursed-into. However, the relative path is easier to produce.

torek
  • 448,244
  • 59
  • 642
  • 775
  • You mentioned, " Will $path be top/subB/contained/C when inside submodule C, or will it be only contained/C?". But to resolve this ambiguity I have asked for complete absolute path. – Porcupine Sep 05 '18 at 18:48
  • 1
    You can pass, to your `foreach`, the path to the top level superproject (`git rev-parse --show-toplevel`). But you'll still have to resolve the above question (I don't know the answer) to decide how to work with `$path`. I would hope that `$path` is always relative to the top level superproject, not the immediate superproject, but I have not verified this. Assuming `$path` is relative to the top superproject, set `prefix` appropriately: `prefix="$abs/$path/"` where `$abs` is already set to the output from `$(git rev-parse --show-toplevel)`. – torek Sep 05 '18 at 18:51
1

Thanks to torek for the idea. This works:

git submodule foreach --quiet 'a=$(printf "$(git ls-files --others --exclude-standard --full-name|sed "s~^~$(pwd)/~")"; printf x); a=${a%x}; echo "$a"' | sed '/^\s*$/d'
Porcupine
  • 5,885
  • 2
  • 19
  • 28