Recursively add files by pattern
Asked Answered
C

13

222

How do I recursively add files by a pattern (or glob) located in different directories?

For example, I'd like to add A/B/C/foo.java and D/E/F/bar.java (and several other java files) with one command:

git add '*.java'

Unfortunately, that doesn't work as expected.

Confraternity answered 18/5, 2010 at 7:11 Comment(6)
It works for me (Mac OS X, Git 1.7.1+). What OS and Git version are you using?Geology
If you have some (already tracked) .java files in your current directory, you may be running into the, er, complicated wildcard handling between bash and the msys command-line “helper”. I am not sure about a solution. You might try multiple layers of quotes: git add '"*.java"' (the single quotes are taken by bash to prevent glob expansion, the double quotes are taken by the msys layer to prevent glob expansion).Geology
Thanks for the note. Unfortunately, that doesn't work either. They say the has been fixed, but I already have the latest version.Griskin
possible duplicate of git: How do I recursively add all files in a directory subtree that match a glob pattern?Outspread
git add *.java works for me (on powershell with GitHub client)Geraldgeralda
git add "*.java" works for me in Ubuntu.Chasten
A
109

Sergio Acosta's answer is probably your best bet if some of the files to be added may not already be tracked. If you want to limit yourself to files git already knows about, you could combine git-ls-files with a filter:

git ls-files [path] | grep '\.java$' | xargs git add

Git doesn't provide any fancy mechanisms for doing this itself, as it's basically a shell problem: how do you get a list of files to provide as arguments to a given command.

Acetum answered 18/5, 2010 at 13:57 Comment(2)
Thanks! I improved your command slightly and now it's doing what I was looking for: git ls-files -co --exclude-standard | grep '\.java$' | xargs git addGriskin
xargs like that did not work for me, given spaces in file names.. what do you think of this version? git ls-files . | grep -ie '\.bat$' | while read x; do git add "$x";doneToscano
H
214

You can use git add [path]/\*.java to add java files from subdirectories,
e.g. git add ./\*.java for current directory.

From git add documentation:

Adds content from all *.txt files under Documentation directory and its subdirectories:

$ git add Documentation/\*.txt

Note that the asterisk * is quoted from the shell in this example; this lets the command include the files from subdirectories of Documentation/ directory.

Hinda answered 24/2, 2012 at 14:20 Comment(4)
@MichelKrämer probably it wasn't, but it is now.Situation
This is the best solution, works in windows as well.Ocasio
"asterisk * is quoted from the shell". I think you mean "escaped". In any case, the backslash prevents the shell from expanding it.Covenantor
@Covenantor I think you're right, but it's not me. It's quote from the docs git-scm.com/docs/git-add#_examplesHinda
A
109

Sergio Acosta's answer is probably your best bet if some of the files to be added may not already be tracked. If you want to limit yourself to files git already knows about, you could combine git-ls-files with a filter:

git ls-files [path] | grep '\.java$' | xargs git add

Git doesn't provide any fancy mechanisms for doing this itself, as it's basically a shell problem: how do you get a list of files to provide as arguments to a given command.

Acetum answered 18/5, 2010 at 13:57 Comment(2)
Thanks! I improved your command slightly and now it's doing what I was looking for: git ls-files -co --exclude-standard | grep '\.java$' | xargs git addGriskin
xargs like that did not work for me, given spaces in file names.. what do you think of this version? git ls-files . | grep -ie '\.bat$' | while read x; do git add "$x";doneToscano
A
94

With zsh you can run:

git add "**/*.java"

and all your *.java files will be added recursively.

Afflatus answered 18/5, 2010 at 7:30 Comment(6)
Thanks for that, but I'm working with msysgit on Windows and it has a bash only.Griskin
Well, I see that there is a zsh shell for windows... you will do yourself a favour to use it instead of bash, imho.Afflatus
The Windows port of zsh is based on a very old version and crashes all the time (for example when I enter ls).Griskin
Wow, perfect. I didn't know about that feature!Pestana
This works fine on bash too. Just make sure the files you're targeting are below your current working directory. So you may have to do something like git add ../../**/*.java (You don't need quotes surrounding the glob on bash)Jay
This is nothing to do with zsh. I think this answer misses the point that you want to pass the path with wildcards intact to git and let git process it. You therefore want to prevent whichever shell you are using from expanding the wildcard(s). To do this you need to quote it some way that's appropriate for the shell you're using. See other answers for details.Covenantor
P
65

A bit off topic (not specifically git related) but if you're on linux/unix a workaround could be:

find . -name '*.java' | xargs git add

And if you expect paths with spaces:

find . -name '*.java' -print0 | xargs -0 git add

But I know that is not exactly what you asked.

Pindling answered 18/5, 2010 at 7:15 Comment(5)
Minor issue with this approach -- if you "find" a file that's matched by your .gitignore then git complains that you're trying to add a file you told it to ignore.Bodega
@Bodega - I just added -f on the end, and that fixed thatDormant
@Maslow, that will forcibly add the file, even though it was supposed to be ignored (based on .gitignore). I'd prefer to get the complaint and not add the file (which is the default behaviour.)Bodega
How is this in any way better than letting git perform the wildcard expansion?Covenantor
@Covenantor git doesn't do the wildcard expansion, the shell does. The original question is just a shell problem as mentioned above. So in a sense, this answer here is quite correct, .gitignore notwithstanding.Immodest
S
12

Sergey's answer (don't credit me) is working:

You can use git add [path]/\*.java

with a recent git:

$git version
git version 1.7.3.4

Files for the test:

