From: Phil Hord <hidden> Date: 2016-06-15 22:58:31
When a commit is grepped and matching filenames are printed, grep-objects
creates the filename by prefixing the original cmdline argument to the
matched path separated by a colon. Normally this forms a valid blob
reference to the filename, like this:
git grep -l foo HEAD
HEAD:some/path/to/foo.txt
^
But a tree path may be given to grep instead; in this case the colon is
not a valid delimiter to use since it is placed inside a path.
git grep -l foo HEAD:some
HEAD:some:path/to/foo.txt
^
The slash path delimiter should be used instead. Fix git grep to
discern the correct delimiter so it can report valid object names.
git grep -l foo HEAD:some
HEAD:some/path/to/foo.txt
^
Also, prevent the delimiter being added twice, as happens now in these
examples:
git grep -l foo HEAD:
HEAD::some/path/to/foo.txt
^
git grep -l foo HEAD:some/
HEAD:some/:path/to/foo.txt
^
Add a test to confirm correct path forming.
---
This version is a bit more deterministic and also adds a test.
It accepts the expense of examining the path argument again to
determine if it is a tree-ish + path rather than just a tree (commit).
The get_sha1 call occurs one extra time for each tree-ish argument,
so it's not expensive. We avoid mucking with the object_array API this
way, and also do not rely on the object-type to tell us anything about
the way the object name was spelled.
This one also adds a check to avoid duplicating an extant delimiter.
builtin/grep.c | 9 ++++++++-
t/t7810-grep.sh | 15 +++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
From: Jeff King <hidden> Date: 2016-06-15 22:58:31
On Mon, Aug 26, 2013 at 10:46:12AM -0400, Phil Hord wrote:
This version is a bit more deterministic and also adds a test.
It accepts the expense of examining the path argument again to
determine if it is a tree-ish + path rather than just a tree (commit).
The get_sha1 call occurs one extra time for each tree-ish argument,
so it's not expensive.
I don't like this approach in general because it lacks atomicity. IOW,
the thing you are looking up may change between the two get_sha1 calls.
You're _almost_ good here because you don't actually care what the
second call returns, but only which features it _would_ have used. But
you may see the second call fail because the ref doesn't exist anymore,
or points to a different tree, and you will erroneously use ":" instead
of "/".
I admit this is not that likely, but I'd really rather avoid introducing
such races if we can.
We avoid mucking with the object_array API this way, and also do not
rely on the object-type to tell us anything about the way the object
name was spelled.
Changing the object_array API would be hard, but I don't think we need
to do it here. Can we simply stop using object_array to pass the list,
and instead just have a custom list?
I'll see how painful that is.
-Peff
From: Jeff King <hidden> Date: 2016-06-15 22:58:31
On Mon, Aug 26, 2013 at 03:28:26PM -0400, Jeff King wrote:
Changing the object_array API would be hard, but I don't think we need
to do it here. Can we simply stop using object_array to pass the list,
and instead just have a custom list?
I'll see how painful that is.
Not very, I think. Here's the series.
[1/2]: grep: stop using object_array
[2/2]: grep: use slash for path delimiter, not colon
-Peff
From: Jeff King <hidden> Date: 2016-06-15 22:58:31
We use an object_array to store the set of objects to grep
that we received on the command-line. There is no particular
reason to use object_array here except that its code was
already written, and it contained the elements we needed
(though we did not care about mode at all).
However, future patches will need to remember more about the
arguments than object_array can provide. Let's use our own
custom struct. Thanks to the ALLOC_GROW macro, this is really
only a few lines longer (and we even save a few bytes of
memory as we do not care about the mode, and we know that we
do not have to copy the name strings, as they come from
argv).
Signed-off-by: Jeff King <redacted>
---
builtin/grep.c | 31 ++++++++++++++++++++-----------
1 file changed, 20 insertions(+), 11 deletions(-)
@@ -861,7 +870,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)pathspec.max_depth=opt.max_depth;pathspec.recursive=1;-if(show_in_pager&&(cached||list.nr))+if(show_in_pager&&(cached||list_nr))die(_("--open-files-in-pager only works on the worktree"));if(show_in_pager&&opt.pattern_list&&!opt.pattern_list->next){
@@ -889,12 +898,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)if(!use_index||untracked){intuse_exclude=(opt_exclude<0)?use_index:!!opt_exclude;-if(list.nr)+if(list_nr)die(_("--no-index or --untracked cannot be used with revs."));hit=grep_directory(&opt,&pathspec,use_exclude);}elseif(0<=opt_exclude){die(_("--[no-]exclude-standard cannot be used for tracked contents."));-}elseif(!list.nr){+}elseif(!list_nr){if(!cached)setup_work_tree();
@@ -902,7 +911,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)}else{if(cached)die(_("both --cached and trees are given."));-hit=grep_objects(&opt,&pathspec,&list);+hit=grep_objects(&opt,&pathspec,list,list_nr);}if(use_threads)
From: Jeff King <hidden> Date: 2016-06-15 22:58:31
From: Phil Hord <redacted>
When a commit is grepped and matching filenames are printed, grep-objects
creates the filename by prefixing the original cmdline argument to the
matched path separated by a colon. Normally this forms a valid blob
reference to the filename, like this:
git grep -l foo HEAD
HEAD:some/path/to/foo.txt
^
But a tree path may be given to grep instead; in this case the colon is
not a valid delimiter to use since it is placed inside a path.
git grep -l foo HEAD:some
HEAD:some:path/to/foo.txt
^
The slash path delimiter should be used instead. Fix git grep to
discern the correct delimiter so it can report valid object names.
git grep -l foo HEAD:some
HEAD:some/path/to/foo.txt
^
Also, prevent the delimiter being added twice, as happens now in these
examples:
git grep -l foo HEAD:
HEAD::some/path/to/foo.txt
^
git grep -l foo HEAD:some/
HEAD:some/:path/to/foo.txt
^
Add a test to confirm correct path forming.
Signed-off-by: Jeff King <redacted>
---
I left the author as you, since you have done all the hard work; this is
really just me rebasing your patch on top of mine. But note that you did
not signoff the original.
builtin/grep.c | 13 +++++++++----
t/t7810-grep.sh | 15 +++++++++++++++
2 files changed, 24 insertions(+), 4 deletions(-)
@@ -822,8 +825,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)for(i=0;i<argc;i++){constchar*arg=argv[i];unsignedcharsha1[20];+structobject_contextoc;/* Is it a rev? */-if(!get_sha1(arg,sha1)){+if(!get_sha1_with_context(arg,0,sha1,&oc)){structobject*object=parse_object_or_die(sha1,arg);if(!seen_dashdash)verify_non_filename(prefix,arg);
From: Jeff King <hidden> Date: 2016-06-15 22:58:31
On Mon, Aug 26, 2013 at 10:13:14PM +0200, Johannes Sixt wrote:
Am 26.08.2013 21:56, schrieb Jeff King:
quoted
Also, prevent the delimiter being added twice, as happens now in these
examples:
git grep -l foo HEAD:
HEAD::some/path/to/foo.txt
^
Which one of these two does it print then?
HEAD:/some/path/to/foo.txt
HEAD:some/path/to/foo.txt
It should (and does) print the latter.
But I do note that our pathspec handling for subdirectories seems buggy.
If you do:
$ cd Documentation
$ git grep -l foo | head -1
RelNotes/1.5.1.5.txt
that's fine; we limit to the current directory. But then if you do:
$ git grep -l foo HEAD | head -1
HEAD:RelNotes/1.5.1.5.txt
we still limit to the current directory, but the output does note note
this (it should be "HEAD:./RelNotes/1.5.1.5.txt"). I think this bug is
orthogonal to Phil's patch, though.
-Peff
From: Phil Hord <hidden> Date: 2016-06-15 22:58:31
On Mon, Aug 26, 2013 at 4:13 PM, Johannes Sixt [off-list ref] wrote:
Am 26.08.2013 21:56, schrieb Jeff King:
quoted
Also, prevent the delimiter being added twice, as happens now in these
examples:
git grep -l foo HEAD:
HEAD::some/path/to/foo.txt
^
Which one of these two does it print then?
HEAD:/some/path/to/foo.txt
HEAD:some/path/to/foo.txt
With my patch it prints the latter.
This is because get_sha1_with_context("HEAD:"...) returns an empty
'path' string. The code decides to use ':' as the delimiter in that
case, but it sees there already is one at the end of "HEAD:".
Phil
From: Phil Hord <hidden> Date: 2016-06-15 22:58:31
On Mon, Aug 26, 2013 at 4:52 PM, Jeff King [off-list ref] wrote:
On Mon, Aug 26, 2013 at 10:13:14PM +0200, Johannes Sixt wrote:
quoted
Am 26.08.2013 21:56, schrieb Jeff King:
quoted
Also, prevent the delimiter being added twice, as happens now in these
examples:
git grep -l foo HEAD:
HEAD::some/path/to/foo.txt
^
Which one of these two does it print then?
HEAD:/some/path/to/foo.txt
HEAD:some/path/to/foo.txt
It should (and does) print the latter.
But I do note that our pathspec handling for subdirectories seems buggy.
If you do:
$ cd Documentation
$ git grep -l foo | head -1
RelNotes/1.5.1.5.txt
that's fine; we limit to the current directory. But then if you do:
$ git grep -l foo HEAD | head -1
HEAD:RelNotes/1.5.1.5.txt
we still limit to the current directory, but the output does not note
this (it should be "HEAD:./RelNotes/1.5.1.5.txt"). I think this bug is
orthogonal to Phil's patch, though.
Maybe not. My path completes the assumption that the L:R value
returned by grep is an object ref; but Junio still thought it wasn't.
I think this is another case where his view was correct.
There's more bad news on this front.
$ cd Documentation
$ git grep -l foo HEAD .. | head -1
HEAD:../.gitignore
That's not a valid ref, either (though maybe it could be).
Phil
From: Jeff King <hidden> Date: 2016-06-15 22:58:31
On Mon, Aug 26, 2013 at 05:03:04PM -0400, Phil Hord wrote:
quoted
$ git grep -l foo HEAD | head -1
HEAD:RelNotes/1.5.1.5.txt
we still limit to the current directory, but the output does not note
this (it should be "HEAD:./RelNotes/1.5.1.5.txt"). I think this bug is
orthogonal to Phil's patch, though.
Maybe not. My path completes the assumption that the L:R value
returned by grep is an object ref; but Junio still thought it wasn't.
I think this is another case where his view was correct.
I certainly assumed it was, because it is in most cases it is. And something
like "HEAD:RelNotes/1.5.1.5.txt" certainly _looks_ like one, and is
generated by the current git. And what is the point of coming up with a
file listing if the names you return do not actually exist?
There's more bad news on this front.
$ cd Documentation
$ git grep -l foo HEAD .. | head -1
HEAD:../.gitignore
That's not a valid ref, either (though maybe it could be).
Yes, though we seem to normalize paths already. So the other entries
from that command are (in git.git):
HEAD:../.mailmap
HEAD:RelNotes/1.5.1.5.txt
So we could either:
1. Prepend the current path before normalizing to yield:
HEAD:.mailmap
HEAD:Documentation/RelNotes/1.5.1.5.txt
2. Teach the get_sha1 path parser about "..", and prepend "./" when we
are in a prefixed subdir.
HEAD:./../.mailmap
HEAD:./RelNotes/1.5.1.5.txt
-Peff