From f4235f8b2ef875b85ead74ffa199d827f9ee9d8d Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 15 Apr 2006 03:16:46 -0700
Subject: [PATCH] Extract "log [diff options]" parser out.

Merging of the log-tree-opt structure with rev-info structure
did not work out very well and it broke things that did not want
diff options and/or rev options.

This is an alternative approach to define a combined interface
that can be used by commands that wants both.  The use of it is
opt-in to reduce the risk of breaking existing programs.

We might want to slurp "setup_revisions() places things in
pending objects list" part from Linus's earlier attempt.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git.c      | 96 ++++++++++++------------------------------------------
 log-tree.c | 65 ++++++++++++++++++++++++++++++++++++
 log-tree.h | 14 ++++++++
 3 files changed, 100 insertions(+), 75 deletions(-)

diff --git a/git.c b/git.c
index 78ed403ed1..0741c5a36d 100644
--- a/git.c
+++ b/git.c
@@ -280,89 +280,35 @@ static int cmd_help(int argc, const char **argv, char **envp)
 
 static int cmd_log(int argc, const char **argv, char **envp)
 {
-	struct rev_info rev;
+	struct whatchanged_opt wcopt;
 	struct commit *commit;
 	char *buf = xmalloc(LOGSIZE);
-	static enum cmit_fmt commit_format = CMIT_FMT_DEFAULT;
-	int abbrev = DEFAULT_ABBREV;
-	int abbrev_commit = 0;
 	const char *commit_prefix = "commit ";
-	struct log_tree_opt opt;
 	int shown = 0;
-	int do_diff = 0;
-	int full_diff = 0;
-
-	init_log_tree_opt(&opt);
-	argc = setup_revisions(argc, argv, &rev, "HEAD");
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strncmp(arg, "--pretty", 8)) {
-			commit_format = get_commit_format(arg + 8);
-			if (commit_format == CMIT_FMT_ONELINE)
-				commit_prefix = "";
-		}
-		else if (!strcmp(arg, "--no-abbrev")) {
-			abbrev = 0;
-		}
-		else if (!strcmp(arg, "--abbrev")) {
-			abbrev = DEFAULT_ABBREV;
-		}
-		else if (!strcmp(arg, "--abbrev-commit")) {
-			abbrev_commit = 1;
-		}
-		else if (!strncmp(arg, "--abbrev=", 9)) {
-			abbrev = strtoul(arg + 9, NULL, 10);
-			if (abbrev && abbrev < MINIMUM_ABBREV)
-				abbrev = MINIMUM_ABBREV;
-			else if (40 < abbrev)
-				abbrev = 40;
-		}
-		else if (!strcmp(arg, "--full-diff")) {
-			do_diff = 1;
-			full_diff = 1;
-		}
-		else {
-			int cnt = log_tree_opt_parse(&opt, argv+1, argc-1);
-			if (0 < cnt) {
-				do_diff = 1;
-				argv += cnt;
-				argc -= cnt;
-				continue;
-			}
-			die("unrecognized argument: %s", arg);
-		}
+	struct rev_info *rev = &wcopt.revopt;
+	struct log_tree_opt *opt = &wcopt.logopt;
 
-		argc--; argv++;
-	}
+	init_log_tree_opt(&wcopt.logopt);
+	wcopt.commit_format = CMIT_FMT_DEFAULT;
+	wcopt.abbrev = DEFAULT_ABBREV;
+	argc = parse_whatchanged_opt(argc, argv, &wcopt);
 
-	if (do_diff) {
-		opt.diffopt.abbrev = abbrev;
-		opt.verbose_header = 0;
-		opt.always_show_header = 0;
-		opt.no_commit_id = 1;
-		if (opt.combine_merges)
-			opt.ignore_merges = 0;
-		if (opt.dense_combined_merges)
-			opt.diffopt.output_format = DIFF_FORMAT_PATCH;
-		if (opt.diffopt.output_format == DIFF_FORMAT_PATCH)
-			opt.diffopt.recursive = 1;
-		if (!full_diff && rev.prune_data)
-			diff_tree_setup_paths(rev.prune_data, &opt.diffopt);
-		diff_setup_done(&opt.diffopt);
-	}
+	if (wcopt.logopt.commit_format == CMIT_FMT_ONELINE)
+		commit_prefix = "";
 
-	prepare_revision_walk(&rev);
+	prepare_revision_walk(rev);
 	setup_pager();
