Skip to content

Commit

Permalink
Merge branch 'ds/reachable'
Browse files Browse the repository at this point in the history
Recent update broke the reachability algorithm when refs (e.g.
tags) that point at objects that are not commit were involved,
which has been fixed.

* ds/reachable:
  commit-reach: fix memory and flag leaks
  commit-reach: properly peel tags
  • Loading branch information
gitster committed Sep 24, 2018
2 parents faadedb + 4067a64 commit 0f7ac90
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 14 deletions.
41 changes: 34 additions & 7 deletions commit-reach.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,20 +547,42 @@ int can_all_from_reach_with_flag(struct object_array *from,
{
struct commit **list = NULL;
int i;
int nr_commits;
int result = 1;

ALLOC_ARRAY(list, from->nr);
nr_commits = 0;
for (i = 0; i < from->nr; i++) {
list[i] = (struct commit *)from->objects[i].item;
struct object *from_one = from->objects[i].item;

if (parse_commit(list[i]) ||
list[i]->generation < min_generation)
return 0;
if (!from_one || from_one->flags & assign_flag)
continue;

from_one = deref_tag(the_repository, from_one,
"a from object", 0);
if (!from_one || from_one->type != OBJ_COMMIT) {
/* no way to tell if this is reachable by
* looking at the ancestry chain alone, so
* leave a note to ourselves not to worry about
* this object anymore.
*/
from->objects[i].item->flags |= assign_flag;
continue;
}

list[nr_commits] = (struct commit *)from_one;
if (parse_commit(list[nr_commits]) ||
list[nr_commits]->generation < min_generation) {
result = 0;
goto cleanup;
}

nr_commits++;
}

QSORT(list, from->nr, compare_commits_by_gen);
QSORT(list, nr_commits, compare_commits_by_gen);

for (i = 0; i < from->nr; i++) {
for (i = 0; i < nr_commits; i++) {
/* DFS from list[i] */
struct commit_list *stack = NULL;

Expand Down Expand Up @@ -603,10 +625,15 @@ int can_all_from_reach_with_flag(struct object_array *from,
}

cleanup:
for (i = 0; i < from->nr; i++) {
for (i = 0; i < nr_commits; i++) {
clear_commit_marks(list[i], RESULT);
clear_commit_marks(list[i], assign_flag);
}
free(list);

for (i = 0; i < from->nr; i++)
from->objects[i].item->flags &= ~assign_flag;

return result;
}

Expand Down
22 changes: 17 additions & 5 deletions t/helper/test-reach.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int cmd__reach(int ac, const char **av)
struct object_id oid_A, oid_B;
struct commit *A, *B;
struct commit_list *X, *Y;
struct object_array X_obj = OBJECT_ARRAY_INIT;
struct commit **X_array;
int X_nr, X_alloc;
struct strbuf buf = STRBUF_INIT;
Expand All @@ -49,22 +50,23 @@ int cmd__reach(int ac, const char **av)

while (strbuf_getline(&buf, stdin) != EOF) {
struct object_id oid;
struct object *o;
struct object *orig;
struct object *peeled;
struct commit *c;
if (buf.len < 3)
continue;

if (get_oid_committish(buf.buf + 2, &oid))
die("failed to resolve %s", buf.buf + 2);

o = parse_object(r, &oid);
o = deref_tag_noverify(o);
orig = parse_object(r, &oid);
peeled = deref_tag_noverify(orig);

if (!o)
if (!peeled)
die("failed to load commit for input %s resulting in oid %s\n",
buf.buf, oid_to_hex(&oid));

c = object_as_type(r, o, OBJ_COMMIT, 0);
c = object_as_type(r, peeled, OBJ_COMMIT, 0);

if (!c)
die("failed to load commit for input %s resulting in oid %s\n",
Expand All @@ -85,6 +87,7 @@ int cmd__reach(int ac, const char **av)
commit_list_insert(c, &X);
ALLOC_GROW(X_array, X_nr + 1, X_alloc);
X_array[X_nr++] = c;
add_object_array(orig, NULL, &X_obj);
break;

case 'Y':
Expand Down Expand Up @@ -113,6 +116,15 @@ int cmd__reach(int ac, const char **av)
print_sorted_commit_ids(list);
} else if (!strcmp(av[1], "can_all_from_reach")) {
printf("%s(X,Y):%d\n", av[1], can_all_from_reach(X, Y, 1));
} else if (!strcmp(av[1], "can_all_from_reach_with_flag")) {
struct commit_list *iter = Y;

while (iter) {
iter->item->object.flags |= 2;
iter = iter->next;
}

printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
} else if (!strcmp(av[1], "commit_contains")) {
struct ref_filter filter;
struct contains_cache cache;
Expand Down
30 changes: 28 additions & 2 deletions t/t6600-test-reach.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,22 @@ test_expect_success 'setup' '
for i in $(test_seq 1 10)
do
test_commit "1-$i" &&
git branch -f commit-1-$i
git branch -f commit-1-$i &&
git tag -a -m "1-$i" tag-1-$i commit-1-$i
done &&
for j in $(test_seq 1 9)
do
git reset --hard commit-$j-1 &&
x=$(($j + 1)) &&
test_commit "$x-1" &&
git branch -f commit-$x-1 &&
git tag -a -m "$x-1" tag-$x-1 commit-$x-1 &&
for i in $(test_seq 2 10)
do
git merge commit-$j-$i -m "$x-$i" &&
git branch -f commit-$x-$i
git branch -f commit-$x-$i &&
git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i
done
done &&
git commit-graph write --reachable &&
Expand Down Expand Up @@ -205,6 +208,29 @@ test_expect_success 'can_all_from_reach:miss' '
test_three_modes can_all_from_reach
'

test_expect_success 'can_all_from_reach_with_flag: tags case' '
cat >input <<-\EOF &&
X:tag-2-10
X:tag-3-9
X:tag-4-8
X:commit-5-7
X:commit-6-6
X:commit-7-5
X:commit-8-4
X:commit-9-3
Y:tag-1-9
Y:tag-2-8
Y:tag-3-7
Y:commit-4-6
Y:commit-5-5
Y:commit-6-4
Y:commit-7-3
Y:commit-8-1
EOF
echo "can_all_from_reach_with_flag(X,_,_,0,0):1" >expect &&
test_three_modes can_all_from_reach_with_flag
'

test_expect_success 'commit_contains:hit' '
cat >input <<-\EOF &&
A:commit-7-7
Expand Down

0 comments on commit 0f7ac90

Please sign in to comment.