diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index fa4d133..8554068 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -15,7 +15,7 @@ SYNOPSIS
 	   [-E | --extended-regexp] [-G | --basic-regexp]
 	   [-F | --fixed-strings] [-n]
 	   [-l | --files-with-matches] [-L | --files-without-match]
-	   [-c | --count] [--all-match]
+	   [-c | --count] [--all-match] [--color | --no-color]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
 	   [-f <file>] [-e] <pattern>
 	   [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
@@ -99,6 +99,13 @@ OPTIONS
 	Instead of showing every matched line, show the number of
 	lines that match.
 
+--color::
+	Colorize grep output highlighting matches, file names and line numbers.
+
+--no-color::
+	Turn off grep colors, even when the configuration file gives the
+	default to color output.
+
 -[ABC] <context>::
 	Show `context` trailing (`A` -- after), or leading (`B`
 	-- before), or both (`C` -- context) lines, and place a
diff --git a/builtin-grep.c b/builtin-grep.c
index 631129d..29e7d5a 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -11,6 +11,7 @@
 #include "tree-walk.h"
 #include "builtin.h"
 #include "grep.h"
+#include "color.h"
 
 #ifndef NO_EXTERNAL_GREP
 #ifdef __unix__
@@ -20,6 +21,7 @@
 #endif
 #endif
 
+static int grep_use_color = -1;
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
  * pathname wildcards are allowed.
@@ -386,7 +388,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
 	 * we grep through the checked-out files. It tends to
 	 * be a lot more optimized
 	 */
-	if (!cached) {
+	if (!cached && !opt->color) {
 		hit = external_grep(opt, paths, cached);
 		if (hit >= 0)
 			return hit;
@@ -507,6 +509,22 @@ static const char emsg_missing_context_len[] =
 static const char emsg_missing_argument[] =
 "option requires an argument -%s";
 
+static int git_grep_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "color.grep")) {
+		grep_use_color = git_config_colorbool(var, value, -1);
+		return 0;
+	}
+    if (!prefixcmp(var, "color.grep.")) {
+		int slot = parse_grep_color_slot(var, 11);
+		if (!value)
+			return config_error_nonbool(var);
+		color_parse(value, var, grep_get_color(slot));
+		return 0;
+	}
+	return git_color_default_config(var, value, cb);
+}
+
 int cmd_grep(int argc, const char **argv, const char *prefix)
 {
 	int hit = 0;
@@ -524,6 +542,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 	opt.pattern_tail = &opt.pattern_list;
 	opt.regflags = REG_NEWLINE;
 
+	git_config(git_grep_config, NULL);
+	if (grep_use_color == -1)
+		grep_use_color = git_use_color_default;
+    opt.color = grep_use_color;
+
 	/*
 	 * If there is no -- then the paths must exist in the working
 	 * tree.  If there is no explicit pattern specified with -e or
@@ -711,6 +734,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			opt.relative = 0;
 			continue;
 		}
+		if (!strcmp("--color", arg)) {
+			opt.color = 1;
+			continue;
+		}
+		if (!strcmp("--no-color", arg)) {
+			opt.color = 0;
+			continue;
+		}
 		if (!strcmp("--", arg)) {
 			/* later processing wants to have this at argv[1] */
 			argv--;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3396e35..ee4d509 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1157,7 +1157,7 @@ _git_config ()
 		__gitcomp "$(__git_merge_strategies)"
 		return
 		;;
-	color.branch|color.diff|color.status)
+	color.branch|color.diff|color.status|color.grep)
 		__gitcomp "always never auto"
 		return
 		;;
@@ -1240,6 +1240,11 @@ _git_config ()
 		color.diff.new
 		color.diff.commit
 		color.diff.whitespace
+		color.grep
+		color.grep.filepath
+		color.grep.lineno
+		color.grep.separator
+		color.grep.match
 		color.pager
 		color.status
 		color.status.header
@@ -1390,6 +1395,22 @@ _git_shortlog ()
 	__git_complete_revlist
 }
 
+_git_grep ()
+{
+    local cur="${COMP_WORDS[COMP_CWORD]}"
+    case "$cur" in
+    --*)
+        __gitcomp "
+        --cached --text --ignore-case --word-regexp --invert-match
+        --full-name --extend-regexp --fixed-strings --files-with-matches
+        --files-without-match --count --and --or --not --all-match
+        --color
+        "
+        return
+        ;;
+    esac
+}
+
 _git_show ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -1642,7 +1663,7 @@ _git ()
 	fetch)       _git_fetch ;;
 	format-patch) _git_format_patch ;;
 	gc)          _git_gc ;;
-	grep)        _git_grep ;;
+    grep)        _git_grep ;;
 	help)        _git_help ;;
 	init)        _git_init ;;
 	log)         _git_log ;;
diff --git a/grep.c b/grep.c
index f67d671..5c3e6cd 100644
--- a/grep.c
+++ b/grep.c
@@ -1,7 +1,39 @@
 #include "cache.h"
 #include "grep.h"
+#include "color.h"
 #include "xdiff-interface.h"
 
