Merge branch 'cb/maint-1.6.3-grep-relative-up' into maint

* cb/maint-1.6.3-grep-relative-up:
  grep: accept relative paths outside current working directory
  grep: fix exit status if external_grep() punts

Conflicts:
	t/t7002-grep.sh
diff --git a/builtin-grep.c b/builtin-grep.c
index f477659..fd450bc 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -13,6 +13,7 @@
 #include "parse-options.h"
 #include "userdiff.h"
 #include "grep.h"
+#include "quote.h"
 
 #ifndef NO_EXTERNAL_GREP
 #ifdef __unix__
@@ -125,8 +126,8 @@
 	unsigned long size;
 	char *data;
 	enum object_type type;
-	char *to_free = NULL;
 	int hit;
+	struct strbuf pathbuf = STRBUF_INIT;
 
 	data = read_sha1_file(sha1, &type, &size);
 	if (!data) {
@@ -134,26 +135,13 @@
 		return 0;
 	}
 	if (opt->relative && opt->prefix_length) {
-		static char name_buf[PATH_MAX];
-		char *cp;
-		int name_len = strlen(name) - opt->prefix_length + 1;
-
-		if (!tree_name_len)
-			name += opt->prefix_length;
-		else {
-			if (ARRAY_SIZE(name_buf) <= name_len)
-				cp = to_free = xmalloc(name_len);
-			else
-				cp = name_buf;
-			memcpy(cp, name, tree_name_len);
-			strcpy(cp + tree_name_len,
-			       name + tree_name_len + opt->prefix_length);
-			name = cp;
-		}
+		quote_path_relative(name + tree_name_len, -1, &pathbuf, opt->prefix);
+		strbuf_insert(&pathbuf, 0, name, tree_name_len);
+		name = pathbuf.buf;
 	}
 	hit = grep_buffer(opt, name, data, size);
+	strbuf_release(&pathbuf);
 	free(data);
-	free(to_free);
 	return hit;
 }
 
@@ -163,6 +151,7 @@
 	int i;
 	char *data;
 	size_t sz;
+	struct strbuf buf = STRBUF_INIT;
 
 	if (lstat(filename, &st) < 0) {
 	err_ret:
@@ -187,8 +176,9 @@
 	}
 	close(i);
 	if (opt->relative && opt->prefix_length)
-		filename += opt->prefix_length;
+		filename = quote_path_relative(filename, -1, &buf, opt->prefix);
 	i = grep_buffer(opt, filename, data, sz);
+	strbuf_release(&buf);
 	free(data);
 	return i;
 }
@@ -471,6 +461,7 @@
 		hit = external_grep(opt, paths, cached);
 		if (hit >= 0)
 			return hit;
+		hit = 0;
 	}
 #endif
 
@@ -763,6 +754,7 @@
 	};
 
 	memset(&opt, 0, sizeof(opt));
+	opt.prefix = prefix;
 	opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
 	opt.relative = 1;
 	opt.pathname = 1;
@@ -832,15 +824,8 @@
 			verify_filename(prefix, argv[j]);
 	}
 
-	if (i < argc) {
+	if (i < argc)
 		paths = get_pathspec(prefix, argv + i);
-		if (opt.prefix_length && opt.relative) {
-			/* Make sure we do not get outside of paths */
-			for (i = 0; paths[i]; i++)
-				if (strncmp(prefix, paths[i], opt.prefix_length))
-					die("git grep: cannot generate relative filenames containing '..'");
-		}
-	}
 	else if (prefix) {
 		paths = xcalloc(2, sizeof(const char *));
 		paths[0] = prefix;
diff --git a/grep.h b/grep.h
index f00db0e..5581363 100644
--- a/grep.h
+++ b/grep.h
@@ -59,6 +59,7 @@
 	struct grep_pat *pattern_list;
 	struct grep_pat **pattern_tail;
 	struct grep_expr *pattern_expression;
+	const char *prefix;
 	int prefix_length;
 	regex_t regexp;
 	int linenum;
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index b13aa7e..6ca11d7 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -279,4 +279,21 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'grep from a subdirectory to search wider area (1)' '
+	mkdir -p s &&
+	(
+		cd s && git grep "x x x" ..
+	)
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (2)' '
+	mkdir -p s &&
+	(
+		cd s || exit 1
+		( git grep xxyyzz .. >out ; echo $? >status )
+		! test -s out &&
+		test 1 = $(cat status)
+	)
+'
+
 test_done