Skip to content

Commit 797d443

Browse files
committed
Merge branch 'pb/log-first-parent-p-m'
* pb/log-first-parent-p-m: show --first-parent/-m: do not default to --cc show -c: show patch text revision: introduce setup_revision_opt t4013: add tests for log -p -m --first-parent git log -p -m: document -m and honor --first-parent
2 parents 954f7cf + 2bf6587 commit 797d443

17 files changed

+653
-30
lines changed

Documentation/diff-generate-patch.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ combined diff format
5656

5757
"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or
5858
'--cc' option to produce 'combined diff'. For showing a merge commit
59-
with "git log -p", this is the default format.
59+
with "git log -p", this is the default format; you can force showing
60+
full diff with the '-m' option.
6061
A 'combined diff' format looks like this:
6162

6263
------------

Documentation/git-log.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ git log master --not --remotes=*/master::
118118
Shows all commits that are in local master but not in any remote
119119
repository master branches.
120120

121+
git log -p -m --first-parent::
122+
123+
Shows the history including change diffs, but only from the
124+
"main branch" perspective, skipping commits that come from merged
125+
branches, and showing full diffs of changes introduced by the merges.
126+
This makes sense only when following a strict policy of merging all
127+
topic branches when staying on a single integration branch.
128+
129+
121130
Discussion
122131
----------
123132

Documentation/rev-list-options.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ options may be given. See linkgit:git-diff-files[1] for more options.
108108

109109
-c::
110110

111-
This flag changes the way a merge commit is displayed. It shows
112-
the differences from each of the parents to the merge result
111+
With this option, diff output for a merge commit
112+
shows the differences from each of the parents to the merge result
113113
simultaneously instead of showing pairwise diff between a parent
114114
and the result one at a time. Furthermore, it lists only files
115115
which were modified from all parents.
@@ -121,6 +121,15 @@ options may be given. See linkgit:git-diff-files[1] for more options.
121121
the parents have only two variants and the merge result picks
122122
one of them without modification.
123123

124+
-m::
125+
126+
This flag makes the merge commits show the full diff like
127+
regular commits; for each merge parent, a separate log entry
128+
and diff is generated. An exception is that only diff against
129+
the first parent is shown when '--first-parent' option is given;
130+
in that case, the output represents the changes the merge
131+
brought _into_ the then-current branch.
132+
124133
-r::
125134

126135
Show recursive diffs.

builtin/diff-tree.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,33 @@ static const char diff_tree_usage[] =
9292
" --root include the initial commit as diff against /dev/null\n"
9393
COMMON_DIFF_OPTIONS_HELP;
9494