$find -name .git -prune -o -type f -print | sort
./dirA/dirA-1/dirA-1-1/file1.txt
./dirA/dirA-1/dirA-1-2/file2.html
./dirA/dirA-1/dirA-1-2/file3.txt
./dirA/dirA-1/file4.txt
./dirB/dirB-1/dirB-1-1/file5.html
./dirB/dirB-1/dirB-1-1/file6.txt
./file7.txt

Git status:

$git status -s
?? dirA/
?? dirB/
?? file7.txt

Adding *.txt:

$git add \*.txt

Updated status:

$git status -s
A  dirA/dirA-1/dirA-1-1/file1.txt
A  dirA/dirA-1/dirA-1-2/file3.txt
A  dirA/dirA-1/file4.txt
A  dirB/dirB-1/dirB-1-1/file6.txt
A  file7.txt
?? dirA/dirA-1/dirA-1-2/file2.html
?? dirB/dirB-1/dirB-1-1/file5.html
Situation answered 14/3, 2012 at 19:7 Comment(0)
S
11

As it's not mentioned here, consider also the "magic signature" :/ to recursively add anything matching below the project root:

git add :/*.{java,pom}

Tx to: https://mcmap.net/q/120434/-is-there-a-way-to-just-commit-one-file-type-extension-from-a-folder

Speakeasy answered 22/1, 2021 at 2:11 Comment(3)
do you mind explaining your pathspec and what it is meant to achieve? I gather you mean to add all files with a .java or .pom extension, starting from the project root, even though one may be calling git add elsewhere. Also, it be great with some explanation/documentation on the curly-braces. I can't get this to work myselfPietro
Hi @Pietro - yup - you've nailed the usecase and interpretation of the pathspec. I'm unsure of the curlies - it may be a bashism (I've just tested it again, and it worked for me on bash), but I didn't find any references specific to git pathspec, sorry 🤷‍♂️ I suggest searches on bash globbing and expansion 👍Speakeasy
thanks for the info. It didn't work as expected in CMD which is why I'm guessing this is expanded by the shell before becoming a pathspec for git.Pietro
A
9

If you are already tracking your files and have made changes to them and now you want to add them selectively based on a pattern, you can use the --modified flag

git ls-files --modified | grep '<pattern>' | xargs git add

For example, if you only want to add the CSS changes to this commit, you can do

git ls-files --modified | grep '\.css$' | xargs git add

See man git-ls-files for more flags

Agle answered 13/4, 2012 at 4:58 Comment(0)
B
9

Just use git add *\*.java. This will add all .java files in root directory and all subdirectories.

Brayton answered 20/12, 2017 at 13:40 Comment(1)
Great answer. I did git add */pom.xml to add all files names pom.xml.Trickery
T
5

Adding a Windows command line solution that was not yet mentioned:

for /f "delims=" %G in ('dir /b/s *.java') do @git add %G
Tension answered 19/12, 2018 at 8:9 Comment(0)
D
3

I wanted to only add files that had a certain string based on git status:

git status | grep string | xargs git add

and then was able to git commit -m 'commit msg to commit all changed files with "string" in the title of the file

Dipteran answered 26/7, 2017 at 23:6 Comment(0)
G
2

As mentioned in "git: How do I recursively add all files in a directory subtree that match a glob pattern?", if you properly escape or quote your pathspec globbing (like '*.java'), then yes, git add '*.java'

Git 2.13 (Q2 2017) improves that for interactive add:

See commit 7288e12 (14 Mar 2017) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 153e0d7, 17 Mar 2017)

add --interactive: do not expand pathspecs with ls-files

When we want to get the list of modified files, we first expand any user-provided pathspecs with "ls-files", and then feed the resulting list of paths as arguments to "diff-index" and "diff-files".
If your pathspec expands into a large number of paths, you may run into one of two problems:

  1. The OS may complain about the size of the argument list, and refuse to run. For example:

    $ (ulimit -s 128 && git add -p drivers)
    Can't exec "git": Argument list too long at .../git-add--interactive line 177.
    Died at .../git-add--interactive line 177.
    

That's on the linux.git repository, which has about 20K files in the "drivers" directory (none of them modified in this case). The "ulimit -s" trick is necessary to show the problem on Linux even for such a gigantic set of paths.
Other operating systems have much smaller limits (e.g., a real-world case was seen with only 5K files on OS X).

  1. Even when it does work, it's really slow. The pathspec code is not optimized for huge numbers of paths. Here's the same case without the ulimit:

    $ time git add -p drivers
      No changes.
    
    real  0m16.559s
    user    0m53.140s
    sys 0m0.220s
    

We can improve this by skipping "ls-files" completely, and just feeding the original pathspecs to the diff commands.

Historically the pathspec language supported by "diff-index" was weaker, but that is no longer the case.

Gangling answered 10/4, 2017 at 20:34 Comment(0)
C
1

put line in ~/.gitconfig

[alias] addt = !sh -c 'git ls-files | grep \"\\.$1*\" | xargs git add' -

If you want to add all modified java file can just do: git addt java

Samely, if you want to add all modified python file can just do: git addt py

Conjunctive answered 9/6, 2019 at 6:51 Comment(0)
A
-1
A variation that I used in a GNUmakefile
.PHONY: docs
docs:
    @echo docs
    bash -c "if pgrep MacDown; then pkill MacDown; fi"
    bash -c 'cat $(PWD)/HEADER.md                >  $(PWD)/README.md'
    bash -c 'cat $(PWD)/COMMANDS.md              >> $(PWD)/README.md'
    bash -c 'cat $(PWD)/FOOTER.md                >> $(PWD)/README.md'
    bash -c 'pandoc -s README.md -o index.html'
    bash -c "if hash open 2>/dev/null; then open README.md; fi || echo failed to open README.md"
    #git add --ignore-errors *.md
    git ls-files -co --exclude-standard | grep '\.md/$\' | xargs git 
Accrue answered 2/7, 2021 at 18:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.