mirror of
https://github.com/git/git.git
synced 2024-11-27 12:03:55 +08:00
Merge branch 'jk/blame-contents-with-arbitrary-commit'
"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
This commit is contained in:
commit
62df03c277
@ -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
|
||||
|
@ -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
|
||||
-----------
|
||||
|
40
blame.c
40
blame.c
@ -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;
|
||||
@ -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);
|
||||
|
||||
@ -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), ":");
|
||||
}
|
||||
|
||||
|
@ -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 &&
|
||||
(
|
||||
@ -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 &&
|
||||
|
Loading…
Reference in New Issue
Block a user