Skip to content

Commit

Permalink
diff: teach diff to display submodule difference with an inline diff
Browse files Browse the repository at this point in the history
Teach git-diff and friends a new format for displaying the difference
of a submodule. The new format is an inline diff of the contents of the
submodule between the commit range of the update. This allows the user
to see the actual code change caused by a submodule update.

Add tests for the new format and option.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
jacob-keller authored and gitster committed Sep 1, 2016
1 parent 8e6df65 commit fd47ae6
Show file tree
Hide file tree
Showing 7 changed files with 863 additions and 21 deletions.
9 changes: 5 additions & 4 deletions Documentation/diff-config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,11 @@ diff.suppressBlankEmpty::

diff.submodule::
Specify the format in which differences in submodules are
shown. The "log" format lists the commits in the range like
linkgit:git-submodule[1] `summary` does. The "short" format
format just shows the names of the commits at the beginning
and end of the range. Defaults to short.
shown. The "short" format just shows the names of the commits
at the beginning and end of the range. The "log" format lists
the commits in the range like linkgit:git-submodule[1] `summary`
does. The "diff" format shows an inline diff of the changed
contents of the submodule. Defaults to "short".

diff.wordRegex::
A POSIX Extended Regular Expression used to determine what is a "word"
Expand Down
17 changes: 10 additions & 7 deletions Documentation/diff-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,16 @@ any of those replacements occurred.
of the `--diff-filter` option on what the status letters mean.

--submodule[=<format>]::
Specify how differences in submodules are shown. When `--submodule`
or `--submodule=log` is given, the 'log' format is used. This format lists
the commits in the range like linkgit:git-submodule[1] `summary` does.
Omitting the `--submodule` option or specifying `--submodule=short`,
uses the 'short' format. This format just shows the names of the commits
at the beginning and end of the range. Can be tweaked via the
`diff.submodule` configuration variable.
Specify how differences in submodules are shown. When specifying
`--submodule=short` the 'short' format is used. This format just
shows the names of the commits at the beginning and end of the range.
When `--submodule` or `--submodule=log` is specified, the 'log'
format is used. This format lists the commits in the range like
linkgit:git-submodule[1] `summary` does. When `--submodule=diff`
is specified, the 'diff' format is used. This format shows an
inline diff of the changes in the submodule contents between the
commit range. Defaults to `diff.submodule` or the 'short' format
if the config option is unset.

--color[=<when>]::
Show colored diff.
Expand Down
31 changes: 22 additions & 9 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ static int parse_submodule_params(struct diff_options *options, const char *valu
options->submodule_format = DIFF_SUBMODULE_LOG;
else if (!strcmp(value, "short"))
options->submodule_format = DIFF_SUBMODULE_SHORT;
else if (!strcmp(value, "diff"))
options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
else
return -1;
return 0;
Expand Down Expand Up @@ -2300,6 +2302,15 @@ static void builtin_diff(const char *name_a,
struct strbuf header = STRBUF_INIT;
const char *line_prefix = diff_line_prefix(o);

diff_set_mnemonic_prefix(o, "a/", "b/");
if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
a_prefix = o->b_prefix;
b_prefix = o->a_prefix;
} else {
a_prefix = o->a_prefix;
b_prefix = o->b_prefix;
}

if (o->submodule_format == DIFF_SUBMODULE_LOG &&
(!one->mode || S_ISGITLINK(one->mode)) &&
(!two->mode || S_ISGITLINK(two->mode))) {
Expand All @@ -2311,22 +2322,24 @@ static void builtin_diff(const char *name_a,
two->dirty_submodule,
meta, del, add, reset);
return;
} else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
(!one->mode || S_ISGITLINK(one->mode)) &&
(!two->mode || S_ISGITLINK(two->mode))) {
const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
line_prefix,
&one->oid, &two->oid,
two->dirty_submodule,
meta, del, add, reset, o);
return;
}

if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
textconv_one = get_textconv(one);
textconv_two = get_textconv(two);
}

diff_set_mnemonic_prefix(o, "a/", "b/");
if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
a_prefix = o->b_prefix;
b_prefix = o->a_prefix;
} else {
a_prefix = o->a_prefix;
b_prefix = o->b_prefix;
}

/* Never use a non-valid filename anywhere if at all possible */
name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
Expand Down
3 changes: 2 additions & 1 deletion diff.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ enum diff_words_type {

enum diff_submodule_format {
DIFF_SUBMODULE_SHORT = 0,
DIFF_SUBMODULE_LOG
DIFF_SUBMODULE_LOG,
DIFF_SUBMODULE_INLINE_DIFF
};

struct diff_options {
Expand Down
69 changes: 69 additions & 0 deletions submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,75 @@ void show_submodule_summary(FILE *f, const char *path,
clear_commit_marks(right, ~0);
}

void show_submodule_inline_diff(FILE *f, const char *path,
const char *line_prefix,
struct object_id *one, struct object_id *two,
unsigned dirty_submodule, const char *meta,
const char *del, const char *add, const char *reset,
const struct diff_options *o)
{
const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
struct commit *left = NULL, *right = NULL;
struct commit_list *merge_bases = NULL;
struct strbuf submodule_dir = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;

show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
meta, reset, &left, &right, &merge_bases);

/* We need a valid left and right commit to display a difference */
if (!(left || is_null_oid(one)) ||
!(right || is_null_oid(two)))
goto done;

if (left)
old = one;
if (right)
new = two;

fflush(f);
cp.git_cmd = 1;
cp.dir = path;
cp.out = dup(fileno(f));
cp.no_stdin = 1;

/* TODO: other options may need to be passed here. */
argv_array_push(&cp.args, "diff");
argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
o->b_prefix, path);
argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
o->a_prefix, path);
} else {
argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
o->a_prefix, path);
argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
o->b_prefix, path);
}
argv_array_push(&cp.args, oid_to_hex(old));
/*
* If the submodule has modified content, we will diff against the
* work tree, under the assumption that the user has asked for the
* diff format and wishes to actually see all differences even if they
* haven't yet been committed to the submodule yet.
*/
if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
argv_array_push(&cp.args, oid_to_hex(new));

if (run_command(&cp))
fprintf(f, "(diff failed)\n");

done:
strbuf_release(&submodule_dir);
if (merge_bases)
free_commit_list(merge_bases);
if (left)
clear_commit_marks(left, ~0);
if (right)
clear_commit_marks(right, ~0);
}

void set_config_fetch_recurse_submodules(int value)
{
config_fetch_recurse_submodules = value;
Expand Down
6 changes: 6 additions & 0 deletions submodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ void show_submodule_summary(FILE *f, const char *path,
struct object_id *one, struct object_id *two,
unsigned dirty_submodule, const char *meta,
const char *del, const char *add, const char *reset);
void show_submodule_inline_diff(FILE *f, const char *path,
const char *line_prefix,
struct object_id *one, struct object_id *two,
unsigned dirty_submodule, const char *meta,
const char *del, const char *add, const char *reset,
const struct diff_options *opt);
void set_config_fetch_recurse_submodules(int value);
void check_for_new_submodule_commits(unsigned char new_sha1[20]);
int fetch_populated_submodules(const struct argv_array *options,
Expand Down
Loading

0 comments on commit fd47ae6

Please sign in to comment.