{"thread":{"id":"6859","subject":"[PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","startedAt":"2007-02-17T17:39:50Z","lastAt":"2007-02-18T15:41:17Z","messageCount":9,"participants":["Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"34875","messageId":"Pine.LNX.4.63.0702171838510.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6859","inReplyTo":null,"subject":"[PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-17T17:39:50Z","receivedAt":"2007-02-17T17:39:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIf you want to know which revisions contained a certain version\nof a file, just say\n\n\tgit name-rev --file <filename>\n\nwhich will read the file, and give you a list of revisions\ncontaining a file with the same contents. If <filename> is \"-\",\nit will read the contents from stdin. Of course, this is a really\nexpensive operation.\n\nThis feature was suggested by Mike Coleman.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin-name-rev.c |  118 +++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 117 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-name-rev.c b/builtin-name-rev.c\nindex 89ea95d..f08b065 100644\n--- a/builtin-name-rev.c\n+++ b/builtin-name-rev.c\n@@ -3,10 +3,95 @@\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"refs.h\"\n+#include \"tree-walk.h\"\n+#include \"object-hash.h\"\n #include <regex.h>\n \n static const char name_rev_usage[] =\n-\t\"git-name-rev [--tags | --ref-filter=<regexp>] ( --all | --stdin | committish [committish...] )\\n\";\n+\t\"git-name-rev [--tags | --ref-filter=<regexp>] ( --all | --stdin | committish [committish...] | --file <filename> )\\n\";\n+\n+static unsigned char *file_sha1;\n+static struct object_hash file_trees = { 0, 0, NULL };\n+static struct object_array file_commits = { 0, 0, NULL };\n+#define CONTAINS_FILE (1u<<10)\n+\n+static int get_file(const char *path)\n+{\n+\tint fd, ret;\n+\tstruct stat st;\n+\n+\tfile_sha1 = xmalloc(20);\n+\tif (!strcmp(path, \"-\"))\n+\t\tret = index_pipe(file_sha1, 0, \"blob\", 0);\n+\telse {\n+\t\tif ((fd = open(path, O_RDONLY)) < 0 || fstat(fd, &st) < 0)\n+\t\t\treturn -1;\n+\t\tret = index_fd(file_sha1, fd, &st, 0, \"blob\");\n+\t}\n+\tif (ret)\n+\t\treturn ret;\n+\tif (!parse_object(file_sha1))\n+\t\treturn error(\"Object not found for '%s'\", path);\n+\treturn 0;\n+}\n+\n+struct object_name {\n+\tint len;\n+\tchar *name;\n+};\n+\n+static struct object_name *name_file(struct tree *tree)\n+{\n+\tstatic struct object_name null_name = { 0, NULL };\n+\tstruct object_name *name;\n+\tstruct tree_desc desc;\n+\tstruct name_entry entry;\n+\n+\tif (!tree->object.parsed)\n+\t\tparse_tree(tree);\n+\telse if ((name = lookup_object_in_hash(&tree->object, &file_trees)))\n+\t\treturn name->len ? name : NULL;\n+\n+\tif (!tree->buffer) {\n+\t\tadd_object_to_hash(&tree->object, &null_name, &file_trees);\n+\t\treturn NULL;\n+\t}\n+\n+\tdesc.buf = tree->buffer;\n+\tdesc.size = tree->size;\n+\n+\twhile (tree_entry(&desc, &entry))\n+\t\tif (!hashcmp(file_sha1, entry.sha1)) {\n+\t\t\tname = xcalloc(sizeof(struct object_name), 1);\n+\t\t\tname->len = entry.pathlen;\n+\t\t\tname->name = xstrdup(entry.path);\n+\t\t\tbreak;\n+\t\t} else if (S_ISDIR(entry.mode)) {\n+\t\t\tstruct object *subtree = parse_object(entry.sha1);\n+\t\t\tstruct object_name *subname;\n+\n+\t\t\t/* just to be safe */\n+\t\t\tif (subtree->type != OBJ_TREE)\n+\t\t\t\tdie(\"%s is not a tree?\",\n+\t\t\t\t\t\tsha1_to_hex(entry.sha1));\n+\n+\t\t\tsubname = name_file((struct tree *)subtree);\n+\t\t\tif (!subname)\n+\t\t\t\tcontinue;\n+\t\t\tname = xcalloc(sizeof(struct object_name), 1);\n+\t\t\tname->len = entry.pathlen + 1 + subname->len;\n+\t\t\tname->name = xmalloc(name->len + 1);\n+\t\t\tmemcpy(name->name, entry.path, entry.pathlen);\n+\t\t\tname->name[entry.pathlen] = '/';\n+\t\t\tstrncpy(name->name + entry.pathlen + 1,\n+\t\t\t\t\tsubname->name, subname->len);\n+\t\t\tbreak;\n+\t\t}\n+\tadd_object_to_hash(&tree->object, name ? name : &null_name,\n+\t\t\t&file_trees);\n+\n+\treturn name;\n+}\n \n typedef struct rev_name {\n \tconst char *tip_name;\n@@ -23,6 +108,7 @@ static void name_rev(struct commit *commit,\n \tstruct rev_name *name = (struct rev_name *)commit->util;\n \tstruct commit_list *parents;\n \tint parent_number = 1;\n+\tstruct object_name *file_name;\n \n \tif (!commit->object.parsed)\n \t\tparse_commit(commit);\n@@ -54,6 +140,12 @@ copy_data:\n \t} else\n \t\treturn;\n \n+\tif (file_sha1 && !(commit->object.flags & CONTAINS_FILE) &&\n+\t\t\t(file_name = name_file(commit->tree))) {\n+\t\tcommit->object.flags |= CONTAINS_FILE;\n+\t\tadd_object_array(&commit->object, NULL, &file_commits);\n+\t}\n+\n \tfor (parents = commit->parents;\n \t\t\tparents;\n \t\t\tparents = parents->next, parent_number++) {\n@@ -175,6 +267,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \t\t\t\ttransform_stdin = 1;\n \t\t\t\tcutoff = 0;\n \t\t\t\tcontinue;\n+\t\t\t} else if (!strcmp(*argv, \"--file\")) {\n+\t\t\t\tif (argc != 2)\n+\t\t\t\t\tusage(name_rev_usage);\n+\t\t\t\tif (get_file(argv[1]))\n+\t\t\t\t\tdie(\"Could not read '%s'\", argv[1]);\n+\t\t\t\tcutoff = 0;\n+\t\t\t\targc = 1;\n+\t\t\t\tcontinue;\n \t\t\t}\n \t\t\tusage(name_rev_usage);\n \t\t}\n@@ -202,6 +302,22 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \n \tfor_each_ref(name_ref, &data);\n \n+\tif (file_sha1) {\n+\t\tint i;\n+\t\tfor (i = 0; i < file_commits.nr; i++) {\n+\t\t\tstruct commit *commit =\n+\t\t\t\t(struct commit *)file_commits.objects[i].item;\n+\t\t\tstruct rev_name *rev_name = commit->util;\n+\t\t\tstruct object_name *obj_name = name_file(commit->tree);\n+\n+\t\t\tprintf(\"%s\", rev_name->tip_name);\n+\t\t\tif (rev_name->generation)\n+\t\t\t\tprintf(\"^%d\", rev_name->generation);\n+\t\t\tprintf(\":%s\\n\", obj_name->name);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n \tif (transform_stdin) {\n \t\tchar buffer[2048];\n \t\tchar *p, *p_start;\n-- \n1.5.0.2139.gdafc9-dirty\n"},{"id":"34884","messageId":"7vr6sotzft.fsf@assigned-by-dhcp.cox.net","threadId":"6859","inReplyTo":"Pine.LNX.4.63.0702171838510.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-17T18:24:22Z","receivedAt":"2007-02-17T18:24:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> If you want to know which revisions contained a certain version\n> of a file, just say\n>\n> \tgit name-rev --file <filename>\n>\n> which will read the file, and give you a list of revisions\n> containing a file with the same contents. If <filename> is \"-\",\n> it will read the contents from stdin. Of course, this is a really\n> expensive operation.\n\nI expected this to take arbitrary object name and let the caller\nto do 'hash-object', so that you could also find a certain tree,\nnot just blob.\n"},{"id":"34886","messageId":"7vire0tz51.fsf@assigned-by-dhcp.cox.net","threadId":"6859","inReplyTo":"Pine.LNX.4.63.0702171838510.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-17T18:30:50Z","receivedAt":"2007-02-17T18:30:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> +static struct object_hash file_trees = { 0, 0, NULL };\n> +static struct object_array file_commits = { 0, 0, NULL };\n\nThese don't look good for two reasons: (1) you could leave BSS\nto do the 0 initialization; (2) you need to change this if you\nneed to change the shape of \"struct object_hash\" later.\n\n> +#define CONTAINS_FILE (1u<<10)\n\nI am partly at fault, but I think we should have a consolidated\nbit assignment policy in place before introducing new users of\nobject flags.  Some older code says in their comments that\nrevision.h reserves lower 8 bits while others say 16.  I offhand\nknow who is correct X-<.\n"},{"id":"34906","messageId":"Pine.LNX.4.63.0702180026000.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6859","inReplyTo":"7vire0tz51.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-17T23:31:34Z","receivedAt":"2007-02-17T23:31:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 17 Feb 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > +static struct object_hash file_trees = { 0, 0, NULL };\n> > +static struct object_array file_commits = { 0, 0, NULL };\n> \n> These don't look good for two reasons: (1) you could leave BSS\n> to do the 0 initialization; (2) you need to change this if you\n> need to change the shape of \"struct object_hash\" later.\n\nYes, you are right on both accounts. Can you please just ammend the \ncommit?\n\n> > +#define CONTAINS_FILE (1u<<10)\n> \n> I am partly at fault, but I think we should have a consolidated\n> bit assignment policy in place before introducing new users of\n> object flags.  Some older code says in their comments that\n> revision.h reserves lower 8 bits while others say 16.  I offhand\n> know who is correct X-<.\n\nSomething like this? (I did not know where to put the comment, so I let it \nbe...)\n\n--\n[PATCH] The lower 16 bits of the object flags are reserved for rev_walk\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\n---\n\n builtin-blame.c    |    4 ++--\n builtin-describe.c |    2 +-\n builtin-reflog.c   |    4 ++--\n 3 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 7a5665f..8323dbf 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -66,8 +66,8 @@ static unsigned blame_copy_score;\n #define BLAME_DEFAULT_COPY_SCORE\t40\n \n /* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */\n-#define METAINFO_SHOWN\t\t(1u<<12)\n-#define MORE_THAN_ONE_PATH\t(1u<<13)\n+#define METAINFO_SHOWN\t\t(1u<<16)\n+#define MORE_THAN_ONE_PATH\t(1u<<17)\n \n /*\n  * One blob in a commit that is being suspected\ndiff --git a/builtin-describe.c b/builtin-describe.c\nindex bcc6456..07a96a4 100644\n--- a/builtin-describe.c\n+++ b/builtin-describe.c\n@@ -4,7 +4,7 @@\n #include \"refs.h\"\n #include \"builtin.h\"\n \n-#define SEEN\t\t(1u<<0)\n+#define SEEN\t\t(1u<<16)\n #define MAX_TAGS\t(FLAG_BITS - 1)\n \n static const char describe_usage[] =\ndiff --git a/builtin-reflog.c b/builtin-reflog.c\nindex 3415551..3aa6902 100644\n--- a/builtin-reflog.c\n+++ b/builtin-reflog.c\n@@ -34,8 +34,8 @@ struct expire_reflog_cb {\n \tstruct cmd_reflog_expire_cb *cmd;\n };\n \n-#define INCOMPLETE\t(1u<<10)\n-#define STUDYING\t(1u<<11)\n+#define INCOMPLETE\t(1u<<16)\n+#define STUDYING\t(1u<<17)\n \n static int tree_is_complete(const unsigned char *sha1)\n {\n"},{"id":"34909","messageId":"Pine.LNX.4.63.0702180049210.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6859","inReplyTo":"7vr6sotzft.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-17T23:52:14Z","receivedAt":"2007-02-17T23:52:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 17 Feb 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > If you want to know which revisions contained a certain version\n> > of a file, just say\n> >\n> > \tgit name-rev --file <filename>\n> >\n> > which will read the file, and give you a list of revisions\n> > containing a file with the same contents. If <filename> is \"-\",\n> > it will read the contents from stdin. Of course, this is a really\n> > expensive operation.\n> \n> I expected this to take arbitrary object name and let the caller\n> to do 'hash-object', so that you could also find a certain tree,\n> not just blob.\n\nThat was easier than I feared. Here's a patch on top of 2/2:\n\n--\n[PATCH] name-rev --file: also accept SHA1 of blobs and trees\n\nThis allows you to say\n\n\tgit name-rev --file master:t/\n\nto see in which revisions the whole directory \"t\" is identical to what it \nis in \"master\".\n\nThe first hunk is a bugfix for a potential segv.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin-name-rev.c |   10 ++++++----\n 1 files changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin-name-rev.c b/builtin-name-rev.c\nindex f08b065..e4addc7 100644\n--- a/builtin-name-rev.c\n+++ b/builtin-name-rev.c\n@@ -43,7 +43,7 @@ struct object_name {\n static struct object_name *name_file(struct tree *tree)\n {\n \tstatic struct object_name null_name = { 0, NULL };\n-\tstruct object_name *name;\n+\tstruct object_name *name = NULL;\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n \n@@ -141,7 +141,8 @@ copy_data:\n \t\treturn;\n \n \tif (file_sha1 && !(commit->object.flags & CONTAINS_FILE) &&\n-\t\t\t(file_name = name_file(commit->tree))) {\n+\t\t\t((file_name = name_file(commit->tree)) ||\n+\t\t\t !hashcmp(commit->tree->object.sha1, file_sha1))) {\n \t\tcommit->object.flags |= CONTAINS_FILE;\n \t\tadd_object_array(&commit->object, NULL, &file_commits);\n \t}\n@@ -270,7 +271,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(*argv, \"--file\")) {\n \t\t\t\tif (argc != 2)\n \t\t\t\t\tusage(name_rev_usage);\n-\t\t\t\tif (get_file(argv[1]))\n+\t\t\t\tif (get_file(argv[1]) &&\n+\t\t\t\t\t\tget_sha1(argv[1], file_sha1))\n \t\t\t\t\tdie(\"Could not read '%s'\", argv[1]);\n \t\t\t\tcutoff = 0;\n \t\t\t\targc = 1;\n@@ -313,7 +315,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \t\t\tprintf(\"%s\", rev_name->tip_name);\n \t\t\tif (rev_name->generation)\n \t\t\t\tprintf(\"^%d\", rev_name->generation);\n-\t\t\tprintf(\":%s\\n\", obj_name->name);\n+\t\t\tprintf(\":%s\\n\", obj_name ? obj_name->name : \"\");\n \t\t}\n \t\treturn 0;\n \t}\n-- \n1.5.0.2139.gdafc9-dirty\n"},{"id":"34913","messageId":"Pine.LNX.4.63.0702180103520.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6859","inReplyTo":"7vr6sotzft.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-18T00:04:20Z","receivedAt":"2007-02-18T00:04:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nbrown paper bag, please\n\n builtin-name-rev.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-name-rev.c b/builtin-name-rev.c\nindex e4addc7..e79ef1c 100644\n--- a/builtin-name-rev.c\n+++ b/builtin-name-rev.c\n@@ -314,7 +314,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \n \t\t\tprintf(\"%s\", rev_name->tip_name);\n \t\t\tif (rev_name->generation)\n-\t\t\t\tprintf(\"^%d\", rev_name->generation);\n+\t\t\t\tprintf(\"~%d\", rev_name->generation);\n \t\t\tprintf(\":%s\\n\", obj_name ? obj_name->name : \"\");\n \t\t}\n \t\treturn 0;\n"},{"id":"34941","messageId":"7vmz3bj248.fsf@assigned-by-dhcp.cox.net","threadId":"6859","inReplyTo":"Pine.LNX.4.63.0702171838510.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-18T08:34:15Z","receivedAt":"2007-02-18T08:34:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Having said what I said in the comments on [PATCH 1/2], I think\nyou are either solving a wrong problem, or even if you are\nsolving the right problem, you are solving in a wrong way in\nthis patch.\n\nI do not think Mike wants a way to deal with what he gets in his\nmailbox from an end user who did this:\n\n\t$ cat a-file-whose-name-was-not-told-to-mike >junk\n\t$ mailx mike.coleman <junk\n\nIt's more like he learns the contents _and_ the path from an\nend-user who is having trouble with a tarball of unknown\nvintage, and wants to identify which snapshot had that contents\nat that path [*1*].\n\nSo I do not think it is solving the right problem to run around\nall over the tree without knowing the path, looking for a\nparticular object name.  It certainly makes the solution much\nmore expensive.\n\nEven if that were Mike's problem, there is a problem that the\nsame object can appear at different places in different trees.\nKeeping a single pathname registered in object_hash would mean\nthe answer is \"whatever happens to be found first\", regardless\nof the path.\n\nTo make \"whatever happens to be found first\" semantics useful, I\nthink you would need to make the traversal more controllable by\nthe user of name-rev.  For example, Mike may know what he gave\nthe end user was from maintenance series of 1.4.4 software, in\nwhich case you would want to let him say \"traverse from the tip\nof 1.4.4 maintenance branch down, do not worry about other\nbranches such as 1.5.0 series or 1.4.3 maintenance series\".\n\nYour earlier --ref-filter patch would certainly help it, but a\nsolution based on grep over \"rev-list | ls-tree --stdin\" (which\ncurrently does not exist) might be more appropriate and\nflexible?  I dunno.\n\n[Footnote]\n\n*1* To find a revision that had a particular object at a known\npath, you should be able to do:\n\n\tgit rev-list <starting refs> |\n        git diff-tree --stdin --raw -r -Z |\n\tgrep -z that-object-name\n\nbut an earlier commit abd4e222 needs to be further enhanced to\nadd -Z option that means \"output fields are not NUL terminated,\noutput records are\", which did not happen.\n"},{"id":"34944","messageId":"7vejonj1h3.fsf@assigned-by-dhcp.cox.net","threadId":"6859","inReplyTo":"7vmz3bj248.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-02-18T08:48:08Z","receivedAt":"2007-02-18T08:48:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> [Footnote]\n>\n> *1* To find a revision that had a particular object at a known\n> path, you should be able to do:\n>\n> \tgit rev-list <starting refs> |\n>       git diff-tree --stdin --raw -r -Z |\n> \tgrep -z that-object-name\n\nSheesh, please add \"-- <path>\" after \"-Z\".\n\n>\n> but an earlier commit abd4e222 needs to be further enhanced to\n> add -Z option that means \"output fields are not NUL terminated,\n> output records are\", which did not happen.\n"},{"id":"34956","messageId":"Pine.LNX.4.63.0702181639480.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"6859","inReplyTo":"7vmz3bj248.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] Teach name-rev to identify revisions containing a certain blob","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-02-18T15:41:17Z","receivedAt":"2007-02-18T15:41:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 18 Feb 2007, Junio C Hamano wrote:\n\n> It's more like he learns the contents _and_ the path from an end-user \n> who is having trouble with a tarball of unknown vintage, and wants to \n> identify which snapshot had that contents at that path [*1*].\n\nAh! Then it would make more sense to make it a new revision-walk filter?\n\nCiao,\nDscho\n"}]}