1

I want to find within my project directory, but I'm only looking for files that are tracked by Git. Is there a command I can use as a filter for the find output (or any other means) to limit the results to only the files that Git is tracking?

Example:

find . -name '*.postinst' | git-no-untracked

What do I use for git-no-untracked above?

  • git ls-files | grep 'postinst$' may do the job – Httqm May 24 at 9:51
  • @Httqm, yes that would work for this simple case (and thanks), but less good when the find command has other predicates (such as -mtime). Maybe I might get somewhere by using comm to compare git ls-files with the find output. I'll have a go at that. – Toby Speight May 24 at 12:55
  • Comparing using comm works, if you're careful to get the paths consistent (i.e. find . breaks it because of the leading ./ in the pathnames). I'll write an answer. – Toby Speight May 24 at 13:02
3

If switching to zsh is an option, you can have the list of tracked files in an array:

tracked_files=(${(0)"$(git ls-files -z)"})

And then do the intersection of the list of files you're interested in like:

postinst_files=(**/*.postinst)
tracked_postinst_files=(${postinst_files:*tracked_files})

You can do with zsh globs most of what you can do with find. For instance, if you want only regular files last modified in the last 2 hours:

postinst_files=(**/*.postinst(.mh-2))

Or you could directly search for your pattern in the list of tracked files:

tracked_postinst_files=(${(M)tracked_files:#*.postinst})

Or define a glob qualifier function that checks if the file is in the list of tracked files:

istracked() ((${tracked_files[(Ie)${1-$REPLY}]}))

tracked_postinst_files=(**/*.postinst(+istracked))
  • Sounds interesting. I'm a Bash user by default/preference, but this certainly gives an insight into what zsh could do for me. – Toby Speight May 24 at 14:13
0

What about this one :

find . -name '*.postinst' | xargs -I something bash -c 'git ls-files | grep $(basename something)'
0

Following the hint to use git ls-files, I created a short script to run a command and filter its output using comm. The subtleties are that comm requires sorted input, and the lines have to be an exact textual match (so no prefixes such as ./ that the typical find command might produce).

#!/bin/bash
set -eu

usage()
{
    cat <<EOF
Usage:
   $0 cmd [args... ]

Runs _cmd_ (with arguments, if provided) and filter the output to
files currently tracked by Git.

Example:
    $0 find . -name README -mmin -60

EOF
}

trap 'usage >&2' ERR

case "${1:-}" in
    -h|--help|'-?')
        usage
        exit 0
        ;;
    '')
        usage >&2
        exit 1
        ;;
esac

exec comm -12 \
     <("$@" | xargs -d '\n' -n 1 realpath -s --relative-to=. | sort) \
     <(git ls-files | sort)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.