summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/diff-format.txt20
-rw-r--r--Documentation/diff-generate-patch.txt13
-rw-r--r--Documentation/git-diff-tree.txt11
-rw-r--r--Documentation/rev-list-options.txt7
-rw-r--r--builtin/diff-tree.c6
-rw-r--r--combine-diff.c76
-rw-r--r--diff.h1
-rw-r--r--revision.c6
-rw-r--r--revision.h1
-rwxr-xr-xt/t4038-diff-combined.sh88
10 files changed, 212 insertions, 17 deletions
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index cdcc17f0ad..4d846d7346 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -95,12 +95,26 @@ from the format described above in the following way:
. there are more "src" modes and "src" sha1
. status is concatenated status characters for each parent
. no optional "score" number
-. single path, only for "dst"
+. tab-separated pathname(s) of the file
-Example:
+For `-c` and `--cc`, only the destination or final path is shown even
+if the file was renamed on any side of history. With
+`--combined-all-paths`, the name of the path in each parent is shown
+followed by the name of the path in the merge commit.
+
+Examples for `-c` and `--cc` without `--combined-all-paths`:
+------------------------------------------------
+::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c
+::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM bar.sh
+::100644 100644 100644 e07d6c5 9042e82 ee91881 RR phooey.c
+------------------------------------------------
+
+Examples when `--combined-all-paths` added to either `-c` or `--cc`:
------------------------------------------------
-::100644 100644 100644 fabadb8 cc95eb0 4866510 MM describe.c
+::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c desc.c desc.c
+::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM foo.sh bar.sh bar.sh
+::100644 100644 100644 e07d6c5 9042e82 ee91881 RR fooey.c fuey.c phooey.c
------------------------------------------------
Note that 'combined diff' lists only files which were modified from
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index 231105cff4..f10ca410ad 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -143,6 +143,19 @@ copying detection) are designed to work with diff of two
Similar to two-line header for traditional 'unified' diff
format, `/dev/null` is used to signal created or deleted
files.
++
+However, if the --combined-all-paths option is provided, instead of a
+two-line from-file/to-file you get a N+1 line from-file/to-file header,
+where N is the number of parents in the merge commit
+
+ --- a/file
+ --- a/file
+ --- a/file
+ +++ b/file
++
+This extended format can be useful if rename or copy detection is
+active, to allow you to see the original name of the file in different
+parents.
4. Chunk header format is modified to prevent people from
accidentally feeding it to `patch -p1`. Combined diff format
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 43daa7c046..24f32e8c54 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -10,8 +10,8 @@ SYNOPSIS
--------
[verse]
'git diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]
- [-t] [-r] [-c | --cc] [--root] [<common diff options>]
- <tree-ish> [<tree-ish>] [<path>...]
+ [-t] [-r] [-c | --cc] [--combined-all-paths] [--root]
+ [<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
DESCRIPTION
-----------
@@ -105,6 +105,13 @@ include::pretty-options.txt[]
itself and the commit log message is not shown, just like in any other
"empty diff" case.
+--combined-all-paths::
+ This flag causes combined diffs (used for merge commits) to
+ list the name of the file from all parents. It thus only has
+ effect when -c or --cc are specified, and is likely only
+ useful if filename changes are detected (i.e. when either
+ rename or copy detection have been requested).
+
--always::
Show the commit itself and the commit log message even
if the diff itself is empty.
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index cad711ce0a..ca959a7286 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -960,6 +960,13 @@ options may be given. See linkgit:git-diff-files[1] for more options.
the parents have only two variants and the merge result picks
one of them without modification.
+--combined-all-paths::
+ This flag causes combined diffs (used for merge commits) to
+ list the name of the file from all parents. It thus only has
+ effect when -c or --cc are specified, and is likely only
+ useful if filename changes are detected (i.e. when either
+ rename or copy detection have been requested).
+
-m::
This flag makes the merge commits show the full diff like
regular commits; for each merge parent, a separate log entry
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a90681bcba..cb9ea79367 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -83,9 +83,13 @@ static int diff_tree_stdin(char *line)
}
static const char diff_tree_usage[] =
-"git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
+"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
" -r diff recursively\n"
+" -c show combined diff for merge commits\n"
+" --cc show combined diff for merge commits removing uninteresting hunks\n"
+" --combined-all-paths\n"
+" show name of file in all parents for combined diffs\n"
" --root include the initial commit as diff against /dev/null\n"
COMMON_DIFF_OPTIONS_HELP;
diff --git a/combine-diff.c b/combine-diff.c
index 23d8fabe75..3e49f3bda8 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -23,11 +23,20 @@ static int compare_paths(const struct combine_diff_path *one,
two->path, strlen(two->path), two->mode);
}
-static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
+static int filename_changed(char status)
+{
+ return status == 'R' || status == 'C';
+}
+
+static struct combine_diff_path *intersect_paths(
+ struct combine_diff_path *curr,
+ int n,
+ int num_parent,
+ int combined_all_paths)
{
struct diff_queue_struct *q = &diff_queued_diff;
struct combine_diff_path *p, **tail = &curr;
- int i, cmp;
+ int i, j, cmp;
if (!n) {
for (i = 0; i < q->nr; i++) {
@@ -50,6 +59,13 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
p->parent[n].mode = q->queue[i]->one->mode;
p->parent[n].status = q->queue[i]->status;
+
+ if (combined_all_paths &&
+ filename_changed(p->parent[n].status)) {
+ strbuf_init(&p->parent[n].path, 0);
+ strbuf_addstr(&p->parent[n].path,
+ q->queue[i]->one->path);
+ }
*tail = p;
tail = &p->next;
}
@@ -68,6 +84,10 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
if (cmp < 0) {
/* p->path not in q->queue[]; drop it */
*tail = p->next;
+ for (j = 0; j < num_parent; j++)
+ if (combined_all_paths &&
+ filename_changed(p->parent[j].status))
+ strbuf_release(&p->parent[j].path);
free(p);
continue;
}
@@ -81,6 +101,10 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
p->parent[n].mode = q->queue[i]->one->mode;
p->parent[n].status = q->queue[i]->status;
+ if (combined_all_paths &&
+ filename_changed(p->parent[n].status))
+ strbuf_addstr(&p->parent[n].path,
+ q->queue[i]->one->path);
tail = &p->next;
i++;
@@ -960,12 +984,25 @@ static void show_combined_header(struct combine_diff_path *elem,
if (!show_file_header)
return;
- if (added)
- dump_quoted_path("--- ", "", "/dev/null",
- line_prefix, c_meta, c_reset);
- else
- dump_quoted_path("--- ", a_prefix, elem->path,
- line_prefix, c_meta, c_reset);
+ if (rev->combined_all_paths) {
+ for (i = 0; i < num_parent; i++) {
+ char *path = filename_changed(elem->parent[i].status)
+ ? elem->parent[i].path.buf : elem->path;
+ if (elem->parent[i].status == DIFF_STATUS_ADDED)
+ dump_quoted_path("--- ", "", "/dev/null",
+ line_prefix, c_meta, c_reset);
+ else
+ dump_quoted_path("--- ", a_prefix, path,
+ line_prefix, c_meta, c_reset);
+ }
+ } else {
+ if (added)
+ dump_quoted_path("--- ", "", "/dev/null",
+ line_prefix, c_meta, c_reset);
+ else
+ dump_quoted_path("--- ", a_prefix, elem->path,
+ line_prefix, c_meta, c_reset);
+ }
if (deleted)
dump_quoted_path("+++ ", "", "/dev/null",
line_prefix, c_meta, c_reset);
@@ -1227,6 +1264,15 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
putchar(inter_name_termination);
}
+ for (i = 0; i < num_parent; i++)
+ if (rev->combined_all_paths) {
+ if (filename_changed(p->parent[i].status))
+ write_name_quoted(p->parent[i].path.buf, stdout,
+ inter_name_termination);
+ else
+ write_name_quoted(p->path, stdout,
+ inter_name_termination);
+ }
write_name_quoted(p->path, stdout, line_termination);
}
@@ -1332,7 +1378,9 @@ static const char *path_path(void *obj)
/* find set of paths that every parent touches */
static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
- const struct oid_array *parents, struct diff_options *opt)
+ const struct oid_array *parents,
+ struct diff_options *opt,
+ int combined_all_paths)
{
struct combine_diff_path *paths = NULL;
int i, num_parent = parents->nr;
@@ -1357,7 +1405,8 @@ static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
opt->output_format = DIFF_FORMAT_NO_OUTPUT;
diff_tree_oid(&parents->oid[i], oid, "", opt);
diffcore_std(opt);
- paths = intersect_paths(paths, i, num_parent);
+ paths = intersect_paths(paths, i, num_parent,
+ combined_all_paths);
/* if showing diff, show it in requested order */
if (opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
@@ -1467,7 +1516,8 @@ void diff_tree_combined(const struct object_id *oid,
* diff(sha1,parent_i) for all i to do the job, specifically
* for parent0.
*/
- paths = find_paths_generic(oid, parents, &diffopts);
+ paths = find_paths_generic(oid, parents, &diffopts,
+ rev->combined_all_paths);
}
else {
int stat_opt;
@@ -1540,6 +1590,10 @@ void diff_tree_combined(const struct object_id *oid,
while (paths) {
struct combine_diff_path *tmp = paths;
paths = paths->next;
+ for (i = 0; i < num_parent; i++)
+ if (rev->combined_all_paths &&
+ filename_changed(tmp->parent[i].status))
+ strbuf_release(&tmp->parent[i].path);
free(tmp);
}
diff --git a/diff.h b/diff.h
index d9ad73f0e1..3199ba31f8 100644
--- a/diff.h
+++ b/diff.h
@@ -296,6 +296,7 @@ struct combine_diff_path {
char status;
unsigned int mode;
struct object_id oid;
+ struct strbuf path;
} parent[FLEX_ARRAY];
};
#define combine_diff_path_size(n, l) \
diff --git a/revision.c b/revision.c
index 162d511d46..eb8e51bc63 100644
--- a/revision.c
+++ b/revision.c
@@ -2151,6 +2151,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->diff = 1;
revs->dense_combined_merges = 0;
revs->combine_merges = 1;
+ } else if (!strcmp(arg, "--combined-all-paths")) {
+ revs->diff = 1;
+ revs->combined_all_paths = 1;
} else if (!strcmp(arg, "--cc")) {
revs->diff = 1;
revs->dense_combined_merges = 1;
@@ -2647,6 +2650,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
}
if (revs->combine_merges)
revs->ignore_merges = 0;
+ if (revs->combined_all_paths && !revs->combine_merges)
+ die("--combined-all-paths makes no sense without -c or --cc");
+
revs->diffopt.abbrev = revs->abbrev;
if (revs->line_level_traverse) {
diff --git a/revision.h b/revision.h
index d32d62abc6..4134dc6029 100644
--- a/revision.h
+++ b/revision.h
@@ -172,6 +172,7 @@ struct rev_info {
verbose_header:1,
ignore_merges:1,
combine_merges:1,
+ combined_all_paths:1,
dense_combined_merges:1,
always_show_header:1;
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index e2824d3437..07b49f6d6d 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -435,4 +435,92 @@ test_expect_success 'combine diff gets tree sorting right' '
test_cmp expect actual
'
+test_expect_success 'setup for --combined-all-paths' '
+ git branch side1c &&
+ git branch side2c &&
+ git checkout side1c &&
+ test_seq 1 10 >filename-side1c &&
+ git add filename-side1c &&
+ git commit -m with &&
+ git checkout side2c &&
+ test_seq 1 9 >filename-side2c &&
+ echo ten >>filename-side2c &&
+ git add filename-side2c &&
+ git commit -m iam &&
+ git checkout -b mergery side1c &&
+ git merge --no-commit side2c &&
+ git rm filename-side1c &&
+ echo eleven >>filename-side2c &&
+ git mv filename-side2c filename-merged &&
+ git add filename-merged &&
+ git commit
+'
+
+test_expect_success '--combined-all-paths and --raw' '
+ cat <<-\EOF >expect &&
+ ::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR filename-side1c filename-side2c filename-merged
+ EOF
+ git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp &&
+ sed 1d <actual.tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--combined-all-paths and --cc' '
+ cat <<-\EOF >expect &&
+ --- a/filename-side1c
+ --- a/filename-side2c
+ +++ b/filename-merged
+ EOF
+ git diff-tree --cc -M --combined-all-paths HEAD >actual.tmp &&
+ grep ^[-+][-+][-+] <actual.tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES 'setup for --combined-all-paths with funny names' '
+ git branch side1d &&
+ git branch side2d &&
+ git checkout side1d &&
+ test_seq 1 10 >$(printf "file\twith\ttabs") &&
+ git add file* &&
+ git commit -m with &&
+ git checkout side2d &&
+ test_seq 1 9 >$(printf "i\tam\ttabbed") &&
+ echo ten >>$(printf "i\tam\ttabbed") &&
+ git add *tabbed &&
+ git commit -m iam &&
+ git checkout -b funny-names-mergery side1d &&
+ git merge --no-commit side2d &&
+ git rm *tabs &&
+ echo eleven >>$(printf "i\tam\ttabbed") &&
+ git mv "$(printf "i\tam\ttabbed")" "$(printf "fickle\tnaming")" &&
+ git add fickle* &&
+ git commit
+'
+
+test_expect_success FUNNYNAMES '--combined-all-paths and --raw and funny names' '
+ cat <<-\EOF >expect &&
+ ::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR "file\twith\ttabs" "i\tam\ttabbed" "fickle\tnaming"
+ EOF
+ git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp &&
+ sed 1d <actual.tmp >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--combined-all-paths and --raw -and -z and funny names' '
+ printf "aaf8087c3cbd4db8e185a2d074cf27c53cfb75d7\0::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR\0file\twith\ttabs\0i\tam\ttabbed\0fickle\tnaming\0" >expect &&
+ git diff-tree -c -M --raw --combined-all-paths -z HEAD >actual &&
+ test_cmp -a expect actual
+'
+
+test_expect_success FUNNYNAMES '--combined-all-paths and --cc and funny names' '
+ cat <<-\EOF >expect &&
+ --- "a/file\twith\ttabs"
+ --- "a/i\tam\ttabbed"
+ +++ "b/fickle\tnaming"
+ EOF
+ git diff-tree --cc -M --combined-all-paths HEAD >actual.tmp &&
+ grep ^[-+][-+][-+] <actual.tmp >actual &&
+ test_cmp expect actual
+'
+
test_done