95+
static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
96+
{
97+
if (!rev->diffopt.output_format) {
98+
if (rev->dense_combined_merges)
99+
rev->diffopt.output_format = DIFF_FORMAT_PATCH;
100+
else
101+
rev->diffopt.output_format = DIFF_FORMAT_RAW;
102+
}
103+
}
104+
95105
int cmd_diff_tree(int argc, const char **argv, const char *prefix)
96106
{
97107
int nr_sha1;
98108
char line[1000];
99109
struct object *tree1, *tree2;
100110
static struct rev_info *opt = &log_tree_opt;
111+
struct setup_revision_opt s_r_opt;
101112
int read_stdin = 0;
102113

103114
init_revisions(opt, prefix);
104115
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
105116
opt->abbrev = 0;
106117
opt->diff = 1;
107118
opt->disable_stdin = 1;
108-
argc = setup_revisions(argc, argv, opt, NULL);
119+
memset(&s_r_opt, 0, sizeof(s_r_opt));
120+
s_r_opt.tweak = diff_tree_tweak_rev;
121+
argc = setup_revisions(argc, argv, opt, &s_r_opt);
109122

110123
while (--argc > 0) {
111124
const char *arg = *++argv;
@@ -117,9 +130,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
117130
usage(diff_tree_usage);
118131
}
119132

120-
if (!opt->diffopt.output_format)
121-
opt->diffopt.output_format = DIFF_FORMAT_RAW;
122-
123133
/*
124134
* NOTE! We expect "a ^b" to be equal to "a..b", so we
125135
* reverse the order of the objects if the second one

builtin/log.c

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static const char * const builtin_log_usage =
3232
" or: git show [options] <object>...";
3333

3434
static void cmd_log_init(int argc, const char **argv, const char *prefix,
35-
struct rev_info *rev)
35+
struct rev_info *rev, struct setup_revision_opt *opt)
3636
{
3737
int i;
3838
int decoration_style = 0;
@@ -56,7 +56,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
5656
*/
5757
if (argc == 2 && !strcmp(argv[1], "-h"))
5858
usage(builtin_log_usage);
59-
argc = setup_revisions(argc, argv, rev, "HEAD");
59+
argc = setup_revisions(argc, argv, rev, opt);
6060

6161
if (!rev->show_notes_given && !rev->pretty_given)
6262
rev->show_notes = 1;
@@ -262,6 +262,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
262262
int cmd_whatchanged(int argc, const char **argv, const char *prefix)
263263
{
264264
struct rev_info rev;
265+
struct setup_revision_opt opt;
265266

266267
git_config(git_log_config, NULL);
267268

@@ -271,7 +272,9 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
271272
init_revisions(&rev, prefix);
272273
rev.diff = 1;
273274
rev.simplify_history = 0;
274-
cmd_log_init(argc, argv, prefix, &rev);
275+
memset(&opt, 0, sizeof(opt));
276+
opt.def = "HEAD";
277+
cmd_log_init(argc, argv, prefix, &rev, &opt);
275278
if (!rev.diffopt.output_format)
276279
rev.diffopt.output_format = DIFF_FORMAT_RAW;
277280
return cmd_log_walk(&rev);
@@ -324,10 +327,26 @@ static int show_tree_object(const unsigned char *sha1,
324327
return 0;
325328
}
326329

330+
static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
331+
{
332+
if (rev->ignore_merges) {
333+
/* There was no "-m" on the command line */
334+
rev->ignore_merges = 0;
335+
if (!rev->first_parent_only && !rev->combine_merges) {
336+
/* No "--first-parent", "-c", nor "--cc" */
337+
rev->combine_merges = 1;
338+
rev->dense_combined_merges = 1;
339+
}
340+
}
341+
if (!rev->diffopt.output_format)
342+
rev->diffopt.output_format = DIFF_FORMAT_PATCH;
343+
}
344+
327345
int cmd_show(int argc, const char **argv, const char *prefix)
328346
{
329347
struct rev_info rev;
330348
struct object_array_entry *objects;
349+
struct setup_revision_opt opt;
331350
int i, count, ret = 0;
332351

333352
git_config(git_log_config, NULL);
@@ -337,12 +356,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
337356

338357
init_revisions(&rev, prefix);
339358
rev.diff = 1;
340-
rev.combine_merges = 1;
341-
rev.dense_combined_merges = 1;
342359
rev.always_show_header = 1;
343-
rev.ignore_merges = 0;
344360
rev.no_walk = 1;
345-
cmd_log_init(argc, argv, prefix, &rev);
361+
memset(&opt, 0, sizeof(opt));
362+
opt.def = "HEAD";
363+
opt.tweak = show_rev_tweak_rev;
364+
cmd_log_init(argc, argv, prefix, &rev, &opt);
346365

347366
count = rev.pending.nr;
348367
objects = rev.pending.objects;
@@ -405,6 +424,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
405424
int cmd_log_reflog(int argc, const char **argv, const char *prefix)
406425
{
407426
struct rev_info rev;
427+
struct setup_revision_opt opt;
408428

409429
git_config(git_log_config, NULL);
410430

@@ -415,7 +435,9 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
415435
init_reflog_walk(&rev.reflog_info);
416436
rev.abbrev_commit = 1;
417437
rev.verbose_header = 1;
418-
cmd_log_init(argc, argv, prefix, &rev);
438+
memset(&opt, 0, sizeof(opt));
439+
opt.def = "HEAD";
440+
cmd_log_init(argc, argv, prefix, &rev, &opt);
419441

420442
/*
421443
* This means that we override whatever commit format the user gave
@@ -438,6 +460,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
438460
int cmd_log(int argc, const char **argv, const char *prefix)
439461
{
440462
struct rev_info rev;
463+
struct setup_revision_opt opt;
441464

442465
git_config(git_log_config, NULL);
443466

@@ -446,7 +469,9 @@ int cmd_log(int argc, const char **argv, const char *prefix)
446469

447470
init_revisions(&rev, prefix);
448471
rev.always_show_header = 1;
449-
cmd_log_init(argc, argv, prefix, &rev);
472+
memset(&opt, 0, sizeof(opt));
473+
opt.def = "HEAD";
474+
cmd_log_init(argc, argv, prefix, &rev, &opt);
450475
return cmd_log_walk(&rev);
451476
}
452477

@@ -902,6 +927,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
902927
struct commit *commit;
903928
struct commit **list = NULL;
904929
struct rev_info rev;
930+
struct setup_revision_opt s_r_opt;
905931
int nr = 0, total, i;
906932
int use_stdout = 0;
907933
int start_number = -1;
@@ -983,8 +1009,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
9831009
rev.combine_merges = 0;
9841010
rev.ignore_merges = 1;
9851011
DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
986-
9871012
rev.subject_prefix = fmt_patch_subject_prefix;
1013+
memset(&s_r_opt, 0, sizeof(s_r_opt));
1014+
s_r_opt.def = "HEAD";
9881015

9891016
if (default_attach) {
9901017
rev.mime_boundary = default_attach;
@@ -1056,7 +1083,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
10561083
if (keep_subject && subject_prefix)
10571084
die ("--subject-prefix and -k are mutually exclusive.");
10581085

1059-
argc = setup_revisions(argc, argv, &rev, "HEAD");
1086+
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
10601087
if (argc > 1)
10611088
die ("unrecognized argument: %s", argv[1]);
10621089

diff-lib.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,12 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
510510
int index_differs_from(const char *def, int diff_flags)
511511
{
512512
struct rev_info rev;
513+
struct setup_revision_opt opt;
513514

514515
init_revisions(&rev, NULL);
515-
setup_revisions(0, NULL, &rev, def);
516+
memset(&opt, 0, sizeof(opt));
517+
opt.def = def;
518+
setup_revisions(0, NULL, &rev, &opt);
516519
DIFF_OPT_SET(&rev.diffopt, QUICK);
517520
DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
518521
rev.diffopt.flags |= diff_flags;

log-tree.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,16 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
514514
return 0;
515515
else if (opt->combine_merges)
516516
return do_diff_combined(opt, commit);
517+
else if (opt->first_parent_only) {
518+
/*
519+
* Generate merge log entry only for the first
520+
* parent, showing summary diff of the others
521+
* we merged _in_.
522+
*/
523+
diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
524+
log_tree_diff_flush(opt);
525+
return !opt->loginfo;
526+
}
517527

518528
/* If we show individual diffs, show the parent info */
519529
log->parent = parents->item;

revision.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,7 @@ static void append_prune_data(const char ***prune_data, const char **av)
13321332
* Returns the number of arguments left that weren't recognized
13331333
* (which are also moved to the head of the argument list)
13341334
*/
1335-
int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
1335+
int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
13361336
{
13371337
int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
13381338
const char **prune_data = NULL;
@@ -1468,7 +1468,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
14681468
revs->prune_data = get_pathspec(revs->prefix, prune_data);
14691469

14701470
if (revs->def == NULL)
1471-
revs->def = def;
1471+
revs->def = opt ? opt->def : NULL;
1472+
if (opt && opt->tweak)
1473+
opt->tweak(revs, opt);
14721474
if (revs->show_merge)
14731475
prepare_show_merge(revs);
14741476
if (revs->def && !revs->pending.nr && !got_rev_arg) {
@@ -1502,11 +1504,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
15021504
if (!revs->full_diff)
15031505
diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
15041506
}
1505-
if (revs->combine_merges) {
1507+
if (revs->combine_merges)
15061508
revs->ignore_merges = 0;
1507-
if (revs->dense_combined_merges && !revs->diffopt.output_format)
1508-
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
1509-
}
15101509
revs->diffopt.abbrev = revs->abbrev;
15111510
if (diff_setup_done(&revs->diffopt) < 0)
15121511
die("diff_setup_done failed");

revision.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,13 @@ struct rev_info {
137137
typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
138138
extern volatile show_early_output_fn_t show_early_output;
139139

140+
struct setup_revision_opt {
141+
const char *def;
142+
void (*tweak)(struct rev_info *, struct setup_revision_opt *);
143+
};
144+
140145
extern void init_revisions(struct rev_info *revs, const char *prefix);
141-
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
146+
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *);
142147
extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
143148
const struct option *options,
144149
const char * const usagestr[]);

t/t4013-diff-various.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ log --root --patch-with-stat --summary master
204204
log --root -c --patch-with-stat --summary master
205205
# improved by Timo's patch
206206
log --root --cc --patch-with-stat --summary master
207+
log -p --first-parent master
208+
log -m -p --first-parent master
209+
log -m -p master
207210
log -SF master
208211
log -SF -p master
209212
log --decorate --all
@@ -235,6 +238,9 @@ show initial
235238
show --root initial
236239
show side
237240
show master
241+
show -c master
242+
show -m master
243+
show --first-parent master
238244
show --stat side
239245
show --stat --summary side
240246
show --patch-with-stat side

0 commit comments

Comments
 (0)