{"thread":{"id":"29366","subject":"Re: [PATCH] grep: resolve symlinks for --no-index and untracked symlinks for --untracked","startedAt":"2012-01-14T07:14:53Z","lastAt":"2012-01-14T07:14:53Z","messageCount":1,"participants":["Pang Yan Han"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"182537","messageId":"20120114071453.GA8054@myhost","threadId":"29366","inReplyTo":null,"subject":"Re: [PATCH] grep: resolve symlinks for --no-index and untracked symlinks for --untracked","fromName":"Pang Yan Han","fromEmail":"pangyanhan@gmail.com","sentAt":"2012-01-14T07:14:53Z","receivedAt":"2012-01-14T07:14:53Z","isPatch":true,"sender":{"key":"pangyanhan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/539739?v=4"},"body":"Sorry, this should be a PATCH/RFC\n\nOn Sat, Jan 14, 2012 at 03:10:50PM +0800, Pang Yan Han wrote:\n> For a tracked symbolic link, git tracks where the symbolic link points to\n> and as such, git grep does not search for patterns in where the symbolic link\n> points to.\n> \n> However, git grep with --no-index is supposed to work similarly to GNU grep by\n> pretending that the current directory is not a git repository and hence resolve\n> symbolic links.\n> \n> When used with the --untracked option, untracked symbolic links should also\n> be resolved.\n> \n> Teach git grep to resolve symbolic links for --no-index and untracked symbolic\n> links for --untracked.\n> ---\n>  builtin/grep.c  |   49 +++++++++++++++++++++++++++++++++++--------------\n>  t/t7810-grep.sh |   35 +++++++++++++++++++++++++++++++++++\n>  2 files changed, 70 insertions(+), 14 deletions(-)\n> \n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 9ce064a..c7883c3 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -29,9 +29,12 @@ static int use_threads = 1;\n>  #define THREADS 8\n>  static pthread_t threads[THREADS];\n>  \n> +#define UNTRACKED 0\n> +#define TRACKED 1\n> +\n>  static void *load_sha1(const unsigned char *sha1, unsigned long *size,\n>  \t\t       const char *name);\n> -static void *load_file(const char *filename, size_t *sz);\n> +static void *load_file(const char *filename, size_t *sz, int tracked);\n>  \n>  enum work_type {WORK_SHA1, WORK_FILE};\n>  \n> @@ -50,6 +53,11 @@ struct work_item {\n>  \tvoid *identifier;\n>  \tchar done;\n>  \tstruct strbuf out;\n> +\t/* indicates whether file is tracked by git.\n> +\t * with --no-index, resolve all symlinks.\n> +\t * with --untracked, resolve only untracked symlinks.\n> +\t */\n> +\tint tracked;\n>  };\n>  \n>  /* In the range [todo_done, todo_start) in 'todo' we have work_items\n> @@ -113,7 +121,7 @@ static pthread_cond_t cond_result;\n>  \n>  static int skip_first_line;\n>  \n> -static void add_work(enum work_type type, char *name, void *id)\n> +static void add_work(enum work_type type, char *name, void *id, int tracked)\n>  {\n>  \tgrep_lock();\n>  \n> @@ -125,6 +133,7 @@ static void add_work(enum work_type type, char *name, void *id)\n>  \ttodo[todo_end].name = name;\n>  \ttodo[todo_end].identifier = id;\n>  \ttodo[todo_end].done = 0;\n> +\ttodo[todo_end].tracked = tracked;\n>  \tstrbuf_reset(&todo[todo_end].out);\n>  \ttodo_end = (todo_end + 1) % ARRAY_SIZE(todo);\n>  \n> @@ -157,13 +166,13 @@ static void grep_sha1_async(struct grep_opt *opt, char *name,\n>  \tunsigned char *s;\n>  \ts = xmalloc(20);\n>  \tmemcpy(s, sha1, 20);\n> -\tadd_work(WORK_SHA1, name, s);\n> +\tadd_work(WORK_SHA1, name, s, TRACKED);\n>  }\n>  \n>  static void grep_file_async(struct grep_opt *opt, char *name,\n> -\t\t\t    const char *filename)\n> +\t\t\t    const char *filename, int tracked)\n>  {\n> -\tadd_work(WORK_FILE, name, xstrdup(filename));\n> +\tadd_work(WORK_FILE, name, xstrdup(filename), tracked);\n>  }\n>  \n>  static void work_done(struct work_item *w)\n> @@ -226,7 +235,7 @@ static void *run(void *arg)\n>  \t\t\t}\n>  \t\t} else if (w->type == WORK_FILE) {\n>  \t\t\tsize_t sz;\n> -\t\t\tvoid* data = load_file(w->identifier, &sz);\n> +\t\t\tvoid* data = load_file(w->identifier, &sz, w->tracked);\n>  \t\t\tif (data) {\n>  \t\t\t\thit |= grep_buffer(opt, w->name, data, sz);\n>  \t\t\t\tfree(data);\n> @@ -429,7 +438,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,\n>  \t}\n>  }\n>  \n> -static void *load_file(const char *filename, size_t *sz)\n> +static void *load_file(const char *filename, size_t *sz, int tracked)\n>  {\n>  \tstruct stat st;\n>  \tchar *data;\n> @@ -441,6 +450,12 @@ static void *load_file(const char *filename, size_t *sz)\n>  \t\t\terror(_(\"'%s': %s\"), filename, strerror(errno));\n>  \t\treturn NULL;\n>  \t}\n> +\t/* Resolve symlink if file is not tracked */\n> +\tif (S_ISLNK(st.st_mode) && !tracked) {\n> +\t\tmemset(&st, 0, sizeof(st));\n> +\t\tif (stat(filename, &st) < 0)\n> +\t\t\tgoto err_ret;\n> +\t}\n>  \tif (!S_ISREG(st.st_mode))\n>  \t\treturn NULL;\n>  \t*sz = xsize_t(st.st_size);\n> @@ -459,7 +474,8 @@ static void *load_file(const char *filename, size_t *sz)\n>  \treturn data;\n>  }\n>  \n> -static int grep_file(struct grep_opt *opt, const char *filename)\n> +static int grep_file(struct grep_opt *opt, const char *filename,\n> +\t\tint tracked)\n>  {\n>  \tstruct strbuf buf = STRBUF_INIT;\n>  \tchar *name;\n> @@ -472,14 +488,14 @@ static int grep_file(struct grep_opt *opt, const char *filename)\n>  \n>  #ifndef NO_PTHREADS\n>  \tif (use_threads) {\n> -\t\tgrep_file_async(opt, name, filename);\n> +\t\tgrep_file_async(opt, name, filename, tracked);\n>  \t\treturn 0;\n>  \t} else\n>  #endif\n>  \t{\n>  \t\tint hit;\n>  \t\tsize_t sz;\n> -\t\tvoid *data = load_file(filename, &sz);\n> +\t\tvoid *data = load_file(filename, &sz, tracked);\n>  \t\tif (!data)\n>  \t\t\thit = 0;\n>  \t\telse\n> @@ -541,7 +557,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int\n>  \t\t\thit |= grep_sha1(opt, ce->sha1, ce->name, 0);\n>  \t\t}\n>  \t\telse\n> -\t\t\thit |= grep_file(opt, ce->name);\n> +\t\t\thit |= grep_file(opt, ce->name, TRACKED);\n>  \t\tif (ce_stage(ce)) {\n>  \t\t\tdo {\n>  \t\t\t\tnr++;\n> @@ -658,7 +674,7 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,\n>  }\n>  \n>  static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,\n> -\t\t\t  int exc_std)\n> +\t\t\t  int exc_std, int use_index)\n>  {\n>  \tstruct dir_struct dir;\n>  \tint i, hit = 0;\n> @@ -668,12 +684,17 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,\n>  \t\tsetup_standard_excludes(&dir);\n>  \n>  \tfill_directory(&dir, pathspec->raw);\n> +\tif (use_index)\n> +\t\tread_cache();\n>  \tfor (i = 0; i < dir.nr; i++) {\n>  \t\tconst char *name = dir.entries[i]->name;\n>  \t\tint namelen = strlen(name);\n>  \t\tif (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))\n>  \t\t\tcontinue;\n> -\t\thit |= grep_file(opt, dir.entries[i]->name);\n> +\t\tif (use_index && cache_name_exists(name, namelen, ignore_case))\n> +\t\t\thit |= grep_file(opt, dir.entries[i]->name, TRACKED);\n> +\t\telse\n> +\t\t\thit |= grep_file(opt, dir.entries[i]->name, UNTRACKED);\n>  \t\tif (hit && opt->status_only)\n>  \t\t\tbreak;\n>  \t}\n> @@ -1083,7 +1104,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>  \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n>  \t\tif (list.nr)\n>  \t\t\tdie(_(\"--no-index or --untracked cannot be used with revs.\"));\n> -\t\thit = grep_directory(&opt, &pathspec, use_exclude);\n> +\t\thit = grep_directory(&opt, &pathspec, use_exclude, use_index);\n>  \t} else if (0 <= opt_exclude) {\n>  \t\tdie(_(\"--[no-]exclude-standard cannot be used for tracked contents.\"));\n>  \t} else if (!list.nr) {\n> diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\n> index 7ba5b16..fe41095 100755\n> --- a/t/t7810-grep.sh\n> +++ b/t/t7810-grep.sh\n> @@ -647,6 +647,41 @@ test_expect_success 'inside git repository but with --no-index' '\n>  \t)\n>  '\n>  \n> +test_expect_success '--no-index greps contents of targets of symlinks' '\n> +\tmkdir -p repo/sub &&\n> +\techo hello >repo/file &&\n> +\techo hello there >repo/sub/file1 &&\n> +\t(cd repo/sub && ln -s ../file link1 && ln -s ../file link2 &&\n> +\tgit init && git add link1 && git commit -m \"first\" &&\n> +\ttest_must_fail git grep \"hello\" &&\n> +\tcat >../expected <<-EOF &&\n> +\tfile1:hello there\n> +\tlink1:hello\n> +\tlink2:hello\n> +\tEOF\n> +\tgit grep --no-index \"hello\" >../actual &&\n> +\ttest_cmp ../expected ../actual\n> +\t) &&\n> +\trm -rf repo\n> +'\n> +\n> +test_expect_success '--untracked greps targets of untracked symlinks' '\n> +\tmkdir -p repo/sub &&\n> +\techo hello >repo/file &&\n> +\techo hello there > repo/sub/file1 &&\n> +\t(cd repo/sub && ln -s ../file link1 && ln -s ../file link2 &&\n> +\tgit init && git add link1 && git commit -m \"first\" &&\n> +\ttest_must_fail git grep \"hello\" &&\n> +\tcat >../expected <<-EOF &&\n> +\tfile1:hello there\n> +\tlink2:hello\n> +\tEOF\n> +\tgit grep --untracked \"hello\" >../actual &&\n> +\ttest_cmp ../expected ../actual\n> +\t) &&\n> +\trm -rf repo\n> +'\n> +\n>  test_expect_success 'setup double-dash tests' '\n>  cat >double-dash <<EOF &&\n>  --\n> -- \n> 1.7.9.rc0.24.ga4351\n"}]}