[PATCH] grep: resolve symlinks for --no-index and untracked symlinks for --untracked

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For a tracked symbolic link, git tracks where the symbolic link points to
and as such, git grep does not search for patterns in where the symbolic link
points to.

However, git grep with --no-index is supposed to work similarly to GNU grep by
pretending that the current directory is not a git repository and hence resolve
symbolic links.

When used with the --untracked option, untracked symbolic links should also
be resolved.

Teach git grep to resolve symbolic links for --no-index and untracked symbolic
links for --untracked.
---
 builtin/grep.c  |   49 +++++++++++++++++++++++++++++++++++--------------
 t/t7810-grep.sh |   35 +++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 14 deletions(-)

diff --git a/builtin/grep.c b/builtin/grep.c
index 9ce064a..c7883c3 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -29,9 +29,12 @@ static int use_threads = 1;
 #define THREADS 8
 static pthread_t threads[THREADS];
 
+#define UNTRACKED 0
+#define TRACKED 1
+
 static void *load_sha1(const unsigned char *sha1, unsigned long *size,
 		       const char *name);
-static void *load_file(const char *filename, size_t *sz);
+static void *load_file(const char *filename, size_t *sz, int tracked);
 
 enum work_type {WORK_SHA1, WORK_FILE};
 
@@ -50,6 +53,11 @@ struct work_item {
 	void *identifier;
 	char done;
 	struct strbuf out;
+	/* indicates whether file is tracked by git.
+	 * with --no-index, resolve all symlinks.
+	 * with --untracked, resolve only untracked symlinks.
+	 */
+	int tracked;
 };
 
 /* In the range [todo_done, todo_start) in 'todo' we have work_items
@@ -113,7 +121,7 @@ static pthread_cond_t cond_result;
 
 static int skip_first_line;
 
-static void add_work(enum work_type type, char *name, void *id)
+static void add_work(enum work_type type, char *name, void *id, int tracked)
 {
 	grep_lock();
 
@@ -125,6 +133,7 @@ static void add_work(enum work_type type, char *name, void *id)
 	todo[todo_end].name = name;
 	todo[todo_end].identifier = id;
 	todo[todo_end].done = 0;
+	todo[todo_end].tracked = tracked;
 	strbuf_reset(&todo[todo_end].out);
 	todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
 
@@ -157,13 +166,13 @@ static void grep_sha1_async(struct grep_opt *opt, char *name,
 	unsigned char *s;
 	s = xmalloc(20);
 	memcpy(s, sha1, 20);
-	add_work(WORK_SHA1, name, s);
+	add_work(WORK_SHA1, name, s, TRACKED);
 }
 
 static void grep_file_async(struct grep_opt *opt, char *name,
-			    const char *filename)
+			    const char *filename, int tracked)
 {
-	add_work(WORK_FILE, name, xstrdup(filename));
+	add_work(WORK_FILE, name, xstrdup(filename), tracked);
 }
 
 static void work_done(struct work_item *w)
@@ -226,7 +235,7 @@ static void *run(void *arg)
 			}
 		} else if (w->type == WORK_FILE) {
 			size_t sz;
-			void* data = load_file(w->identifier, &sz);
+			void* data = load_file(w->identifier, &sz, w->tracked);
 			if (data) {
 				hit |= grep_buffer(opt, w->name, data, sz);
 				free(data);
@@ -429,7 +438,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
 	}
 }
 
-static void *load_file(const char *filename, size_t *sz)
+static void *load_file(const char *filename, size_t *sz, int tracked)
 {
 	struct stat st;
 	char *data;
@@ -441,6 +450,12 @@ static void *load_file(const char *filename, size_t *sz)
 			error(_("'%s': %s"), filename, strerror(errno));
 		return NULL;
 	}
+	/* Resolve symlink if file is not tracked */
+	if (S_ISLNK(st.st_mode) && !tracked) {
+		memset(&st, 0, sizeof(st));
+		if (stat(filename, &st) < 0)
+			goto err_ret;
+	}
 	if (!S_ISREG(st.st_mode))
 		return NULL;
 	*sz = xsize_t(st.st_size);
