Line | Count | Source |
1 | | #define USE_THE_REPOSITORY_VARIABLE |
2 | | |
3 | | #include "git-compat-util.h" |
4 | | #include "hex.h" |
5 | | #include "repository.h" |
6 | | #include "tempfile.h" |
7 | | #include "lockfile.h" |
8 | | #include "odb.h" |
9 | | #include "commit.h" |
10 | | #include "tag.h" |
11 | | #include "pkt-line.h" |
12 | | #include "refs.h" |
13 | | #include "oid-array.h" |
14 | | #include "path.h" |
15 | | #include "diff.h" |
16 | | #include "revision.h" |
17 | | #include "commit-slab.h" |
18 | | #include "list-objects.h" |
19 | | #include "commit-reach.h" |
20 | | #include "shallow.h" |
21 | | #include "statinfo.h" |
22 | | #include "trace.h" |
23 | | |
24 | | void set_alternate_shallow_file(struct repository *r, const char *path) |
25 | 0 | { |
26 | 0 | if (r->parsed_objects->is_shallow != -1) |
27 | 0 | BUG("is_repository_shallow must not be called before set_alternate_shallow_file"); |
28 | 0 | free(r->parsed_objects->alternate_shallow_file); |
29 | 0 | r->parsed_objects->alternate_shallow_file = xstrdup_or_null(path); |
30 | 0 | } |
31 | | |
32 | | int register_shallow(struct repository *r, const struct object_id *oid) |
33 | 0 | { |
34 | 0 | struct commit_graft *graft = |
35 | 0 | xmalloc(sizeof(struct commit_graft)); |
36 | 0 | struct commit *commit = lookup_commit(r, oid); |
37 | |
|
38 | 0 | oidcpy(&graft->oid, oid); |
39 | 0 | graft->nr_parent = -1; |
40 | 0 | if (commit && commit->object.parsed) { |
41 | 0 | commit_list_free(commit->parents); |
42 | 0 | commit->parents = NULL; |
43 | 0 | } |
44 | 0 | return register_commit_graft(r, graft, 0); |
45 | 0 | } |
46 | | |
47 | | int unregister_shallow(const struct object_id *oid) |
48 | 0 | { |
49 | 0 | int pos = commit_graft_pos(the_repository, oid); |
50 | 0 | if (pos < 0) |
51 | 0 | return -1; |
52 | 0 | free(the_repository->parsed_objects->grafts[pos]); |
53 | 0 | if (pos + 1 < the_repository->parsed_objects->grafts_nr) |
54 | 0 | MOVE_ARRAY(the_repository->parsed_objects->grafts + pos, |
55 | 0 | the_repository->parsed_objects->grafts + pos + 1, |
56 | 0 | the_repository->parsed_objects->grafts_nr - pos - 1); |
57 | 0 | the_repository->parsed_objects->grafts_nr--; |
58 | 0 | return 0; |
59 | 0 | } |
60 | | |
61 | | int is_repository_shallow(struct repository *r) |
62 | 0 | { |
63 | 0 | FILE *fp; |
64 | 0 | char buf[1024]; |
65 | 0 | const char *path = r->parsed_objects->alternate_shallow_file; |
66 | |
|
67 | 0 | if (r->parsed_objects->is_shallow >= 0) |
68 | 0 | return r->parsed_objects->is_shallow; |
69 | | |
70 | 0 | if (!path) |
71 | 0 | path = git_path_shallow(r); |
72 | | /* |
73 | | * fetch-pack sets '--shallow-file ""' as an indicator that no |
74 | | * shallow file should be used. We could just open it and it |
75 | | * will likely fail. But let's do an explicit check instead. |
76 | | */ |
77 | 0 | if (!*path || (fp = fopen(path, "r")) == NULL) { |
78 | 0 | stat_validity_clear(r->parsed_objects->shallow_stat); |
79 | 0 | r->parsed_objects->is_shallow = 0; |
80 | 0 | return r->parsed_objects->is_shallow; |
81 | 0 | } |
82 | 0 | stat_validity_update(r->parsed_objects->shallow_stat, fileno(fp)); |
83 | 0 | r->parsed_objects->is_shallow = 1; |
84 | |
|
85 | 0 | while (fgets(buf, sizeof(buf), fp)) { |
86 | 0 | struct object_id oid; |
87 | 0 | if (get_oid_hex(buf, &oid)) |
88 | 0 | die("bad shallow line: %s", buf); |
89 | 0 | register_shallow(r, &oid); |
90 | 0 | } |
91 | 0 | fclose(fp); |
92 | 0 | return r->parsed_objects->is_shallow; |
93 | 0 | } |
94 | | |
95 | | static void reset_repository_shallow(struct repository *r) |
96 | 0 | { |
97 | 0 | r->parsed_objects->is_shallow = -1; |
98 | 0 | stat_validity_clear(r->parsed_objects->shallow_stat); |
99 | 0 | parsed_object_pool_reset_commit_grafts(r->parsed_objects); |
100 | 0 | } |
101 | | |
102 | | int commit_shallow_file(struct repository *r, struct shallow_lock *lk) |
103 | 0 | { |
104 | 0 | int res = commit_lock_file(&lk->lock); |
105 | 0 | reset_repository_shallow(r); |
106 | | |
107 | | /* |
108 | | * Update in-memory data structures with the new shallow information, |
109 | | * including unparsing all commits that now have grafts. |
110 | | */ |
111 | 0 | is_repository_shallow(r); |
112 | |
|
113 | 0 | return res; |
114 | 0 | } |
115 | | |
116 | | void rollback_shallow_file(struct repository *r, struct shallow_lock *lk) |
117 | 0 | { |
118 | 0 | rollback_lock_file(&lk->lock); |
119 | 0 | reset_repository_shallow(r); |
120 | 0 | } |
121 | | |
122 | | /* |
123 | | * TODO: use "int" elemtype instead of "int *" when/if commit-slab |
124 | | * supports a "valid" flag. |
125 | | */ |
126 | 0 | define_commit_slab(commit_depth, int *); Unexecuted instantiation: shallow.c:init_commit_depth_with_stride Unexecuted instantiation: shallow.c:commit_depth_at_peek Unexecuted instantiation: shallow.c:clear_commit_depth |
127 | 0 | static void free_depth_in_slab(int **ptr) |
128 | 0 | { |
129 | 0 | FREE_AND_NULL(*ptr); |
130 | 0 | } |
131 | | /* |
132 | | * This is a common internal function that can either return a list of |
133 | | * shallow commits or calculate the current maximum depth of a shallow |
134 | | * repository, depending on the input parameters. |
135 | | * |
136 | | * Depth calculation is triggered by passing the `shallows` parameter. |
137 | | * In this case, the computed depth is stored in `max_cur_depth` (if it is |
138 | | * provided), and the function returns NULL. |
139 | | * |
140 | | * Otherwise, `max_cur_depth` remains unchanged and the function returns |
141 | | * a list of shallow commits. |
142 | | */ |
143 | | static struct commit_list *get_shallows_or_depth(struct object_array *heads, |
144 | | struct object_array *shallows, int *max_cur_depth, |
145 | | int depth, int shallow_flag, int not_shallow_flag) |
146 | 0 | { |
147 | 0 | size_t i = 0; |
148 | 0 | int cur_depth = 0, cur_depth_shallow = 0; |
149 | 0 | struct commit_list *result = NULL; |
150 | 0 | struct object_array stack = OBJECT_ARRAY_INIT; |
151 | 0 | struct commit *commit = NULL; |
152 | 0 | struct commit_graft *graft; |
153 | 0 | struct commit_depth depths; |
154 | |
|
155 | 0 | init_commit_depth(&depths); |
156 | 0 | while (commit || i < heads->nr || stack.nr) { |
157 | 0 | struct commit_list *p; |
158 | 0 | if (!commit) { |
159 | 0 | if (i < heads->nr) { |
160 | 0 | int **depth_slot; |
161 | 0 | commit = (struct commit *) |
162 | 0 | deref_tag(the_repository, |
163 | 0 | heads->objects[i++].item, |
164 | 0 | NULL, 0); |
165 | 0 | if (!commit || commit->object.type != OBJ_COMMIT) { |
166 | 0 | commit = NULL; |
167 | 0 | continue; |
168 | 0 | } |
169 | 0 | depth_slot = commit_depth_at(&depths, commit); |
170 | 0 | if (!*depth_slot) |
171 | 0 | *depth_slot = xmalloc(sizeof(int)); |
172 | 0 | **depth_slot = 0; |
173 | 0 | cur_depth = 0; |
174 | 0 | } else { |
175 | 0 | commit = (struct commit *) |
176 | 0 | object_array_pop(&stack); |
177 | 0 | cur_depth = **commit_depth_at(&depths, commit); |
178 | 0 | } |
179 | 0 | } |
180 | 0 | parse_commit_or_die(commit); |
181 | 0 | cur_depth++; |
182 | 0 | if (shallows) { |
183 | 0 | for (size_t j = 0; j < shallows->nr; j++) |
184 | 0 | if (oideq(&commit->object.oid, &shallows->objects[j].item->oid)) |
185 | 0 | if (!cur_depth_shallow || cur_depth < cur_depth_shallow) |
186 | 0 | cur_depth_shallow = cur_depth; |
187 | |
|
188 | 0 | if ((is_repository_shallow(the_repository) && !commit->parents && |
189 | 0 | (graft = lookup_commit_graft(the_repository, &commit->object.oid)) != NULL && |
190 | 0 | graft->nr_parent < 0)) { |
191 | 0 | commit = NULL; |
192 | 0 | continue; |
193 | 0 | } |
194 | 0 | } else { |
195 | 0 | if ((depth != INFINITE_DEPTH && cur_depth >= depth) || |
196 | 0 | (is_repository_shallow(the_repository) && !commit->parents && |
197 | 0 | (graft = lookup_commit_graft(the_repository, &commit->object.oid)) != NULL && |
198 | 0 | graft->nr_parent < 0)) { |
199 | 0 | commit_list_insert(commit, &result); |
200 | 0 | commit->object.flags |= shallow_flag; |
201 | 0 | commit = NULL; |
202 | 0 | continue; |
203 | 0 | } |
204 | 0 | commit->object.flags |= not_shallow_flag; |
205 | 0 | } |
206 | 0 | for (p = commit->parents, commit = NULL; p; p = p->next) { |
207 | 0 | int **depth_slot = commit_depth_at(&depths, p->item); |
208 | 0 | if (!*depth_slot) { |
209 | 0 | *depth_slot = xmalloc(sizeof(int)); |
210 | 0 | **depth_slot = cur_depth; |
211 | 0 | } else { |
212 | 0 | if (cur_depth >= **depth_slot) |
213 | 0 | continue; |
214 | 0 | **depth_slot = cur_depth; |
215 | 0 | } |
216 | 0 | if (p->next) |
217 | 0 | add_object_array(&p->item->object, |
218 | 0 | NULL, &stack); |
219 | 0 | else { |
220 | 0 | commit = p->item; |
221 | 0 | cur_depth = **commit_depth_at(&depths, commit); |
222 | 0 | } |
223 | 0 | } |
224 | 0 | } |
225 | 0 | deep_clear_commit_depth(&depths, free_depth_in_slab); |
226 | 0 | object_array_clear(&stack); |
227 | |
|
228 | 0 | if (shallows && max_cur_depth) |
229 | 0 | *max_cur_depth = cur_depth_shallow; |
230 | 0 | return result; |
231 | 0 | } |
232 | | |
233 | | int get_shallows_depth(struct object_array *heads, struct object_array *shallows) |
234 | 0 | { |
235 | 0 | int max_cur_depth = 0; |
236 | 0 | get_shallows_or_depth(heads, shallows, &max_cur_depth, 0, 0, 0); |
237 | 0 | return max_cur_depth; |
238 | |
|
239 | 0 | } |
240 | | |
241 | | struct commit_list *get_shallow_commits(struct object_array *heads, |
242 | | struct object_array *shallows, int deepen_relative, |
243 | | int depth, int shallow_flag, int not_shallow_flag) |
244 | 0 | { |
245 | 0 | if (shallows && deepen_relative) { |
246 | 0 | int cur_shallow_depth = get_shallows_depth(heads, shallows); |
247 | 0 | if (cur_shallow_depth) |
248 | 0 | depth += cur_shallow_depth; |
249 | 0 | else |
250 | 0 | return NULL; |
251 | 0 | } |
252 | 0 | return get_shallows_or_depth(heads, NULL, NULL, |
253 | 0 | depth, shallow_flag, not_shallow_flag); |
254 | 0 | } |
255 | | |
256 | | static void show_commit(struct commit *commit, void *data) |
257 | 0 | { |
258 | 0 | commit_list_insert(commit, data); |
259 | 0 | } |
260 | | |
261 | | /* |
262 | | * Given rev-list arguments, run rev-list. All reachable commits |
263 | | * except border ones are marked with not_shallow_flag. Border commits |
264 | | * are marked with shallow_flag. The list of border/shallow commits |
265 | | * are also returned. |
266 | | */ |
267 | | struct commit_list *get_shallow_commits_by_rev_list(struct strvec *argv, |
268 | | int shallow_flag, |
269 | | int not_shallow_flag) |
270 | 0 | { |
271 | 0 | struct commit_list *result = NULL, *p; |
272 | 0 | struct commit_list *not_shallow_list = NULL; |
273 | 0 | struct rev_info revs; |
274 | 0 | int both_flags = shallow_flag | not_shallow_flag; |
275 | | |
276 | | /* |
277 | | * SHALLOW (excluded) and NOT_SHALLOW (included) should not be |
278 | | * set at this point. But better be safe than sorry. |
279 | | */ |
280 | 0 | clear_object_flags(the_repository, both_flags); |
281 | |
|
282 | 0 | is_repository_shallow(the_repository); /* make sure shallows are read */ |
283 | |
|
284 | 0 | repo_init_revisions(the_repository, &revs, NULL); |
285 | 0 | save_commit_buffer = 0; |
286 | 0 | setup_revisions_from_strvec(argv, &revs, NULL); |
287 | |
|
288 | 0 | if (prepare_revision_walk(&revs)) |
289 | 0 | die("revision walk setup failed"); |
290 | 0 | traverse_commit_list(&revs, show_commit, NULL, ¬_shallow_list); |
291 | |
|
292 | 0 | if (!not_shallow_list) |
293 | 0 | die("no commits selected for shallow requests"); |
294 | | |
295 | | /* Mark all reachable commits as NOT_SHALLOW */ |
296 | 0 | for (p = not_shallow_list; p; p = p->next) |
297 | 0 | p->item->object.flags |= not_shallow_flag; |
298 | | |
299 | | /* |
300 | | * mark border commits SHALLOW + NOT_SHALLOW. |
301 | | * We cannot clear NOT_SHALLOW right now. Imagine border |
302 | | * commit A is processed first, then commit B, whose parent is |
303 | | * A, later. If NOT_SHALLOW on A is cleared at step 1, B |
304 | | * itself is considered border at step 2, which is incorrect. |
305 | | */ |
306 | 0 | for (p = not_shallow_list; p; p = p->next) { |
307 | 0 | struct commit *c = p->item; |
308 | 0 | struct commit_list *parent; |
309 | |
|
310 | 0 | if (repo_parse_commit(the_repository, c)) |
311 | 0 | die("unable to parse commit %s", |
312 | 0 | oid_to_hex(&c->object.oid)); |
313 | | |
314 | 0 | for (parent = c->parents; parent; parent = parent->next) |
315 | 0 | if (!(parent->item->object.flags & not_shallow_flag)) { |
316 | 0 | c->object.flags |= shallow_flag; |
317 | 0 | commit_list_insert(c, &result); |
318 | 0 | break; |
319 | 0 | } |
320 | 0 | } |
321 | 0 | commit_list_free(not_shallow_list); |
322 | | |
323 | | /* |
324 | | * Now we can clean up NOT_SHALLOW on border commits. Having |
325 | | * both flags set can confuse the caller. |
326 | | */ |
327 | 0 | for (p = result; p; p = p->next) { |
328 | 0 | struct object *o = &p->item->object; |
329 | 0 | if ((o->flags & both_flags) == both_flags) |
330 | 0 | o->flags &= ~not_shallow_flag; |
331 | 0 | } |
332 | 0 | release_revisions(&revs); |
333 | 0 | return result; |
334 | 0 | } |
335 | | |
336 | | static void check_shallow_file_for_update(struct repository *r) |
337 | 0 | { |
338 | 0 | if (r->parsed_objects->is_shallow == -1) |
339 | 0 | BUG("shallow must be initialized by now"); |
340 | | |
341 | 0 | if (!stat_validity_check(r->parsed_objects->shallow_stat, |
342 | 0 | git_path_shallow(r))) |
343 | 0 | die("shallow file has changed since we read it"); |
344 | 0 | } |
345 | | |
346 | 0 | #define SEEN_ONLY 1 |
347 | 0 | #define VERBOSE 2 |
348 | 0 | #define QUICK 4 |
349 | | |
350 | | struct write_shallow_data { |
351 | | struct strbuf *out; |
352 | | int use_pack_protocol; |
353 | | int count; |
354 | | unsigned flags; |
355 | | }; |
356 | | |
357 | | static int write_one_shallow(const struct commit_graft *graft, void *cb_data) |
358 | 0 | { |
359 | 0 | struct write_shallow_data *data = cb_data; |
360 | 0 | char hex[GIT_MAX_HEXSZ + 1]; |
361 | |
|
362 | 0 | oid_to_hex_r(hex, &graft->oid); |
363 | 0 | if (graft->nr_parent != -1) |
364 | 0 | return 0; |
365 | 0 | if (data->flags & QUICK) { |
366 | 0 | if (!odb_has_object(the_repository->objects, &graft->oid, |
367 | 0 | ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) |
368 | 0 | return 0; |
369 | 0 | } else if (data->flags & SEEN_ONLY) { |
370 | 0 | struct commit *c = lookup_commit(the_repository, &graft->oid); |
371 | 0 | if (!c || !(c->object.flags & SEEN)) { |
372 | 0 | if (data->flags & VERBOSE) |
373 | 0 | printf("Removing %s from .git/shallow\n", hex); |
374 | 0 | return 0; |
375 | 0 | } |
376 | 0 | } |
377 | 0 | data->count++; |
378 | 0 | if (data->use_pack_protocol) |
379 | 0 | packet_buf_write(data->out, "shallow %s", hex); |
380 | 0 | else { |
381 | 0 | strbuf_addstr(data->out, hex); |
382 | 0 | strbuf_addch(data->out, '\n'); |
383 | 0 | } |
384 | 0 | return 0; |
385 | 0 | } |
386 | | |
387 | | static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol, |
388 | | const struct oid_array *extra, |
389 | | unsigned flags) |
390 | 0 | { |
391 | 0 | struct write_shallow_data data = { |
392 | 0 | .out = out, |
393 | 0 | .use_pack_protocol = use_pack_protocol, |
394 | 0 | .flags = flags, |
395 | 0 | }; |
396 | |
|
397 | 0 | for_each_commit_graft(write_one_shallow, &data); |
398 | 0 | if (!extra) |
399 | 0 | return data.count; |
400 | 0 | for (size_t i = 0; i < extra->nr; i++) { |
401 | 0 | strbuf_add_oid_hex(out, extra->oid + i); |
402 | 0 | strbuf_addch(out, '\n'); |
403 | 0 | data.count++; |
404 | 0 | } |
405 | 0 | return data.count; |
406 | 0 | } |
407 | | |
408 | | int write_shallow_commits(struct strbuf *out, int use_pack_protocol, |
409 | | const struct oid_array *extra) |
410 | 0 | { |
411 | 0 | return write_shallow_commits_1(out, use_pack_protocol, extra, 0); |
412 | 0 | } |
413 | | |
414 | | const char *setup_temporary_shallow(const struct oid_array *extra) |
415 | 0 | { |
416 | 0 | struct tempfile *temp; |
417 | 0 | struct strbuf sb = STRBUF_INIT; |
418 | |
|
419 | 0 | if (write_shallow_commits(&sb, 0, extra)) { |
420 | 0 | char *path = repo_git_path(the_repository, "shallow_XXXXXX"); |
421 | 0 | temp = xmks_tempfile(path); |
422 | 0 | free(path); |
423 | |
|
424 | 0 | if (write_in_full(temp->fd, sb.buf, sb.len) < 0 || |
425 | 0 | close_tempfile_gently(temp) < 0) |
426 | 0 | die_errno("failed to write to %s", |
427 | 0 | get_tempfile_path(temp)); |
428 | 0 | strbuf_release(&sb); |
429 | 0 | return get_tempfile_path(temp); |
430 | 0 | } |
431 | | /* |
432 | | * is_repository_shallow() sees empty string as "no shallow |
433 | | * file". |
434 | | */ |
435 | 0 | return ""; |
436 | 0 | } |
437 | | |
438 | | void setup_alternate_shallow(struct shallow_lock *shallow_lock, |
439 | | const char **alternate_shallow_file, |
440 | | const struct oid_array *extra) |
441 | 0 | { |
442 | 0 | struct strbuf sb = STRBUF_INIT; |
443 | 0 | int fd; |
444 | |
|
445 | 0 | fd = hold_lock_file_for_update(&shallow_lock->lock, |
446 | 0 | git_path_shallow(the_repository), |
447 | 0 | LOCK_DIE_ON_ERROR); |
448 | 0 | check_shallow_file_for_update(the_repository); |
449 | 0 | if (write_shallow_commits(&sb, 0, extra)) { |
450 | 0 | if (write_in_full(fd, sb.buf, sb.len) < 0) |
451 | 0 | die_errno("failed to write to %s", |
452 | 0 | get_lock_file_path(&shallow_lock->lock)); |
453 | 0 | *alternate_shallow_file = get_lock_file_path(&shallow_lock->lock); |
454 | 0 | } else |
455 | | /* |
456 | | * is_repository_shallow() sees empty string as "no |
457 | | * shallow file". |
458 | | */ |
459 | 0 | *alternate_shallow_file = ""; |
460 | 0 | strbuf_release(&sb); |
461 | 0 | } |
462 | | |
463 | | static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb) |
464 | 0 | { |
465 | 0 | int fd = *(int *)cb; |
466 | 0 | if (graft->nr_parent == -1) |
467 | 0 | packet_write_fmt(fd, "shallow %s\n", oid_to_hex(&graft->oid)); |
468 | 0 | return 0; |
469 | 0 | } |
470 | | |
471 | | void advertise_shallow_grafts(int fd) |
472 | 0 | { |
473 | 0 | if (!is_repository_shallow(the_repository)) |
474 | 0 | return; |
475 | 0 | for_each_commit_graft(advertise_shallow_grafts_cb, &fd); |
476 | 0 | } |
477 | | |
478 | | /* |
479 | | * mark_reachable_objects() should have been run prior to this and all |
480 | | * reachable commits marked as "SEEN", except when quick_prune is non-zero, |
481 | | * in which case lines are excised from the shallow file if they refer to |
482 | | * commits that do not exist (any longer). |
483 | | */ |
484 | | void prune_shallow(unsigned options) |
485 | 0 | { |
486 | 0 | struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT; |
487 | 0 | struct strbuf sb = STRBUF_INIT; |
488 | 0 | unsigned flags = SEEN_ONLY; |
489 | 0 | int fd; |
490 | |
|
491 | 0 | if (options & PRUNE_QUICK) |
492 | 0 | flags |= QUICK; |
493 | |
|
494 | 0 | if (options & PRUNE_SHOW_ONLY) { |
495 | 0 | flags |= VERBOSE; |
496 | 0 | write_shallow_commits_1(&sb, 0, NULL, flags); |
497 | 0 | strbuf_release(&sb); |
498 | 0 | return; |
499 | 0 | } |
500 | 0 | fd = hold_lock_file_for_update(&shallow_lock.lock, |
501 | 0 | git_path_shallow(the_repository), |
502 | 0 | LOCK_DIE_ON_ERROR); |
503 | 0 | check_shallow_file_for_update(the_repository); |
504 | 0 | if (write_shallow_commits_1(&sb, 0, NULL, flags)) { |
505 | 0 | if (write_in_full(fd, sb.buf, sb.len) < 0) |
506 | 0 | die_errno("failed to write to %s", |
507 | 0 | get_lock_file_path(&shallow_lock.lock)); |
508 | 0 | commit_shallow_file(the_repository, &shallow_lock); |
509 | 0 | } else { |
510 | 0 | unlink(git_path_shallow(the_repository)); |
511 | 0 | rollback_shallow_file(the_repository, &shallow_lock); |
512 | 0 | } |
513 | 0 | strbuf_release(&sb); |
514 | 0 | } |
515 | | |
516 | | struct trace_key trace_shallow = TRACE_KEY_INIT(SHALLOW); |
517 | | |
518 | | /* |
519 | | * Step 1, split sender shallow commits into "ours" and "theirs" |
520 | | * Step 2, clean "ours" based on .git/shallow |
521 | | */ |
522 | | void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa) |
523 | 0 | { |
524 | 0 | trace_printf_key(&trace_shallow, "shallow: prepare_shallow_info\n"); |
525 | 0 | memset(info, 0, sizeof(*info)); |
526 | 0 | commit_stack_init(&info->commits); |
527 | 0 | info->shallow = sa; |
528 | 0 | if (!sa) |
529 | 0 | return; |
530 | 0 | ALLOC_ARRAY(info->ours, sa->nr); |
531 | 0 | ALLOC_ARRAY(info->theirs, sa->nr); |
532 | 0 | for (size_t i = 0; i < sa->nr; i++) { |
533 | 0 | if (odb_has_object(the_repository->objects, sa->oid + i, |
534 | 0 | ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) { |
535 | 0 | struct commit_graft *graft; |
536 | 0 | graft = lookup_commit_graft(the_repository, |
537 | 0 | &sa->oid[i]); |
538 | 0 | if (graft && graft->nr_parent < 0) |
539 | 0 | continue; |
540 | 0 | info->ours[info->nr_ours++] = i; |
541 | 0 | } else |
542 | 0 | info->theirs[info->nr_theirs++] = i; |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | | void clear_shallow_info(struct shallow_info *info) |
547 | 0 | { |
548 | 0 | if (info->used_shallow) { |
549 | 0 | for (size_t i = 0; i < info->shallow->nr; i++) |
550 | 0 | free(info->used_shallow[i]); |
551 | 0 | free(info->used_shallow); |
552 | 0 | } |
553 | |
|
554 | 0 | free(info->need_reachability_test); |
555 | 0 | free(info->reachable); |
556 | 0 | free(info->shallow_ref); |
557 | 0 | free(info->ours); |
558 | 0 | free(info->theirs); |
559 | 0 | commit_stack_clear(&info->commits); |
560 | 0 | } |
561 | | |
562 | | /* Step 4, remove non-existent ones in "theirs" after getting the pack */ |
563 | | |
564 | | void remove_nonexistent_theirs_shallow(struct shallow_info *info) |
565 | 0 | { |
566 | 0 | struct object_id *oid = info->shallow->oid; |
567 | 0 | size_t i, dst; |
568 | 0 | trace_printf_key(&trace_shallow, "shallow: remove_nonexistent_theirs_shallow\n"); |
569 | 0 | for (i = dst = 0; i < info->nr_theirs; i++) { |
570 | 0 | if (i != dst) |
571 | 0 | info->theirs[dst] = info->theirs[i]; |
572 | 0 | if (odb_has_object(the_repository->objects, oid + info->theirs[i], |
573 | 0 | ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) |
574 | 0 | dst++; |
575 | 0 | } |
576 | 0 | info->nr_theirs = dst; |
577 | 0 | } |
578 | | |
579 | 0 | define_commit_slab(ref_bitmap, uint32_t *); Unexecuted instantiation: shallow.c:init_ref_bitmap_with_stride Unexecuted instantiation: shallow.c:ref_bitmap_at_peek Unexecuted instantiation: shallow.c:clear_ref_bitmap |
580 | 0 |
|
581 | 0 | #define POOL_SIZE (512 * 1024) |
582 | | |
583 | | struct paint_info { |
584 | | struct ref_bitmap ref_bitmap; |
585 | | unsigned nr_bits; |
586 | | char **pools; |
587 | | char *free, *end; |
588 | | unsigned pool_count; |
589 | | }; |
590 | | |
591 | | static uint32_t *paint_alloc(struct paint_info *info) |
592 | 0 | { |
593 | 0 | unsigned nr = DIV_ROUND_UP(info->nr_bits, 32); |
594 | 0 | unsigned size = nr * sizeof(uint32_t); |
595 | 0 | void *p; |
596 | 0 | if (!info->pool_count || info->end < info->free + size) { |
597 | 0 | if (size > POOL_SIZE) |
598 | 0 | BUG("pool size too small for %d in paint_alloc()", |
599 | 0 | size); |
600 | 0 | info->pool_count++; |
601 | 0 | REALLOC_ARRAY(info->pools, info->pool_count); |
602 | 0 | info->free = xmalloc(POOL_SIZE); |
603 | 0 | info->pools[info->pool_count - 1] = info->free; |
604 | 0 | info->end = info->free + POOL_SIZE; |
605 | 0 | } |
606 | 0 | p = info->free; |
607 | 0 | info->free += size; |
608 | 0 | return p; |
609 | 0 | } |
610 | | |
611 | | /* |
612 | | * Given a commit SHA-1, walk down to parents until either SEEN, |
613 | | * UNINTERESTING or BOTTOM is hit. Set the id-th bit in ref_bitmap for |
614 | | * all walked commits. |
615 | | */ |
616 | | static void paint_down(struct paint_info *info, const struct object_id *oid, |
617 | | unsigned int id) |
618 | 0 | { |
619 | 0 | unsigned int i, nr; |
620 | 0 | struct commit_list *head = NULL; |
621 | 0 | size_t bitmap_nr = DIV_ROUND_UP(info->nr_bits, 32); |
622 | 0 | size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr); |
623 | 0 | struct commit *c = lookup_commit_reference_gently(the_repository, oid, |
624 | 0 | 1); |
625 | 0 | uint32_t *tmp; /* to be freed before return */ |
626 | 0 | uint32_t *bitmap; |
627 | |
|
628 | 0 | if (!c) |
629 | 0 | return; |
630 | | |
631 | 0 | tmp = xmalloc(bitmap_size); |
632 | 0 | bitmap = paint_alloc(info); |
633 | 0 | memset(bitmap, 0, bitmap_size); |
634 | 0 | bitmap[id / 32] |= (1U << (id % 32)); |
635 | 0 | commit_list_insert(c, &head); |
636 | 0 | while (head) { |
637 | 0 | struct commit_list *p; |
638 | 0 | struct commit *c = pop_commit(&head); |
639 | 0 | uint32_t **refs = ref_bitmap_at(&info->ref_bitmap, c); |
640 | | |
641 | | /* XXX check "UNINTERESTING" from pack bitmaps if available */ |
642 | 0 | if (c->object.flags & (SEEN | UNINTERESTING)) |
643 | 0 | continue; |
644 | 0 | else |
645 | 0 | c->object.flags |= SEEN; |
646 | | |
647 | 0 | if (!*refs) |
648 | 0 | *refs = bitmap; |
649 | 0 | else { |
650 | 0 | memcpy(tmp, *refs, bitmap_size); |
651 | 0 | for (i = 0; i < bitmap_nr; i++) |
652 | 0 | tmp[i] |= bitmap[i]; |
653 | 0 | if (memcmp(tmp, *refs, bitmap_size)) { |
654 | 0 | *refs = paint_alloc(info); |
655 | 0 | memcpy(*refs, tmp, bitmap_size); |
656 | 0 | } |
657 | 0 | } |
658 | |
|
659 | 0 | if (c->object.flags & BOTTOM) |
660 | 0 | continue; |
661 | | |
662 | 0 | if (repo_parse_commit(the_repository, c)) |
663 | 0 | die("unable to parse commit %s", |
664 | 0 | oid_to_hex(&c->object.oid)); |
665 | | |
666 | 0 | for (p = c->parents; p; p = p->next) { |
667 | 0 | if (p->item->object.flags & SEEN) |
668 | 0 | continue; |
669 | 0 | commit_list_insert(p->item, &head); |
670 | 0 | } |
671 | 0 | } |
672 | | |
673 | 0 | nr = get_max_object_index(the_repository); |
674 | 0 | for (i = 0; i < nr; i++) { |
675 | 0 | struct object *o = get_indexed_object(the_repository, i); |
676 | 0 | if (o && o->type == OBJ_COMMIT) |
677 | 0 | o->flags &= ~SEEN; |
678 | 0 | } |
679 | |
|
680 | 0 | free(tmp); |
681 | 0 | } |
682 | | |
683 | | static int mark_uninteresting(const struct reference *ref, void *cb_data UNUSED) |
684 | 0 | { |
685 | 0 | struct commit *commit = lookup_commit_reference_gently(the_repository, |
686 | 0 | ref->oid, 1); |
687 | 0 | if (!commit) |
688 | 0 | return 0; |
689 | 0 | commit->object.flags |= UNINTERESTING; |
690 | 0 | mark_parents_uninteresting(NULL, commit); |
691 | 0 | return 0; |
692 | 0 | } |
693 | | |
694 | | static void post_assign_shallow(struct shallow_info *info, |
695 | | struct ref_bitmap *ref_bitmap, |
696 | | int *ref_status); |
697 | | /* |
698 | | * Step 6(+7), associate shallow commits with new refs |
699 | | * |
700 | | * info->ref must be initialized before calling this function. |
701 | | * |
702 | | * If used is not NULL, it's an array of info->shallow->nr |
703 | | * bitmaps. The n-th bit set in the m-th bitmap if ref[n] needs the |
704 | | * m-th shallow commit from info->shallow. |
705 | | * |
706 | | * If used is NULL, "ours" and "theirs" are updated. And if ref_status |
707 | | * is not NULL it's an array of ref->nr ints. ref_status[i] is true if |
708 | | * the ref needs some shallow commits from either info->ours or |
709 | | * info->theirs. |
710 | | */ |
711 | | void assign_shallow_commits_to_refs(struct shallow_info *info, |
712 | | uint32_t **used, int *ref_status) |
713 | 0 | { |
714 | 0 | struct object_id *oid = info->shallow->oid; |
715 | 0 | struct oid_array *ref = info->ref; |
716 | 0 | unsigned int i, nr; |
717 | 0 | size_t *shallow, nr_shallow = 0; |
718 | 0 | struct paint_info pi; |
719 | |
|
720 | 0 | trace_printf_key(&trace_shallow, "shallow: assign_shallow_commits_to_refs\n"); |
721 | 0 | ALLOC_ARRAY(shallow, info->nr_ours + info->nr_theirs); |
722 | 0 | for (i = 0; i < info->nr_ours; i++) |
723 | 0 | shallow[nr_shallow++] = info->ours[i]; |
724 | 0 | for (i = 0; i < info->nr_theirs; i++) |
725 | 0 | shallow[nr_shallow++] = info->theirs[i]; |
726 | | |
727 | | /* |
728 | | * Prepare the commit graph to track what refs can reach what |
729 | | * (new) shallow commits. |
730 | | */ |
731 | 0 | nr = get_max_object_index(the_repository); |
732 | 0 | for (i = 0; i < nr; i++) { |
733 | 0 | struct object *o = get_indexed_object(the_repository, i); |
734 | 0 | if (!o || o->type != OBJ_COMMIT) |
735 | 0 | continue; |
736 | | |
737 | 0 | o->flags &= ~(UNINTERESTING | BOTTOM | SEEN); |
738 | 0 | } |
739 | |
|
740 | 0 | memset(&pi, 0, sizeof(pi)); |
741 | 0 | init_ref_bitmap(&pi.ref_bitmap); |
742 | 0 | pi.nr_bits = ref->nr; |
743 | | |
744 | | /* |
745 | | * "--not --all" to cut short the traversal if new refs |
746 | | * connect to old refs. If not (e.g. force ref updates) it'll |
747 | | * have to go down to the current shallow commits. |
748 | | */ |
749 | 0 | refs_head_ref(get_main_ref_store(the_repository), mark_uninteresting, |
750 | 0 | NULL); |
751 | 0 | refs_for_each_ref(get_main_ref_store(the_repository), |
752 | 0 | mark_uninteresting, NULL); |
753 | | |
754 | | /* Mark potential bottoms so we won't go out of bound */ |
755 | 0 | for (i = 0; i < nr_shallow; i++) { |
756 | 0 | struct commit *c = lookup_commit(the_repository, |
757 | 0 | &oid[shallow[i]]); |
758 | 0 | c->object.flags |= BOTTOM; |
759 | 0 | } |
760 | |
|
761 | 0 | for (i = 0; i < ref->nr; i++) |
762 | 0 | paint_down(&pi, ref->oid + i, i); |
763 | |
|
764 | 0 | if (used) { |
765 | 0 | int bitmap_size = DIV_ROUND_UP(pi.nr_bits, 32) * sizeof(uint32_t); |
766 | 0 | MEMZERO_ARRAY(used, info->shallow->nr); |
767 | 0 | for (i = 0; i < nr_shallow; i++) { |
768 | 0 | const struct commit *c = lookup_commit(the_repository, |
769 | 0 | &oid[shallow[i]]); |
770 | 0 | uint32_t **map = ref_bitmap_at(&pi.ref_bitmap, c); |
771 | 0 | if (*map) |
772 | 0 | used[shallow[i]] = xmemdupz(*map, bitmap_size); |
773 | 0 | } |
774 | | /* |
775 | | * unreachable shallow commits are not removed from |
776 | | * "ours" and "theirs". The user is supposed to run |
777 | | * step 7 on every ref separately and not trust "ours" |
778 | | * and "theirs" any more. |
779 | | */ |
780 | 0 | } else |
781 | 0 | post_assign_shallow(info, &pi.ref_bitmap, ref_status); |
782 | |
|
783 | 0 | clear_ref_bitmap(&pi.ref_bitmap); |
784 | 0 | for (i = 0; i < pi.pool_count; i++) |
785 | 0 | free(pi.pools[i]); |
786 | 0 | free(pi.pools); |
787 | 0 | free(shallow); |
788 | 0 | } |
789 | | |
790 | | static int add_ref(const struct reference *ref, void *cb_data) |
791 | 0 | { |
792 | 0 | struct commit_stack *cs = cb_data; |
793 | 0 | struct commit *commit = lookup_commit_reference_gently(the_repository, |
794 | 0 | ref->oid, 1); |
795 | 0 | if (commit) |
796 | 0 | commit_stack_push(cs, commit); |
797 | 0 | return 0; |
798 | 0 | } |
799 | | |
800 | | static void update_refstatus(int *ref_status, size_t nr, uint32_t *bitmap) |
801 | 0 | { |
802 | 0 | if (!ref_status) |
803 | 0 | return; |
804 | 0 | for (size_t i = 0; i < nr; i++) |
805 | 0 | if (bitmap[i / 32] & (1U << (i % 32))) |
806 | 0 | ref_status[i]++; |
807 | 0 | } |
808 | | |
809 | | /* |
810 | | * Step 7, reachability test on "ours" at commit level |
811 | | */ |
812 | | static void post_assign_shallow(struct shallow_info *info, |
813 | | struct ref_bitmap *ref_bitmap, |
814 | | int *ref_status) |
815 | 0 | { |
816 | 0 | struct object_id *oid = info->shallow->oid; |
817 | 0 | struct commit *c; |
818 | 0 | uint32_t **bitmap; |
819 | 0 | size_t dst, i, j; |
820 | 0 | size_t bitmap_nr = DIV_ROUND_UP(info->ref->nr, 32); |
821 | 0 | struct commit_stack cs = COMMIT_STACK_INIT; |
822 | |
|
823 | 0 | trace_printf_key(&trace_shallow, "shallow: post_assign_shallow\n"); |
824 | 0 | if (ref_status) |
825 | 0 | MEMZERO_ARRAY(ref_status, info->ref->nr); |
826 | | |
827 | | /* Remove unreachable shallow commits from "theirs" */ |
828 | 0 | for (i = dst = 0; i < info->nr_theirs; i++) { |
829 | 0 | if (i != dst) |
830 | 0 | info->theirs[dst] = info->theirs[i]; |
831 | 0 | c = lookup_commit(the_repository, &oid[info->theirs[i]]); |
832 | 0 | bitmap = ref_bitmap_at(ref_bitmap, c); |
833 | 0 | if (!*bitmap) |
834 | 0 | continue; |
835 | 0 | for (j = 0; j < bitmap_nr; j++) |
836 | 0 | if (bitmap[0][j]) { |
837 | 0 | update_refstatus(ref_status, info->ref->nr, *bitmap); |
838 | 0 | dst++; |
839 | 0 | break; |
840 | 0 | } |
841 | 0 | } |
842 | 0 | info->nr_theirs = dst; |
843 | |
|
844 | 0 | refs_head_ref(get_main_ref_store(the_repository), add_ref, &cs); |
845 | 0 | refs_for_each_ref(get_main_ref_store(the_repository), add_ref, &cs); |
846 | | |
847 | | /* Remove unreachable shallow commits from "ours" */ |
848 | 0 | for (i = dst = 0; i < info->nr_ours; i++) { |
849 | 0 | if (i != dst) |
850 | 0 | info->ours[dst] = info->ours[i]; |
851 | 0 | c = lookup_commit(the_repository, &oid[info->ours[i]]); |
852 | 0 | bitmap = ref_bitmap_at(ref_bitmap, c); |
853 | 0 | if (!*bitmap) |
854 | 0 | continue; |
855 | 0 | for (j = 0; j < bitmap_nr; j++) |
856 | 0 | if (bitmap[0][j]) { |
857 | | /* Step 7, reachability test at commit level */ |
858 | 0 | int ret = repo_in_merge_bases_many(the_repository, c, cs.nr, cs.items, 1); |
859 | 0 | if (ret < 0) |
860 | 0 | exit(128); |
861 | 0 | if (!ret) { |
862 | 0 | update_refstatus(ref_status, info->ref->nr, *bitmap); |
863 | 0 | dst++; |
864 | 0 | break; |
865 | 0 | } |
866 | 0 | } |
867 | 0 | } |
868 | 0 | info->nr_ours = dst; |
869 | |
|
870 | 0 | commit_stack_clear(&cs); |
871 | 0 | } |
872 | | |
873 | | /* (Delayed) step 7, reachability test at commit level */ |
874 | | int delayed_reachability_test(struct shallow_info *si, int c) |
875 | 0 | { |
876 | 0 | if (si->need_reachability_test[c]) { |
877 | 0 | struct commit *commit = lookup_commit(the_repository, |
878 | 0 | &si->shallow->oid[c]); |
879 | |
|
880 | 0 | if (!si->commits.nr) { |
881 | 0 | refs_head_ref(get_main_ref_store(the_repository), |
882 | 0 | add_ref, &si->commits); |
883 | 0 | refs_for_each_ref(get_main_ref_store(the_repository), |
884 | 0 | add_ref, &si->commits); |
885 | 0 | } |
886 | |
|
887 | 0 | si->reachable[c] = repo_in_merge_bases_many(the_repository, |
888 | 0 | commit, |
889 | 0 | si->commits.nr, |
890 | 0 | si->commits.items, |
891 | 0 | 1); |
892 | 0 | if (si->reachable[c] < 0) |
893 | 0 | exit(128); |
894 | 0 | si->need_reachability_test[c] = 0; |
895 | 0 | } |
896 | 0 | return si->reachable[c]; |
897 | 0 | } |