tags:

views:

294

answers:

4

I have a dirty working tree, dirty because I made changes to source files and touched up some images. I was trying to add just the images to the index, so I ran this command:

git add *.png

But, this doesn't add the files. There were a few new image files that were added, but none of the ones that were modified/pre-existing were added.

What gives?

Edit: Here is some relevant terminal output

$ git status
# On branch master
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/main/java/net/plugins/analysis/FormMatcher.java
#   modified:   src/main/resources/icons/doctor_edit_male.png
#   modified:   src/main/resources/icons/doctor_female.png
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   src/main/resources/icons/arrow_up.png
#   src/main/resources/icons/bullet_arrow_down.png
#   src/main/resources/icons/bullet_arrow_up.png
no changes added to commit (use "git add" and/or "git commit -a")

Then executed "git add *.png" (no output after command)

Then:

$ git status
# On branch master
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   src/main/resources/icons/arrow_up.png
#   new file:   src/main/resources/icons/bullet_arrow_down.png
#   new file:   src/main/resources/icons/bullet_arrow_up.png
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/main/java/net/plugins/analysis/FormMatcher.java
#   modified:   src/main/resources/icons/doctor_edit_female.png
#   modified:   src/main/resources/icons/doctor_edit_male.png
A: 

should work just fine.

what message do you get after the git add command?

ryw
A: 

In doubt, try:

git -A -- *.png 

, which might be more comprehensive (git add man page)

-A
--all

Like -u, but match <filepattern> against files in the working tree in addition to the index.
That means that it will find new files as well as staging modified content and removing files that are no longer in the working tree.

See SO question "Difference of “git add -A” and “git add ."

VonC
tried it, but it didn't work :(
D Lawson
+5  A: 

Michael Mrozek's comment is essentially the answer. *.png matches files of that name in the current directory, not in subdirectories. If you want to add ones in a subdirectory, do so:

git add src/main/resources/icons/*.png

Or, depending on your shell, you may be able to do:

git add **/*.png

The point is that it's the shell that does the globbing (expands *.png into a list of filenames). Git has nothing to do with that; it just takes the arguments the shell gives it.

Edit: Since this managed to get accepted, I should go ahead and point out as others did that some git commands do support globbing internally (via fnmatch), so if you quote a glob pattern, it'll be passed unmodified by the shell to git, where the globbing expansion will take place.

Jefromi
Good catch. +1. I have seen odd things with the underlying `fnname()` method which differ per OS: see for instance: http://stackoverflow.com/questions/2330471/gitignore-does-not-understand-my-folder-wildcard-on-windows and http://stackoverflow.com/questions/1470572/
VonC
+2  A: 

Some of Git's commands (including git add) can handle filename patterns themselves. But first you have to make sure the pattern gets to Git.

In general, unquoted patterns may not make it to the invoked command. As a special case, if the pattern matches no files (in the current directory if there is no slash in the pattern), bash will pass the unexpanded pattern to the command (unless the nullglob option is set, in which case the pattern argument will be dropped from the arguments passed to the command). But the behavior varies among shells. By default, zsh issues an error like “no matches found” unless the nomatch option is unset (passes the unexpanded pattern as an argument) or the null_glob option is set (drops the pattern from the list of arguments).

So using unquoted patterns and expecting them to get to the underlying command is only reliable if you know the behavior of your shell and you know the contents of any directories specified in the pattern (usually just the current directory for patterns without a slash).

So, for maximum reliability, if you want git add to get the literal string *.png as an argument, then you should quote it.

git add \*.png
git add "*.png"
git add '*'.png
# etc.

Once you have successfully passed a filename pattern to Git, you will encounter some differences from how shells handles them.

The primary difference of concern in this question is that matching is done by fnmatch(3) without FNM_PATHNAME set. This means that a pattern like *.png will match a file foo.png in the current directory (just like a shell), but it will also match dir/bar.png (because without FNM_PATHNAME the * can match against the slash). Git calls its patterns “pathspecs” to differentiate them from shell “glob” patterns.

Unfortunately, there is an inconsistency in the way that git add handles pathspecs. It always applies them to untracked files, but it never applies them to tracked files (instead, filename-type arguments like pathspecs are only checked for exact matches against the list of tracked files). This is apparently exactly what the OP actually ran into, as it would add new files that match the pathspec, but would fail to update (already) tracked files that match the pathspec.

Jefromi's workaround (git add **/*.png) works as long as your shell supports the ** extended pattern (or an equivalent).

You can actually make Git do the work, but using the shell is probably easier (if your shell supports it).

# update all tracked files matching the Git pathspec *.png
git ls-files --cached -z \*.png | git update-index --add -z --stdin

Smoothing out Git's internal pathspec handling is a “medium term” goal, but not one that anyone with the time, interest, and pertinent experience has stepped forward to fix.

It was brought up as a project idea for Google Summer of Code 2010 (not picked up by anyone), and related issues come up on the mailing list once in a while (In January and again in March 2010 someone reported a symptom much like the OP's, Git's maintainer explained what he would like to see in the long term).

Chris Johnsen
+1, Way more complete than my answer.
Jefromi