Merge branch 'jc/blame-reverse'

It is a common mistake to say "git blame --reverse OLD path",
expecting that the command line is dwimmed as if asking how lines
in path in an old revision OLD have survived up to the current
commit.

* jc/blame-reverse:
  blame: dwim "blame --reverse OLD" as "blame --reverse OLD.."
  blame: improve diagnosis for "--reverse NEW"
This commit is contained in:
Junio C Hamano 2016-10-10 14:03:51 -07:00
commit 1172e16af0
3 changed files with 44 additions and 5 deletions

View File

@ -28,12 +28,13 @@ include::line-range-format.txt[]
-S <revs-file>:: -S <revs-file>::
Use revisions from revs-file instead of calling linkgit:git-rev-list[1]. Use revisions from revs-file instead of calling linkgit:git-rev-list[1].
--reverse:: --reverse <rev>..<rev>::
Walk history forward instead of backward. Instead of showing Walk history forward instead of backward. Instead of showing
the revision in which a line appeared, this shows the last the revision in which a line appeared, this shows the last
revision in which a line has existed. This requires a range of revision in which a line has existed. This requires a range of
revision like START..END where the path to blame exists in revision like START..END where the path to blame exists in
START. START. `git blame --reverse START` is taken as `git blame
--reverse START..HEAD` for convenience.
-p:: -p::
--porcelain:: --porcelain::

View File

@ -10,7 +10,7 @@ SYNOPSIS
[verse] [verse]
'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
[--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]
[--] <file> [--] <file>
DESCRIPTION DESCRIPTION

View File

@ -2456,6 +2456,41 @@ static char *prepare_final(struct scoreboard *sb)
return xstrdup_or_null(name); return xstrdup_or_null(name);
} }
static const char *dwim_reverse_initial(struct scoreboard *sb)
{
/*
* DWIM "git blame --reverse ONE -- PATH" as
* "git blame --reverse ONE..HEAD -- PATH" but only do so
* when it makes sense.
*/
struct object *obj;
struct commit *head_commit;
unsigned char head_sha1[20];
if (sb->revs->pending.nr != 1)
return NULL;
/* Is that sole rev a committish? */
obj = sb->revs->pending.objects[0].item;
obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
return NULL;
/* Do we have HEAD? */
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
return NULL;
head_commit = lookup_commit_reference_gently(head_sha1, 1);
if (!head_commit)
return NULL;
/* Turn "ONE" into "ONE..HEAD" then */
obj->flags |= UNINTERESTING;
add_pending_object(sb->revs, &head_commit->object, "HEAD");
sb->final = (struct commit *)obj;
return sb->revs->pending.objects[0].name;
}
static char *prepare_initial(struct scoreboard *sb) static char *prepare_initial(struct scoreboard *sb)
{ {
int i; int i;
@ -2474,14 +2509,17 @@ static char *prepare_initial(struct scoreboard *sb)
if (obj->type != OBJ_COMMIT) if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name); die("Non commit %s?", revs->pending.objects[i].name);
if (sb->final) if (sb->final)
die("More than one commit to dig down to %s and %s?", die("More than one commit to dig up from, %s and %s?",
revs->pending.objects[i].name, revs->pending.objects[i].name,
final_commit_name); final_commit_name);
sb->final = (struct commit *) obj; sb->final = (struct commit *) obj;
final_commit_name = revs->pending.objects[i].name; final_commit_name = revs->pending.objects[i].name;
} }
if (!final_commit_name) if (!final_commit_name)
die("No commit to dig down to?"); final_commit_name = dwim_reverse_initial(sb);
if (!final_commit_name)
die("No commit to dig up from?");
return xstrdup(final_commit_name); return xstrdup(final_commit_name);
} }