git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* [PATCH v5 1/3] diff.c: remove output_prefix_length field
@ 2016-08-11 22:59 Jacob Keller
  2016-08-11 22:59 ` [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix Jacob Keller
  2016-08-11 22:59 ` [PATCH v5 3/3] diff: add SUBMODULE_DIFF format to display submodule diff Jacob Keller
  0 siblings, 2 replies; 10+ messages in thread
From: Jacob Keller @ 2016-08-11 22:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

From: Junio C Hamano <gitster@pobox.com>

"diff/log --stat" has a logic that determines the display columns
available for the diffstat part of the output and apportions it for
pathnames and diffstat graph automatically.

5e71a84a (Add output_prefix_length to diff_options, 2012-04-16)
added the output_prefix_length field to diff_options structure to
allow this logic subtract the display columns used for display the
history graph part from the total "terminal width"; this matters
when the "git log --graph -p" option is in use.

The field be set to the number of display columns needed to show the
output from the output_prefix() callback.  Any new output_prefix()
callback must also update the field accordingly, which is error
prone.  As there is only one user of the field, and the user has the
actual value of the prefix string, let's get rid of the field and
have the user count the display width itself.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
This is the same as what Junio submitted already, just thought I'd add
it for clarity.

 diff.c  | 2 +-
 diff.h  | 1 -
 graph.c | 2 --
 3 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/diff.c b/diff.c
index b43d3dd2ecb7..ae069c303077 100644
--- a/diff.c
+++ b/diff.c
@@ -1625,7 +1625,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
 	 */
 
 	if (options->stat_width == -1)
-		width = term_columns() - options->output_prefix_length;
+		width = term_columns() - strlen(line_prefix);
 	else
 		width = options->stat_width ? options->stat_width : 80;
 	number_width = decimal_width(max_change) > number_width ?
diff --git a/diff.h b/diff.h
index 125447be09eb..49e4aaafb2da 100644
--- a/diff.h
+++ b/diff.h
@@ -174,7 +174,6 @@ struct diff_options {
 	diff_format_fn_t format_callback;
 	void *format_callback_data;
 	diff_prefix_fn_t output_prefix;
-	int output_prefix_length;
 	void *output_prefix_data;
 
 	int diff_path_counter;
diff --git a/graph.c b/graph.c
index dd1720148dc5..a46803840511 100644
--- a/graph.c
+++ b/graph.c
@@ -197,7 +197,6 @@ static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void
 	assert(opt);
 	assert(graph);
 
-	opt->output_prefix_length = graph->width;
 	strbuf_reset(&msgbuf);
 	graph_padding_line(graph, &msgbuf);
 	return &msgbuf;
@@ -245,7 +244,6 @@ struct git_graph *graph_init(struct rev_info *opt)
 	 */
 	opt->diffopt.output_prefix = diff_output_prefix_callback;
 	opt->diffopt.output_prefix_data = graph;
-	opt->diffopt.output_prefix_length = 0;
 
 	return graph;
 }
