5

I have been seeking it on Google for 2 hours and still can't find the solution.

I want to checkout all files with wildcard to a specific revision. I used following command:

git checkout 663090b1c 'src/app/**/*.spec.ts'

But it says:

error: pathspec 'src/app/**/*.spec.ts' did not match any file(s) known to git.

Can anyone help me?

Thanh Nguyen
  • 5,174
  • 11
  • 43
  • 74
  • Does commit `663090b1c` contain any files that match the `src/app/app/**/*.spec.ts` pathspec? (Try `git ls-tree -r 663090b1c` and search for files whose name starts with `src/app/app/`, contains any number of directories, and ends with `*.spec.ts`.) – torek Jul 25 '19 at 02:27
  • @torek Sorry, it was my copy/paste issue. I have updated it. – Thanh Nguyen Jul 25 '19 at 02:28
  • Ah, then search the `git ls-tree -r 663090b1c` output for the non-doubled `app/` file names. – torek Jul 25 '19 at 02:30
  • I tried your commands and it showed many changed files with `*.spec.ts`. – Thanh Nguyen Jul 25 '19 at 02:33
  • `git ls-tree` doesn't show *changed* files, it just shows *files*. You specified files that are specifically named `src/app/.spec.ts`—just ending in `.spec.ts` is not sufficient; their names must *start* with `src/app/` as well. If such files exist, `git checkout` really should have extracted them. One other important item: are you currently in the *top level* of your work-tree, or a subdirectory? – torek Jul 25 '19 at 02:37
  • Yeah, it showed only files only. For example: ```100644 blob src/app/component/.component.spec.ts``` And I am at the project root. – Thanh Nguyen Jul 25 '19 at 02:44
  • 1
    OK, then it seems like that *should* have worked. ... Interesting, I *can* seem to reproduce the problem in a different repository. Not sure yet why (and will be off line for a while and can't look into it further at the moment). – torek Jul 25 '19 at 02:51

2 Answers2

5

I believe that this should work, but clearly it doesn't. Here's the effect of using the most recent Git (which you can get at https://github.com/git/git) on Documentation/**/*.txt.

I built Git, then used the system Git (at 2.21, slightly behind this version which is 2.22.0.545.g9c9b961d7e) to do this:

$ rm Documentation/*/*.txt
$ git status --short | head
 D Documentation/RelNotes/1.5.0.1.txt
 D Documentation/RelNotes/1.5.0.2.txt
 D Documentation/RelNotes/1.5.0.3.txt
 D Documentation/RelNotes/1.5.0.4.txt
 D Documentation/RelNotes/1.5.0.5.txt
 D Documentation/RelNotes/1.5.0.6.txt
 D Documentation/RelNotes/1.5.0.7.txt
 D Documentation/RelNotes/1.5.0.txt
 D Documentation/RelNotes/1.5.1.1.txt
 D Documentation/RelNotes/1.5.1.2.txt
$ git checkout -- 'Documentation/**/*.txt'
$ git status --short | head
$ 

Now, however:

$ ./git checkout HEAD -- 'Documentation/**/*.txt'
error: pathspec 'Documentation/**/*.txt' did not match any file(s) known to git

It seems that pathspecs work when used with the index, but not when used with a commit hash.

As a workaround—note that this workaround is somewhat defective, though you can patch around it further—you can first read the desired commit into a temporary index, then do the git checkout from the temporary index. The following script is completely untested.

#! /bin/sh
# git-co-c-ps: git checkout <commit> <pathspec>
# work around a bug in which combining <commit> and <pathspec> does not work
. git-sh-setup
case $# in
2) ;;
*) die "usage: $0 <commit> <pathspec>";;
esac

hash=$(git rev-parse "$1^{commit}") || exit 1
export GIT_INDEX_FILE=$(mktemp) || exit 1
rm -f $GIT_INDEX_FILE
trap "rm -f $GIT_INDEX_FILE" 0 1 2 3 15
git read-tree "$hash" || exit 1
git checkout -- "$2"

The (untested) idea here is to read the commit specified by "$1" into a temporary index. (We could relax the ^{commit} to be just ^{tree} as any tree-ish should suffice.) Then, having read that into a temporary index, we use the mode of "git checkout" with a pathspec ($2) that works, using the temporary index. This writes to the work-tree.

The defect is that the now-updated work-tree files are not also updated in the index. A real git checkout would have copied these same files to the index. It should be possible to unset $GIT_INDEX_FILE and git add the added files to the real index. This is left as an exercise. Note that it is not as trivial as just git add -- "$2" since some work-tree files may be in the work-tree not because they were read into the temporary index by the git read-tree operation, but rather because they were already in the work-tree. (Some may be tracked and modified-but-not-yet-staged/added, and others may be untracked.)

Another, perhaps better, workaround is to use git ls-files on the temporary index, to find the files of interest. Use xargs or similar to pass these file names to a git checkout that is run without the temporary index:

files_file=$(mktemp)
trap "rm -f $files_file" 0 1 2 3 15
(
    export GIT_INDEX_FILE=$(mktemp) || exit 125
    rm -f $GIT_INDEX_FILE
    trap "rm -f $GIT_INDEX_FILE" 0 1 2 3 15
    git read-tree $hash
    git ls-files -z -- "$2"
) > $files_file
# if the inner shell exited with an error, exit now
case $? in 125) exit 1;; esac
xargs -0 ...

(Filling in the rest of this to make a usable Git shell command is also left as an exercise.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • Note: I have see wildcard use with pathspec in git log (since Git 2.8): https://stackoverflow.com/a/35622183/6309. But it does not seem to work with git checkout indeed. – VonC Jul 25 '19 at 06:43
2

It would be easier to check first a find command:

find . src/app/**/*.spec.ts

See if the list is correct.
As a workaround, you can then do:

find . src/app/**/*.spec.ts | xargs git checkout <sha1> -- 
# or 
find . src/app/**/*.spec.ts -exec git checkout <sha1> -- {} \; 
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250