Skip to content

Commit

Permalink
Merge branch 'mh/diff-indent-heuristic'
Browse files Browse the repository at this point in the history
Output from "git diff" can be made easier to read by selecting
which lines are common and which lines are added/deleted
intelligently when the lines before and after the changed section
are the same.  A command line option is added to help with the
experiment to find a good heuristics.

* mh/diff-indent-heuristic:
  blame: honor the diff heuristic options and config
  parse-options: add parse_opt_unknown_cb()
  diff: improve positioning of add/delete blocks in diffs
  xdl_change_compact(): introduce the concept of a change group
  recs_match(): take two xrecord_t pointers as arguments
  is_blank_line(): take a single xrecord_t as argument
  xdl_change_compact(): only use heuristic if group can't be matched
  xdl_change_compact(): fix compaction heuristic to adjust ixo
  • Loading branch information
gitster committed Sep 26, 2016
2 parents b3e588a + 5b16287 commit b7af6ae
Show file tree
Hide file tree
Showing 14 changed files with 828 additions and 115 deletions.
7 changes: 4 additions & 3 deletions Documentation/diff-config.txt
Expand Up @@ -171,10 +171,11 @@ diff.tool::

include::mergetools-diff.txt[]

diff.indentHeuristic::
diff.compactionHeuristic::
Set this option to `true` to enable an experimental heuristic that
shifts the hunk boundary in an attempt to make the resulting
patch easier to read.
Set one of these options to `true` to enable one of two
experimental heuristics that shift diff hunk boundaries to
make patches easier to read.

diff.algorithm::
Choose a diff algorithm. The variants are as follows:
Expand Down
7 changes: 7 additions & 0 deletions Documentation/diff-heuristic-options.txt
@@ -0,0 +1,7 @@
--indent-heuristic::
--no-indent-heuristic::
--compaction-heuristic::
--no-compaction-heuristic::
These are to help debugging and tuning experimental heuristics
(which are off by default) that shift diff hunk boundaries to
make patches easier to read.
7 changes: 1 addition & 6 deletions Documentation/diff-options.txt
Expand Up @@ -63,12 +63,7 @@ ifndef::git-format-patch[]
Synonym for `-p --raw`.
endif::git-format-patch[]

--compaction-heuristic::
--no-compaction-heuristic::
These are to help debugging and tuning an experimental
heuristic (which is off by default) that shifts the hunk
boundary in an attempt to make the resulting patch easier
to read.
include::diff-heuristic-options.txt[]

--minimal::
Spend extra time to make sure the smallest possible
Expand Down
1 change: 1 addition & 0 deletions Documentation/git-annotate.txt
Expand Up @@ -23,6 +23,7 @@ familiar command name for people coming from other SCM systems.
OPTIONS
-------
include::blame-options.txt[]
include::diff-heuristic-options.txt[]

SEE ALSO
--------
Expand Down
2 changes: 2 additions & 0 deletions Documentation/git-blame.txt
Expand Up @@ -89,6 +89,8 @@ include::blame-options.txt[]
abbreviated object name, use <n>+1 digits. Note that 1 column
is used for a caret to mark the boundary commit.

include::diff-heuristic-options.txt[]


THE PORCELAIN FORMAT
--------------------
Expand Down
12 changes: 12 additions & 0 deletions builtin/blame.c
Expand Up @@ -2220,6 +2220,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)
return 0;
}

if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
if (userdiff_config(var, value) < 0)
return -1;

Expand Down Expand Up @@ -2550,6 +2552,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),

/*
* The following two options are parsed by parse_revision_opt()
* and are only included here to get included in the "-h"
* output:
*/
{ OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental indent-based heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },
{ OPTION_LOWLEVEL_CALLBACK, 0, "compaction-heuristic", NULL, NULL, N_("Use an experimental blank-line-based heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },

OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
Expand Down Expand Up @@ -2596,6 +2607,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
}
parse_done:
no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
xdl_opts |= revs.diffopt.xdl_opts & (XDF_COMPACTION_HEURISTIC | XDF_INDENT_HEURISTIC);
DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
argc = parse_options_end(&ctx);

Expand Down
36 changes: 29 additions & 7 deletions diff.c
Expand Up @@ -27,6 +27,7 @@
#endif

static int diff_detect_rename_default;
static int diff_indent_heuristic; /* experimental */
static int diff_compaction_heuristic; /* experimental */
static int diff_rename_limit_default = 400;
static int diff_suppress_blank_empty;
Expand Down Expand Up @@ -177,6 +178,21 @@ void init_diff_ui_defaults(void)
diff_detect_rename_default = 1;
}

int git_diff_heuristic_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.indentheuristic")) {
diff_indent_heuristic = git_config_bool(var, value);
if (diff_indent_heuristic)
diff_compaction_heuristic = 0;
}
if (!strcmp(var, "diff.compactionheuristic")) {
diff_compaction_heuristic = git_config_bool(var, value);
if (diff_compaction_heuristic)
diff_indent_heuristic = 0;
}
return 0;
}

