blame: dwim "blame --reverse OLD" as "blame --reverse OLD.."

Instead of always requiring both ends of a range, we could DWIM "OLD", which could be a misspelt "OLD..", to be a range that ends at the current commit. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jun 14, 2016 at 11:41 UTC e1d09701a4fa3a4d15dd005e2b698934e4ae9ae0
2 files changed +41 -2
Documentation/blame-options.txt
+3 -2
@@ -28,12 +28,13 @@ include::line-range-format.txt[]
28 -S <revs-file>::
29 Use revisions from revs-file instead of calling linkgit:git-rev-list[1].
30
31 ---reverse::
31 +--reverse <rev>..<rev>::
32 Walk history forward instead of backward. Instead of showing
33 the revision in which a line appeared, this shows the last
34 revision in which a line has existed. This requires a range of
35 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.
38
39 -p::
40 --porcelain::
builtin/blame.c
+38
@@ -2447,6 +2447,41 @@ static char *prepare_final(struct scoreboard *sb)
2447 return xstrdup_or_null(name);
2448 }
2449
2450 +static const char *dwim_reverse_initial(struct scoreboard *sb)
2451 +{
2452 + /*
2453 + * DWIM "git blame --reverse ONE -- PATH" as
2454 + * "git blame --reverse ONE..HEAD -- PATH" but only do so
2455 + * when it makes sense.
2456 + */
2457 + struct object *obj;
2458 + struct commit *head_commit;
2459 + unsigned char head_sha1[20];
2460 +
2461 + if (sb->revs->pending.nr != 1)
2462 + return NULL;
2463 +
2464 + /* Is that sole rev a committish? */
2465 + obj = sb->revs->pending.objects[0].item;
2466 + obj = deref_tag(obj, NULL, 0);
2467 + if (obj->type != OBJ_COMMIT)
2468 + return NULL;
2469 +
2470 + /* Do we have HEAD? */
2471 + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
2472 + return NULL;
2473 + head_commit = lookup_commit_reference_gently(head_sha1, 1);
2474 + if (!head_commit)
2475 + return NULL;
2476 +
2477 + /* Turn "ONE" into "ONE..HEAD" then */
2478 + obj->flags |= UNINTERESTING;
2479 + add_pending_object(sb->revs, &head_commit->object, "HEAD");
2480 +
2481 + sb->final = (struct commit *)obj;
2482 + return sb->revs->pending.objects[0].name;
2483 +}
2484 +
2485 static char *prepare_initial(struct scoreboard *sb)
2486 {
2487 int i;
@@ -2472,6 +2507,9 @@ static char *prepare_initial(struct scoreboard *sb)
2507 sb->final = (struct commit *) obj;
2508 final_commit_name = revs->pending.objects[i].name;
2509 }
2510 +
2511 + if (!final_commit_name)
2512 + final_commit_name = dwim_reverse_initial(sb);
2513 if (!final_commit_name)
2514 die("No commit to dig up from?");
2515 return xstrdup(final_commit_name);