Skip to content

Commit 291123e

Browse files
committed
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
2 parents 1961efe + 85daa01 commit 291123e

File tree

5 files changed

+197
-5
lines changed

5 files changed

+197
-5
lines changed

commit-reach.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,3 +690,72 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
690690
object_array_clear(&from_objs);
691691
return result;
692692
}
693+
694+
struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
695+
struct commit **to, int nr_to,
696+
unsigned int reachable_flag)
697+
{
698+
struct commit **item;
699+
struct commit *current;
700+
struct commit_list *found_commits = NULL;
701+
struct commit **to_last = to + nr_to;
702+
struct commit **from_last = from + nr_from;
703+
uint32_t min_generation = GENERATION_NUMBER_INFINITY;
704+
int num_to_find = 0;
705+
706+
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
707+
708+
for (item = to; item < to_last; item++) {
709+
struct commit *c = *item;
710+
711+
parse_commit(c);
712+
if (c->generation < min_generation)
713+
min_generation = c->generation;
714+
715+
if (!(c->object.flags & PARENT1)) {
716+
c->object.flags |= PARENT1;
717+
num_to_find++;
718+
}
719+
}
720+
721+
for (item = from; item < from_last; item++) {
722+
struct commit *c = *item;
723+
if (!(c->object.flags & PARENT2)) {
724+
c->object.flags |= PARENT2;
725+
parse_commit(c);
726+
727+
prio_queue_put(&queue, *item);
728+
}
729+
}
730+
731+
while (num_to_find && (current = prio_queue_get(&queue)) != NULL) {
732+
struct commit_list *parents;
733+
734+
if (current->object.flags & PARENT1) {
735+
current->object.flags &= ~PARENT1;
736+
current->object.flags |= reachable_flag;
737+
commit_list_insert(current, &found_commits);
738+
num_to_find--;
739+
}
740+
741+
for (parents = current->parents; parents; parents = parents->next) {
742+
struct commit *p = parents->item;
743+
744+
parse_commit(p);
745+
746+
if (p->generation < min_generation)
747+
continue;
748+
749+
if (p->object.flags & PARENT2)
750+
continue;
751+
752+
p->object.flags |= PARENT2;
753+
prio_queue_put(&queue, p);
754+
}
755+
}
756+
757+
clear_commit_marks_many(nr_to, to, PARENT1);
758+
clear_commit_marks_many(nr_from, from, PARENT2);
759+
760+
return found_commits;
761+
}

commit-reach.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,17 @@ int can_all_from_reach_with_flag(struct object_array *from,
7575
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
7676
int commit_date_cutoff);
7777

78+
79+
/*
80+
* Return a list of commits containing the commits in the 'to' array
81+
* that are reachable from at least one commit in the 'from' array.
82+
* Also add the given 'flag' to each of the commits in the returned list.
83+
*
84+
* This method uses the PARENT1 and PARENT2 flags during its operation,
85+
* so be sure these flags are not set before calling the method.
86+
*/
87+
struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
88+
struct commit **to, int nr_to,
89+
unsigned int reachable_flag);
90+
7891
#endif

remote.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,9 +1205,36 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
12051205
* sent to the other side.
12061206
*/
12071207
if (sent_tips.nr) {
1208+
const int reachable_flag = 1;
1209+
struct commit_list *found_commits;
1210+
struct commit **src_commits;
1211+
int nr_src_commits = 0, alloc_src_commits = 16;
1212+
ALLOC_ARRAY(src_commits, alloc_src_commits);
1213+
12081214
for_each_string_list_item(item, &src_tag) {
12091215
struct ref *ref = item->util;
1216+
struct commit *commit;
1217+
1218+
if (is_null_oid(&ref->new_oid))
1219+
continue;
1220+
commit = lookup_commit_reference_gently(the_repository,
1221+
&ref->new_oid,
1222+
1);
1223+
if (!commit)
1224+
/* not pushing a commit, which is not an error */
1225+
continue;
1226+
1227+
ALLOC_GROW(src_commits, nr_src_commits + 1, alloc_src_commits);
1228+
src_commits[nr_src_commits++] = commit;
1229+
}
1230+
1231+
found_commits = get_reachable_subset(sent_tips.tip, sent_tips.nr,
1232+
src_commits, nr_src_commits,
1233+
reachable_flag);
1234+
1235+
for_each_string_list_item(item, &src_tag) {
12101236
struct ref *dst_ref;
1237+
struct ref *ref = item->util;
12111238
struct commit *commit;
12121239

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

12291256
/* Add it in */
12301257
dst_ref = make_linked_ref(ref->name, dst_tail);
12311258
oidcpy(&dst_ref->new_oid, &ref->new_oid);
12321259
dst_ref->peer_ref = copy_ref(ref);
12331260
}
1261+
1262+
clear_commit_marks_many(nr_src_commits, src_commits, reachable_flag);
1263+
free(src_commits);
1264+
free_commit_list(found_commits);
12341265
}
1266+
12351267
string_list_clear(&src_tag, 0);
12361268
free(sent_tips.tip);
12371269
}

