Skip to content

Commit b6fb70c

Browse files
committed
Merge branch 'dl/diff-merge-base'
"git diff A...B" learned "git diff --merge-base A B", which is a longer short-hand to say the same thing. * dl/diff-merge-base: contrib/completion: complete `git diff --merge-base` builtin/diff-tree: learn --merge-base builtin/diff-index: learn --merge-base t4068: add --merge-base tests diff-lib: define diff_get_merge_base() diff-lib: accept option flags in run_diff_index() contrib/completion: extract common diff/difftool options git-diff.txt: backtick quote command text git-diff-index.txt: make --cached description a proper sentence t4068: remove unnecessary >tmp
2 parents 761a4e9 + cce7d6e commit b6fb70c

File tree

11 files changed

+356
-141
lines changed

11 files changed

+356
-141
lines changed

Documentation/git-diff-index.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-diff-index - Compare a tree to the working tree or index
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git diff-index' [-m] [--cached] [<common diff options>] <tree-ish> [<path>...]
12+
'git diff-index' [-m] [--cached] [--merge-base] [<common diff options>] <tree-ish> [<path>...]
1313

1414
DESCRIPTION
1515
-----------
@@ -27,7 +27,12 @@ include::diff-options.txt[]
2727
The id of a tree object to diff against.
2828

2929
--cached::
30-
do not consider the on-disk file at all
30+
Do not consider the on-disk file at all.
31+
32+
--merge-base::
33+
Instead of comparing <tree-ish> directly, use the merge base
34+
between <tree-ish> and HEAD instead. <tree-ish> must be a
35+
commit.
3136

3237
-m::
3338
By default, files recorded in the index but not checked

Documentation/git-diff-tree.txt

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

1616
DESCRIPTION
@@ -43,6 +43,11 @@ include::diff-options.txt[]
4343
When `--root` is specified the initial commit will be shown as a big
4444
creation event. This is equivalent to a diff against the NULL tree.
4545

46+
--merge-base::
47+
Instead of comparing the <tree-ish>s directly, use the merge
48+
base between the two <tree-ish>s as the "before" side. There
49+
must be two <tree-ish>s given and they must both be commits.
50+
4651
--stdin::
4752
When `--stdin` is specified, the command does not take
4853
<tree-ish> arguments from the command line. Instead, it

Documentation/git-diff.txt

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git diff' [<options>] [<commit>] [--] [<path>...]
13-
'git diff' [<options>] --cached [<commit>] [--] [<path>...]
14-
'git diff' [<options>] <commit> [<commit>...] <commit> [--] [<path>...]
13+
'git diff' [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]
14+
'git diff' [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]
1515
'git diff' [<options>] <commit>...<commit> [--] [<path>...]
1616
'git diff' [<options>] <blob> <blob>
1717
'git diff' [<options>] --no-index [--] <path> <path>
@@ -40,7 +40,7 @@ files on disk.
4040
or when running the command outside a working tree
4141
controlled by Git. This form implies `--exit-code`.
4242

43-
'git diff' [<options>] --cached [<commit>] [--] [<path>...]::
43+
'git diff' [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]::
4444

4545
This form is to view the changes you staged for the next
4646
commit relative to the named <commit>. Typically you
@@ -49,6 +49,10 @@ files on disk.
4949
If HEAD does not exist (e.g. unborn branches) and
5050
<commit> is not given, it shows all staged changes.
5151
--staged is a synonym of --cached.
52+
+
53+
If --merge-base is given, instead of using <commit>, use the merge base
54+
of <commit> and HEAD. `git diff --merge-base A` is equivalent to
55+
`git diff $(git merge-base A HEAD)`.
5256

5357
'git diff' [<options>] <commit> [--] [<path>...]::
5458

@@ -58,23 +62,27 @@ files on disk.
5862
branch name to compare with the tip of a different
5963
branch.
6064

61-
'git diff' [<options>] <commit> <commit> [--] [<path>...]::
65+
'git diff' [<options>] [--merge-base] <commit> <commit> [--] [<path>...]::
6266

6367
This is to view the changes between two arbitrary
6468
<commit>.
69+
+
70+
If --merge-base is given, use the merge base of the two commits for the
71+
"before" side. `git diff --merge-base A B` is equivalent to
72+
`git diff $(git merge-base A B) B`.
6573

6674
'git diff' [<options>] <commit> <commit>... <commit> [--] [<path>...]::
6775

6876
This form is to view the results of a merge commit. The first
6977
listed <commit> must be the merge itself; the remaining two or
7078
more commits should be its parents. A convenient way to produce
71-
the desired set of revisions is to use the {caret}@ suffix.
79+
the desired set of revisions is to use the `^@` suffix.
7280
For instance, if `master` names a merge commit, `git diff master
7381
master^@` gives the same combined diff as `git show master`.
7482

7583
'git diff' [<options>] <commit>..<commit> [--] [<path>...]::
7684

