{"thread":{"id":"5204","subject":"[PATCH] git-grep --relative: show pathnames relative to the current directory","startedAt":"2006-08-11T07:50:12Z","lastAt":"2006-08-11T15:23:05Z","messageCount":3,"participants":["Junio C Hamano","Karl Hasselström","Sam Ravnborg"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"25124","messageId":"7vpsf7u2x7.fsf@assigned-by-dhcp.cox.net","threadId":"5204","inReplyTo":null,"subject":"[PATCH] git-grep --relative: show pathnames relative to the current directory","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-11T07:50:12Z","receivedAt":"2006-08-11T07:50:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"By default, the command shows pathnames relative to the project\nroot, even when run from a subdirectory.  With --relative flag,\nhowever, the command outputs pathnames relative to the current\ndirectory.\n\nThis makes it very pleasant to run in Emacs compilation (or\n\"grep-find\") buffer.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n * Now the question is, shouldn't this be the default, with\n   the current output available as an option --full-name, just\n   like ls-files?\n\n   Changing the default always risks breaking people's habit and\n   scripts, but I tend to think the current output is a\n   misfeature which we'd better fix sooner rather than later.\n\n   Opinions? \n\n Documentation/git-grep.txt |    7 +++++\n builtin-grep.c             |   58 ++++++++++++++++++++++++++++++++++++--------\n 2 files changed, 54 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex dc76833..6cdc433 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -11,7 +11,7 @@ SYNOPSIS\n [verse]\n 'git-grep' [--cached]\n \t   [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]\n-\t   [-v | --invert-match]\n+\t   [-v | --invert-match] [--relative]\n \t   [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]\n \t   [-n] [-l | --files-with-matches] [-L | --files-without-match]\n \t   [-c | --count]\n@@ -47,6 +47,11 @@ OPTIONS\n -v | --invert-match::\n \tSelect non-matching lines.\n \n+--relative::\n+\tWhen run from a subdirectory, show filenames relative to\n+\tthe current directory, instead of the usual full\n+\tpathname down from the project root.\n+\n -E | --extended-regexp | -G | --basic-regexp::\n \tUse POSIX extended/basic regexp for patterns.  Default\n \tis to use basic regexp.\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 93b7e07..f6309f5 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -123,6 +123,7 @@ struct grep_opt {\n \tstruct grep_pat *pattern_list;\n \tstruct grep_pat **pattern_tail;\n \tstruct grep_expr *pattern_expression;\n+\tint prefix_length;\n \tregex_t regexp;\n \tunsigned linenum:1;\n \tunsigned invert:1;\n@@ -136,6 +137,7 @@ #define GREP_BINARY_NOMATCH\t1\n #define GREP_BINARY_TEXT\t2\n \tunsigned binary:2;\n \tunsigned extended:1;\n+\tunsigned relative:1;\n \tint regflags;\n \tunsigned pre_context;\n \tunsigned post_context;\n@@ -632,19 +634,40 @@ static int grep_buffer(struct grep_opt *\n \treturn !!last_hit;\n }\n \n-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)\n+static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)\n {\n \tunsigned long size;\n \tchar *data;\n \tchar type[20];\n+\tchar *to_free = NULL;\n \tint hit;\n+\n \tdata = read_sha1_file(sha1, type, &size);\n \tif (!data) {\n \t\terror(\"'%s': unable to read %s\", name, sha1_to_hex(sha1));\n \t\treturn 0;\n \t}\n+\tif (opt->relative && opt->prefix_length) {\n+\t\tstatic char name_buf[PATH_MAX];\n+\t\tchar *cp;\n+\t\tint name_len = strlen(name) - opt->prefix_length + 1;\n+\n+\t\tif (!tree_name_len)\n+\t\t\tname += opt->prefix_length;\n+\t\telse {\n+\t\t\tif (ARRAY_SIZE(name_buf) <= name_len)\n+\t\t\t\tcp = to_free = xmalloc(name_len);\n+\t\t\telse\n+\t\t\t\tcp = name_buf;\n+\t\t\tmemcpy(cp, name, tree_name_len);\n+\t\t\tstrcpy(cp + tree_name_len,\n+\t\t\t       name + tree_name_len + opt->prefix_length);\n+\t\t\tname = cp;\n+\t\t}\n+\t}\n \thit = grep_buffer(opt, name, data, size);\n \tfree(data);\n+\tfree(to_free);\n \treturn hit;\n }\n \n@@ -674,6 +697,8 @@ static int grep_file(struct grep_opt *op\n \t\treturn 0;\n \t}\n \tclose(i);\n+\tif (opt->relative && opt->prefix_length)\n+\t\tfilename += opt->prefix_length;\n \ti = grep_buffer(opt, filename, data, st.st_size);\n \tfree(data);\n \treturn i;\n@@ -720,7 +745,7 @@ static int external_grep(struct grep_opt\n \tchar *argptr = randarg;\n \tstruct grep_pat *p;\n \n-\tif (opt->extended)\n+\tif (opt->extended || (opt->relative && opt->prefix_length))\n \t\treturn -1;\n \tlen = nr = 0;\n \tpush_arg(\"grep\");\n@@ -845,7 +870,7 @@ #endif\n \t\tif (!pathspec_matches(paths, ce->name))\n \t\t\tcontinue;\n \t\tif (cached)\n-\t\t\thit |= grep_sha1(opt, ce->sha1, ce->name);\n+\t\t\thit |= grep_sha1(opt, ce->sha1, ce->name, 0);\n \t\telse\n \t\t\thit |= grep_file(opt, ce->name);\n \t}\n@@ -860,11 +885,12 @@ static int grep_tree(struct grep_opt *op\n \tint hit = 0;\n \tstruct name_entry entry;\n \tchar *down;\n-\tchar *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);\n+\tint tn_len = strlen(tree_name);\n+\tchar *path_buf = xmalloc(PATH_MAX + tn_len + 100);\n \n-\tif (tree_name[0]) {\n-\t\tint offset = sprintf(path_buf, \"%s:\", tree_name);\n-\t\tdown = path_buf + offset;\n+\tif (tn_len) {\n+\t\ttn_len = sprintf(path_buf, \"%s:\", tree_name);\n+\t\tdown = path_buf + tn_len;\n \t\tstrcat(down, base);\n \t}\n \telse {\n@@ -886,7 +912,7 @@ static int grep_tree(struct grep_opt *op\n \t\tif (!pathspec_matches(paths, down))\n \t\t\t;\n \t\telse if (S_ISREG(entry.mode))\n-\t\t\thit |= grep_sha1(opt, entry.sha1, path_buf);\n+\t\t\thit |= grep_sha1(opt, entry.sha1, path_buf, tn_len);\n \t\telse if (S_ISDIR(entry.mode)) {\n \t\t\tchar type[20];\n \t\t\tstruct tree_desc sub;\n@@ -907,7 +933,7 @@ static int grep_object(struct grep_opt *\n \t\t       struct object *obj, const char *name)\n {\n \tif (obj->type == OBJ_BLOB)\n-\t\treturn grep_sha1(opt, obj->sha1, name);\n+\t\treturn grep_sha1(opt, obj->sha1, name, 0);\n \tif (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {\n \t\tstruct tree_desc tree;\n \t\tvoid *data;\n@@ -945,6 +971,7 @@ int cmd_grep(int argc, const char **argv\n \tint i;\n \n \tmemset(&opt, 0, sizeof(opt));\n+\topt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;\n \topt.pattern_tail = &opt.pattern_list;\n \topt.regflags = REG_NEWLINE;\n \n@@ -1118,6 +1145,10 @@ int cmd_grep(int argc, const char **argv\n \t\t\t}\n \t\t\tdie(emsg_missing_argument, arg);\n \t\t}\n+\t\tif (!strcmp(\"--relative\", arg)) {\n+\t\t\topt.relative = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(\"--\", arg)) {\n \t\t\t/* later processing wants to have this at argv[1] */\n \t\t\targv--;\n@@ -1176,8 +1207,15 @@ int cmd_grep(int argc, const char **argv\n \t\t\tverify_filename(prefix, argv[j]);\n \t}\n \n-\tif (i < argc)\n+\tif (i < argc) {\n \t\tpaths = get_pathspec(prefix, argv + i);\n+\t\tif (opt.prefix_length && opt.relative) {\n+\t\t\t/* Make sure we do not get outside of paths */\n+\t\t\tfor (i = 0; paths[i]; i++)\n+\t\t\t\tif (strncmp(prefix, paths[i], opt.prefix_length))\n+\t\t\t\t\tdie(\"git-grep: cannot generate relative filenames containing '..'\");\n+\t\t}\n+\t}\n \telse if (prefix) {\n \t\tpaths = xcalloc(2, sizeof(const char *));\n \t\tpaths[0] = prefix;\n-- \n1.4.2.rc4.g9d8df\n"},{"id":"25130","messageId":"20060811120334.GA6713@diana.vm.bytemark.co.uk","threadId":"5204","inReplyTo":"7vpsf7u2x7.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-grep --relative: show pathnames relative to the current directory","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2006-08-11T12:03:34Z","receivedAt":"2006-08-11T12:03:34Z","isPatch":true,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"On 2006-08-11 00:50:12 -0700, Junio C Hamano wrote:\n\n> By default, the command shows pathnames relative to the project\n> root, even when run from a subdirectory. With --relative flag,\n> however, the command outputs pathnames relative to the current\n> directory.\n>\n> This makes it very pleasant to run in Emacs compilation (or\n> \"grep-find\") buffer.\n\n[...]\n\n>  * Now the question is, shouldn't this be the default, with the\n>    current output available as an option --full-name, just like\n>    ls-files?\n>\n>    Changing the default always risks breaking people's habit and\n>    scripts, but I tend to think the current output is a misfeature\n>    which we'd better fix sooner rather than later.\n\nI think this behavior should be the default, precisely because it\notherwise breaks the habits formed during use of plain \"grep\".\n\n-- \nKarl Hasselström, kha@treskal.com\n      www.treskal.com/kalle\n"},{"id":"25133","messageId":"20060811152305.GB9211@mars.ravnborg.org","threadId":"5204","inReplyTo":"7vpsf7u2x7.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-grep --relative: show pathnames relative to the current directory","fromName":"Sam Ravnborg","fromEmail":"sam@ravnborg.org","sentAt":"2006-08-11T15:23:05Z","receivedAt":"2006-08-11T15:23:05Z","isPatch":true,"sender":{"key":"sam@ravnborg.org","avatar":"https://gravatar.com/avatar/168a912606ed0742d840bb365e3cc21db390c36531a58341dc7a069cc1f15f62?d=mp&s=160"},"body":"On Fri, Aug 11, 2006 at 12:50:12AM -0700, Junio C Hamano wrote:\n> By default, the command shows pathnames relative to the project\n> root, even when run from a subdirectory.  With --relative flag,\n> however, the command outputs pathnames relative to the current\n> directory.\n> \n> This makes it very pleasant to run in Emacs compilation (or\n> \"grep-find\") buffer.\n> \n> Signed-off-by: Junio C Hamano <junkio@cox.net>\n> ---\n> \n>  * Now the question is, shouldn't this be the default, with\n>    the current output available as an option --full-name, just\n>    like ls-files?\n> \n>    Changing the default always risks breaking people's habit and\n>    scripts, but I tend to think the current output is a\n>    misfeature which we'd better fix sooner rather than later.\n> \n>    Opinions? \n\nI always felt confused when path was relative to root of .git directory\nand not relative to current directory.\nBut I never looked into changing this or reporting it - so it has not\nbeen a big issue.\n\n\tSam\n"}]}