t/helper/test-reach.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ int cmd__reach(int ac, const char **av)
3232
struct commit *A, *B;
3333
struct commit_list *X, *Y;
3434
struct object_array X_obj = OBJECT_ARRAY_INIT;
35-
struct commit **X_array;
36-
int X_nr, X_alloc;
35+
struct commit **X_array, **Y_array;
36+
int X_nr, X_alloc, Y_nr, Y_alloc;
3737
struct strbuf buf = STRBUF_INIT;
3838
struct repository *r = the_repository;
3939

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

4545
A = B = NULL;
4646
X = Y = NULL;
47-
X_nr = 0;
48-
X_alloc = 16;
47+
X_nr = Y_nr = 0;
48+
X_alloc = Y_alloc = 16;
4949
ALLOC_ARRAY(X_array, X_alloc);
50+
ALLOC_ARRAY(Y_array, Y_alloc);
5051

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

9394
case 'Y':
9495
commit_list_insert(c, &Y);
96+
ALLOC_GROW(Y_array, Y_nr + 1, Y_alloc);
97+
Y_array[Y_nr++] = c;
9598
break;
9699

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

138141
printf("%s(_,A,X,_):%d\n", av[1], commit_contains(&filter, A, X, &cache));
142+
} else if (!strcmp(av[1], "get_reachable_subset")) {
143+
const int reachable_flag = 1;
144+
int i, count = 0;
145+
struct commit_list *current;
146+
struct commit_list *list = get_reachable_subset(X_array, X_nr,
147+
Y_array, Y_nr,
148+
reachable_flag);
149+
printf("get_reachable_subset(X,Y)\n");
150+
for (current = list; current; current = current->next) {
151+
if (!(list->item->object.flags & reachable_flag))
152+
die(_("commit %s is not marked reachable"),
153+
oid_to_hex(&list->item->object.oid));
154+
count++;
155+
}
156+
for (i = 0; i < Y_nr; i++) {
157+
if (Y_array[i]->object.flags & reachable_flag)
158+
count--;
159+
}
160+
161+
if (count < 0)
162+
die(_("too many commits marked reachable"));
163+
164+
print_sorted_commit_ids(list);
139165
}
140166

141167
exit(0);

t/t6600-test-reach.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,56 @@ test_expect_success 'commit_contains:miss' '
265265
test_three_modes commit_contains --tag
266266
'
267267

268+
test_expect_success 'get_reachable_subset:all' '
269+
cat >input <<-\EOF &&
270+
X:commit-9-1
271+
X:commit-8-3
272+
X:commit-7-5
273+
X:commit-6-6
274+
X:commit-1-7
275+
Y:commit-3-3
276+
Y:commit-1-7
277+
Y:commit-5-6
278+
EOF
279+
(
280+
echo "get_reachable_subset(X,Y)" &&
281+
git rev-parse commit-3-3 \
282+
commit-1-7 \
283+
commit-5-6 | sort
284+
) >expect &&
285+
test_three_modes get_reachable_subset
286+
'
287+
288+
test_expect_success 'get_reachable_subset:some' '
289+
cat >input <<-\EOF &&
290+
X:commit-9-1
291+
X:commit-8-3
292+
X:commit-7-5
293+
X:commit-1-7
294+
Y:commit-3-3
295+
Y:commit-1-7
296+
Y:commit-5-6
297+
EOF
298+
(
299+
echo "get_reachable_subset(X,Y)" &&
300+
git rev-parse commit-3-3 \
301+
commit-1-7 | sort
302+
) >expect &&
303+
test_three_modes get_reachable_subset
304+
'
305+
306+
test_expect_success 'get_reachable_subset:none' '
307+
cat >input <<-\EOF &&
308+
X:commit-9-1
309+
X:commit-8-3
310+
X:commit-7-5
311+
X:commit-1-7
312+
Y:commit-9-3
313+
Y:commit-7-6
314+
Y:commit-2-8
315+
EOF
316+
echo "get_reachable_subset(X,Y)" >expect &&
317+
test_three_modes get_reachable_subset
318+
'
319+
268320
test_done

0 commit comments

Comments
 (0)