77-
This is synonymous to the earlier form (without the "..") for
85+
This is synonymous to the earlier form (without the `..`) for
7886
viewing the changes between two arbitrary <commit>. If <commit> on
7987
one side is omitted, it will have the same effect as
8088
using HEAD instead.
@@ -83,20 +91,20 @@ files on disk.
8391

8492
This form is to view the changes on the branch containing
8593
and up to the second <commit>, starting at a common ancestor
86-
of both <commit>. "git diff A\...B" is equivalent to
87-
"git diff $(git merge-base A B) B". You can omit any one
94+
of both <commit>. `git diff A...B` is equivalent to
95+
`git diff $(git merge-base A B) B`. You can omit any one
8896
of <commit>, which has the same effect as using HEAD instead.
8997

9098
Just in case you are doing something exotic, it should be
9199
noted that all of the <commit> in the above description, except
92-
in the last two forms that use ".." notations, can be any
93-
<tree>.
100+
in the `--merge-base` case and in the last two forms that use `..`
101+
notations, can be any <tree>.
94102

95103
For a more complete list of ways to spell <commit>, see
96104
"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
97105
However, "diff" is about comparing two _endpoints_, not ranges,
98-
and the range notations ("<commit>..<commit>" and
99-
"<commit>\...<commit>") do not mean a range as defined in the
106+
and the range notations (`<commit>..<commit>` and
107+
`<commit>...<commit>`) do not mean a range as defined in the
100108
"SPECIFYING RANGES" section in linkgit:gitrevisions[7].
101109

102110
'git diff' [<options>] <blob> <blob>::
@@ -144,9 +152,9 @@ $ git diff HEAD <3>
144152
+
145153
<1> Changes in the working tree not yet staged for the next commit.
146154
<2> Changes between the index and your last commit; what you
147-
would be committing if you run "git commit" without "-a" option.
155+
would be committing if you run `git commit` without `-a` option.
148156
<3> Changes in the working tree since your last commit; what you
149-
would be committing if you run "git commit -a"
157+
would be committing if you run `git commit -a`
150158

151159
Comparing with arbitrary commits::
152160
+

builtin/diff-index.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ COMMON_DIFF_OPTIONS_HELP;
1515
int cmd_diff_index(int argc, const char **argv, const char *prefix)
1616
{
1717
struct rev_info rev;
18-
int cached = 0;
18+
unsigned int option = 0;
1919
int i;
2020
int result;
2121

@@ -32,7 +32,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
3232
const char *arg = argv[i];
3333

3434
if (!strcmp(arg, "--cached"))
35-
cached = 1;
35+
option |= DIFF_INDEX_CACHED;
36+
else if (!strcmp(arg, "--merge-base"))
37+
option |= DIFF_INDEX_MERGE_BASE;
3638
else
3739
usage(diff_cache_usage);
3840
}
@@ -46,7 +48,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
4648
if (rev.pending.nr != 1 ||
4749
rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
4850
usage(diff_cache_usage);
49-
if (!cached) {
51+
if (!(option & DIFF_INDEX_CACHED)) {
5052
setup_work_tree();
5153
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
5254
perror("read_cache_preload");
@@ -56,7 +58,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
5658
perror("read_cache");
5759
return -1;
5860
}
59-
result = run_diff_index(&rev, cached);
61+
result = run_diff_index(&rev, option);
6062
UNLEAK(rev);
6163
return diff_result_code(&rev.diffopt, result);
6264
}

builtin/diff-tree.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
111111
struct setup_revision_opt s_r_opt;
112112
struct userformat_want w;
113113
int read_stdin = 0;
114+
int merge_base = 0;
114115

115116
if (argc == 2 && !strcmp(argv[1], "-h"))
116117
usage(diff_tree_usage);
@@ -143,9 +144,18 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
143144
read_stdin = 1;
144145
continue;
145146
}
147+
if (!strcmp(arg, "--merge-base")) {
148+
merge_base = 1;
149+
continue;
150+
}
146151
usage(diff_tree_usage);
147152
}
148153

