Skip to content

Commit c425d36

Browse files
committed
Merge branch 'en/combined-all-paths'
Output from "diff --cc" did not show the original paths when the merge involved renames. A new option adds the paths in the original trees to the output. * en/combined-all-paths: log,diff-tree: add --combined-all-paths option
2 parents cf0879f + d76ce4f commit c425d36

File tree

10 files changed

+212
-17
lines changed

10 files changed

+212
-17
lines changed

Documentation/diff-format.txt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,26 @@ from the format described above in the following way:
9595
. there are more "src" modes and "src" sha1
9696
. status is concatenated status characters for each parent
9797
. no optional "score" number
98-
. single path, only for "dst"
98+
. tab-separated pathname(s) of the file
9999

100-
Example:
100+
For `-c` and `--cc`, only the destination or final path is shown even
101+
if the file was renamed on any side of history. With
102+
`--combined-all-paths`, the name of the path in each parent is shown
103+
followed by the name of the path in the merge commit.
104+
105+
Examples for `-c` and `--cc` without `--combined-all-paths`:
106+
------------------------------------------------
107+
::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c
108+
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM bar.sh
109+
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR phooey.c
110+
------------------------------------------------
111+
112+
Examples when `--combined-all-paths` added to either `-c` or `--cc`:
101113

102114
------------------------------------------------
103-
::100644 100644 100644 fabadb8 cc95eb0 4866510 MM describe.c
115+
::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c desc.c desc.c
116+
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM foo.sh bar.sh bar.sh
117+
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR fooey.c fuey.c phooey.c
104118
------------------------------------------------
105119

106120
Note that 'combined diff' lists only files which were modified from

Documentation/diff-generate-patch.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,19 @@ copying detection) are designed to work with diff of two
143143
Similar to two-line header for traditional 'unified' diff
144144
format, `/dev/null` is used to signal created or deleted
145145
files.
146+
+
147+
However, if the --combined-all-paths option is provided, instead of a
148+
two-line from-file/to-file you get a N+1 line from-file/to-file header,
149+
where N is the number of parents in the merge commit
150+
151+
--- a/file
152+
--- a/file
153+
--- a/file
154+
+++ b/file
155+
+
156+
This extended format can be useful if rename or copy detection is
157+
active, to allow you to see the original name of the file in different
158+
parents.
146159

147160
4. Chunk header format is modified to prevent people from
148161
accidentally feeding it to `patch -p1`. Combined diff format

Documentation/git-diff-tree.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]
13-
[-t] [-r] [-c | --cc] [--root] [<common diff options>]
14-
<tree-ish> [<tree-ish>] [<path>...]
13+
[-t] [-r] [-c | --cc] [--combined-all-paths] [--root]
14+
[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
1515

1616
DESCRIPTION
1717
-----------
@@ -105,6 +105,13 @@ include::pretty-options.txt[]
105105
itself and the commit log message is not shown, just like in any other
106106
"empty diff" case.
107107

108+
--combined-all-paths::
109+
This flag causes combined diffs (used for merge commits) to
110+
list the name of the file from all parents. It thus only has
111+
effect when -c or --cc are specified, and is likely only
112+
useful if filename changes are detected (i.e. when either
113+
rename or copy detection have been requested).
114+
108115
--always::
109116
Show the commit itself and the commit log message even
110117
if the diff itself is empty.

Documentation/rev-list-options.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,13 @@ options may be given. See linkgit:git-diff-files[1] for more options.
960960
the parents have only two variants and the merge result picks
961961
one of them without modification.
962962

963+
--combined-all-paths::
964+
This flag causes combined diffs (used for merge commits) to
965+
list the name of the file from all parents. It thus only has
966+
effect when -c or --cc are specified, and is likely only
967+
useful if filename changes are detected (i.e. when either
968+
rename or copy detection have been requested).
969+
963970
-m::
964971
This flag makes the merge commits show the full diff like
965972
regular commits; for each merge parent, a separate log entry

builtin/diff-tree.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,13 @@ static int diff_tree_stdin(char *line)
8383
}
8484

