Browse Source

Merge branch 'ds/add-missing-tags'

The history traversal used to implement the tag-following has been
optimized by introducing a new helper.

* ds/add-missing-tags:
  remote: make add_missing_tags() linear
  test-reach: test get_reachable_subset
  commit-reach: implement get_reachable_subset
maint
Junio C Hamano 6 years ago
parent
commit
291123e69b
  1. 69
      commit-reach.c
  2. 13
      commit-reach.h
  3. 34
      remote.c
  4. 34
      t/helper/test-reach.c
  5. 52
      t/t6600-test-reach.sh

69
commit-reach.c

@ -690,3 +690,72 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to, @@ -690,3 +690,72 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
object_array_clear(&from_objs);
return result;
}

struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
struct commit **to, int nr_to,
unsigned int reachable_flag)
{
struct commit **item;
struct commit *current;
struct commit_list *found_commits = NULL;
struct commit **to_last = to + nr_to;
struct commit **from_last = from + nr_from;
uint32_t min_generation = GENERATION_NUMBER_INFINITY;
int num_to_find = 0;

struct prio_queue queue = { compare_commits_by_gen_then_commit_date };

for (item = to; item < to_last; item++) {
struct commit *c = *item;

parse_commit(c);
if (c->generation < min_generation)
min_generation = c->generation;

if (!(c->object.flags & PARENT1)) {
c->object.flags |= PARENT1;
num_to_find++;
}
}

for (item = from; item < from_last; item++) {
struct commit *c = *item;
if (!(c->object.flags & PARENT2)) {
c->object.flags |= PARENT2;
parse_commit(c);

prio_queue_put(&queue, *item);
}
}

while (num_to_find && (current = prio_queue_get(&queue)) != NULL) {
struct commit_list *parents;

if (current->object.flags & PARENT1) {
current->object.flags &= ~PARENT1;
current->object.flags |= reachable_flag;
commit_list_insert(current, &found_commits);
num_to_find--;
}

for (parents = current->parents; parents; parents = parents->next) {
struct commit *p = parents->item;

parse_commit(p);

if (p->generation < min_generation)
continue;

if (p->object.flags & PARENT2)
continue;

p->object.flags |= PARENT2;
prio_queue_put(&queue, p);
}
}

clear_commit_marks_many(nr_to, to, PARENT1);
clear_commit_marks_many(nr_from, from, PARENT2);

return found_commits;
}

13
commit-reach.h

@ -75,4 +75,17 @@ int can_all_from_reach_with_flag(struct object_array *from, @@ -75,4 +75,17 @@ int can_all_from_reach_with_flag(struct object_array *from,
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
int commit_date_cutoff);


/*
* Return a list of commits containing the commits in the 'to' array
* that are reachable from at least one commit in the 'from' array.
* Also add the given 'flag' to each of the commits in the returned list.
*
* This method uses the PARENT1 and PARENT2 flags during its operation,
* so be sure these flags are not set before calling the method.
*/
struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
struct commit **to, int nr_to,
unsigned int reachable_flag);

#endif

34
remote.c

@ -1205,9 +1205,36 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds @@ -1205,9 +1205,36 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
* sent to the other side.
*/
if (sent_tips.nr) {
const int reachable_flag = 1;
struct commit_list *found_commits;
struct commit **src_commits;
int nr_src_commits = 0, alloc_src_commits = 16;
ALLOC_ARRAY(src_commits, alloc_src_commits);

for_each_string_list_item(item, &src_tag) {
struct ref *ref = item->util;
struct commit *commit;

if (is_null_oid(&ref->new_oid))
continue;
commit = lookup_commit_reference_gently(the_repository,
&ref->new_oid,
1);
if (!commit)
/* not pushing a commit, which is not an error */
continue;

ALLOC_GROW(src_commits, nr_src_commits + 1, alloc_src_commits);
src_commits[nr_src_commits++] = commit;
}

found_commits = get_reachable_subset(sent_tips.tip, sent_tips.nr,
src_commits, nr_src_commits,
reachable_flag);

for_each_string_list_item(item, &src_tag) {
struct ref *dst_ref;
struct ref *ref = item->util;
struct commit *commit;

if (is_null_oid(&ref->new_oid))
@ -1223,7 +1250,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds @@ -1223,7 +1250,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
* Is this tag, which they do not have, reachable from
* any of the commits we are sending?
*/
if (!in_merge_bases_many(commit, sent_tips.nr, sent_tips.tip))
if (!(commit->object.flags & reachable_flag))
continue;

/* Add it in */
@ -1231,7 +1258,12 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds @@ -1231,7 +1258,12 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
oidcpy(&dst_ref->new_oid, &ref->new_oid);
dst_ref->peer_ref = copy_ref(ref);
}

clear_commit_marks_many(nr_src_commits, src_commits, reachable_flag);
free(src_commits);
free_commit_list(found_commits);
}

