Subject: [PATCH] log: allow to specify diff pathspec in addition to prune pathspec

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Thu, 3 Nov 2011 19:15:56 +0700

Pathspec in "git log -p <pathspec>" is used for both commit pruning
and diff generation. If --full-diff is given, then diff pathspec is
reset to generate complete diff.

This patch gives more control to diff generation. The first pathspec
in "git log -p -- <pathspec> -- <pathspec>" is used as commit pruning
as usual. The second one is used for diff generation. So --full-diff
now is essentially "git log -p -- <pathspec> --".

This form requires specifying "--" twice. If a file name happens to be
"--", it may be misunderstood as the second "--" marker. This is an
unfortunate consequence for this syntax. Users can still use "./--" or
similar to workaround this.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 Version 2. Now it looks more acceptable.

 Documentation/git-log.txt |    9 +++++++--
 revision.c                |   28 +++++++++++++++++++++++++---
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 249fc87..8e00dbc 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -9,7 +9,7 @@ git-log - Show commit logs
 SYNOPSIS
 --------
 [verse]
-'git log' [<options>] [<since>..<until>] [[\--] <path>...]
+'git log' [<options>] [<since>..<until>] [[\--] <path>... [\-- <path>...]]
 
 DESCRIPTION
 -----------
@@ -52,11 +52,12 @@ OPTIONS
 	commit was reached.
 
 --full-diff::
-	Without this flag, "git log -p <path>..." shows commits that
+	Without this flag, `git log -p <path>...` shows commits that
 	touch the specified paths, and diffs about the same specified
 	paths.  With this, the full diff is shown for commits that touch
 	the specified paths; this means that "<path>..." limits only
 	commits, and doesn't limit diff for those commits.
+	It is equivalent to `git log -p \-- <path>... \--`.
 +
 Note that this affects all diff-based output types, e.g. those
 produced by --stat etc.
@@ -76,6 +77,10 @@ produced by --stat etc.
 +
 To prevent confusion with options and branch names, paths may need to
 be prefixed with "\-- " to separate them from options or refnames.
++
+If the second "\--" is found, the following pathspec is used to limit
+diff generation. Note that this affects all diff-based output types,
+e.g. those produced by --stat etc.
 
 include::rev-list-options.txt[]
 
diff --git a/revision.c b/revision.c
index 8764dde..f560647 100644
--- a/revision.c
+++ b/revision.c
@@ -1682,20 +1682,37 @@ static int handle_revision_pseudo_opt(const char *submodule,
  */
 int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
 {
-	int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
+	int i, flags, left, read_from_stdin, got_rev_arg = 0;
+	int seen_dashdash, seen_second_dashdash;
 	struct cmdline_pathspec prune_data;
+	struct cmdline_pathspec diff_data;
 	const char *submodule = NULL;
 
 	memset(&prune_data, 0, sizeof(prune_data));
+	memset(&diff_data, 0, sizeof(diff_data));
 	if (opt)
 		submodule = opt->submodule;
 
 	/* First, search for "--" */
-	seen_dashdash = 0;
+	seen_dashdash = seen_second_dashdash = 0;
 	for (i = 1; i < argc; i++) {
+		int i2;
 		const char *arg = argv[i];
 		if (strcmp(arg, "--"))
 			continue;
+
+		/* Search for second "--" */
+		for (i2 = i + 1; i2 < argc; i2++) {
+			const char *arg = argv[i2];
+			if (strcmp(arg, "--"))
+				continue;
+			argv[i2] = NULL;
+			if (argv[i2 + 1])
+				append_prune_data(&diff_data, argv + i2 + 1);
+			seen_second_dashdash = 1;
+			break;
+		}
+
 		argv[i] = NULL;
 		argc = i;
 		if (argv[i + 1])
@@ -1817,7 +1834,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 		/* Can't prune commits with rename following: the paths change.. */
 		if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 			revs->prune = 1;
-		if (!revs->full_diff)
+		if (seen_second_dashdash) {
+			ALLOC_GROW(diff_data.path, diff_data.nr+1, diff_data.alloc);
+			diff_data.path[diff_data.nr++] = NULL;
+			diff_tree_setup_paths(diff_data.path, &revs->diffopt);
+		}
+		else if (!revs->full_diff)
 			diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
 	}
 	if (revs->combine_merges)
-- 
1.7.4.74.g639db