8585
static const char diff_tree_usage[] =
86-
"git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
86+
"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
8787
"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
8888
" -r diff recursively\n"
89+
" -c show combined diff for merge commits\n"
90+
" --cc show combined diff for merge commits removing uninteresting hunks\n"
91+
" --combined-all-paths\n"
92+
" show name of file in all parents for combined diffs\n"
8993
" --root include the initial commit as diff against /dev/null\n"
9094
COMMON_DIFF_OPTIONS_HELP;
9195

combine-diff.c

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,20 @@ static int compare_paths(const struct combine_diff_path *one,
2323
two->path, strlen(two->path), two->mode);
2424
}
2525

26-
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
26+
static int filename_changed(char status)
27+
{
28+
return status == 'R' || status == 'C';
29+
}
30+
31+
static struct combine_diff_path *intersect_paths(
32+
struct combine_diff_path *curr,
33+
int n,
34+
int num_parent,
35+
int combined_all_paths)
2736
{
2837
struct diff_queue_struct *q = &diff_queued_diff;
2938
struct combine_diff_path *p, **tail = &curr;
30-
int i, cmp;
39+
int i, j, cmp;
3140

3241
if (!n) {
3342
for (i = 0; i < q->nr; i++) {
@@ -50,6 +59,13 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
5059
oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
5160
p->parent[n].mode = q->queue[i]->one->mode;
5261
p->parent[n].status = q->queue[i]->status;
62+
63+
if (combined_all_paths &&
64+
filename_changed(p->parent[n].status)) {
65+
strbuf_init(&p->parent[n].path, 0);
66+
strbuf_addstr(&p->parent[n].path,
67+
q->queue[i]->one->path);
68+
}
5369
*tail = p;
5470
tail = &p->next;
5571
}
@@ -68,6 +84,10 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
6884
if (cmp < 0) {
6985
/* p->path not in q->queue[]; drop it */
7086
*tail = p->next;
87+
for (j = 0; j < num_parent; j++)
88+
if (combined_all_paths &&
89+
filename_changed(p->parent[j].status))
90+
strbuf_release(&p->parent[j].path);
7191
free(p);
7292
continue;
7393
}
@@ -81,6 +101,10 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
81101
oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
82102
p->parent[n].mode = q->queue[i]->one->mode;
83103
p->parent[n].status = q->queue[i]->status;
104+
if (combined_all_paths &&
105+
filename_changed(p->parent[n].status))
106+
strbuf_addstr(&p->parent[n].path,
107+
q->queue[i]->one->path);
84108

85109
tail = &p->next;
86110
i++;
@@ -960,12 +984,25 @@ static void show_combined_header(struct combine_diff_path *elem,
960984
if (!show_file_header)
961985
return;
962986

963-
if (added)
964-
dump_quoted_path("--- ", "", "/dev/null",
965-
line_prefix, c_meta, c_reset);
966-
else
967-
dump_quoted_path("--- ", a_prefix, elem->path,
968-
line_prefix, c_meta, c_reset);
987+
if (rev->combined_all_paths) {
988+
for (i = 0; i < num_parent; i++) {
989+
char *path = filename_changed(elem->parent[i].status)
990+
? elem->parent[i].path.buf : elem->path;
991+
if (elem->parent[i].status == DIFF_STATUS_ADDED)
992+
dump_quoted_path("--- ", "", "/dev/null",
993+
line_prefix, c_meta, c_reset);
994+
else
995+
dump_quoted_path("--- ", a_prefix, path,
996+
line_prefix, c_meta, c_reset);
997+
}
998+
} else {
999+
if (added)
1000+
dump_quoted_path("--- ", "", "/dev/null",
1001+
line_prefix, c_meta, c_reset);
1002+
else
1003+
dump_quoted_path("--- ", a_prefix, elem->path,
1004+
line_prefix, c_meta, c_reset);
1005+
}
9691006
if (deleted)
9701007
dump_quoted_path("+++ ", "", "/dev/null",
9711008
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
12271264
putchar(inter_name_termination);
12281265
}
12291266

1267+
for (i = 0; i < num_parent; i++)
1268+
if (rev->combined_all_paths) {
1269+
if (filename_changed(p->parent[i].status))
1270+
write_name_quoted(p->parent[i].path.buf, stdout,
1271+
inter_name_termination);
1272+
else
1273+
write_name_quoted(p->path, stdout,
1274+
inter_name_termination);
1275+
}
12301276
write_name_quoted(p->path, stdout, line_termination);
12311277
}
12321278

@@ -1332,7 +1378,9 @@ static const char *path_path(void *obj)
13321378

13331379
/* find set of paths that every parent touches */
13341380
static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
1335-
const struct oid_array *parents, struct diff_options *opt)
1381+
const struct oid_array *parents,
1382+
struct diff_options *opt,
1383+
int combined_all_paths)
13361384
{
13371385
struct combine_diff_path *paths = NULL;
13381386
int i, num_parent = parents->nr;
@@ -1357,7 +1405,8 @@ static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
13571405
opt->output_format = DIFF_FORMAT_NO_OUTPUT;
13581406
diff_tree_oid(&parents->oid[i], oid, "", opt);
13591407
diffcore_std(opt);
1360-
paths = intersect_paths(paths, i, num_parent);
1408+
paths = intersect_paths(paths, i, num_parent,
1409+
combined_all_paths);
13611410

13621411
/* if showing diff, show it in requested order */
13631412
if (opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
@@ -1467,7 +1516,8 @@ void diff_tree_combined(const struct object_id *oid,
14671516
* diff(sha1,parent_i) for all i to do the job, specifically
14681517
* for parent0.
14691518
*/
1470-
paths = find_paths_generic(oid, parents, &diffopts);
1519+
paths = find_paths_generic(oid, parents, &diffopts,
1520+
rev->combined_all_paths);
14711521
}
14721522
else {
14731523
int stat_opt;
@@ -1540,6 +1590,10 @@ void diff_tree_combined(const struct object_id *oid,
15401590
while (paths) {
15411591
struct combine_diff_path *tmp = paths;
15421592
paths = paths->next;
1593+
for (i = 0; i < num_parent; i++)
1594+
if (rev->combined_all_paths &&
1595+
filename_changed(tmp->parent[i].status))
1596+
strbuf_release(&tmp->parent[i].path);
15431597
free(tmp);
15441598
}
15451599

diff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ struct combine_diff_path {
296296
char status;
297297
unsigned int mode;
298298
struct object_id oid;
299+
struct strbuf path;
299300
} parent[FLEX_ARRAY];
300301
};
301302
#define combine_diff_path_size(n, l) \

revision.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
21512151
revs->diff = 1;
21522152
revs->dense_combined_merges = 0;
21532153
revs->combine_merges = 1;
2154+
} else if (!strcmp(arg, "--combined-all-paths")) {
2155+
revs->diff = 1;
2156+
revs->combined_all_paths = 1;
21542157
} else if (!strcmp(arg, "--cc")) {
21552158
revs->diff = 1;
21562159
revs->dense_combined_merges = 1;
@@ -2647,6 +2650,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
26472650
}
26482651
if (revs->combine_merges)
26492652
revs->ignore_merges = 0;
2653+
if (revs->combined_all_paths && !revs->combine_merges)
2654+
die("--combined-all-paths makes no sense without -c or --cc");
2655+
26502656
revs->diffopt.abbrev = revs->abbrev;
26512657

26522658
if (revs->line_level_traverse) {

revision.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ struct rev_info {
172172
verbose_header:1,
173173
ignore_merges:1,
174174
combine_merges:1,
175+
combined_all_paths:1,
175176
dense_combined_merges:1,
176177
always_show_header:1;
177178

t/t4038-diff-combined.sh

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,4 +435,92 @@ test_expect_success 'combine diff gets tree sorting right' '
435435
test_cmp expect actual
436436
'
437437

438+
test_expect_success 'setup for --combined-all-paths' '
439+
git branch side1c &&
440+
git branch side2c &&
441+
git checkout side1c &&
442+
test_seq 1 10 >filename-side1c &&
443+
git add filename-side1c &&
444+
git commit -m with &&
445+
git checkout side2c &&
446+
test_seq 1 9 >filename-side2c &&
447+
echo ten >>filename-side2c &&
448+
git add filename-side2c &&
449+
git commit -m iam &&
450+
git checkout -b mergery side1c &&
451+
git merge --no-commit side2c &&
452+
git rm filename-side1c &&
453+
echo eleven >>filename-side2c &&
454+
git mv filename-side2c filename-merged &&
455+
git add filename-merged &&
456+
git commit
457+
'
458+
459+
test_expect_success '--combined-all-paths and --raw' '
460+
cat <<-\EOF >expect &&
461+
::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR filename-side1c filename-side2c filename-merged
462+
EOF
463+
git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp &&
464+
sed 1d <actual.tmp >actual &&
465+
test_cmp expect actual
466+
'
467+
468+
test_expect_success '--combined-all-paths and --cc' '
469+
cat <<-\EOF >expect &&
470+
--- a/filename-side1c
471+
--- a/filename-side2c
472+
+++ b/filename-merged
473+
EOF
474+
git diff-tree --cc -M --combined-all-paths HEAD >actual.tmp &&
475+
grep ^[-+][-+][-+] <actual.tmp >actual &&
476+
test_cmp expect actual
477+
'
478+
479+
test_expect_success FUNNYNAMES 'setup for --combined-all-paths with funny names' '
480+
git branch side1d &&
481+
git branch side2d &&
482+
git checkout side1d &&
483+
test_seq 1 10 >$(printf "file\twith\ttabs") &&
484+
git add file* &&
485+
git commit -m with &&
486+
git checkout side2d &&
487+
test_seq 1 9 >$(printf "i\tam\ttabbed") &&
488+
echo ten >>$(printf "i\tam\ttabbed") &&
489+
git add *tabbed &&
490+
git commit -m iam &&
491+
git checkout -b funny-names-mergery side1d &&
492+
git merge --no-commit side2d &&
493+
git rm *tabs &&
494+
echo eleven >>$(printf "i\tam\ttabbed") &&
495+
git mv "$(printf "i\tam\ttabbed")" "$(printf "fickle\tnaming")" &&
496+
git add fickle* &&
497+
git commit
498+
'
499+
500+
test_expect_success FUNNYNAMES '--combined-all-paths and --raw and funny names' '
501+
cat <<-\EOF >expect &&
502+
::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR "file\twith\ttabs" "i\tam\ttabbed" "fickle\tnaming"
503+
EOF
504+
git diff-tree -c -M --raw --combined-all-paths HEAD >actual.tmp &&
505+
sed 1d <actual.tmp >actual &&
506+
test_cmp expect actual
507+
'
508+
509+
test_expect_success FUNNYNAMES '--combined-all-paths and --raw -and -z and funny names' '
510+
printf "aaf8087c3cbd4db8e185a2d074cf27c53cfb75d7\0::100644 100644 100644 f00c965d8307308469e537302baa73048488f162 088bd5d92c2a8e0203ca8e7e4c2a5c692f6ae3f7 333b9c62519f285e1854830ade0fe1ef1d40ee1b RR\0file\twith\ttabs\0i\tam\ttabbed\0fickle\tnaming\0" >expect &&
511+
git diff-tree -c -M --raw --combined-all-paths -z HEAD >actual &&
512+
test_cmp -a expect actual
513+
'
514+
515+
test_expect_success FUNNYNAMES '--combined-all-paths and --cc and funny names' '
516+
cat <<-\EOF >expect &&
517+
--- "a/file\twith\ttabs"
518+
--- "a/i\tam\ttabbed"
519+
+++ "b/fickle\tnaming"
520+
EOF
521+
git diff-tree --cc -M --combined-all-paths HEAD >actual.tmp &&
522+
grep ^[-+][-+][-+] <actual.tmp >actual &&
523+
test_cmp expect actual
524+
'
525+
438526
test_done

0 commit comments

Comments
 (0)