Skip to content

Commit 1172e16

Browse files
committed
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"
2 parents a460ea4 + e1d0970 commit 1172e16

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

Documentation/blame-options.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ include::line-range-format.txt[]
2828
-S <revs-file>::
2929
Use revisions from revs-file instead of calling linkgit:git-rev-list[1].
3030

31-
--reverse::
31+
--reverse <rev>..<rev>::
3232
Walk history forward instead of backward. Instead of showing
3333
the revision in which a line appeared, this shows the last
3434
revision in which a line has existed. This requires a range of
3535
revision like START..END where the path to blame exists in
36-
START.
36+
START. `git blame --reverse START` is taken as `git blame
37+
--reverse START..HEAD` for convenience.
3738

3839
-p::
3940
--porcelain::

Documentation/git-blame.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ SYNOPSIS
1010
[verse]
1111
'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
1212
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
13-
[--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>]
13+
[--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]
1414
[--] <file>
1515

1616
DESCRIPTION

builtin/blame.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,6 +2456,41 @@ static char *prepare_final(struct scoreboard *sb)
24562456
return xstrdup_or_null(name);
24572457
}
24582458

2459+
static const char *dwim_reverse_initial(struct scoreboard *sb)
2460+
{
2461+
/*
2462+
* DWIM "git blame --reverse ONE -- PATH" as
2463+
* "git blame --reverse ONE..HEAD -- PATH" but only do so
2464+
* when it makes sense.
2465+
*/
2466+
struct object *obj;
2467+
struct commit *head_commit;
2468+
unsigned char head_sha1[20];
2469+
2470+
if (sb->revs->pending.nr != 1)
2471+
return NULL;
2472+
2473+
/* Is that sole rev a committish? */
2474+
obj = sb->revs->pending.objects[0].item;
2475+
obj = deref_tag(obj, NULL, 0);
2476+
if (obj->type != OBJ_COMMIT)
2477+
return NULL;
2478+
2479+
/* Do we have HEAD? */
2480+
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
2481+
return NULL;
2482+
head_commit = lookup_commit_reference_gently(head_sha1, 1);
2483+
if (!head_commit)
2484+
return NULL;
2485+
2486+
/* Turn "ONE" into "ONE..HEAD" then */
2487+
obj->flags |= UNINTERESTING;
2488+
add_pending_object(sb->revs, &head_commit->object, "HEAD");
2489+
2490+
sb->final = (struct commit *)obj;
2491+
return sb->revs->pending.objects[0].name;
2492+
}
2493+
24592494
static char *prepare_initial(struct scoreboard *sb)
24602495
{
24612496
int i;
@@ -2474,14 +2509,17 @@ static char *prepare_initial(struct scoreboard *sb)
24742509
if (obj->type != OBJ_COMMIT)
24752510
die("Non commit %s?", revs->pending.objects[i].name);
24762511
if (sb->final)
2477-
die("More than one commit to dig down to %s and %s?",
2512+
die("More than one commit to dig up from, %s and %s?",
24782513
revs->pending.objects[i].name,
24792514
final_commit_name);
24802515
sb->final = (struct commit *) obj;
24812516
final_commit_name = revs->pending.objects[i].name;
24822517
}
2518+
2519+
if (!final_commit_name)
2520+
final_commit_name = dwim_reverse_initial(sb);
24832521
if (!final_commit_name)
2484-
die("No commit to dig down to?");
2522+
die("No commit to dig up from?");
24852523
return xstrdup(final_commit_name);
24862524
}
24872525

0 commit comments

Comments
 (0)