3

I am looking at the git repository of a rather large project whose test-suite is sprinkled across several files in various sub-directories. All test case filenames follow the same wildcard pattern (say, test-*). Given this, how do I list the newest test cases committed to the repository?

I'm not too particular about whether those commits create a test case or just last modified a test case. Either is fine. I just want to see some of the newer tests written/modified by the project's contributors.

As far as I can make out:

git log -- */test*

Only shows commits that touch test cases one level down in the hierarchy, and not lower.

ArjunShankar
  • 23,020
  • 5
  • 61
  • 83
  • I just tried `git log -- */test*` on a dummy repo, and it shows log entries in which, for instance, `foo/bar/test1.py` was created. Which version of Git are you using? I'm running Git 2.0.1. – jub0bs Aug 23 '14 at 00:58
  • @Jubobs - which version of git are you running? Mine (1.8.4) does not. – ArjunShankar Aug 23 '14 at 00:59
  • I'll post my baby example showing that it works in Git 2.0.1. Perhaps the source changed between your version and mine... – jub0bs Aug 23 '14 at 01:08
  • @kev, there was nothing particularly wrong with your (now deleted) answer. The diff works pretty fine. – ArjunShankar Aug 23 '14 at 01:12
  • @Jubobs - Great. I am not going to bother upgrading my git, but probably others will find it useful. Thanks! – ArjunShankar Aug 23 '14 at 01:13

3 Answers3

5

If */test* matches some file(s), your shell will expand this expression. Try:

echo */test*

at the shell prompt. Compare, for instance:

$ cd /tmp
$ mkdir empty
$ cd empty
$ echo */test*
*/test*

/tmp/empty is empty so */test* matches nothing. But then:

$ mkdir dir; touch dir/test.file
$ echo */test*
dir/test.file

So, if you have nothing matching */test* and you run:

$ somegitcommand */test*

the git command will see */test* as its argument; but if you have something that matches, the git command will see dir/test.file as its argument. The *s have vanished.

The fix is really super-simple: quote the */test* part:

$ git log --oneline -- '*/test*'

Now git log will see */test*, rather than dir/test.file, and git will be able to do recursive matching.


It's worth mentioning that not all shells behave the same way. In csh and tcsh, if there is no match for a shell glob like */test.*, you get:

> rm -r dir
> echo */test*
echo: No match.

In bash, you can choose the behavior:

bash$ echo */test*
*/test*
bash$ shopt -s failglob
bash$ echo */test*
bash: no match: */test*

(in fact you can do the same in csh and tcsh; it's just inverted: set nonomatch and the */test* is passed through to echo).

Also, this illustrates the difference between the shell's wildcard globbing (which does not recurse into subdirectories) and git's (which does). See Jubobs' answer and try (in the shell) echo */*/test* vs echo */test* (all without quotes). Some shells have recursive globbing that can be enabled in various ways, such as using ** to indicate recursion is desired: echo **/test*.


One last note: git's wildcard matching has changed somewhat from one version to another, especially in .gitignore files. Specifically, ** meaning "zero or more directories" (specifically in .gitignore files) was added to git in version 1.8.2.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Weird. It seems to work without quotes in my example. Did I miss something? – jub0bs Aug 23 '14 at 01:40
  • @Jubobs: all your test files are several levels down. Try the `echo */test*` diagnostic! :-) Note that git's matching allows directories, while the shell's doesn't. – torek Aug 23 '14 at 01:42
  • @Jubobs: exactly. This is why I actually prefer csh's default of simply failing to run the command: you don't get used to having glob expressions like, e.g., `[a-f][a-f]` pass through because all your own file names are 3 characters long or more, only to have it expand instead unexpectedly for the first time that you're in a directory with a file named `ed`. – torek Aug 23 '14 at 01:57
  • .. taking it in :) I see; the shell expands the glob pattern too soon, if there is a match. But if there is none, it's passed to Git unexpanded and Git interprets the glob pattern differently from how the shell does. – jub0bs Aug 23 '14 at 02:26
  • Could the answer to [this question of mine](http://stackoverflow.com/questions/25362738/why-isnt-the-pathspec-magic-exclude-excluding-the-specified-files-from-git-l) also be an untimely expansion? – jub0bs Aug 23 '14 at 02:40
  • @Jubobs: hm, no, shouldn't be doing globbing in double quotes (variables yes, glob no). – torek Aug 23 '14 at 05:51
  • Amazing catching that shell expansion. I totally forgot about it. – ArjunShankar Aug 23 '14 at 18:35
2

Disclaimer: This isn't the complete picture; I'm only leaving this answer live because torek makes reference to it in his much more complete answer.

I use Git 2.0.1 in bash, and I can't reproduce the behaviour you're describing: as demonstrated below,

git log -1 -- */test*

shows commits that add/remove/modify files that match test* two levels down the hierarchy of the working tree. Perhaps the way glob patterns are handled changed between v1.8.3 and v2.0.1. Consider updating to a more recent version.

# set things up
cd ~/Desktop
mkdir babyexample
cd babyexample
git init

# add three test files under foo/bar/
mkdir foo
cd foo
mkdir bar
cd bar
touch test1.py
touch test2.c
touch testmypatience.baz
cd ../..
git add .
git commit -m "add three test files"

# add a README
touch README.md
git add README.md
git commit -m "add README"

# remove test1.py
git rm foo/bar/test1.py
git commit -m "remove Python file"

# modify test2.c
printf "hello\n" > foo/bar/test2.c
git add test2.c
git commit -m "say hello in C file"

Then,

git log --oneline

outputs

9537199 say hello in C file
20b90f2 remove Python file
494cf11 add README
bfb6686 add three test files

whereas

git log --oneline -- */test*

filters the log output as expected:

9537199 say hello in C file
20b90f2 remove Python file
bfb6686 add three test files
Community
  • 1
  • 1
jub0bs
  • 60,866
  • 25
  • 183
  • 186
  • Hmm, so all this is finally explained by the shell expansion. Because the missing match causes the * to be passed to `git`, which then uses it correctly. – ArjunShankar Aug 23 '14 at 18:37
  • 1
    @ArjunShankar Yes :) I was way off, but torek found the answer. – jub0bs Aug 23 '14 at 18:39
1

Use git diff to list all recently changed tests:

git diff --stat HEAD^ -- '**/test*'
 path/to/test_x        | 17 -----------------
 path/to/test_y        | 1  +
 2 files changed, 1 insertions(+), 17 deletions(-)
kev
  • 155,172
  • 47
  • 273
  • 272
  • Where do you get `3.days.ago` from? I don't think the OP knows the timestamp of the last commit that added test files to the repo. – jub0bs Aug 23 '14 at 01:07