+static char grep_colors[][COLOR_MAXLEN] = {
+	"\033[m",          /* reset */
+	"",	               /* PLAIN (normal) */
+	"\033[35m",        /* FILEPATH (magenta) */
+	"\033[32m",        /* LINENO (green) */
+	"\033[36m",        /* SEPARATOR (cyan) */
+    "\033[1m\033[31m", /* MATCH (red)*/
+};
+
+const char *grep_get_color(enum color_grep ix)
+{
+	return grep_colors[ix];
+}
+
+int parse_grep_color_slot(const char *var, int ofs)
+{
+	if (!strcasecmp(var+ofs, "plain"))
+		return COLOR_GREP_PLAIN;
+	if (!strcasecmp(var+ofs, "reset"))
+		return COLOR_GREP_RESET;
+	if (!strcasecmp(var+ofs, "filepath"))
+		return COLOR_GREP_FILEPATH;
+	if (!strcasecmp(var+ofs, "lineno"))
+		return COLOR_GREP_LINENO;
+	if (!strcasecmp(var+ofs, "separator"))
+		return COLOR_GREP_SEPARATOR;
+	if (!strcasecmp(var+ofs, "match"))
+		return COLOR_GREP_MATCH;
+	die("bad config variable '%s'", var);
+}
+
 void append_grep_pattern(struct grep_opt *opt, const char *pat,
 			 const char *origin, int no, enum grep_pat_token t)
 {
@@ -247,7 +279,10 @@ static int fixmatch(const char *pattern, char *line, regmatch_t *match)
 	}
 }
 
-static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
+static int match_one_pattern_1(struct grep_opt *opt, struct grep_pat *p,
+			       char *bol, char *eol,
+			       enum grep_context ctx, int eflags,
+			       int *rm_so, int *rm_eo)
 {
 	int hit = 0;
 	int at_true_bol = 1;
@@ -261,7 +296,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
 	if (!opt->fixed) {
 		regex_t *exp = &p->regexp;
 		hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-			       pmatch, 0);
+			       pmatch, eflags);
 	}
 	else {
 		hit = !fixmatch(p->pattern, bol, pmatch);
@@ -298,9 +333,20 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
 			goto again;
 		}
 	}
+	if (hit) {
+		if (rm_so)
+			*rm_so = pmatch[0].rm_so;
+		if (rm_eo)
+			*rm_eo = pmatch[0].rm_eo;
+	}
 	return hit;
 }
 
+static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
+{
+	return match_one_pattern_1(opt, p, bol, eol, ctx, 0, NULL, NULL);
+}
+
 static int match_expr_eval(struct grep_opt *o,
 			   struct grep_expr *x,
 			   char *bol, char *eol,
@@ -365,6 +411,54 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
 	return 0;
 }
 
+static void show_line_colored(struct grep_opt *opt, char *bol, char *eol,
+			      const char *name, unsigned lno, char sign)
+{
+	struct grep_pat *p;
+	int rm_so, rm_eo, eflags = 0;
+	int ch;
+
+	if (opt->pathname)
+		printf("%s%s%s%c%s", 
+               grep_get_color(COLOR_GREP_FILEPATH), 
+               name, 
+               grep_get_color(COLOR_GREP_SEPARATOR), 
+               sign, 
+               grep_get_color(COLOR_GREP_RESET));
+	if (opt->linenum)
+		printf("%s%d%s%c%s", 
+               grep_get_color(COLOR_GREP_LINENO), 
+               lno, 
+               grep_get_color(COLOR_GREP_SEPARATOR), 
+               sign, 
+               grep_get_color(COLOR_GREP_RESET));
+
+	ch = *eol;
+	*eol = 0;
+	while (bol < eol) {
+		for (p = opt->pattern_list; p; p = p->next) {
+			if (match_one_pattern_1(opt, p, bol, eol, GREP_CONTEXT_BODY, eflags, &rm_so, &rm_eo))
+				break;
+		}
+
+		/* No match, break the loop */
+		if (!p ||
+		    (rm_so < 0) || (eol - bol) <= rm_so ||
+		    (rm_eo < 0) || (eol - bol) < rm_eo)
+			break;
+
+		printf("%.*s%s%.*s%s",
+		       rm_so, bol,
+               grep_get_color(COLOR_GREP_MATCH), 
+		       rm_eo-rm_so, bol+rm_so,
+               grep_get_color(COLOR_GREP_RESET));
+		bol += rm_eo;
+		eflags = REG_NOTBOL;
+	}
+	printf("%s\n", bol);
+	*eol = ch;
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
 			 char *buf, unsigned long size, int collect_hits)
 {
@@ -466,8 +560,12 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 			}
 			if (last_shown && lno != last_shown + 1)
 				printf(hunk_mark);
-			if (!opt->count)
-				show_line(opt, bol, eol, name, lno, ':');
+			if (!opt->count) {
+				if (opt->color)
+					show_line_colored(opt, bol, eol, name, lno, ':');
+				else
+					show_line(opt, bol, eol, name, lno, ':');
+			}
 			last_shown = last_hit = lno;
 		}
 		else if (last_hit &&
diff --git a/grep.h b/grep.h
index d252dd2..9073114 100644
--- a/grep.h
+++ b/grep.h
@@ -33,6 +33,15 @@ enum grep_expr_node {
 	GREP_NODE_OR,
 };
 
+enum color_grep {
+	COLOR_GREP_RESET = 0,
+	COLOR_GREP_PLAIN = 1,
+	COLOR_GREP_FILEPATH = 2,
+	COLOR_GREP_LINENO = 3,
+	COLOR_GREP_SEPARATOR = 4,
+    COLOR_GREP_MATCH = 5,
+};
+
 struct grep_expr {
 	enum grep_expr_node node;
 	unsigned hit;
@@ -68,11 +77,14 @@ struct grep_opt {
 	unsigned extended:1;
 	unsigned relative:1;
 	unsigned pathname:1;
+	unsigned color:1;
 	int regflags;
 	unsigned pre_context;
 	unsigned post_context;
 };
 
+extern const char *grep_get_color(enum color_grep ix);
+extern int parse_grep_color_slot(const char *var, int ofs);
 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
 extern void compile_grep_patterns(struct grep_opt *opt);
 extern void free_grep_patterns(struct grep_opt *opt);
