mirror of
https://github.com/git/git
synced 2024-08-27 19:49:21 +00:00
Merge branch 'mk/blame-first-parent'
"git blame" learnt to take "--first-parent" and "--reverse" at the same time when it makes sense. * mk/blame-first-parent: blame: allow blame --reverse --first-parent when it makes sense blame: extract find_single_final blame: test to describe use of blame --reverse --first-parent
This commit is contained in:
commit
6784eb5ad9
|
@ -2402,16 +2402,11 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
|
||||||
return commit;
|
return commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *prepare_final(struct scoreboard *sb)
|
static struct object_array_entry *find_single_final(struct rev_info *revs)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const char *final_commit_name = NULL;
|
struct object_array_entry *found = NULL;
|
||||||
struct rev_info *revs = sb->revs;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There must be one and only one positive commit in the
|
|
||||||
* revs->pending array.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < revs->pending.nr; i++) {
|
for (i = 0; i < revs->pending.nr; i++) {
|
||||||
struct object *obj = revs->pending.objects[i].item;
|
struct object *obj = revs->pending.objects[i].item;
|
||||||
if (obj->flags & UNINTERESTING)
|
if (obj->flags & UNINTERESTING)
|
||||||
|
@ -2420,14 +2415,24 @@ static char *prepare_final(struct scoreboard *sb)
|
||||||
obj = deref_tag(obj, NULL, 0);
|
obj = deref_tag(obj, NULL, 0);
|
||||||
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 (found)
|
||||||
die("More than one commit to dig from %s and %s?",
|
die("More than one commit to dig from %s and %s?",
|
||||||
revs->pending.objects[i].name,
|
revs->pending.objects[i].name,
|
||||||
final_commit_name);
|
found->name);
|
||||||
sb->final = (struct commit *) obj;
|
found = &(revs->pending.objects[i]);
|
||||||
final_commit_name = revs->pending.objects[i].name;
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *prepare_final(struct scoreboard *sb)
|
||||||
|
{
|
||||||
|
struct object_array_entry *found = find_single_final(sb->revs);
|
||||||
|
if (found) {
|
||||||
|
sb->final = (struct commit *) found->item;
|
||||||
|
return xstrdup(found->name);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
return xstrdup_or_null(final_commit_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *prepare_initial(struct scoreboard *sb)
|
static char *prepare_initial(struct scoreboard *sb)
|
||||||
|
@ -2503,6 +2508,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||||
long dashdash_pos, lno;
|
long dashdash_pos, lno;
|
||||||
char *final_commit_name = NULL;
|
char *final_commit_name = NULL;
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
|
struct commit *final_commit = NULL;
|
||||||
|
|
||||||
static struct string_list range_list;
|
static struct string_list range_list;
|
||||||
static int output_option = 0, opt = 0;
|
static int output_option = 0, opt = 0;
|
||||||
|
@ -2692,11 +2698,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||||
}
|
}
|
||||||
else if (contents_from)
|
else if (contents_from)
|
||||||
die("--contents and --reverse do not blend well.");
|
die("--contents and --reverse do not blend well.");
|
||||||
else if (revs.first_parent_only)
|
|
||||||
die("combining --first-parent and --reverse is not supported");
|
|
||||||
else {
|
else {
|
||||||
final_commit_name = prepare_initial(&sb);
|
final_commit_name = prepare_initial(&sb);
|
||||||
sb.commits.compare = compare_commits_by_reverse_commit_date;
|
sb.commits.compare = compare_commits_by_reverse_commit_date;
|
||||||
|
if (revs.first_parent_only)
|
||||||
|
revs.children.name = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sb.final) {
|
if (!sb.final) {
|
||||||
|
@ -2713,6 +2719,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||||
else if (contents_from)
|
else if (contents_from)
|
||||||
die("Cannot use --contents with final commit object name");
|
die("Cannot use --contents with final commit object name");
|
||||||
|
|
||||||
|
if (reverse && revs.first_parent_only) {
|
||||||
|
struct object_array_entry *entry = find_single_final(sb.revs);
|
||||||
|
if (!entry)
|
||||||
|
die("--reverse and --first-parent together require specified latest commit");
|
||||||
|
else
|
||||||
|
final_commit = (struct commit*) entry->item;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have bottom, this will mark the ancestors of the
|
* If we have bottom, this will mark the ancestors of the
|
||||||
* bottom commits we would reach while traversing as
|
* bottom commits we would reach while traversing as
|
||||||
|
@ -2721,6 +2735,25 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||||
if (prepare_revision_walk(&revs))
|
if (prepare_revision_walk(&revs))
|
||||||
die(_("revision walk setup failed"));
|
die(_("revision walk setup failed"));
|
||||||
|
|
||||||
|
if (reverse && revs.first_parent_only) {
|
||||||
|
struct commit *c = final_commit;
|
||||||
|
|
||||||
|
sb.revs->children.name = "children";
|
||||||
|
while (c->parents &&
|
||||||
|
hashcmp(c->object.sha1, sb.final->object.sha1)) {
|
||||||
|
struct commit_list *l = xcalloc(1, sizeof(*l));
|
||||||
|
|
||||||
|
l->item = c;
|
||||||
|
if (add_decoration(&sb.revs->children,
|
||||||
|
&c->parents->item->object, l))
|
||||||
|
die("BUG: not unique item in first-parent chain");
|
||||||
|
c = c->parents->item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashcmp(c->object.sha1, sb.final->object.sha1))
|
||||||
|
die("--reverse --first-parent together require range along first-parent chain");
|
||||||
|
}
|
||||||
|
|
||||||
if (is_null_sha1(sb.final->object.sha1)) {
|
if (is_null_sha1(sb.final->object.sha1)) {
|
||||||
o = sb.final->util;
|
o = sb.final->util;
|
||||||
sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
|
sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
|
||||||
|
|
36
t/t8009-blame-vs-topicbranches.sh
Executable file
36
t/t8009-blame-vs-topicbranches.sh
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='blaming trough history with topic branches'
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
# Creates the history shown below. '*'s mark the first parent in the merges.
|
||||||
|
# The only line of file.t is changed in commit B2
|
||||||
|
#
|
||||||
|
# +---C1
|
||||||
|
# / \
|
||||||
|
# A0--A1--*A2--*A3
|
||||||
|
# \ /
|
||||||
|
# B1-B2
|
||||||
|
#
|
||||||
|
test_expect_success setup '
|
||||||
|
test_commit A0 file.t line0 &&
|
||||||
|
test_commit A1 &&
|
||||||
|
git reset --hard A0 &&
|
||||||
|
test_commit B1 &&
|
||||||
|
test_commit B2 file.t line0changed &&
|
||||||
|
git reset --hard A1 &&
|
||||||
|
test_merge A2 B2 &&
|
||||||
|
git reset --hard A1 &&
|
||||||
|
test_commit C1 &&
|
||||||
|
git reset --hard A2 &&
|
||||||
|
test_merge A3 C1
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'blame --reverse --first-parent finds A1' '
|
||||||
|
git blame --porcelain --reverse --first-parent A0..A3 -- file.t >actual_full &&
|
||||||
|
head -n 1 <actual_full | sed -e "s/ .*//" >actual &&
|
||||||
|
git rev-parse A1 >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Loading…
Reference in a new issue