-- 
2.9.2.872.g367ebef.dirty


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-11 22:59 [PATCH v5 1/3] diff.c: remove output_prefix_length field Jacob Keller
@ 2016-08-11 22:59 ` Jacob Keller
  2016-08-12 21:15   ` Junio C Hamano
  2016-08-11 22:59 ` [PATCH v5 3/3] diff: add SUBMODULE_DIFF format to display submodule diff Jacob Keller
  1 sibling, 1 reply; 10+ messages in thread
From: Jacob Keller @ 2016-08-11 22:59 UTC (permalink / raw)
  To: git; +Cc: Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

Add an option to pass additional prefix to be displayed before diff
output. This feature will be used in a following patch to output correct
--graph prefix when using a child_process/run_command interface for
submodules.

The prefix shall come first prior to any other prefix associated with
the --graph option or other source.

Add tests for the same.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
- v5
* Changed name to --diff-line-prefix since --line-prefix might indicate
  for other commands such as log, when it only modifies diff output

 Documentation/diff-options.txt                     |  3 +++
 diff.c                                             | 16 ++++++++++--
 diff.h                                             |  1 +
 t/t4013-diff-various.sh                            |  6 +++++
 ...diff_--diff-line-prefix=-->_master_master^_side | 29 ++++++++++++++++++++++
 .../diff.diff_--diff-line-prefix_--cached_--_file0 | 15 +++++++++++
 6 files changed, 68 insertions(+), 2 deletions(-)
 create mode 100644 t/t4013/diff.diff_--diff-line-prefix=-->_master_master^_side
 create mode 100644 t/t4013/diff.diff_--diff-line-prefix_--cached_--_file0

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 705a87394200..f924f57d4f62 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -569,5 +569,8 @@ endif::git-format-patch[]
 --no-prefix::
 	Do not show any source or destination prefix.
 
+--diff-line-prefix=<prefix>::
+	Prepend an additional prefix to every line of diff output.
+
 For more detailed explanation on these common options, see also
 linkgit:gitdiffcore[7].
diff --git a/diff.c b/diff.c
index ae069c303077..73dda58c440c 100644
--- a/diff.c
+++ b/diff.c
@@ -1167,10 +1167,18 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
 const char *diff_line_prefix(struct diff_options *opt)
 {
 	struct strbuf *msgbuf;
-	if (!opt->output_prefix)
-		return "";
+
+	if (!opt->output_prefix) {
+		if (opt->line_prefix)
+			return opt->line_prefix;
+		else
+			return "";
+	}
 
 	msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+	/* line prefix must be printed before the output_prefix() */
+	if (opt->line_prefix)
+		strbuf_insert(msgbuf, 0, opt->line_prefix, strlen(opt->line_prefix));
 	return msgbuf->buf;
 }
 
@@ -3966,6 +3974,10 @@ int diff_opt_parse(struct diff_options *options,
 		options->a_prefix = optarg;
 		return argcount;
 	}
+	else if ((argcount = parse_long_opt("diff-line-prefix", av, &optarg))) {
+		options->line_prefix = optarg;
+		return argcount;
+	}
 	else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
 		options->b_prefix = optarg;
 		return argcount;
diff --git a/diff.h b/diff.h
index 49e4aaafb2da..83d0b1ae8580 100644
--- a/diff.h
+++ b/diff.h
@@ -115,6 +115,7 @@ struct diff_options {
 	const char *pickaxe;
 	const char *single_follow;
 	const char *a_prefix, *b_prefix;
+	const char *line_prefix;
 	unsigned flags;
 	unsigned touched_flags;
 
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 94ef5000e787..5204645eb92b 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -306,6 +306,8 @@ diff --no-index --name-status dir2 dir
 diff --no-index --name-status -- dir2 dir
 diff --no-index dir dir3
 diff master master^ side
+# Can't use spaces...
+diff --diff-line-prefix=--> master master^ side
 diff --dirstat master~1 master~2
 diff --dirstat initial rearrange
 diff --dirstat-by-file initial rearrange
@@ -325,6 +327,10 @@ test_expect_success 'diff --cached -- file on unborn branch' '
 	git diff --cached -- file0 >result &&
 	test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result
 '
+test_expect_success 'diff --diff-line-prefix with spaces' '
+	git diff --diff-line-prefix="| | | " --cached -- file0 >result &&
+	test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--diff-line-prefix_--cached_--_file0" result
+'
 
 test_expect_success 'diff-tree --stdin with log formatting' '
 	cat >expect <<-\EOF &&
diff --git a/t/t4013/diff.diff_--diff-line-prefix=-->_master_master^_side b/t/t4013/diff.diff_--diff-line-prefix=-->_master_master^_side
new file mode 100644
index 000000000000..5cc90f27c2d9
--- /dev/null
+++ b/t/t4013/diff.diff_--diff-line-prefix=-->_master_master^_side
@@ -0,0 +1,29 @@
+$ git diff --diff-line-prefix=--> master master^ side
+-->diff --cc dir/sub
+-->index cead32e,7289e35..992913c
+-->--- a/dir/sub
+-->+++ b/dir/sub
+-->@@@ -1,6 -1,4 +1,8 @@@
+-->  A
+-->  B
+--> +C
+--> +D
+--> +E
+--> +F
+-->+ 1
+-->+ 2
+-->diff --cc file0
+-->index b414108,f4615da..10a8a9f
+-->--- a/file0
+-->+++ b/file0
+-->@@@ -1,6 -1,6 +1,9 @@@
+-->  1
+-->  2
+-->  3
+--> +4
+--> +5
+--> +6
+-->+ A
+-->+ B
+-->+ C
+$
diff --git a/t/t4013/diff.diff_--diff-line-prefix_--cached_--_file0 b/t/t4013/diff.diff_--diff-line-prefix_--cached_--_file0
new file mode 100644
index 000000000000..f41ba4d36aa1
--- /dev/null
+++ b/t/t4013/diff.diff_--diff-line-prefix_--cached_--_file0
@@ -0,0 +1,15 @@
+| | | diff --git a/file0 b/file0
+| | | new file mode 100644
+| | | index 0000000..10a8a9f
+| | | --- /dev/null
+| | | +++ b/file0
+| | | @@ -0,0 +1,9 @@
+| | | +1
+| | | +2
+| | | +3
+| | | +4
+| | | +5
+| | | +6
+| | | +A
+| | | +B
+| | | +C
-- 
2.9.2.872.g367ebef.dirty


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5 3/3] diff: add SUBMODULE_DIFF format to display submodule diff
  2016-08-11 22:59 [PATCH v5 1/3] diff.c: remove output_prefix_length field Jacob Keller
  2016-08-11 22:59 ` [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix Jacob Keller
@ 2016-08-11 22:59 ` Jacob Keller
  1 sibling, 0 replies; 10+ messages in thread
From: Jacob Keller @ 2016-08-11 22:59 UTC (permalink / raw)
  To: git; +Cc: Jacob Keller

From: Jacob Keller <jacob.keller@gmail.com>

Teach git-diff and friends a new format for displaying the difference of
a submodule using git-diff inside the submodule project. This allows
users to easily see exactly what source changed in a given commit that
updates the submodule pointer. To do this, remove DIFF_SUBMODULE_LOG bit
from the diff options, and instead add a new enum type for these
formats.

Add some tests for the new format and option.

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
Major changes in v5 after adding tests, I will try to give an overview.
Unfortunately an interdiff is not easy as I do not remember which reflog
entry was v4 and it's been many edits, so I haven't bothered to dig
enough yet.

- v5
* reword "can be tweaked" text in the diff options
* remove unused variables
* print the submodule contains untracked or modified header piece
* print (new submodule) and (deleted submodule)
* handle case where the submodule is not initialized at all, and thus
  has no .git/modules/xyz, making a diff impossible
* handle the git directory better, plus comments explaining
* handle modified files better. Untracked unfortunately is more
  difficult. Suggestions for this welcome.
* handle null sha1 correctly (replace with empty tree)

I also added tests, mostly for diff output. These should be viewed
carefully since it's possible I didn't check the diff output very
carefully. Also, I haven't yet added a test for --graph -p mode yet, as
I haven't found a suitable place for it to be located.

 Documentation/diff-config.txt                |   3 +-
 Documentation/diff-options.txt               |   7 +-
 diff.c                                       |  41 +-
 diff.h                                       |   9 +-
 submodule.c                                  | 130 +++++
 submodule.h                                  |   6 +
 t/t4059-diff-submodule-option-diff-format.sh | 738 +++++++++++++++++++++++++++
 7 files changed, 915 insertions(+), 19 deletions(-)
 create mode 100755 t/t4059-diff-submodule-option-diff-format.sh

diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt
index d5a5b17d5088..f5d693afad6c 100644
--- a/Documentation/diff-config.txt
+++ b/Documentation/diff-config.txt
@@ -123,7 +123,8 @@ 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
+	linkgit:git-submodule[1] `summary` does.  The "diff" format shows the
+	diff between the beginning and end of the range. The "short" format
 	format just shows the names of the commits at the beginning
 	and end of the range.  Defaults to short.
 
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index f924f57d4f62..9a8e86ba1477 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -215,8 +215,11 @@ any of those replacements occurred.
 	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.
+	at the beginning and end of the range. When `--submodule=diff` is
+	given, the 'diff' format is used. This format shows the diff between
+	the old and new submodule commmit from the perspective of the
+	submodule.  Defaults to `diff.submodule` or 'short' if the config
+	option is unset.
 
 --color[=<when>]::
 	Show colored diff.
diff --git a/diff.c b/diff.c
index 73dda58c440c..660fdb4cfe46 100644
--- a/diff.c
+++ b/diff.c
@@ -131,9 +131,11 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
 static int parse_submodule_params(struct diff_options *options, const char *value)
 {
 	if (!strcmp(value, "log"))
-		DIFF_OPT_SET(options, SUBMODULE_LOG);
+		options->submodule_format = DIFF_SUBMODULE_LOG;
+	else if (!strcmp(value, "diff"))
+		options->submodule_format = DIFF_SUBMODULE_DIFF;
 	else if (!strcmp(value, "short"))
-		DIFF_OPT_CLR(options, SUBMODULE_LOG);
+		options->submodule_format = DIFF_SUBMODULE_SHORT;
 	else
 		return -1;
 	return 0;
@@ -2307,9 +2309,18 @@ static void builtin_diff(const char *name_a,
 	struct strbuf header = STRBUF_INIT;
 	const char *line_prefix = diff_line_prefix(o);
 
-	if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
-			(!one->mode || S_ISGITLINK(one->mode)) &&
-			(!two->mode || S_ISGITLINK(two->mode))) {
+	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))) {
 		const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
 		const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
 		show_submodule_summary(o->file, one->path ? one->path : two->path,
@@ -2318,6 +2329,15 @@ static void builtin_diff(const char *name_a,
 				two->dirty_submodule,
 				meta, del, add, reset);
 		return;
+	} else if (o->submodule_format == DIFF_SUBMODULE_DIFF &&
+		   (!one->mode || S_ISGITLINK(one->mode)) &&
+		   (!two->mode || S_ISGITLINK(two->mode))) {
+		show_submodule_diff(o->file, one->path ? one->path : two->path,
+				line_prefix,
+				one->oid.hash, two->oid.hash,
+				two->dirty_submodule,
+				meta, a_prefix, b_prefix, reset);
+		return;
 	}
 
 	if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
@@ -2325,15 +2345,6 @@ static void builtin_diff(const char *name_a,
 		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;
@@ -3923,7 +3934,7 @@ int diff_opt_parse(struct diff_options *options,
 		DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
 		handle_ignore_submodules_arg(options, arg);
 	} else if (!strcmp(arg, "--submodule"))
-		DIFF_OPT_SET(options, SUBMODULE_LOG);
+		options->submodule_format = DIFF_SUBMODULE_LOG;
 	else if (skip_prefix(arg, "--submodule=", &arg))
 		return parse_submodule_opt(options, arg);
 	else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
diff --git a/diff.h b/diff.h
index 83d0b1ae8580..abaa9a49c036 100644
--- a/diff.h
+++ b/diff.h
@@ -83,7 +83,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
 #define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
 #define DIFF_OPT_DIFF_FROM_CONTENTS  (1 << 22)
-#define DIFF_OPT_SUBMODULE_LOG       (1 << 23)
+/* (1 << 23) unused */
 #define DIFF_OPT_DIRTY_SUBMODULES    (1 << 24)
 #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
 #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
@@ -110,6 +110,12 @@ enum diff_words_type {
 	DIFF_WORDS_COLOR
 };
 
+enum diff_submodule_format {
+	DIFF_SUBMODULE_SHORT = 0,
+	DIFF_SUBMODULE_LOG,
+	DIFF_SUBMODULE_DIFF
+};
+
 struct diff_options {
 	const char *orderfile;
 	const char *pickaxe;
@@ -156,6 +162,7 @@ struct diff_options {
 	int stat_count;
 	const char *word_regex;
 	enum diff_words_type word_diff;
+	enum diff_submodule_format submodule_format;
 
 	/* this is set by diffcore for DIFF_FORMAT_PATCH */
 	int found_changes;
diff --git a/submodule.c b/submodule.c
index 1b5cdfb7e784..fb0866cb0c68 100644
--- a/submodule.c
+++ b/submodule.c
@@ -333,6 +333,136 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
 	strbuf_release(&sb);
 }
 
+void show_submodule_diff(FILE *f, const char *path,
+		const char *line_prefix,
+		unsigned char one[20], unsigned char two[20],
+		unsigned dirty_submodule, const char *meta,
+		const char *a_prefix, const char *b_prefix,
+		const char *reset)
+{
+	struct strbuf submodule_git_dir = STRBUF_INIT, sb = STRBUF_INIT;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *git_dir;
+
+	if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) {
+		fprintf(f, "%sSubmodule %s contains untracked content\n",
+			line_prefix, path);
+	}
+	if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED) {
+		fprintf(f, "%sSubmodule %s contains modified content\n",
+			line_prefix, path);
+	}
+
+	strbuf_addf(&sb, "%s%sSubmodule %s %s..",
+		    line_prefix, meta, path,
+		    find_unique_abbrev(one, DEFAULT_ABBREV));
+	strbuf_addf(&sb, "%s:%s",
+		    find_unique_abbrev(two, DEFAULT_ABBREV),
+		    reset);
+	fwrite(sb.buf, sb.len, 1, f);
+
+	if (is_null_sha1(one))
+		fprintf(f, " (new submodule)");
+	if (is_null_sha1(two))
+		fprintf(f, " (submodule deleted)");
+
+	/*
+	 * We need to determine the most accurate location to call the sub
+	 * command, and handle the various corner cases involved. We'll first
+	 * attempt to use the path directly if the submodule is checked out.
+	 * Then, if that fails, we'll check the standard module location in
+	 * the git directory. If even this fails, it means we can't do the
+	 * lookup because the module has not been initialized.
+	 */
+	strbuf_addf(&submodule_git_dir, "%s/.git", path);
+	git_dir = resolve_gitdir(submodule_git_dir.buf);
+	if (git_dir) {
+		/*
+		 * If we can resolve a git dir, this means that the submodule
+		 * has been checked out. In this case, just use the original
+		 * path since we want access to the work tree
+		 */
+		git_dir = path;
+	} else {
+		/*
+		 * If we can't resolve a git dir, this means that the
+		 * submodule has not been checked out. In this case, try the
+		 * standard location for modules
+		 */
+		strbuf_reset(&submodule_git_dir);
+		strbuf_addf(&submodule_git_dir, "%s/modules/%s", get_git_dir(), path);
+		git_dir = resolve_gitdir(submodule_git_dir.buf);
+		if (!git_dir) {
+			/*
+			 * If we failed to find a git directory here, then the
+			 * submodule must not have been initialized. Without
+			 * the initialized contents of the submodule, we won't
+			 * be able to perform the difference.
+			 */
+			fprintf(f, " (submodule not initialized)\n");
+			goto out;
+		}
+	}
+
+	/*
+	 * print a newline and flush the file so that the diff output appears
+	 * starting on its own line
+	 */
+	fprintf(f, "\n");
+	fflush(f);
+
+	cp.git_cmd = 1;
+	cp.dir = git_dir;
+	cp.out = dup(fileno(f));
+	cp.no_stdin = 1;
+
+	argv_array_push(&cp.args, "diff");
+	argv_array_pushf(&cp.args, "--diff-line-prefix=%s", line_prefix);
+	argv_array_pushf(&cp.args, "--src-prefix=%s%s/", a_prefix, path);
+	argv_array_pushf(&cp.args, "--dst-prefix=%s%s/", b_prefix, path);
+
+	if (is_null_sha1(one)) {
+		/*
+		 * If the origin commit is null, we want to use the empty tree
+		 * so that we see a diff of all the new contents added.
+		 */
+		argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
+	} else {
+		/* Use the old commit as the diff base */
+		argv_array_push(&cp.args, sha1_to_hex(one));
+	}
+
+	if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED) {
+		/*
+		 * If the submodule has modified contents we want to diff
+		 * against the work tree, so don't add a second parameter.
+		 * This is essentially equivalent of using diff-index instead.
+		 * Note that we can't (easily) show the diff of any untracked
+		 * files.
+		 */
+	} else if (is_null_sha1(two)) {
+		/*
+		 * If new commit is null, we should diff against the empty
+		 * tree, so that we see a diff of all submodule contents
+		 * removed.
+		 */
+		argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
+	} else {
+		/*
+		 * Finally, in other cases use the new commit as the end
+		 * result.
+		 */
+		argv_array_push(&cp.args, sha1_to_hex(two));
+	}
+
+	if (run_command(&cp))
+		fprintf(f, "(diff failed)\n");
+
+out:
+	strbuf_release(&submodule_git_dir);
+	strbuf_release(&sb);
+}
+
 void show_submodule_summary(FILE *f, const char *path,
 		const char *line_prefix,
 		unsigned char one[20], unsigned char two[20],
diff --git a/submodule.h b/submodule.h
index 2af939099819..f32a25c667ab 100644
--- a/submodule.h
+++ b/submodule.h
@@ -41,6 +41,12 @@ int parse_submodule_update_strategy(const char *value,
 		struct submodule_update_strategy *dst);
 const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
+void show_submodule_diff(FILE *f, const char *path,
+		const char *line_prefix,
+		unsigned char one[20], unsigned char two[20],
+		unsigned dirty_submodule, const char *meta,
+		const char *a_prefix, const char *b_prefix,
+		const char *reset);
 void show_submodule_summary(FILE *f, const char *path,
 		const char *line_prefix,
 		unsigned char one[20], unsigned char two[20],
diff --git a/t/t4059-diff-submodule-option-diff-format.sh b/t/t4059-diff-submodule-option-diff-format.sh
new file mode 100755
index 000000000000..d1306b65986c
--- /dev/null
+++ b/t/t4059-diff-submodule-option-diff-format.sh
@@ -0,0 +1,738 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
+# Copyright (c) 2016 Jacob Keller (copy + convert to --submodule=diff)
+#
+
+test_description='Support for diff format verbose submodule difference in git diff
+
+This test tries to verify the sanity of --submodule=diff option of git diff.
+'
+
+. ./test-lib.sh
+
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
+# String "added" in German (translated with Google Translate), encoded in UTF-8,
+# used in sample commit log messages in add_file() function below.
+added=$(printf "hinzugef\303\274gt")
+add_file () {
+	(
+		cd "$1" &&
+		shift &&
+		for name
+		do
+			echo "$name" >"$name" &&
+			git add "$name" &&
+			test_tick &&
+			# "git commit -m" would break MinGW, as Windows refuse to pass
+			# $test_encoding encoded parameter to git.
+			echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding |
+			git -c "i18n.commitEncoding=$test_encoding" commit -F -
+		done >/dev/null &&
+		git rev-parse --short --verify HEAD
+	)
+}
+commit_file () {
+	test_tick &&
+	git commit "$@" -m "Commit $*" >/dev/null
+}
+
+test_create_repo sm1 &&
+add_file . foo >/dev/null
+
+head1=$(add_file sm1 foo1 foo2)
+fullhead1=$(cd sm1; git rev-parse --verify HEAD)
+
+test_expect_success 'added submodule' '
+	git add sm1 &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 0000000..1beffeb: (new submodule)
+	diff --git a/sm1/foo1 b/sm1/foo1
+	new file mode 100644
+	index 0000000..1715acd
+	--- /dev/null
+	+++ b/sm1/foo1
+	@@ -0,0 +1 @@
+	+foo1
+	diff --git a/sm1/foo2 b/sm1/foo2
+	new file mode 100644
+	index 0000000..54b060e
+	--- /dev/null
+	+++ b/sm1/foo2
+	@@ -0,0 +1 @@
+	+foo2
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'added submodule, set diff.submodule' '
+	git config diff.submodule log &&
+	git add sm1 &&
+	git diff --cached >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 0000000...$head1 (new submodule)
+	EOF
+	git config --unset diff.submodule &&
+	test_cmp expected actual
+'
+
+test_expect_success '--submodule=short overrides diff.submodule' '
+	test_config diff.submodule log &&
+	git add sm1 &&
+	git diff --submodule=short --cached >actual &&
+	cat >expected <<-EOF &&
+	diff --git a/sm1 b/sm1
+	new file mode 160000
+	index 0000000..$head1
+	--- /dev/null
+	+++ b/sm1
+	@@ -0,0 +1 @@
+	+Subproject commit $fullhead1
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'diff.submodule does not affect plumbing' '
+	test_config diff.submodule log &&
+	git diff-index -p HEAD >actual &&
+	cat >expected <<-EOF &&
+	diff --git a/sm1 b/sm1
+	new file mode 160000
+	index 0000000..$head1
+	--- /dev/null
+	+++ b/sm1
+	@@ -0,0 +1 @@
+	+Subproject commit $fullhead1
+	EOF
+	test_cmp expected actual
+'
+
+commit_file sm1 &&
+head2=$(add_file sm1 foo3)
+
+test_expect_success 'modified submodule(forward)' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 1beffeb..30b9670:
+	diff --git a/sm1/foo3 b/sm1/foo3
+	new file mode 100644
+	index 0000000..c1ec6c6
+	--- /dev/null
+	+++ b/sm1/foo3
+	@@ -0,0 +1 @@
+	+foo3
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule(forward)' '
+	git diff --submodule=diff >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 1beffeb..30b9670:
+	diff --git a/sm1/foo3 b/sm1/foo3
+	new file mode 100644
+	index 0000000..c1ec6c6
+	--- /dev/null
+	+++ b/sm1/foo3
+	@@ -0,0 +1 @@
+	+foo3
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule(forward) --submodule' '
+	git diff --submodule >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 $head1..$head2:
+	  > Add foo3 ($added foo3)
+	EOF
+	test_cmp expected actual
+'
+
+fullhead2=$(cd sm1; git rev-parse --verify HEAD)
+test_expect_success 'modified submodule(forward) --submodule=short' '
+	git diff --submodule=short >actual &&
+	cat >expected <<-EOF &&
+	diff --git a/sm1 b/sm1
+	index $head1..$head2 160000
+	--- a/sm1
+	+++ b/sm1
+	@@ -1 +1 @@
+	-Subproject commit $fullhead1
+	+Subproject commit $fullhead2
+	EOF
+	test_cmp expected actual
+'
+
+commit_file sm1 &&
+head3=$(
+	cd sm1 &&
+	git reset --hard HEAD~2 >/dev/null &&
+	git rev-parse --short --verify HEAD
+)
+
+test_expect_success 'modified submodule(backward)' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 30b9670..dafb207:
+	diff --git a/sm1/foo2 b/sm1/foo2
+	deleted file mode 100644
+	index 54b060e..0000000
+	--- a/sm1/foo2
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo2
+	diff --git a/sm1/foo3 b/sm1/foo3
+	deleted file mode 100644
+	index c1ec6c6..0000000
+	--- a/sm1/foo3
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo3
+	EOF
+	test_cmp expected actual
+'
+
+head4=$(add_file sm1 foo4 foo5)
+test_expect_success 'modified submodule(backward and forward)' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 30b9670..d176589:
+	diff --git a/sm1/foo2 b/sm1/foo2
+	deleted file mode 100644
+	index 54b060e..0000000
+	--- a/sm1/foo2
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo2
+	diff --git a/sm1/foo3 b/sm1/foo3
+	deleted file mode 100644
+	index c1ec6c6..0000000
+	--- a/sm1/foo3
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo3
+	diff --git a/sm1/foo4 b/sm1/foo4
+	new file mode 100644
+	index 0000000..a0016db
+	--- /dev/null
+	+++ b/sm1/foo4
+	@@ -0,0 +1 @@
+	+foo4
+	diff --git a/sm1/foo5 b/sm1/foo5
+	new file mode 100644
+	index 0000000..d6f2413
+	--- /dev/null
+	+++ b/sm1/foo5
+	@@ -0,0 +1 @@
+	+foo5
+	EOF
+	test_cmp expected actual
+'
+
+commit_file sm1 &&
+mv sm1 sm1-bak &&
+echo sm1 >sm1 &&
+head5=$(git hash-object sm1 | cut -c1-7) &&
+git add sm1 &&
+rm -f sm1 &&
+mv sm1-bak sm1
+
+test_expect_success 'typechanged submodule(submodule->blob), --cached' '
+	git diff --submodule=diff --cached >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 d176589..0000000: (submodule deleted)
+	diff --git a/sm1/foo1 b/sm1/foo1
+	deleted file mode 100644
+	index 1715acd..0000000
+	--- a/sm1/foo1
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo1
+	diff --git a/sm1/foo4 b/sm1/foo4
+	deleted file mode 100644
+	index a0016db..0000000
+	--- a/sm1/foo4
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo4
+	diff --git a/sm1/foo5 b/sm1/foo5
+	deleted file mode 100644
+	index d6f2413..0000000
+	--- a/sm1/foo5
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-foo5
+	diff --git a/sm1 b/sm1
+	new file mode 100644
+	index 0000000..9da5fb8
+	--- /dev/null
+	+++ b/sm1
+	@@ -0,0 +1 @@
+	+sm1
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'typechanged submodule(submodule->blob)' '
+	git diff --submodule=diff >actual &&
+	cat >expected <<-EOF &&
+	diff --git a/sm1 b/sm1
+	deleted file mode 100644
+	index 9da5fb8..0000000
+	--- a/sm1
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-sm1
+	Submodule sm1 0000000..d176589: (new submodule)
+	diff --git a/sm1/foo1 b/sm1/foo1
+	new file mode 100644
+	index 0000000..1715acd
+	--- /dev/null
+	+++ b/sm1/foo1
+	@@ -0,0 +1 @@
+	+foo1
+	diff --git a/sm1/foo4 b/sm1/foo4
+	new file mode 100644
+	index 0000000..a0016db
+	--- /dev/null
+	+++ b/sm1/foo4
+	@@ -0,0 +1 @@
+	+foo4
+	diff --git a/sm1/foo5 b/sm1/foo5
+	new file mode 100644
+	index 0000000..d6f2413
+	--- /dev/null
+	+++ b/sm1/foo5
+	@@ -0,0 +1 @@
+	+foo5
+	EOF
+	test_cmp expected actual
+'
+
+rm -rf sm1 &&
+git checkout-index sm1
+test_expect_success 'typechanged submodule(submodule->blob)' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 $head4..0000000: (submodule deleted) (submodule not initialized)
+	diff --git a/sm1 b/sm1
+	new file mode 100644
+	index 0000000..$head5
+	--- /dev/null
+	+++ b/sm1
+	@@ -0,0 +1 @@
+	+sm1
+	EOF
+	test_cmp expected actual
+'
+
+rm -f sm1 &&
+test_create_repo sm1 &&
+head6=$(add_file sm1 foo6 foo7)
+fullhead6=$(cd sm1; git rev-parse --verify HEAD)
+test_expect_success 'nonexistent commit' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 d176589..17243c9:
+	(diff failed)
+	EOF
+	test_cmp expected actual
+'
+
+commit_file
+test_expect_success 'typechanged submodule(blob->submodule)' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	diff --git a/sm1 b/sm1
+	deleted file mode 100644
+	index 9da5fb8..0000000
+	--- a/sm1
+	+++ /dev/null
+	@@ -1 +0,0 @@
+	-sm1
+	Submodule sm1 0000000..17243c9: (new submodule)
+	diff --git a/sm1/foo6 b/sm1/foo6
+	new file mode 100644
+	index 0000000..462398b
+	--- /dev/null
+	+++ b/sm1/foo6
+	@@ -0,0 +1 @@
+	+foo6
+	diff --git a/sm1/foo7 b/sm1/foo7
+	new file mode 100644
+	index 0000000..6e9262c
+	--- /dev/null
+	+++ b/sm1/foo7
+	@@ -0,0 +1 @@
+	+foo7
+	EOF
+	test_cmp expected actual
+'
+
+commit_file sm1 &&
+test_expect_success 'submodule is up to date' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule contains untracked content' '
+	echo new > sm1/new-file &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains untracked content
+	Submodule sm1 17243c9..17243c9:
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule contains untracked content (untracked ignored)' '
+	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'submodule contains untracked content (dirty ignored)' '
+	git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'submodule contains untracked content (all ignored)' '
+	git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'submodule contains untracked and modified content' '
+	echo new > sm1/foo6 &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains untracked content
+	Submodule sm1 contains modified content
+	Submodule sm1 17243c9..17243c9:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+# NOT OK
+test_expect_success 'submodule contains untracked and modified content (untracked ignored)' '
+	echo new > sm1/foo6 &&
+	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains modified content
+	Submodule sm1 17243c9..17243c9:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule contains untracked and modified content (dirty ignored)' '
+	echo new > sm1/foo6 &&
+	git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'submodule contains untracked and modified content (all ignored)' '
+	echo new > sm1/foo6 &&
+	git diff-index -p --ignore-submodules --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'submodule contains modified content' '
+	rm -f sm1/new-file &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains modified content
+	Submodule sm1 17243c9..17243c9:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+(cd sm1; git commit -mchange foo6 >/dev/null) &&
+head8=$(cd sm1; git rev-parse --short --verify HEAD) &&
+test_expect_success 'submodule is modified' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked content' '
+	echo new > sm1/new-file &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains untracked content
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked content (untracked ignored)' '
+	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked content (dirty ignored)' '
+	git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked content (all ignored)' '
+	git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+test_expect_success 'modified submodule contains untracked and modified content' '
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains untracked content
+	Submodule sm1 contains modified content
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..dfda541 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1,2 @@
+	-foo6
+	+new
+	+modification
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked and modified content (untracked ignored)' '
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains modified content
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..e20e2d9 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1,3 @@
+	-foo6
+	+new
+	+modification
+	+modification
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked and modified content (dirty ignored)' '
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..3e75765 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1 @@
+	-foo6
+	+new
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'modified submodule contains untracked and modified content (all ignored)' '
+	echo modification >> sm1/foo6 &&
+	git diff-index -p --ignore-submodules --submodule=diff HEAD >actual &&
+	! test -s actual
+'
+
+# NOT OK
+test_expect_success 'modified submodule contains modified content' '
+	rm -f sm1/new-file &&
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 contains modified content
+	Submodule sm1 17243c9..cfce562:
+	diff --git a/sm1/foo6 b/sm1/foo6
+	index 462398b..ac466ca 100644
+	--- a/sm1/foo6
+	+++ b/sm1/foo6
+	@@ -1 +1,5 @@
+	-foo6
+	+new
+	+modification
+	+modification
+	+modification
+	+modification
+	EOF
+	test_cmp expected actual
+'
+
+rm -rf sm1
+test_expect_success 'deleted submodule' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..0000000: (submodule deleted) (submodule not initialized)
+	EOF
+	test_cmp expected actual
+'
+
+test_create_repo sm2 &&
+head7=$(add_file sm2 foo8 foo9) &&
+git add sm2
+
+test_expect_success 'multiple submodules' '
+	git diff-index -p --submodule=diff HEAD >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..0000000: (submodule deleted) (submodule not initialized)
+	Submodule sm2 0000000..a5a65c9: (new submodule)
+	diff --git a/sm2/foo8 b/sm2/foo8
+	new file mode 100644
+	index 0000000..db9916b
+	--- /dev/null
+	+++ b/sm2/foo8
+	@@ -0,0 +1 @@
+	+foo8
+	diff --git a/sm2/foo9 b/sm2/foo9
+	new file mode 100644
+	index 0000000..9c3b4f6
+	--- /dev/null
+	+++ b/sm2/foo9
+	@@ -0,0 +1 @@
+	+foo9
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'path filter' '
+	git diff-index -p --submodule=diff HEAD sm2 >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm2 0000000..a5a65c9: (new submodule)
+	diff --git a/sm2/foo8 b/sm2/foo8
+	new file mode 100644
+	index 0000000..db9916b
+	--- /dev/null
+	+++ b/sm2/foo8
+	@@ -0,0 +1 @@
+	+foo8
+	diff --git a/sm2/foo9 b/sm2/foo9
+	new file mode 100644
+	index 0000000..9c3b4f6
+	--- /dev/null
+	+++ b/sm2/foo9
+	@@ -0,0 +1 @@
+	+foo9
+	EOF
+	test_cmp expected actual
+'
+
+commit_file sm2
+test_expect_success 'given commit' '
+	git diff-index -p --submodule=diff HEAD^ >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..0000000: (submodule deleted) (submodule not initialized)
+	Submodule sm2 0000000..a5a65c9: (new submodule)
+	diff --git a/sm2/foo8 b/sm2/foo8
+	new file mode 100644
+	index 0000000..db9916b
+	--- /dev/null
+	+++ b/sm2/foo8
+	@@ -0,0 +1 @@
+	+foo8
+	diff --git a/sm2/foo9 b/sm2/foo9
+	new file mode 100644
+	index 0000000..9c3b4f6
+	--- /dev/null
+	+++ b/sm2/foo9
+	@@ -0,0 +1 @@
+	+foo9
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'setup .git file for sm2' '
+	(cd sm2 &&
+	 REAL="$(pwd)/../.real" &&
+	 mv .git "$REAL"
+	 echo "gitdir: $REAL" >.git)
+'
+
+test_expect_success 'diff --submodule=diff with .git file' '
+	git diff --submodule=diff HEAD^ >actual &&
+	cat >expected <<-EOF &&
+	Submodule sm1 17243c9..0000000: (submodule deleted) (submodule not initialized)
+	Submodule sm2 0000000..a5a65c9: (new submodule)
+	diff --git a/sm2/foo8 b/sm2/foo8
+	new file mode 100644
+	index 0000000..db9916b
+	--- /dev/null
+	+++ b/sm2/foo8
+	@@ -0,0 +1 @@
+	+foo8
+	diff --git a/sm2/foo9 b/sm2/foo9
+	new file mode 100644
+	index 0000000..9c3b4f6
+	--- /dev/null
+	+++ b/sm2/foo9
+	@@ -0,0 +1 @@
+	+foo9
+	EOF
+	test_cmp expected actual
+'
+
+test_done
-- 
2.9.2.872.g367ebef.dirty


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-11 22:59 ` [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix Jacob Keller
@ 2016-08-12 21:15   ` Junio C Hamano
  2016-08-12 21:21     ` Jacob Keller
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2016-08-12 21:15 UTC (permalink / raw)
  To: Jacob Keller; +Cc: git, Jacob Keller

Jacob Keller <jacob.e.keller@intel.com> writes:

> The prefix shall come first prior to any other prefix associated with
> the --graph option or other source.
>
> Add tests for the same.
>
> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
> ---
> - v5
> * Changed name to --diff-line-prefix since --line-prefix might indicate
>   for other commands such as log, when it only modifies diff output

As you are adding this to "diff.c", I think the option would be
visible to "git log" anyway, and more importantly

    git log --line-prefix='I I ' --graph --boundary -p HEAD^..HEAD

should honor the line-prefix for the log message part.  I'd expect
that its output would be like:

I I * commit 3c90ffd2f01e2d0d080c8e42df2ee89709b324de
I I | Author: Jacob Keller <jacob.keller@gmail.com>
I I | Date:   Thu Aug 11 15:59:45 2016 -0700
I I | 
I I |     diff: add --diff-line-prefix option for passing in a prefix
I I |     
I I |     Add an option to pass additional prefix to be displayed before diff
I I |     output. This feature will be used in a following patch to output correct
I I |     ...
I I |     Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
I I |     Signed-off-by: Junio C Hamano <gitster@pobox.com>
I I | 
I I | diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
I I | index 705a873..f924f57 100644
I I | --- a/Documentation/diff-options.txt
I I | +++ b/Documentation/diff-options.txt
I I | ...

Otherwise, a "git log --graph -p --submodule=log-with-diff", when
showing a commit in the superproject that changes submodule from
commit A to commit B, wouldn't be able to run "git log --graph -p A..B",
i.e. as if the command recursed beyond the module boundary, would
it?




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-12 21:15   ` Junio C Hamano
@ 2016-08-12 21:21     ` Jacob Keller
  2016-08-12 21:43       ` Jacob Keller
  0 siblings, 1 reply; 10+ messages in thread
From: Jacob Keller @ 2016-08-12 21:21 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jacob Keller, Git mailing list

On Fri, Aug 12, 2016 at 2:15 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Jacob Keller <jacob.e.keller@intel.com> writes:
>
>> The prefix shall come first prior to any other prefix associated with
>> the --graph option or other source.
>>
>> Add tests for the same.
>>
>> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
>> ---
>> - v5
>> * Changed name to --diff-line-prefix since --line-prefix might indicate
>>   for other commands such as log, when it only modifies diff output
>
> As you are adding this to "diff.c", I think the option would be
> visible to "git log" anyway, and more importantly
>
>     git log --line-prefix='I I ' --graph --boundary -p HEAD^..HEAD
>
> should honor the line-prefix for the log message part.  I'd expect
> that its output would be like:
>
> I I * commit 3c90ffd2f01e2d0d080c8e42df2ee89709b324de
> I I | Author: Jacob Keller <jacob.keller@gmail.com>
> I I | Date:   Thu Aug 11 15:59:45 2016 -0700
> I I |
> I I |     diff: add --diff-line-prefix option for passing in a prefix
> I I |
> I I |     Add an option to pass additional prefix to be displayed before diff
> I I |     output. This feature will be used in a following patch to output correct
> I I |     ...
> I I |     Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
> I I |     Signed-off-by: Junio C Hamano <gitster@pobox.com>
> I I |
> I I | diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
> I I | index 705a873..f924f57 100644
> I I | --- a/Documentation/diff-options.txt
> I I | +++ b/Documentation/diff-options.txt
> I I | ...
>
> Otherwise, a "git log --graph -p --submodule=log-with-diff", when
> showing a commit in the superproject that changes submodule from
> commit A to commit B, wouldn't be able to run "git log --graph -p A..B",
> i.e. as if the command recursed beyond the module boundary, would
> it?
>

Hmmmm, yes. I'll dig through and see if I can get the prefix to be
honored by the git log as well.

Thanks,
Jake

>
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-12 21:21     ` Jacob Keller
@ 2016-08-12 21:43       ` Jacob Keller
       [not found]         ` <CAPc5daVmyx+EX8H0yETfO6Vv+A7DqBM5bsqrnJdYzbEhVnA1wQ@mail.gmail.com>
  0 siblings, 1 reply; 10+ messages in thread
From: Jacob Keller @ 2016-08-12 21:43 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jacob Keller, Git mailing list

On Fri, Aug 12, 2016 at 2:21 PM, Jacob Keller <jacob.keller@gmail.com> wrote:
> On Fri, Aug 12, 2016 at 2:15 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Otherwise, a "git log --graph -p --submodule=log-with-diff", when
>> showing a commit in the superproject that changes submodule from
>> commit A to commit B, wouldn't be able to run "git log --graph -p A..B",
>> i.e. as if the command recursed beyond the module boundary, would
>> it?
>>
>
> Hmmmm, yes. I'll dig through and see if I can get the prefix to be
> honored by the git log as well.
>
> Thanks,
> Jake

Ok so the big problem here is that unlike with diff which has support
for line-prefixes already due to needing it for graph mode, I can't
figure out where to get the prefix added for log output. Especially if
we want it to be honored prior to the graph output. This seems like a
much bigger task, which is better saved for future maybe?

Thanks,
Jake

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
       [not found]           ` <CA+P7+xp_sPk6P1qyyDfOgpkXU1GxWPivfSzvveS4PAvGb-=ggQ@mail.gmail.com>
@ 2016-08-14 21:21             ` Junio C Hamano
  2016-08-15  5:32               ` Jacob Keller
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2016-08-14 21:21 UTC (permalink / raw)
  To: Jacob Keller; +Cc: git

Jacob Keller <jacob.keller@gmail.com> writes:

> On Fri, Aug 12, 2016 at 2:58 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> On Fri, Aug 12, 2016 at 2:43 PM, Jacob Keller <jacob.keller@gmail.com> wrote:
>>> Ok so the big problem here is that unlike with diff which has support
>>> for line-prefixes already due to needing it for graph mode, I can't
>>> figure out where to get the prefix added for log output.
>>
>> Doesn't the --graph mode show the graph lines already? I think most
>> if not all of that processing happens inside log-tree.c::show_log().
>
> Yes but the problem is how to get this additional prefix to be displayed for
>
> (a) non graph mode
> (b) for graph mode but display the prefix before the graph prefix
>
> It doesn't seem straight forward, and I haven't figured out how the
> graph code already works.

Because you only need "diff --line-prefix" to work while leaving
"log --line-prefix" broken for the purpose of your immediate purpose
to update "diff/log [--graph and other options] --submodule=<type>"
to show "diff A B" in the submodule, I think it is OK to leave it
broken, as long as it is clearly documented that "--line-prefix"
should also apply to the log message part but the current code
doesn't.  Eventually, when somebody wants to add a new <type> that
runs "log -p A..B" instead of "diff A B" in submodules and wants to
make it interact well with "log --graph" at the superproject level,
they need to fix the breakage you leave behind.  That would be much
better than "because I cannot figure out how to prefix on the log
part, I'll name this --diff-line-prefix", which will force us to
support that half-baked option forever while having to add a proper
"--line-prefix" eventually anyway.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-14 21:21             ` Junio C Hamano
@ 2016-08-15  5:32               ` Jacob Keller
  2016-08-15 15:37                 ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Jacob Keller @ 2016-08-15  5:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git mailing list

On Sun, Aug 14, 2016 at 2:21 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Jacob Keller <jacob.keller@gmail.com> writes:
>
>> On Fri, Aug 12, 2016 at 2:58 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>> On Fri, Aug 12, 2016 at 2:43 PM, Jacob Keller <jacob.keller@gmail.com> wrote:
>>>> Ok so the big problem here is that unlike with diff which has support
>>>> for line-prefixes already due to needing it for graph mode, I can't
>>>> figure out where to get the prefix added for log output.
>>>
>>> Doesn't the --graph mode show the graph lines already? I think most
>>> if not all of that processing happens inside log-tree.c::show_log().
>>
>> Yes but the problem is how to get this additional prefix to be displayed for
>>
>> (a) non graph mode
>> (b) for graph mode but display the prefix before the graph prefix
>>
>> It doesn't seem straight forward, and I haven't figured out how the
>> graph code already works.
>
> Because you only need "diff --line-prefix" to work while leaving
> "log --line-prefix" broken for the purpose of your immediate purpose
> to update "diff/log [--graph and other options] --submodule=<type>"
> to show "diff A B" in the submodule, I think it is OK to leave it
> broken, as long as it is clearly documented that "--line-prefix"
> should also apply to the log message part but the current code
> doesn't.  Eventually, when somebody wants to add a new <type> that
> runs "log -p A..B" instead of "diff A B" in submodules and wants to
> make it interact well with "log --graph" at the superproject level,
> they need to fix the breakage you leave behind.  That would be much
> better than "because I cannot figure out how to prefix on the log
> part, I'll name this --diff-line-prefix", which will force us to
> support that half-baked option forever while having to add a proper
> "--line-prefix" eventually anyway.

I will look more into how to do the log version tomorrow, if I am
still stuck I will re-work the patches as you suggest here.

I am hoping I can find a good solution for how to handle it though.

Thanks,
Jake

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-15  5:32               ` Jacob Keller
@ 2016-08-15 15:37                 ` Junio C Hamano
  2016-08-15 23:09                   ` Jacob Keller
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2016-08-15 15:37 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Git mailing list

Jacob Keller <jacob.keller@gmail.com> writes:

> I will look more into how to do the log version tomorrow, if I am
> still stuck I will re-work the patches as you suggest here.
>
> I am hoping I can find a good solution for how to handle it though.

Thanks, I am hoping the same, too ;-)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix
  2016-08-15 15:37                 ` Junio C Hamano
@ 2016-08-15 23:09                   ` Jacob Keller
  0 siblings, 0 replies; 10+ messages in thread
From: Jacob Keller @ 2016-08-15 23:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git mailing list

On Mon, Aug 15, 2016 at 8:37 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Jacob Keller <jacob.keller@gmail.com> writes:
>
>> I will look more into how to do the log version tomorrow, if I am
>> still stuck I will re-work the patches as you suggest here.
>>
>> I am hoping I can find a good solution for how to handle it though.
>
> Thanks, I am hoping the same, too ;-)