154+
if (read_stdin && merge_base)
155+
die(_("--stdin and --merge-base are mutually exclusive"));
156+
if (merge_base && opt->pending.nr != 2)
157+
die(_("--merge-base only works with two commits"));
158+
149159
/*
150160
* NOTE! We expect "a..b" to expand to "^a b" but it is
151161
* perfectly valid for revision range parser to yield "b ^a",
@@ -165,7 +175,12 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
165175
case 2:
166176
tree1 = opt->pending.objects[0].item;
167177
tree2 = opt->pending.objects[1].item;
168-
if (tree2->flags & UNINTERESTING) {
178+
if (merge_base) {
179+
struct object_id oid;
180+
181+
diff_get_merge_base(opt, &oid);
182+
tree1 = lookup_object(the_repository, &oid);
183+
} else if (tree2->flags & UNINTERESTING) {
169184
SWAP(tree2, tree1);
170185
}
171186
diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);

builtin/diff.c

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
static const char builtin_diff_usage[] =
2727
"git diff [<options>] [<commit>] [--] [<path>...]\n"
2828
" or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
29-
" or: git diff [<options>] <commit> [<commit>...] <commit> [--] [<path>...]\n"
29+
" or: git diff [<options>] <commit> [--merge-base] [<commit>...] <commit> [--] [<path>...]\n"
3030
" or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n"
3131
" or: git diff [<options>] <blob> <blob>]\n"
3232
" or: git diff [<options>] --no-index [--] <path> <path>]\n"
@@ -134,11 +134,13 @@ static int builtin_diff_blobs(struct rev_info *revs,
134134
static int builtin_diff_index(struct rev_info *revs,
135135
int argc, const char **argv)
136136
{
137-
int cached = 0;
137+
unsigned int option = 0;
138138
while (1 < argc) {
139139
const char *arg = argv[1];
140140
if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
141-
cached = 1;
141+
option |= DIFF_INDEX_CACHED;
142+
else if (!strcmp(arg, "--merge-base"))
143+
option |= DIFF_INDEX_MERGE_BASE;
142144
else
143145
usage(builtin_diff_usage);
144146
argv++; argc--;
@@ -151,7 +153,7 @@ static int builtin_diff_index(struct rev_info *revs,
151153
revs->max_count != -1 || revs->min_age != -1 ||
152154
revs->max_age != -1)
153155
usage(builtin_diff_usage);
154-
if (!cached) {
156+
if (!(option & DIFF_INDEX_CACHED)) {
155157
setup_work_tree();
156158
if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
157159
perror("read_cache_preload");
@@ -161,7 +163,7 @@ static int builtin_diff_index(struct rev_info *revs,
161163
perror("read_cache");
162164
return -1;
163165
}
164-
return run_diff_index(revs, cached);
166+
return run_diff_index(revs, option);
165167
}
166168

167169
static int builtin_diff_tree(struct rev_info *revs,
@@ -170,19 +172,34 @@ static int builtin_diff_tree(struct rev_info *revs,
170172
struct object_array_entry *ent1)
171173
{
172174
const struct object_id *(oid[2]);
173-
int swap = 0;
175+
struct object_id mb_oid;
176+
int merge_base = 0;
174177

175-
if (argc > 1)
176-
usage(builtin_diff_usage);
178+
while (1 < argc) {
179+
const char *arg = argv[1];
180+
if (!strcmp(arg, "--merge-base"))
181+
merge_base = 1;
182+
else
183+
usage(builtin_diff_usage);
184+
argv++; argc--;
185+
}
177186

178-
/*
179-
* We saw two trees, ent0 and ent1. If ent1 is uninteresting,
180-
* swap them.
181-
*/
182-
if (ent1->item->flags & UNINTERESTING)
183-
swap = 1;
184-
oid[swap] = &ent0->item->oid;
185-
oid[1 - swap] = &ent1->item->oid;
187+
if (merge_base) {
188+
diff_get_merge_base(revs, &mb_oid);
189+
oid[0] = &mb_oid;
190+
oid[1] = &revs->pending.objects[1].item->oid;
191+
} else {
192+
int swap = 0;
193+
194+
/*
195+
* We saw two trees, ent0 and ent1. If ent1 is uninteresting,
196+
* swap them.
197+
*/
198+
if (ent1->item->flags & UNINTERESTING)
199+
swap = 1;
200+
oid[swap] = &ent0->item->oid;
201+
oid[1 - swap] = &ent1->item->oid;
202+
}
186203
diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
187204
log_tree_diff_flush(revs);
188205
return 0;

contrib/completion/git-completion.bash

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,10 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
16981698
--patch --no-patch
16991699
"
17001700

1701+
__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex
1702+
--base --ours --theirs --no-index --relative --merge-base
1703+
$__git_diff_common_options"
1704+
17011705
_git_diff ()
17021706
{
17031707
__git_has_doubledash && return
@@ -1720,10 +1724,7 @@ _git_diff ()
17201724
return
17211725
;;
17221726
--*)
1723-
__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
1724-
--base --ours --theirs --no-index
1725-
$__git_diff_common_options
1726-
"
1727+
__gitcomp "$__git_diff_difftool_options"
17271728
return
17281729
;;
17291730
esac
@@ -1745,11 +1746,7 @@ _git_difftool ()
17451746
return
17461747
;;
17471748
--*)
1748-
__gitcomp_builtin difftool "$__git_diff_common_options
1749-
--base --cached --ours --theirs
1750-
--pickaxe-all --pickaxe-regex
1751-
--relative --staged
1752-
"
1749+
__gitcomp_builtin difftool "$__git_diff_difftool_options"
17531750
return
17541751
;;
17551752
esac

0 commit comments

Comments
 (0)