Skip to content

Commit

Permalink
Merge branch 'dl/diff-merge-base'
Browse files Browse the repository at this point in the history
"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
  • Loading branch information
gitster committed Nov 2, 2020
2 parents 761a4e9 + cce7d6e commit b6fb70c
Show file tree
Hide file tree
Showing 11 changed files with 356 additions and 141 deletions.
9 changes: 7 additions & 2 deletions Documentation/git-diff-index.txt
Expand Up @@ -9,7 +9,7 @@ git-diff-index - Compare a tree to the working tree or index
SYNOPSIS
--------
[verse]
'git diff-index' [-m] [--cached] [<common diff options>] <tree-ish> [<path>...]
'git diff-index' [-m] [--cached] [--merge-base] [<common diff options>] <tree-ish> [<path>...]

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

--cached::
do not consider the on-disk file at all
Do not consider the on-disk file at all.

--merge-base::
Instead of comparing <tree-ish> directly, use the merge base
between <tree-ish> and HEAD instead. <tree-ish> must be a
commit.

-m::
By default, files recorded in the index but not checked
Expand Down
7 changes: 6 additions & 1 deletion Documentation/git-diff-tree.txt
Expand Up @@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]
[-t] [-r] [-c | --cc] [--combined-all-paths] [--root]
[-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]
[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]

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

--merge-base::
Instead of comparing the <tree-ish>s directly, use the merge
base between the two <tree-ish>s as the "before" side. There
must be two <tree-ish>s given and they must both be commits.

--stdin::
When `--stdin` is specified, the command does not take
<tree-ish> arguments from the command line. Instead, it
Expand Down
36 changes: 22 additions & 14 deletions Documentation/git-diff.txt
Expand Up @@ -10,8 +10,8 @@ SYNOPSIS
--------
[verse]
'git diff' [<options>] [<commit>] [--] [<path>...]
'git diff' [<options>] --cached [<commit>] [--] [<path>...]
'git diff' [<options>] <commit> [<commit>...] <commit> [--] [<path>...]
'git diff' [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]
'git diff' [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]
'git diff' [<options>] <commit>...<commit> [--] [<path>...]
'git diff' [<options>] <blob> <blob>
'git diff' [<options>] --no-index [--] <path> <path>
Expand Down Expand Up @@ -40,7 +40,7 @@ files on disk.
or when running the command outside a working tree
controlled by Git. This form implies `--exit-code`.

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

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

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

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

'git diff' [<options>] <commit> <commit> [--] [<path>...]::
'git diff' [<options>] [--merge-base] <commit> <commit> [--] [<path>...]::

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

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

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

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

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

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

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

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

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

Comparing with arbitrary commits::
+
Expand Down
10 changes: 6 additions & 4 deletions builtin/diff-index.c
Expand Up @@ -15,7 +15,7 @@ COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_index(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
int cached = 0;
unsigned int option = 0;
int i;
int result;

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

if (!strcmp(arg, "--cached"))
cached = 1;
option |= DIFF_INDEX_CACHED;
else if (!strcmp(arg, "--merge-base"))
option |= DIFF_INDEX_MERGE_BASE;
else
usage(diff_cache_usage);
}
Expand All @@ -46,7 +48,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
if (rev.pending.nr != 1 ||
rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
usage(diff_cache_usage);
if (!cached) {
if (!(option & DIFF_INDEX_CACHED)) {
setup_work_tree();
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
perror("read_cache_preload");
Expand All @@ -56,7 +58,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
perror("read_cache");
return -1;
}
result = run_diff_index(&rev, cached);
result = run_diff_index(&rev, option);
UNLEAK(rev);
return diff_result_code(&rev.diffopt, result);
}
17 changes: 16 additions & 1 deletion builtin/diff-tree.c
Expand Up @@ -111,6 +111,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
struct setup_revision_opt s_r_opt;
struct userformat_want w;
int read_stdin = 0;
int merge_base = 0;

if (argc == 2 && !strcmp(argv[1], "-h"))
usage(diff_tree_usage);
Expand Down Expand Up @@ -143,9 +144,18 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
read_stdin = 1;
continue;
}
if (!strcmp(arg, "--merge-base")) {
merge_base = 1;
continue;
}
usage(diff_tree_usage);
}

if (read_stdin && merge_base)
die(_("--stdin and --merge-base are mutually exclusive"));
if (merge_base && opt->pending.nr != 2)
die(_("--merge-base only works with two commits"));