string_list_clear(&src_tag, 0);
free(sent_tips.tip);
}

34
t/helper/test-reach.c

@ -32,8 +32,8 @@ int cmd__reach(int ac, const char **av) @@ -32,8 +32,8 @@ int cmd__reach(int ac, const char **av)
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 commit **X_array, **Y_array;
int X_nr, X_alloc, Y_nr, Y_alloc;
struct strbuf buf = STRBUF_INIT;
struct repository *r = the_repository;

@ -44,9 +44,10 @@ int cmd__reach(int ac, const char **av) @@ -44,9 +44,10 @@ int cmd__reach(int ac, const char **av)

A = B = NULL;
X = Y = NULL;
X_nr = 0;
X_alloc = 16;
X_nr = Y_nr = 0;
X_alloc = Y_alloc = 16;
ALLOC_ARRAY(X_array, X_alloc);
ALLOC_ARRAY(Y_array, Y_alloc);

while (strbuf_getline(&buf, stdin) != EOF) {
struct object_id oid;
@ -92,6 +93,8 @@ int cmd__reach(int ac, const char **av) @@ -92,6 +93,8 @@ int cmd__reach(int ac, const char **av)

case 'Y':
commit_list_insert(c, &Y);
ALLOC_GROW(Y_array, Y_nr + 1, Y_alloc);
Y_array[Y_nr++] = c;
break;

default:
@ -136,6 +139,29 @@ int cmd__reach(int ac, const char **av) @@ -136,6 +139,29 @@ int cmd__reach(int ac, const char **av)
filter.with_commit_tag_algo = 0;

printf("%s(_,A,X,_):%d\n", av[1], commit_contains(&filter, A, X, &cache));
} else if (!strcmp(av[1], "get_reachable_subset")) {
const int reachable_flag = 1;
int i, count = 0;
struct commit_list *current;
struct commit_list *list = get_reachable_subset(X_array, X_nr,
Y_array, Y_nr,
reachable_flag);
printf("get_reachable_subset(X,Y)\n");
for (current = list; current; current = current->next) {
if (!(list->item->object.flags & reachable_flag))
die(_("commit %s is not marked reachable"),
oid_to_hex(&list->item->object.oid));
count++;
}
for (i = 0; i < Y_nr; i++) {
if (Y_array[i]->object.flags & reachable_flag)
count--;
}

if (count < 0)
die(_("too many commits marked reachable"));

print_sorted_commit_ids(list);
}

exit(0);

52
t/t6600-test-reach.sh

@ -265,4 +265,56 @@ test_expect_success 'commit_contains:miss' ' @@ -265,4 +265,56 @@ test_expect_success 'commit_contains:miss' '
test_three_modes commit_contains --tag
'

test_expect_success 'get_reachable_subset:all' '
cat >input <<-\EOF &&
X:commit-9-1
X:commit-8-3
X:commit-7-5
X:commit-6-6
X:commit-1-7
Y:commit-3-3
Y:commit-1-7
Y:commit-5-6
EOF
(
echo "get_reachable_subset(X,Y)" &&
git rev-parse commit-3-3 \
commit-1-7 \
commit-5-6 | sort
) >expect &&
test_three_modes get_reachable_subset
'

test_expect_success 'get_reachable_subset:some' '
cat >input <<-\EOF &&
X:commit-9-1
X:commit-8-3
X:commit-7-5
X:commit-1-7
Y:commit-3-3
Y:commit-1-7
Y:commit-5-6
EOF
(
echo "get_reachable_subset(X,Y)" &&
git rev-parse commit-3-3 \
commit-1-7 | sort
) >expect &&
test_three_modes get_reachable_subset
'

test_expect_success 'get_reachable_subset:none' '
cat >input <<-\EOF &&
X:commit-9-1
X:commit-8-3
X:commit-7-5
X:commit-1-7
Y:commit-9-3
Y:commit-7-6
Y:commit-2-8
EOF
echo "get_reachable_subset(X,Y)" >expect &&
test_three_modes get_reachable_subset
'

test_done

Loading…
Cancel
Save