Ok, so I found a way that (ab)uses the graph API to enable line-prefix
support. I moved the way we handle diff prefix into the graph callback
handler as well, and I also moved around some code so that the graph
API will work correctly. I found several places that actually broke if
you tried calling the graph API with a null graph, because they would
still try to access the FILE* pointer inside and could lead to a
segfault. This should fix those up as well.

Hopefully this is along the lines of what you were thinking, and it
should allow more advanced calls to a submodule format that shows the
full log if we wanted.

Thanks,
Jake

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-08-15 23:10 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-11 22:59 [PATCH v5 1/3] diff.c: remove output_prefix_length field Jacob Keller
2016-08-11 22:59 ` [PATCH v5 2/3] diff: add --diff-line-prefix option for passing in a prefix Jacob Keller
2016-08-12 21:15   ` Junio C Hamano
2016-08-12 21:21     ` Jacob Keller
2016-08-12 21:43       ` Jacob Keller
     [not found]         ` <CAPc5daVmyx+EX8H0yETfO6Vv+A7DqBM5bsqrnJdYzbEhVnA1wQ@mail.gmail.com>
     [not found]           ` <CA+P7+xp_sPk6P1qyyDfOgpkXU1GxWPivfSzvveS4PAvGb-=ggQ@mail.gmail.com>
2016-08-14 21:21             ` Junio C Hamano
2016-08-15  5:32               ` Jacob Keller
2016-08-15 15:37                 ` Junio C Hamano
2016-08-15 23:09                   ` Jacob Keller
2016-08-11 22:59 ` [PATCH v5 3/3] diff: add SUBMODULE_DIFF format to display submodule diff Jacob Keller

Code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).