[PATCH/RFC v3] Introduce config variable "diff.defaultoptions"

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

 



Improve porcelain diff's accommodation of user preference by allowing any
command-line option to (a) persist over all invocations and (b) stay consistent
over multiple tools (e.g. command-line and gui).  The approach taken here is
good because it delivers the consistency a user expects without breaking any
plumbing.  It works by allowing the user, via git-config, to specify arbitrary
options to pass to porcelain diff on every invocation, including internal
invocations from other programs, e.g. git-gui.

Introduce diff command-line options --default-options and --no-default-options.

Affect only porcelain diff: we suppress default options for plumbing
diff-{files,index,tree}, format-patch, and all other commands unless explicitly
requested using --default-options (opt-in).

Signed-off-by: Keith Cascio <keith@xxxxxxxxxxx>
---

Please notice v3 supports all diff options (improvement over v2).

This is a RFC.  I omitted the documentation and test portions for now.

                                    -- Keith

 diff.h         |   17 ++++++--
 diff.c         |  112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 builtin-diff.c |    1 +
 builtin-log.c  |    1 +
 4 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/diff.h b/diff.h
index 6616877..66f1518 100644
--- a/diff.h
+++ b/diff.h
@@ -66,12 +66,17 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
 #define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
 #define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
+#define DIFF_OPT_ALLOW_DEFAULT_OPTIONS (1 << 22)
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
-#define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
+#define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag),\
+				    ((opts)->mask  |= DIFF_OPT_##flag)
-#define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
+#define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag),\
+				    ((opts)->mask  |=  DIFF_OPT_##flag)
 #define DIFF_XDL_TST(opts, flag)    ((opts)->xdl_opts & XDF_##flag)
-#define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
+#define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag),\
+				    ((opts)->xdl_mask |= XDF_##flag)
-#define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
+#define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag),\
+				    ((opts)->xdl_mask |=  XDF_##flag)
 
 struct diff_options {
 	const char *filter;
@@ -80,6 +85,7 @@ struct diff_options {
 	const char *single_follow;
 	const char *a_prefix, *b_prefix;
 	unsigned flags;
+	unsigned mask;
 	int context;
 	int interhunkcontext;
 	int break_opt;
@@ -98,6 +104,7 @@ struct diff_options {
 	int prefix_length;
 	const char *stat_sep;
 	long xdl_opts;
+	long xdl_mask;
 
 	int stat_width;
 	int stat_name_width;
@@ -193,7 +200,7 @@ extern void diff_unmerge(struct diff_options *,
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
-extern void diff_setup(struct diff_options *);
+extern struct diff_options* diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
 extern int diff_setup_done(struct diff_options *);
 
diff --git a/diff.c b/diff.c
index 75d9fab..1c4fec4 100644
--- a/diff.c
+++ b/diff.c
@@ -26,6 +26,8 @@ static int diff_suppress_blank_empty;
 int diff_use_color_default = -1;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
+static const char *diff_defaults;
+static struct diff_options *defaults;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 
@@ -106,6 +108,8 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
 		diff_rename_limit_default = git_config_int(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "diff.defaultoptions"))
+		return git_config_string(&diff_defaults, var, value);
 
 	switch (userdiff_config(var, value)) {
 		case 0: break;
@@ -2314,7 +2318,101 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
 	builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
 }
 
+struct diff_options* parse_diff_defaults(struct diff_options *options)
+{
+	int count, len, i;
+	const char **new_argv;
+
+	if ((! diff_defaults) || ((len = (strlen(diff_defaults)+1)) < 3))
+		return options;
+	count = split_cmdline(strncpy(xmalloc(len), diff_defaults, len),
+			&new_argv);
+	for (i=0; i<count; i++)
+		diff_opt_parse(options, &new_argv[i], -1);
+	return options;
+}
+
+#define PRISTINE -0x40
+#define COALESCE_PTR(p) master->p = master->p ? master->p : slave->p
+#define COALESCE_INT(i) master->i = master->i != PRISTINE ? master->i : slave->i
+
+struct diff_options* flatten_diff_options(struct diff_options *master,
+					  struct diff_options *slave)
+{
+	/*
+	 * Our desired flags are:
+	 *
+	 *   1. Anything the master hasn't explicitly set, we can take from
+	 *      the slave.
+	 *   2. Anything the slave didn't explicitly set, we can take whether
+	 *      or not the master set it explicitly.
+	 *   3. Anything the master explicitly set, we take.
+	 */
+	master->flags =
+	 /* (1) */ (~master->mask & slave->flags & slave->mask) |
+	 /* (2) */ (master->flags & ~slave->mask) |
+	 /* (3) */ (master->flags & master->mask);
+	master->mask |= slave->mask;
+	master->xdl_opts =
+	 /* (1) */ (~master->xdl_mask & slave->xdl_opts & slave->xdl_mask) |
+	 /* (2) */ (master->xdl_opts & ~slave->xdl_mask) |
+	 /* (3) */ (master->xdl_opts & master->xdl_mask);
+	master->xdl_mask |= slave->xdl_mask;
+	master->output_format |= slave->output_format;
+	master->setup |= slave->setup;
+	COALESCE_PTR(a_prefix);
+	COALESCE_PTR(b_prefix);
+	COALESCE_PTR(filter);
+	COALESCE_PTR(orderfile);
+	COALESCE_PTR(pickaxe);
+	COALESCE_PTR(prefix);
+	COALESCE_PTR(single_follow);
+	COALESCE_PTR(stat_sep);
+	COALESCE_PTR(word_regex);
+	COALESCE_INT(abbrev);
+	COALESCE_INT(break_opt);
+	COALESCE_INT(close_file);
+	COALESCE_INT(context);
+	COALESCE_INT(detect_rename);
+	COALESCE_INT(dirstat_percent);
+	COALESCE_INT(interhunkcontext);
+	COALESCE_INT(line_termination);
+	COALESCE_INT(pickaxe_opts);
+	COALESCE_INT(prefix_length);
+	COALESCE_INT(rename_limit);
+	COALESCE_INT(rename_score);
+	COALESCE_INT(skip_stat_unmatch);
+	COALESCE_INT(stat_name_width);
+	COALESCE_INT(stat_width);
+	COALESCE_INT(warn_on_too_large_rename);
+	COALESCE_PTR(file);
+	COALESCE_PTR(change);
+	COALESCE_PTR(add_remove);
+	COALESCE_PTR(format_callback);
+	COALESCE_PTR(format_callback_data);
+	if((! master->paths) && slave->paths){
+		master->nr_paths = slave->nr_paths;
+		master->paths    = slave->paths;
+		master->pathlens = slave->pathlens;
+	}
+	return master;
+}
+
-void diff_setup(struct diff_options *options)
+struct diff_options* diff_setup(struct diff_options *options)
+{
+	memset(options, 0, sizeof(*options));
+	options->abbrev = options->break_opt = options->close_file =
+	options->context = options->detect_rename =
+	options->dirstat_percent = options->interhunkcontext =
+	options->line_termination = options->pickaxe_opts =
+	options->prefix_length = options->rename_limit =
+	options->rename_score = options->skip_stat_unmatch =
+	options->stat_name_width = options->stat_width =
+	options->warn_on_too_large_rename = PRISTINE;
+	return options;
+}
+
+struct diff_options* diff_fallback_values(struct diff_options *options)
 {
 	memset(options, 0, sizeof(*options));
 
@@ -2336,11 +2434,19 @@ void diff_setup(struct diff_options *options)
 		options->a_prefix = "a/";
 		options->b_prefix = "b/";
 	}
+	return options;
 }
 
 int diff_setup_done(struct diff_options *options)
 {
 	int count = 0;
+	struct diff_options fallback;
+
+	if (DIFF_OPT_TST(options, ALLOW_DEFAULT_OPTIONS))
+		flatten_diff_options(options, defaults ? defaults :
+			parse_diff_defaults(diff_setup(defaults =
+				xmalloc(sizeof(struct diff_options)))));
+	flatten_diff_options(options, diff_fallback_values(&fallback));
 
 	if (options->output_format & DIFF_FORMAT_NAME)
 		count++;
@@ -2615,6 +2721,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 		DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
 	else if (!strcmp(arg, "--ignore-submodules"))
 		DIFF_OPT_SET(options, IGNORE_SUBMODULES);
+	else if (!strcmp(arg, "--default-options"))
+		DIFF_OPT_SET(options, ALLOW_DEFAULT_OPTIONS);
+	else if (!strcmp(arg, "--no-default-options"))
+		DIFF_OPT_CLR(options, ALLOW_DEFAULT_OPTIONS);
 
 	/* misc options */
 	else if (!strcmp(arg, "-z"))
diff --git a/builtin-diff.c b/builtin-diff.c
index d75d69b..34c6ce2 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -283,6 +283,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 		diff_use_color_default = git_use_color_default;
 
 	init_revisions(&rev, prefix);
+	DIFF_OPT_SET(&rev.diffopt, ALLOW_DEFAULT_OPTIONS);
 
 	/* If this is a no-index diff, just run it and exit there. */
 	diff_no_index(&rev, argc, argv, nongit, prefix);
diff --git a/builtin-log.c b/builtin-log.c
index 8af55d2..1fa583f 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -37,6 +37,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 		get_commit_format(fmt_pretty, rev);
 	rev->verbose_header = 1;
 	DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+	DIFF_OPT_SET(&rev->diffopt, ALLOW_DEFAULT_OPTIONS);
 	rev->show_root_diff = default_show_root;
 	rev->subject_prefix = fmt_patch_subject_prefix;
 	DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
-- 
1.6.2.1.337.g3b73.dirty

--
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]

  Powered by Linux