@@ -459,7 +474,8 @@ static void *load_file(const char *filename, size_t *sz)
 	return data;
 }
 
-static int grep_file(struct grep_opt *opt, const char *filename)
+static int grep_file(struct grep_opt *opt, const char *filename,
+		int tracked)
 {
 	struct strbuf buf = STRBUF_INIT;
 	char *name;
@@ -472,14 +488,14 @@ static int grep_file(struct grep_opt *opt, const char *filename)
 
 #ifndef NO_PTHREADS
 	if (use_threads) {
-		grep_file_async(opt, name, filename);
+		grep_file_async(opt, name, filename, tracked);
 		return 0;
 	} else
 #endif
 	{
 		int hit;
 		size_t sz;
-		void *data = load_file(filename, &sz);
+		void *data = load_file(filename, &sz, tracked);
 		if (!data)
 			hit = 0;
 		else
@@ -541,7 +557,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
 			hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
 		}
 		else
-			hit |= grep_file(opt, ce->name);
+			hit |= grep_file(opt, ce->name, TRACKED);
 		if (ce_stage(ce)) {
 			do {
 				nr++;
@@ -658,7 +674,7 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
 }
 
 static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
-			  int exc_std)
+			  int exc_std, int use_index)
 {
 	struct dir_struct dir;
 	int i, hit = 0;
@@ -668,12 +684,17 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
 		setup_standard_excludes(&dir);
 
 	fill_directory(&dir, pathspec->raw);
+	if (use_index)
+		read_cache();
 	for (i = 0; i < dir.nr; i++) {
 		const char *name = dir.entries[i]->name;
 		int namelen = strlen(name);
 		if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))
 			continue;
-		hit |= grep_file(opt, dir.entries[i]->name);
+		if (use_index && cache_name_exists(name, namelen, ignore_case))
+			hit |= grep_file(opt, dir.entries[i]->name, TRACKED);
+		else
+			hit |= grep_file(opt, dir.entries[i]->name, UNTRACKED);
 		if (hit && opt->status_only)
 			break;
 	}
@@ -1083,7 +1104,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 		int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
 		if (list.nr)
 			die(_("--no-index or --untracked cannot be used with revs."));
-		hit = grep_directory(&opt, &pathspec, use_exclude);
+		hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
 	} else if (0 <= opt_exclude) {
 		die(_("--[no-]exclude-standard cannot be used for tracked contents."));
 	} else if (!list.nr) {
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 7ba5b16..fe41095 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -647,6 +647,41 @@ test_expect_success 'inside git repository but with --no-index' '
 	)
 '
 
+test_expect_success '--no-index greps contents of targets of symlinks' '
+	mkdir -p repo/sub &&
+	echo hello >repo/file &&
+	echo hello there >repo/sub/file1 &&
+	(cd repo/sub && ln -s ../file link1 && ln -s ../file link2 &&
+	git init && git add link1 && git commit -m "first" &&
+	test_must_fail git grep "hello" &&
+	cat >../expected <<-EOF &&
+	file1:hello there
+	link1:hello
+	link2:hello
+	EOF
+	git grep --no-index "hello" >../actual &&
+	test_cmp ../expected ../actual
+	) &&
+	rm -rf repo
+'
+
+test_expect_success '--untracked greps targets of untracked symlinks' '
+	mkdir -p repo/sub &&
+	echo hello >repo/file &&
+	echo hello there > repo/sub/file1 &&
+	(cd repo/sub && ln -s ../file link1 && ln -s ../file link2 &&
+	git init && git add link1 && git commit -m "first" &&
+	test_must_fail git grep "hello" &&
+	cat >../expected <<-EOF &&
+	file1:hello there
+	link2:hello
+	EOF
+	git grep --untracked "hello" >../actual &&
+	test_cmp ../expected ../actual
+	) &&
+	rm -rf repo
+'
+
 test_expect_success 'setup double-dash tests' '
 cat >double-dash <<EOF &&
 --
-- 
1.7.9.rc0.24.ga4351

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]