-	while ((commit = get_revision(&rev)) != NULL) {
-		if (shown && do_diff && commit_format != CMIT_FMT_ONELINE)
+	while ((commit = get_revision(rev)) != NULL) {
+		if (shown && wcopt.do_diff &&
+		    wcopt.commit_format != CMIT_FMT_ONELINE)
 			putchar('\n');
 		fputs(commit_prefix, stdout);
-		if (abbrev_commit && abbrev)
-			fputs(find_unique_abbrev(commit->object.sha1, abbrev),
+		if (wcopt.abbrev_commit && wcopt.abbrev)
+			fputs(find_unique_abbrev(commit->object.sha1, wcopt.abbrev),
 			      stdout);
 		else
 			fputs(sha1_to_hex(commit->object.sha1), stdout);
-		if (rev.parents) {
+		if (rev->parents) {
 			struct commit_list *parents = commit->parents;
 			while (parents) {
 				struct object *o = &(parents->item->object);
@@ -381,15 +327,15 @@ static int cmd_log(int argc, const char **argv, char **envp)
 			     parents = parents->next)
 				parents->item->object.flags &= ~TMP_MARK;
 		}
-		if (commit_format == CMIT_FMT_ONELINE)
+		if (wcopt.commit_format == CMIT_FMT_ONELINE)
 			putchar(' ');
 		else
 			putchar('\n');
-		pretty_print_commit(commit_format, commit, ~0, buf,
-				    LOGSIZE, abbrev);
+		pretty_print_commit(wcopt.commit_format, commit, ~0, buf,
+				    LOGSIZE, wcopt.abbrev);
 		printf("%s\n", buf);
-		if (do_diff)
-			log_tree_commit(&opt, commit);
+		if (wcopt.do_diff)
+			log_tree_commit(opt, commit);
 		shown = 1;
 		free(commit->buffer);
 		commit->buffer = NULL;
diff --git a/log-tree.c b/log-tree.c
index 3d404824a1..cb0d0b15e7 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -173,3 +173,68 @@ int log_tree_commit(struct log_tree_opt *opt, struct commit *commit)
 	}
 	return 0;
 }
+
+int parse_whatchanged_opt(int ac, const char **av, struct whatchanged_opt *wcopt)
+{
+	struct rev_info *rev = &wcopt->revopt;
+	struct log_tree_opt *opt = &wcopt->logopt;
+	const char **unrecognized = av+1;
+	int left = 1;
+
+	ac = setup_revisions(ac, av, rev, "HEAD");
+	while (1 < ac) {
+		const char *arg = av[1];
+		if (!strncmp(arg, "--pretty", 8)) {
+			opt->commit_format = get_commit_format(arg + 8);
+		}
+		else if (!strcmp(arg, "--no-abbrev")) {
+			wcopt->abbrev = 0;
+		}
+		else if (!strcmp(arg, "--abbrev")) {
+			wcopt->abbrev = DEFAULT_ABBREV;
+		}
+		else if (!strcmp(arg, "--abbrev-commit")) {
+			wcopt->abbrev_commit = 1;
+		}
+		else if (!strncmp(arg, "--abbrev=", 9)) {
+			wcopt->abbrev = strtoul(arg + 9, NULL, 10);
+			if (wcopt->abbrev && wcopt->abbrev < MINIMUM_ABBREV)
+				wcopt->abbrev = MINIMUM_ABBREV;
+			else if (40 < wcopt->abbrev)
+				wcopt->abbrev = 40;
+		}
+		else if (!strcmp(arg, "--full-diff")) {
+			wcopt->do_diff = 1;
+			wcopt->full_diff = 1;
+		}
+		else {
+			int cnt = log_tree_opt_parse(opt, av+1, ac-1);
+			if (0 < cnt) {
+				wcopt->do_diff = 1;
+				av += cnt;
+				ac -= cnt;
+				continue;
+			}
+			*unrecognized++ = arg;
+			left++;
+		}
+		ac--; av++;
+	}
+
+	if (wcopt->do_diff) {
+		opt->diffopt.abbrev = wcopt->abbrev;
+		opt->verbose_header = 0;
+		opt->always_show_header = 0;
+		opt->no_commit_id = 1;
+		if (opt->combine_merges)
+			opt->ignore_merges = 0;
+		if (opt->dense_combined_merges)
+			opt->diffopt.output_format = DIFF_FORMAT_PATCH;
+		if (opt->diffopt.output_format == DIFF_FORMAT_PATCH)
+			opt->diffopt.recursive = 1;
+		if (!wcopt->full_diff && rev->prune_data)
+			diff_tree_setup_paths(rev->prune_data, &opt->diffopt);
+		diff_setup_done(&opt->diffopt);
+	}
+	return left;
+}
diff --git a/log-tree.h b/log-tree.h
index da166c6f2c..50cbfb3012 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -1,6 +1,8 @@
 #ifndef LOG_TREE_H
 #define LOG_TREE_H
 
+#include "revision.h"
+
 struct log_tree_opt {
 	struct diff_options diffopt;
 	int show_root_diff;
@@ -20,4 +22,16 @@ int log_tree_diff_flush(struct log_tree_opt *);
 int log_tree_commit(struct log_tree_opt *, struct commit *);
 int log_tree_opt_parse(struct log_tree_opt *, const char **, int);
 
+struct whatchanged_opt {
+	struct rev_info revopt;
+	struct log_tree_opt logopt;
+	enum cmit_fmt commit_format;
+	int abbrev;
+	int abbrev_commit;
+	int do_diff;
+	int full_diff;
+};
+
+int parse_whatchanged_opt(int, const char **, struct whatchanged_opt *);
+
 #endif
-- 
2.48.1