/*
* NOTE! We expect "a..b" to expand to "^a b" but it is
* perfectly valid for revision range parser to yield "b ^a",
Expand All @@ -165,7 +175,12 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
case 2:
tree1 = opt->pending.objects[0].item;
tree2 = opt->pending.objects[1].item;
if (tree2->flags & UNINTERESTING) {
if (merge_base) {
struct object_id oid;

diff_get_merge_base(opt, &oid);
tree1 = lookup_object(the_repository, &oid);
} else if (tree2->flags & UNINTERESTING) {
SWAP(tree2, tree1);
}
diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
Expand Down
49 changes: 33 additions & 16 deletions builtin/diff.c
Expand Up @@ -26,7 +26,7 @@
static const char builtin_diff_usage[] =
"git diff [<options>] [<commit>] [--] [<path>...]\n"
" or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
" or: git diff [<options>] <commit> [<commit>...] <commit> [--] [<path>...]\n"
" or: git diff [<options>] <commit> [--merge-base] [<commit>...] <commit> [--] [<path>...]\n"
" or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n"
" or: git diff [<options>] <blob> <blob>]\n"
" or: git diff [<options>] --no-index [--] <path> <path>]\n"
Expand Down Expand Up @@ -134,11 +134,13 @@ static int builtin_diff_blobs(struct rev_info *revs,
static int builtin_diff_index(struct rev_info *revs,
int argc, const char **argv)
{
int cached = 0;
unsigned int option = 0;
while (1 < argc) {
const char *arg = argv[1];
if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
cached = 1;
option |= DIFF_INDEX_CACHED;
else if (!strcmp(arg, "--merge-base"))
option |= DIFF_INDEX_MERGE_BASE;
else
usage(builtin_diff_usage);
argv++; argc--;
Expand All @@ -151,7 +153,7 @@ static int builtin_diff_index(struct rev_info *revs,
revs->max_count != -1 || revs->min_age != -1 ||
revs->max_age != -1)
usage(builtin_diff_usage);
if (!cached) {
if (!(option & DIFF_INDEX_CACHED)) {
setup_work_tree();
if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
perror("read_cache_preload");
Expand All @@ -161,7 +163,7 @@ static int builtin_diff_index(struct rev_info *revs,
perror("read_cache");
return -1;
}
return run_diff_index(revs, cached);
return run_diff_index(revs, option);
}

static int builtin_diff_tree(struct rev_info *revs,
Expand All @@ -170,19 +172,34 @@ static int builtin_diff_tree(struct rev_info *revs,
struct object_array_entry *ent1)
{
const struct object_id *(oid[2]);
int swap = 0;
struct object_id mb_oid;
int merge_base = 0;

if (argc > 1)
usage(builtin_diff_usage);
while (1 < argc) {
const char *arg = argv[1];
if (!strcmp(arg, "--merge-base"))
merge_base = 1;
else
usage(builtin_diff_usage);
argv++; argc--;
}

/*
* We saw two trees, ent0 and ent1. If ent1 is uninteresting,
* swap them.
*/
if (ent1->item->flags & UNINTERESTING)
swap = 1;
oid[swap] = &ent0->item->oid;
oid[1 - swap] = &ent1->item->oid;
if (merge_base) {
diff_get_merge_base(revs, &mb_oid);
oid[0] = &mb_oid;
oid[1] = &revs->pending.objects[1].item->oid;
} else {
int swap = 0;

/*
* We saw two trees, ent0 and ent1. If ent1 is uninteresting,
* swap them.
*/
if (ent1->item->flags & UNINTERESTING)
swap = 1;
oid[swap] = &ent0->item->oid;
oid[1 - swap] = &ent1->item->oid;
}
diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
log_tree_diff_flush(revs);
return 0;
Expand Down
15 changes: 6 additions & 9 deletions contrib/completion/git-completion.bash
Expand Up @@ -1698,6 +1698,10 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
--patch --no-patch
"

__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex
--base --ours --theirs --no-index --relative --merge-base
$__git_diff_common_options"

_git_diff ()
{
__git_has_doubledash && return
Expand All @@ -1720,10 +1724,7 @@ _git_diff ()
return
;;
--*)
__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
--base --ours --theirs --no-index
$__git_diff_common_options
"
__gitcomp "$__git_diff_difftool_options"
return
;;
esac
Expand All @@ -1745,11 +1746,7 @@ _git_difftool ()
return
;;
--*)
__gitcomp_builtin difftool "$__git_diff_common_options
--base --cached --ours --theirs
--pickaxe-all --pickaxe-regex
--relative --staged
"
__gitcomp_builtin difftool "$__git_diff_difftool_options"
return
;;
esac
Expand Down

0 comments on commit b6fb70c

Please sign in to comment.