Reputation: 2180
git diff
can list all file differences between two revisions:
$ git diff --name-only master
foo.txt
bar/bar.txt
bar/baz.qux
It accepts an optional <path>
filter in the form of a glob pattern that names paths to include:
$ git diff --name-only master -- '*.txt'
foo.txt
bar/bar.txt
<path>
is relative to the working directory:
$ cd bar/
$ git diff --name-only master -- '*.txt' # no output
$ git diff --name-only master -- '../*.txt'
foo.txt
bar/bar.txt
Is there a way to invoke git diff
to achieve the effect of
$ cd bar/
$ git diff --name-only master | grep '\.txt$'
foo.txt
bar/bar.txt
without relying on external tools? A <path>
with a prefix of /
does not seem to work.
Upvotes: 1
Views: 55
Reputation: 94483
$ cd bar/
$ git diff --name-only master -- $(git rev-parse --show-cdup)/'*.txt'
foo.txt
bar/bar.txt
See https://git-scm.com/docs/git-rev-parse#Documentation/git-rev-parse.txt---show-cdup
Upvotes: 2
Reputation: 488193
The arguments to git diff
are what Git calls a pathspec, which is defined in the gitglossary. Here is a snippet from the (rather long) description:
A pathspec that begins with a colon
:
has special meaning. In the short form, the leading colon:
is followed by zero or more "magic signature" letters (which optionally is terminated by another colon:
), and the remainder is the pattern to match against the path. The "magic signature" consists of ASCII symbols that are neither alphanumeric, glob, regex special characters nor colon. The optional colon that terminates the "magic signature" can be omitted if the pattern begins with a character that does not belong to "magic signature" symbol set and is not a colon.In the long form, the leading colon
:
is followed by an open parenthesis(
, a comma-separated list of zero or more "magic words", and a close parentheses)
, and the remainder is the pattern to match against the path. ...top
The magic word
top
(magic signature:/
) makes the pattern match from the root of the working tree, even when you are running the command from inside a subdirectory.
Hence we can use :(top)
or :/:
. Here's the same setup as in your example:
$ git diff --name-only
bar/bar.txt
bar/baz.qux
foo.txt
(these are all modified)
$ cd bar
$ git diff --name-only
bar/bar.txt
bar/baz.qux
foo.txt
(all still listed, even in subdirectory, when there is no pathspec)
$ git diff --name-only -- '*.txt'
bar/bar.txt
(only one listed due to being in subdirectory plus pathspec). Now we use the pathspec specifiers:
$ git diff --name-only -- ':(top)*.txt'
bar/bar.txt
foo.txt
$ git diff --name-only -- ':/:*.txt'
bar/bar.txt
foo.txt
(:(top)
or :/:
as a prefix gets everything listed)
See the documentation for the remaining words, which are fairly powerful tools.
Upvotes: 3
Reputation: 2180
Arguably, one option is to change to the repository root, which can be safely done in a sub-shell, before executing the basic command:
$ cd bar/
$ ( cd $(git rev-parse --show-toplevel) &&
git diff --name-only master -- '*.txt' )
foo.txt
bar/bar.txt
This introduces no new external tools—the shell provides sub-shells and cd
, and Git is given—but it doesn't technically satisfy the requirement of no external tools.
Upvotes: 2