Skip to content

Commit

Permalink
Merge branch 'jk/blame-contents-with-arbitrary-commit'
Browse files Browse the repository at this point in the history
"git blame --contents=<file> <rev> -- <path>" used to be forbidden,
but now it finds the origins of lines starting at <file> contents
through the history that leads to <rev>.

* jk/blame-contents-with-arbitrary-commit:
  blame: allow --contents to work with non-HEAD commit
  • Loading branch information
gitster committed Apr 4, 2023
2 parents 6dd9d96 + 1a3119e commit 62df03c
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 20 deletions.
10 changes: 5 additions & 5 deletions Documentation/blame-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ include::line-range-format.txt[]
manual page.

--contents <file>::
When <rev> is not specified, the command annotates the
changes starting backwards from the working tree copy.
This flag makes the command pretend as if the working
tree copy has the contents of the named file (specify
`-` to make the command read from the standard input).
Pretend the file being annotated has a commit with the
contents from the named file and a parent of <rev>,
defaulting to HEAD when no <rev> is specified. You may
specify '-' to make the command read from the standard
input for the file contents.

--date <format>::
Specifies the format used to output dates. If --date is not
Expand Down
2 changes: 1 addition & 1 deletion Documentation/git-blame.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ SYNOPSIS
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
[--ignore-rev <rev>] [--ignore-revs-file <file>]
[--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
[<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
[ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>

DESCRIPTION
-----------
Expand Down
40 changes: 26 additions & 14 deletions blame.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,12 @@ static void set_commit_buffer_from_strbuf(struct repository *r,
static struct commit *fake_working_tree_commit(struct repository *r,
struct diff_options *opt,
const char *path,
const char *contents_from)
const char *contents_from,
struct object_id *oid)
{
struct commit *commit;
struct blame_origin *origin;
struct commit_list **parent_tail, *parent;
struct object_id head_oid;
struct strbuf buf = STRBUF_INIT;
const char *ident;
time_t now;
Expand All @@ -198,10 +198,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
commit->date = now;
parent_tail = &commit->parents;

if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
die("no such ref: HEAD");

parent_tail = append_parent(r, parent_tail, &head_oid);
parent_tail = append_parent(r, parent_tail, oid);
append_merge_parents(r, parent_tail);
verify_working_tree_path(r, commit, path);

Expand Down Expand Up @@ -2772,22 +2769,37 @@ void setup_scoreboard(struct blame_scoreboard *sb,
sb->commits.compare = compare_commits_by_reverse_commit_date;
}

if (sb->final && sb->contents_from)
die(_("cannot use --contents with final commit object name"));

if (sb->reverse && sb->revs->first_parent_only)
sb->revs->children.name = NULL;

if (!sb->final) {
if (sb->contents_from || !sb->final) {
struct object_id head_oid, *parent_oid;

/*
* "--not A B -- path" without anything positive;
* do not default to HEAD, but use the working tree
* or "--contents".
* Build a fake commit at the top of the history, when
* (1) "git blame [^A] --path", i.e. with no positive end
* of the history range, in which case we build such
* a fake commit on top of the HEAD to blame in-tree
* modifications.
* (2) "git blame --contents=file [A] -- path", with or
* without positive end of the history range but with
* --contents, in which case we pretend that there is
* a fake commit on top of the positive end (defaulting to
* HEAD) that has the given contents in the path.
*/
if (sb->final) {
parent_oid = &sb->final->object.oid;
} else {
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
die("no such ref: HEAD");
parent_oid = &head_oid;
}

setup_work_tree();
sb->final = fake_working_tree_commit(sb->repo,
&sb->revs->diffopt,
sb->path, sb->contents_from);
sb->path, sb->contents_from,
parent_oid);
add_pending_object(sb->revs, &(sb->final->object), ":");
}

Expand Down
14 changes: 14 additions & 0 deletions t/annotate-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ test_expect_success 'blame 1 author' '
check_count A 2
'

test_expect_success 'blame with --contents' '
check_count --contents=file A 2
'

test_expect_success 'blame with --contents changed' '
echo "1A quick brown fox jumps over the" >contents &&
echo "another lazy dog" >>contents &&
check_count --contents=contents A 1 "Not Committed Yet" 1
'

test_expect_success 'blame in a bare repo without starting commit' '
git clone --bare . bare.git &&
(
Expand All @@ -98,6 +108,10 @@ test_expect_success 'blame 2 authors' '
check_count A 2 B 2
'

test_expect_success 'blame with --contents and revision' '
check_count -h testTag --contents=file A 2 "Not Committed Yet" 2
'

test_expect_success 'setup B1 lines (branch1)' '
git checkout -b branch1 main &&
echo "3A slow green fox jumps into the" >>file &&
Expand Down

0 comments on commit 62df03c

Please sign in to comment.