{"thread":{"id":"29365","subject":"[PATCH] grep: resolve symlinks for --no-index and untracked symlinks for --untracked","startedAt":"2012-01-14T07:10:50Z","lastAt":"2012-01-14T07:10:50Z","messageCount":1,"participants":["Pang Yan Han"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"182536","messageId":"1326525050-8011-1-git-send-email-pangyanhan@gmail.com","threadId":"29365","inReplyTo":null,"subject":"[PATCH] grep: resolve symlinks for --no-index and untracked symlinks for --untracked","fromName":"Pang Yan Han","fromEmail":"pangyanhan@gmail.com","sentAt":"2012-01-14T07:10:50Z","receivedAt":"2012-01-14T07:10:50Z","isPatch":true,"sender":{"key":"pangyanhan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/539739?v=4"},"body":"For a tracked symbolic link, git tracks where the symbolic link points to\nand as such, git grep does not search for patterns in where the symbolic link\npoints to.\n\nHowever, git grep with --no-index is supposed to work similarly to GNU grep by\npretending that the current directory is not a git repository and hence resolve\nsymbolic links.\n\nWhen used with the --untracked option, untracked symbolic links should also\nbe resolved.\n\nTeach git grep to resolve symbolic links for --no-index and untracked symbolic\nlinks for --untracked.\n---\n builtin/grep.c  |   49 +++++++++++++++++++++++++++++++++++--------------\n t/t7810-grep.sh |   35 +++++++++++++++++++++++++++++++++++\n 2 files changed, 70 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 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) {\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 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-- \n1.7.9.rc0.24.ga4351\n"}]}