int git_diff_ui_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
Expand All @@ -193,10 +209,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_detect_rename_default = git_config_rename(var, value);
return 0;
}
if (!strcmp(var, "diff.compactionheuristic")) {
diff_compaction_heuristic = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "diff.autorefreshindex")) {
diff_auto_refresh_index = git_config_bool(var, value);
return 0;
Expand Down Expand Up @@ -237,6 +249,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0;
}

if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
if (git_color_config(var, value, cb) < 0)
return -1;

Expand Down Expand Up @@ -3296,7 +3310,9 @@ void diff_setup(struct diff_options *options)
options->use_color = diff_use_color_default;
options->detect_rename = diff_detect_rename_default;
options->xdl_opts |= diff_algorithm;
if (diff_compaction_heuristic)
if (diff_indent_heuristic)
DIFF_XDL_SET(options, INDENT_HEURISTIC);
else if (diff_compaction_heuristic)
DIFF_XDL_SET(options, COMPACTION_HEURISTIC);

options->orderfile = diff_order_file_cfg;
Expand Down Expand Up @@ -3818,9 +3834,15 @@ int diff_opt_parse(struct diff_options *options,
DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
else if (!strcmp(arg, "--ignore-blank-lines"))
DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
else if (!strcmp(arg, "--compaction-heuristic"))
else if (!strcmp(arg, "--indent-heuristic")) {
DIFF_XDL_SET(options, INDENT_HEURISTIC);
DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
} else if (!strcmp(arg, "--no-indent-heuristic"))
DIFF_XDL_CLR(options, INDENT_HEURISTIC);
else if (!strcmp(arg, "--compaction-heuristic")) {
DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
else if (!strcmp(arg, "--no-compaction-heuristic"))
DIFF_XDL_CLR(options, INDENT_HEURISTIC);
} else if (!strcmp(arg, "--no-compaction-heuristic"))
DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
else if (!strcmp(arg, "--patience"))
options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
Expand Down
1 change: 1 addition & 0 deletions diff.h
Expand Up @@ -273,6 +273,7 @@ extern int parse_long_opt(const char *opt, const char **argv,
const char **optarg);

extern int git_diff_basic_config(const char *var, const char *value, void *cb);
extern int git_diff_heuristic_config(const char *var, const char *value, void *cb);
extern void init_diff_ui_defaults(void);
extern int git_diff_ui_config(const char *var, const char *value, void *cb);
extern void diff_setup(struct diff_options *);
Expand Down
5 changes: 4 additions & 1 deletion git-add--interactive.perl
Expand Up @@ -45,6 +45,7 @@
my $normal_color = $repo->get_color("", "reset");

my $diff_algorithm = $repo->config('diff.algorithm');
my $diff_indent_heuristic = $repo->config_bool('diff.indentheuristic');
my $diff_compaction_heuristic = $repo->config_bool('diff.compactionheuristic');
my $diff_filter = $repo->config('interactive.difffilter');

Expand Down Expand Up @@ -750,7 +751,9 @@ sub parse_diff {
if (defined $diff_algorithm) {
splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
}
if ($diff_compaction_heuristic) {
if ($diff_indent_heuristic) {
splice @diff_cmd, 1, 0, "--indent-heuristic";
} elsif ($diff_compaction_heuristic) {
splice @diff_cmd, 1, 0, "--compaction-heuristic";
}
if (defined $patch_mode_revision) {
Expand Down
12 changes: 12 additions & 0 deletions parse-options-cb.c
Expand Up @@ -158,6 +158,18 @@ int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
return 0;
}

/**
* Report that the option is unknown, so that other code can handle
* it. This can be used as a callback together with
* OPTION_LOWLEVEL_CALLBACK to allow an option to be documented in the
* "-h" output even if it's not being handled directly by
* parse_options().
*/
int parse_opt_unknown_cb(const struct option *opt, const char *arg, int unset)
{
return -2;
}

/**
* Recreates the command-line option in the strbuf.
*/
Expand Down
1 change: 1 addition & 0 deletions parse-options.h
Expand Up @@ -228,6 +228,7 @@ extern int parse_opt_commits(const struct option *, const char *, int);
extern int parse_opt_tertiary(const struct option *, const char *, int);
extern int parse_opt_string_list(const struct option *, const char *, int);
extern int parse_opt_noop_cb(const struct option *, const char *, int);
extern int parse_opt_unknown_cb(const struct option *, const char *, int);
extern int parse_opt_passthru(const struct option *, const char *, int);
extern int parse_opt_passthru_argv(const struct option *, const char *, int);

Expand Down

0 comments on commit b7af6ae

Please sign in to comment.