Line | Count | Source |
1 | | #include "git-compat-util.h" |
2 | | #include "abspath.h" |
3 | | #include "chdir-notify.h" |
4 | | #include "commit-graph.h" |
5 | | #include "config.h" |
6 | | #include "dir.h" |
7 | | #include "environment.h" |
8 | | #include "gettext.h" |
9 | | #include "hex.h" |
10 | | #include "khash.h" |
11 | | #include "lockfile.h" |
12 | | #include "loose.h" |
13 | | #include "midx.h" |
14 | | #include "object-file-convert.h" |
15 | | #include "object-file.h" |
16 | | #include "odb.h" |
17 | | #include "packfile.h" |
18 | | #include "path.h" |
19 | | #include "promisor-remote.h" |
20 | | #include "quote.h" |
21 | | #include "replace-object.h" |
22 | | #include "run-command.h" |
23 | | #include "setup.h" |
24 | | #include "strbuf.h" |
25 | | #include "strvec.h" |
26 | | #include "submodule.h" |
27 | | #include "tmp-objdir.h" |
28 | | #include "trace2.h" |
29 | | #include "write-or-die.h" |
30 | | |
31 | 0 | KHASH_INIT(odb_path_map, const char * /* key: odb_path */, |
32 | 0 | struct odb_source *, 1, fspathhash, fspatheq) |
33 | 0 |
|
34 | 0 | /* |
35 | 0 | * This is meant to hold a *small* number of objects that you would |
36 | 0 | * want odb_read_object() to be able to return, but yet you do not want |
37 | 0 | * to write them into the object store (e.g. a browse-only |
38 | 0 | * application). |
39 | 0 | */ |
40 | 0 | struct cached_object_entry { |
41 | 0 | struct object_id oid; |
42 | 0 | struct cached_object { |
43 | 0 | enum object_type type; |
44 | 0 | const void *buf; |
45 | 0 | unsigned long size; |
46 | 0 | } value; |
47 | 0 | }; |
48 | 0 |
|
49 | 0 | static const struct cached_object *find_cached_object(struct object_database *object_store, |
50 | 0 | const struct object_id *oid) |
51 | 0 | { |
52 | 0 | static const struct cached_object empty_tree = { |
53 | 0 | .type = OBJ_TREE, |
54 | 0 | .buf = "", |
55 | 0 | }; |
56 | 0 | const struct cached_object_entry *co = object_store->cached_objects; |
57 | |
|
58 | 0 | for (size_t i = 0; i < object_store->cached_object_nr; i++, co++) |
59 | 0 | if (oideq(&co->oid, oid)) |
60 | 0 | return &co->value; |
61 | | |
62 | 0 | if (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree)) |
63 | 0 | return &empty_tree; |
64 | | |
65 | 0 | return NULL; |
66 | 0 | } |
67 | | |
68 | | int odb_mkstemp(struct object_database *odb, |
69 | | struct strbuf *temp_filename, const char *pattern) |
70 | 0 | { |
71 | 0 | int fd; |
72 | | /* |
73 | | * we let the umask do its job, don't try to be more |
74 | | * restrictive except to remove write permission. |
75 | | */ |
76 | 0 | int mode = 0444; |
77 | 0 | repo_git_path_replace(odb->repo, temp_filename, "objects/%s", pattern); |
78 | 0 | fd = git_mkstemp_mode(temp_filename->buf, mode); |
79 | 0 | if (0 <= fd) |
80 | 0 | return fd; |
81 | | |
82 | | /* slow path */ |
83 | | /* some mkstemp implementations erase temp_filename on failure */ |
84 | 0 | repo_git_path_replace(odb->repo, temp_filename, "objects/%s", pattern); |
85 | 0 | safe_create_leading_directories(odb->repo, temp_filename->buf); |
86 | 0 | return xmkstemp_mode(temp_filename->buf, mode); |
87 | 0 | } |
88 | | |
89 | | /* |
90 | | * Return non-zero iff the path is usable as an alternate object database. |
91 | | */ |
92 | | static bool odb_is_source_usable(struct object_database *o, const char *path) |
93 | 0 | { |
94 | 0 | int r; |
95 | 0 | struct strbuf normalized_objdir = STRBUF_INIT; |
96 | 0 | bool usable = false; |
97 | |
|
98 | 0 | strbuf_realpath(&normalized_objdir, o->sources->path, 1); |
99 | | |
100 | | /* Detect cases where alternate disappeared */ |
101 | 0 | if (!is_directory(path)) { |
102 | 0 | error(_("object directory %s does not exist; " |
103 | 0 | "check .git/objects/info/alternates"), |
104 | 0 | path); |
105 | 0 | goto out; |
106 | 0 | } |
107 | | |
108 | | /* |
109 | | * Prevent the common mistake of listing the same |
110 | | * thing twice, or object directory itself. |
111 | | */ |
112 | 0 | if (!o->source_by_path) { |
113 | 0 | khiter_t p; |
114 | |
|
115 | 0 | o->source_by_path = kh_init_odb_path_map(); |
116 | 0 | assert(!o->sources->next); |
117 | 0 | p = kh_put_odb_path_map(o->source_by_path, o->sources->path, &r); |
118 | 0 | assert(r == 1); /* never used */ |
119 | 0 | kh_value(o->source_by_path, p) = o->sources; |
120 | 0 | } |
121 | |
|
122 | 0 | if (fspatheq(path, normalized_objdir.buf)) |
123 | 0 | goto out; |
124 | | |
125 | 0 | if (kh_get_odb_path_map(o->source_by_path, path) < kh_end(o->source_by_path)) |
126 | 0 | goto out; |
127 | | |
128 | 0 | usable = true; |
129 | |
|
130 | 0 | out: |
131 | 0 | strbuf_release(&normalized_objdir); |
132 | 0 | return usable; |
133 | 0 | } |
134 | | |
135 | | static void parse_alternates(const char *string, |
136 | | int sep, |
137 | | const char *relative_base, |
138 | | struct strvec *out) |
139 | 0 | { |
140 | 0 | struct strbuf pathbuf = STRBUF_INIT; |
141 | 0 | struct strbuf buf = STRBUF_INIT; |
142 | |
|
143 | 0 | if (!string || !*string) |
144 | 0 | return; |
145 | | |
146 | 0 | while (*string) { |
147 | 0 | const char *end; |
148 | |
|
149 | 0 | strbuf_reset(&buf); |
150 | 0 | strbuf_reset(&pathbuf); |
151 | |
|
152 | 0 | if (*string == '#') { |
153 | | /* comment; consume up to next separator */ |
154 | 0 | end = strchrnul(string, sep); |
155 | 0 | } else if (*string == '"' && !unquote_c_style(&buf, string, &end)) { |
156 | | /* |
157 | | * quoted path; unquote_c_style has copied the |
158 | | * data for us and set "end". Broken quoting (e.g., |
159 | | * an entry that doesn't end with a quote) falls |
160 | | * back to the unquoted case below. |
161 | | */ |
162 | 0 | } else { |
163 | | /* normal, unquoted path */ |
164 | 0 | end = strchrnul(string, sep); |
165 | 0 | strbuf_add(&buf, string, end - string); |
166 | 0 | } |
167 | |
|
168 | 0 | if (*end) |
169 | 0 | end++; |
170 | 0 | string = end; |
171 | |
|
172 | 0 | if (!buf.len) |
173 | 0 | continue; |
174 | | |
175 | 0 | if (!is_absolute_path(buf.buf) && relative_base) { |
176 | 0 | strbuf_realpath(&pathbuf, relative_base, 1); |
177 | 0 | strbuf_addch(&pathbuf, '/'); |
178 | 0 | } |
179 | 0 | strbuf_addbuf(&pathbuf, &buf); |
180 | |
|
181 | 0 | strbuf_reset(&buf); |
182 | 0 | if (!strbuf_realpath(&buf, pathbuf.buf, 0)) { |
183 | 0 | error(_("unable to normalize alternate object path: %s"), |
184 | 0 | pathbuf.buf); |
185 | 0 | continue; |
186 | 0 | } |
187 | | |
188 | | /* |
189 | | * The trailing slash after the directory name is given by |
190 | | * this function at the end. Remove duplicates. |
191 | | */ |
192 | 0 | while (buf.len && buf.buf[buf.len - 1] == '/') |
193 | 0 | strbuf_setlen(&buf, buf.len - 1); |
194 | |
|
195 | 0 | strvec_push(out, buf.buf); |
196 | 0 | } |
197 | |
|
198 | 0 | strbuf_release(&pathbuf); |
199 | 0 | strbuf_release(&buf); |
200 | 0 | } |
201 | | |
202 | | static void odb_source_read_alternates(struct odb_source *source, |
203 | | struct strvec *out) |
204 | 0 | { |
205 | 0 | struct strbuf buf = STRBUF_INIT; |
206 | 0 | char *path; |
207 | |
|
208 | 0 | path = xstrfmt("%s/info/alternates", source->path); |
209 | 0 | if (strbuf_read_file(&buf, path, 1024) < 0) { |
210 | 0 | warn_on_fopen_errors(path); |
211 | 0 | free(path); |
212 | 0 | return; |
213 | 0 | } |
214 | 0 | parse_alternates(buf.buf, '\n', source->path, out); |
215 | |
|
216 | 0 | strbuf_release(&buf); |
217 | 0 | free(path); |
218 | 0 | } |
219 | | |
220 | | |
221 | | static struct odb_source *odb_source_new(struct object_database *odb, |
222 | | const char *path, |
223 | | bool local) |
224 | 0 | { |
225 | 0 | struct odb_source *source; |
226 | |
|
227 | 0 | CALLOC_ARRAY(source, 1); |
228 | 0 | source->odb = odb; |
229 | 0 | source->local = local; |
230 | 0 | source->path = xstrdup(path); |
231 | 0 | source->loose = odb_source_loose_new(source); |
232 | 0 | source->packfiles = packfile_store_new(source); |
233 | |
|
234 | 0 | return source; |
235 | 0 | } |
236 | | |
237 | | static struct odb_source *odb_add_alternate_recursively(struct object_database *odb, |
238 | | const char *source, |
239 | | int depth) |
240 | 0 | { |
241 | 0 | struct odb_source *alternate = NULL; |
242 | 0 | struct strvec sources = STRVEC_INIT; |
243 | 0 | khiter_t pos; |
244 | 0 | int ret; |
245 | |
|
246 | 0 | if (!odb_is_source_usable(odb, source)) |
247 | 0 | goto error; |
248 | | |
249 | 0 | alternate = odb_source_new(odb, source, false); |
250 | | |
251 | | /* add the alternate entry */ |
252 | 0 | *odb->sources_tail = alternate; |
253 | 0 | odb->sources_tail = &(alternate->next); |
254 | |
|
255 | 0 | pos = kh_put_odb_path_map(odb->source_by_path, alternate->path, &ret); |
256 | 0 | if (!ret) |
257 | 0 | BUG("source must not yet exist"); |
258 | 0 | kh_value(odb->source_by_path, pos) = alternate; |
259 | | |
260 | | /* recursively add alternates */ |
261 | 0 | odb_source_read_alternates(alternate, &sources); |
262 | 0 | if (sources.nr && depth + 1 > 5) { |
263 | 0 | error(_("%s: ignoring alternate object stores, nesting too deep"), |
264 | 0 | source); |
265 | 0 | } else { |
266 | 0 | for (size_t i = 0; i < sources.nr; i++) |
267 | 0 | odb_add_alternate_recursively(odb, sources.v[i], depth + 1); |
268 | 0 | } |
269 | |
|
270 | 0 | error: |
271 | 0 | strvec_clear(&sources); |
272 | 0 | return alternate; |
273 | 0 | } |
274 | | |
275 | | static int odb_source_write_alternate(struct odb_source *source, |
276 | | const char *alternate) |
277 | 0 | { |
278 | 0 | struct lock_file lock = LOCK_INIT; |
279 | 0 | char *path = xstrfmt("%s/%s", source->path, "info/alternates"); |
280 | 0 | FILE *in, *out; |
281 | 0 | int found = 0; |
282 | 0 | int ret; |
283 | |
|
284 | 0 | hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); |
285 | 0 | out = fdopen_lock_file(&lock, "w"); |
286 | 0 | if (!out) { |
287 | 0 | ret = error_errno(_("unable to fdopen alternates lockfile")); |
288 | 0 | goto out; |
289 | 0 | } |
290 | | |
291 | 0 | in = fopen(path, "r"); |
292 | 0 | if (in) { |
293 | 0 | struct strbuf line = STRBUF_INIT; |
294 | |
|
295 | 0 | while (strbuf_getline(&line, in) != EOF) { |
296 | 0 | if (!strcmp(alternate, line.buf)) { |
297 | 0 | found = 1; |
298 | 0 | break; |
299 | 0 | } |
300 | 0 | fprintf_or_die(out, "%s\n", line.buf); |
301 | 0 | } |
302 | |
|
303 | 0 | strbuf_release(&line); |
304 | 0 | fclose(in); |
305 | 0 | } else if (errno != ENOENT) { |
306 | 0 | ret = error_errno(_("unable to read alternates file")); |
307 | 0 | goto out; |
308 | 0 | } |
309 | | |
310 | 0 | if (found) { |
311 | 0 | rollback_lock_file(&lock); |
312 | 0 | } else { |
313 | 0 | fprintf_or_die(out, "%s\n", alternate); |
314 | 0 | if (commit_lock_file(&lock)) { |
315 | 0 | ret = error_errno(_("unable to move new alternates file into place")); |
316 | 0 | goto out; |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | 0 | ret = 0; |
321 | |
|
322 | 0 | out: |
323 | 0 | free(path); |
324 | 0 | return ret; |
325 | 0 | } |
326 | | |
327 | | void odb_add_to_alternates_file(struct object_database *odb, |
328 | | const char *dir) |
329 | 0 | { |
330 | 0 | int ret = odb_source_write_alternate(odb->sources, dir); |
331 | 0 | if (ret < 0) |
332 | 0 | die(NULL); |
333 | 0 | if (odb->loaded_alternates) |
334 | 0 | odb_add_alternate_recursively(odb, dir, 0); |
335 | 0 | } |
336 | | |
337 | | struct odb_source *odb_add_to_alternates_memory(struct object_database *odb, |
338 | | const char *dir) |
339 | 0 | { |
340 | | /* |
341 | | * Make sure alternates are initialized, or else our entry may be |
342 | | * overwritten when they are. |
343 | | */ |
344 | 0 | odb_prepare_alternates(odb); |
345 | 0 | return odb_add_alternate_recursively(odb, dir, 0); |
346 | 0 | } |
347 | | |
348 | | struct odb_source *odb_set_temporary_primary_source(struct object_database *odb, |
349 | | const char *dir, int will_destroy) |
350 | 0 | { |
351 | 0 | struct odb_source *source; |
352 | | |
353 | | /* |
354 | | * Make sure alternates are initialized, or else our entry may be |
355 | | * overwritten when they are. |
356 | | */ |
357 | 0 | odb_prepare_alternates(odb); |
358 | | |
359 | | /* |
360 | | * Make a new primary odb and link the old primary ODB in as an |
361 | | * alternate |
362 | | */ |
363 | 0 | source = odb_source_new(odb, dir, false); |
364 | | |
365 | | /* |
366 | | * Disable ref updates while a temporary odb is active, since |
367 | | * the objects in the database may roll back. |
368 | | */ |
369 | 0 | odb->repo->disable_ref_updates = true; |
370 | 0 | source->will_destroy = will_destroy; |
371 | 0 | source->next = odb->sources; |
372 | 0 | odb->sources = source; |
373 | 0 | return source->next; |
374 | 0 | } |
375 | | |
376 | | static void odb_source_free(struct odb_source *source) |
377 | 0 | { |
378 | 0 | free(source->path); |
379 | 0 | odb_source_loose_free(source->loose); |
380 | 0 | packfile_store_free(source->packfiles); |
381 | 0 | free(source); |
382 | 0 | } |
383 | | |
384 | | void odb_restore_primary_source(struct object_database *odb, |
385 | | struct odb_source *restore_source, |
386 | | const char *old_path) |
387 | 0 | { |
388 | 0 | struct odb_source *cur_source = odb->sources; |
389 | |
|
390 | 0 | if (strcmp(old_path, cur_source->path)) |
391 | 0 | BUG("expected %s as primary object store; found %s", |
392 | 0 | old_path, cur_source->path); |
393 | | |
394 | 0 | if (cur_source->next != restore_source) |
395 | 0 | BUG("we expect the old primary object store to be the first alternate"); |
396 | | |
397 | 0 | odb->repo->disable_ref_updates = false; |
398 | 0 | odb->sources = restore_source; |
399 | 0 | odb_source_free(cur_source); |
400 | 0 | } |
401 | | |
402 | | char *compute_alternate_path(const char *path, struct strbuf *err) |
403 | 0 | { |
404 | 0 | char *ref_git = NULL; |
405 | 0 | const char *repo; |
406 | 0 | int seen_error = 0; |
407 | |
|
408 | 0 | ref_git = real_pathdup(path, 0); |
409 | 0 | if (!ref_git) { |
410 | 0 | seen_error = 1; |
411 | 0 | strbuf_addf(err, _("path '%s' does not exist"), path); |
412 | 0 | goto out; |
413 | 0 | } |
414 | | |
415 | 0 | repo = read_gitfile(ref_git); |
416 | 0 | if (!repo) |
417 | 0 | repo = read_gitfile(mkpath("%s/.git", ref_git)); |
418 | 0 | if (repo) { |
419 | 0 | free(ref_git); |
420 | 0 | ref_git = xstrdup(repo); |
421 | 0 | } |
422 | |
|
423 | 0 | if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) { |
424 | 0 | char *ref_git_git = mkpathdup("%s/.git", ref_git); |
425 | 0 | free(ref_git); |
426 | 0 | ref_git = ref_git_git; |
427 | 0 | } else if (!is_directory(mkpath("%s/objects", ref_git))) { |
428 | 0 | struct strbuf sb = STRBUF_INIT; |
429 | 0 | seen_error = 1; |
430 | 0 | if (get_common_dir(&sb, ref_git)) { |
431 | 0 | strbuf_addf(err, |
432 | 0 | _("reference repository '%s' as a linked " |
433 | 0 | "checkout is not supported yet."), |
434 | 0 | path); |
435 | 0 | goto out; |
436 | 0 | } |
437 | | |
438 | 0 | strbuf_addf(err, _("reference repository '%s' is not a " |
439 | 0 | "local repository."), path); |
440 | 0 | goto out; |
441 | 0 | } |
442 | | |
443 | 0 | if (!access(mkpath("%s/shallow", ref_git), F_OK)) { |
444 | 0 | strbuf_addf(err, _("reference repository '%s' is shallow"), |
445 | 0 | path); |
446 | 0 | seen_error = 1; |
447 | 0 | goto out; |
448 | 0 | } |
449 | | |
450 | 0 | if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) { |
451 | 0 | strbuf_addf(err, |
452 | 0 | _("reference repository '%s' is grafted"), |
453 | 0 | path); |
454 | 0 | seen_error = 1; |
455 | 0 | goto out; |
456 | 0 | } |
457 | | |
458 | 0 | out: |
459 | 0 | if (seen_error) { |
460 | 0 | FREE_AND_NULL(ref_git); |
461 | 0 | } |
462 | |
|
463 | 0 | return ref_git; |
464 | 0 | } |
465 | | |
466 | | struct odb_source *odb_find_source(struct object_database *odb, const char *obj_dir) |
467 | 0 | { |
468 | 0 | struct odb_source *source; |
469 | 0 | char *obj_dir_real = real_pathdup(obj_dir, 1); |
470 | 0 | struct strbuf odb_path_real = STRBUF_INIT; |
471 | |
|
472 | 0 | odb_prepare_alternates(odb); |
473 | 0 | for (source = odb->sources; source; source = source->next) { |
474 | 0 | strbuf_realpath(&odb_path_real, source->path, 1); |
475 | 0 | if (!strcmp(obj_dir_real, odb_path_real.buf)) |
476 | 0 | break; |
477 | 0 | } |
478 | |
|
479 | 0 | free(obj_dir_real); |
480 | 0 | strbuf_release(&odb_path_real); |
481 | |
|
482 | 0 | return source; |
483 | 0 | } |
484 | | |
485 | | struct odb_source *odb_find_source_or_die(struct object_database *odb, const char *obj_dir) |
486 | 0 | { |
487 | 0 | struct odb_source *source = odb_find_source(odb, obj_dir); |
488 | 0 | if (!source) |
489 | 0 | die(_("could not find object directory matching %s"), obj_dir); |
490 | 0 | return source; |
491 | 0 | } |
492 | | |
493 | | void odb_add_submodule_source_by_path(struct object_database *odb, |
494 | | const char *path) |
495 | 0 | { |
496 | 0 | string_list_insert(&odb->submodule_source_paths, path); |
497 | 0 | } |
498 | | |
499 | | static void fill_alternate_refs_command(struct repository *repo, |
500 | | struct child_process *cmd, |
501 | | const char *repo_path) |
502 | 0 | { |
503 | 0 | const char *value; |
504 | |
|
505 | 0 | if (!repo_config_get_value(repo, "core.alternateRefsCommand", &value)) { |
506 | 0 | cmd->use_shell = 1; |
507 | |
|
508 | 0 | strvec_push(&cmd->args, value); |
509 | 0 | strvec_push(&cmd->args, repo_path); |
510 | 0 | } else { |
511 | 0 | cmd->git_cmd = 1; |
512 | |
|
513 | 0 | strvec_pushf(&cmd->args, "--git-dir=%s", repo_path); |
514 | 0 | strvec_push(&cmd->args, "for-each-ref"); |
515 | 0 | strvec_push(&cmd->args, "--format=%(objectname)"); |
516 | |
|
517 | 0 | if (!repo_config_get_value(repo, "core.alternateRefsPrefixes", &value)) { |
518 | 0 | strvec_push(&cmd->args, "--"); |
519 | 0 | strvec_split(&cmd->args, value); |
520 | 0 | } |
521 | 0 | } |
522 | |
|
523 | 0 | strvec_pushv(&cmd->env, (const char **)local_repo_env); |
524 | 0 | cmd->out = -1; |
525 | 0 | } |
526 | | |
527 | | static void read_alternate_refs(struct repository *repo, |
528 | | const char *path, |
529 | | odb_for_each_alternate_ref_fn *cb, |
530 | | void *payload) |
531 | 0 | { |
532 | 0 | struct child_process cmd = CHILD_PROCESS_INIT; |
533 | 0 | struct strbuf line = STRBUF_INIT; |
534 | 0 | FILE *fh; |
535 | |
|
536 | 0 | fill_alternate_refs_command(repo, &cmd, path); |
537 | |
|
538 | 0 | if (start_command(&cmd)) |
539 | 0 | return; |
540 | | |
541 | 0 | fh = xfdopen(cmd.out, "r"); |
542 | 0 | while (strbuf_getline_lf(&line, fh) != EOF) { |
543 | 0 | struct object_id oid; |
544 | 0 | const char *p; |
545 | |
|
546 | 0 | if (parse_oid_hex_algop(line.buf, &oid, &p, repo->hash_algo) || *p) { |
547 | 0 | warning(_("invalid line while parsing alternate refs: %s"), |
548 | 0 | line.buf); |
549 | 0 | break; |
550 | 0 | } |
551 | | |
552 | 0 | cb(&oid, payload); |
553 | 0 | } |
554 | |
|
555 | 0 | fclose(fh); |
556 | 0 | finish_command(&cmd); |
557 | 0 | strbuf_release(&line); |
558 | 0 | } |
559 | | |
560 | | struct alternate_refs_data { |
561 | | odb_for_each_alternate_ref_fn *fn; |
562 | | void *payload; |
563 | | }; |
564 | | |
565 | | static int refs_from_alternate_cb(struct odb_source *alternate, |
566 | | void *payload) |
567 | 0 | { |
568 | 0 | struct strbuf path = STRBUF_INIT; |
569 | 0 | size_t base_len; |
570 | 0 | struct alternate_refs_data *cb = payload; |
571 | |
|
572 | 0 | if (!strbuf_realpath(&path, alternate->path, 0)) |
573 | 0 | goto out; |
574 | 0 | if (!strbuf_strip_suffix(&path, "/objects")) |
575 | 0 | goto out; |
576 | 0 | base_len = path.len; |
577 | | |
578 | | /* Is this a git repository with refs? */ |
579 | 0 | strbuf_addstr(&path, "/refs"); |
580 | 0 | if (!is_directory(path.buf)) |
581 | 0 | goto out; |
582 | 0 | strbuf_setlen(&path, base_len); |
583 | |
|
584 | 0 | read_alternate_refs(alternate->odb->repo, path.buf, cb->fn, cb->payload); |
585 | |
|
586 | 0 | out: |
587 | 0 | strbuf_release(&path); |
588 | 0 | return 0; |
589 | 0 | } |
590 | | |
591 | | void odb_for_each_alternate_ref(struct object_database *odb, |
592 | | odb_for_each_alternate_ref_fn cb, void *payload) |
593 | 0 | { |
594 | 0 | struct alternate_refs_data data; |
595 | 0 | data.fn = cb; |
596 | 0 | data.payload = payload; |
597 | 0 | odb_for_each_alternate(odb, refs_from_alternate_cb, &data); |
598 | 0 | } |
599 | | |
600 | | int odb_for_each_alternate(struct object_database *odb, |
601 | | odb_for_each_alternate_fn cb, void *payload) |
602 | 0 | { |
603 | 0 | struct odb_source *alternate; |
604 | 0 | int r = 0; |
605 | |
|
606 | 0 | odb_prepare_alternates(odb); |
607 | 0 | for (alternate = odb->sources->next; alternate; alternate = alternate->next) { |
608 | 0 | r = cb(alternate, payload); |
609 | 0 | if (r) |
610 | 0 | break; |
611 | 0 | } |
612 | 0 | return r; |
613 | 0 | } |
614 | | |
615 | | void odb_prepare_alternates(struct object_database *odb) |
616 | 0 | { |
617 | 0 | struct strvec sources = STRVEC_INIT; |
618 | |
|
619 | 0 | if (odb->loaded_alternates) |
620 | 0 | return; |
621 | | |
622 | 0 | parse_alternates(odb->alternate_db, PATH_SEP, NULL, &sources); |
623 | 0 | odb_source_read_alternates(odb->sources, &sources); |
624 | 0 | for (size_t i = 0; i < sources.nr; i++) |
625 | 0 | odb_add_alternate_recursively(odb, sources.v[i], 0); |
626 | |
|
627 | 0 | odb->loaded_alternates = 1; |
628 | |
|
629 | 0 | strvec_clear(&sources); |
630 | 0 | } |
631 | | |
632 | | int odb_has_alternates(struct object_database *odb) |
633 | 0 | { |
634 | 0 | odb_prepare_alternates(odb); |
635 | 0 | return !!odb->sources->next; |
636 | 0 | } |
637 | | |
638 | | int obj_read_use_lock = 0; |
639 | | pthread_mutex_t obj_read_mutex; |
640 | | |
641 | | void enable_obj_read_lock(void) |
642 | 0 | { |
643 | 0 | if (obj_read_use_lock) |
644 | 0 | return; |
645 | | |
646 | 0 | obj_read_use_lock = 1; |
647 | 0 | init_recursive_mutex(&obj_read_mutex); |
648 | 0 | } |
649 | | |
650 | | void disable_obj_read_lock(void) |
651 | 0 | { |
652 | 0 | if (!obj_read_use_lock) |
653 | 0 | return; |
654 | | |
655 | 0 | obj_read_use_lock = 0; |
656 | 0 | pthread_mutex_destroy(&obj_read_mutex); |
657 | 0 | } |
658 | | |
659 | | int fetch_if_missing = 1; |
660 | | |
661 | | static int register_all_submodule_sources(struct object_database *odb) |
662 | 0 | { |
663 | 0 | int ret = odb->submodule_source_paths.nr; |
664 | |
|
665 | 0 | for (size_t i = 0; i < odb->submodule_source_paths.nr; i++) |
666 | 0 | odb_add_to_alternates_memory(odb, |
667 | 0 | odb->submodule_source_paths.items[i].string); |
668 | 0 | if (ret) { |
669 | 0 | string_list_clear(&odb->submodule_source_paths, 0); |
670 | 0 | trace2_data_intmax("submodule", odb->repo, |
671 | 0 | "register_all_submodule_sources/registered", ret); |
672 | 0 | if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0)) |
673 | 0 | BUG("register_all_submodule_sources() called"); |
674 | 0 | } |
675 | 0 | return ret; |
676 | 0 | } |
677 | | |
678 | | static int do_oid_object_info_extended(struct object_database *odb, |
679 | | const struct object_id *oid, |
680 | | struct object_info *oi, unsigned flags) |
681 | 0 | { |
682 | 0 | const struct cached_object *co; |
683 | 0 | const struct object_id *real = oid; |
684 | 0 | int already_retried = 0; |
685 | |
|
686 | 0 | if (flags & OBJECT_INFO_LOOKUP_REPLACE) |
687 | 0 | real = lookup_replace_object(odb->repo, oid); |
688 | |
|
689 | 0 | if (is_null_oid(real)) |
690 | 0 | return -1; |
691 | | |
692 | 0 | co = find_cached_object(odb, real); |
693 | 0 | if (co) { |
694 | 0 | if (oi) { |
695 | 0 | if (oi->typep) |
696 | 0 | *(oi->typep) = co->type; |
697 | 0 | if (oi->sizep) |
698 | 0 | *(oi->sizep) = co->size; |
699 | 0 | if (oi->disk_sizep) |
700 | 0 | *(oi->disk_sizep) = 0; |
701 | 0 | if (oi->delta_base_oid) |
702 | 0 | oidclr(oi->delta_base_oid, odb->repo->hash_algo); |
703 | 0 | if (oi->contentp) |
704 | 0 | *oi->contentp = xmemdupz(co->buf, co->size); |
705 | 0 | oi->whence = OI_CACHED; |
706 | 0 | } |
707 | 0 | return 0; |
708 | 0 | } |
709 | | |
710 | 0 | odb_prepare_alternates(odb); |
711 | |
|
712 | 0 | while (1) { |
713 | 0 | struct odb_source *source; |
714 | | |
715 | | /* Most likely it's a loose object. */ |
716 | 0 | for (source = odb->sources; source; source = source->next) { |
717 | 0 | if (!packfile_store_read_object_info(source->packfiles, real, oi, flags) || |
718 | 0 | !odb_source_loose_read_object_info(source, real, oi, flags)) |
719 | 0 | return 0; |
720 | 0 | } |
721 | | |
722 | | /* Not a loose object; someone else may have just packed it. */ |
723 | 0 | if (!(flags & OBJECT_INFO_QUICK)) { |
724 | 0 | odb_reprepare(odb->repo->objects); |
725 | 0 | for (source = odb->sources; source; source = source->next) |
726 | 0 | if (!packfile_store_read_object_info(source->packfiles, real, oi, flags)) |
727 | 0 | return 0; |
728 | 0 | } |
729 | | |
730 | | /* |
731 | | * This might be an attempt at accessing a submodule object as |
732 | | * if it were in main object store (having called |
733 | | * `odb_add_submodule_source_by_path()` on that submodule's |
734 | | * ODB). If any such ODBs exist, register them and try again. |
735 | | */ |
736 | 0 | if (register_all_submodule_sources(odb)) |
737 | | /* We added some alternates; retry */ |
738 | 0 | continue; |
739 | | |
740 | | /* Check if it is a missing object */ |
741 | 0 | if (fetch_if_missing && repo_has_promisor_remote(odb->repo) && |
742 | 0 | !already_retried && |
743 | 0 | !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) { |
744 | 0 | promisor_remote_get_direct(odb->repo, real, 1); |
745 | 0 | already_retried = 1; |
746 | 0 | continue; |
747 | 0 | } |
748 | | |
749 | 0 | if (flags & OBJECT_INFO_DIE_IF_CORRUPT) { |
750 | 0 | const struct packed_git *p; |
751 | 0 | if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid)) |
752 | 0 | die(_("replacement %s not found for %s"), |
753 | 0 | oid_to_hex(real), oid_to_hex(oid)); |
754 | 0 | if ((p = has_packed_and_bad(odb->repo, real))) |
755 | 0 | die(_("packed object %s (stored in %s) is corrupt"), |
756 | 0 | oid_to_hex(real), p->pack_name); |
757 | 0 | } |
758 | 0 | return -1; |
759 | 0 | } |
760 | 0 | } |
761 | | |
762 | | static int oid_object_info_convert(struct repository *r, |
763 | | const struct object_id *input_oid, |
764 | | struct object_info *input_oi, unsigned flags) |
765 | 0 | { |
766 | 0 | const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo]; |
767 | 0 | int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT; |
768 | 0 | enum object_type type; |
769 | 0 | struct object_id oid, delta_base_oid; |
770 | 0 | struct object_info new_oi, *oi; |
771 | 0 | unsigned long size; |
772 | 0 | void *content; |
773 | 0 | int ret; |
774 | |
|
775 | 0 | if (repo_oid_to_algop(r, input_oid, r->hash_algo, &oid)) { |
776 | 0 | if (do_die) |
777 | 0 | die(_("missing mapping of %s to %s"), |
778 | 0 | oid_to_hex(input_oid), r->hash_algo->name); |
779 | 0 | return -1; |
780 | 0 | } |
781 | | |
782 | | /* Is new_oi needed? */ |
783 | 0 | oi = input_oi; |
784 | 0 | if (input_oi && (input_oi->delta_base_oid || input_oi->sizep || |
785 | 0 | input_oi->contentp)) { |
786 | 0 | new_oi = *input_oi; |
787 | | /* Does delta_base_oid need to be converted? */ |
788 | 0 | if (input_oi->delta_base_oid) |
789 | 0 | new_oi.delta_base_oid = &delta_base_oid; |
790 | | /* Will the attributes differ when converted? */ |
791 | 0 | if (input_oi->sizep || input_oi->contentp) { |
792 | 0 | new_oi.contentp = &content; |
793 | 0 | new_oi.sizep = &size; |
794 | 0 | new_oi.typep = &type; |
795 | 0 | } |
796 | 0 | oi = &new_oi; |
797 | 0 | } |
798 | |
|
799 | 0 | ret = odb_read_object_info_extended(r->objects, &oid, oi, flags); |
800 | 0 | if (ret) |
801 | 0 | return -1; |
802 | 0 | if (oi == input_oi) |
803 | 0 | return ret; |
804 | | |
805 | 0 | if (new_oi.contentp) { |
806 | 0 | struct strbuf outbuf = STRBUF_INIT; |
807 | |
|
808 | 0 | if (type != OBJ_BLOB) { |
809 | 0 | ret = convert_object_file(r, &outbuf, |
810 | 0 | r->hash_algo, input_algo, |
811 | 0 | content, size, type, !do_die); |
812 | 0 | free(content); |
813 | 0 | if (ret == -1) |
814 | 0 | return -1; |
815 | 0 | size = outbuf.len; |
816 | 0 | content = strbuf_detach(&outbuf, NULL); |
817 | 0 | } |
818 | 0 | if (input_oi->sizep) |
819 | 0 | *input_oi->sizep = size; |
820 | 0 | if (input_oi->contentp) |
821 | 0 | *input_oi->contentp = content; |
822 | 0 | else |
823 | 0 | free(content); |
824 | 0 | if (input_oi->typep) |
825 | 0 | *input_oi->typep = type; |
826 | 0 | } |
827 | 0 | if (new_oi.delta_base_oid == &delta_base_oid) { |
828 | 0 | if (repo_oid_to_algop(r, &delta_base_oid, input_algo, |
829 | 0 | input_oi->delta_base_oid)) { |
830 | 0 | if (do_die) |
831 | 0 | die(_("missing mapping of %s to %s"), |
832 | 0 | oid_to_hex(&delta_base_oid), |
833 | 0 | input_algo->name); |
834 | 0 | return -1; |
835 | 0 | } |
836 | 0 | } |
837 | 0 | input_oi->whence = new_oi.whence; |
838 | 0 | input_oi->u = new_oi.u; |
839 | 0 | return ret; |
840 | 0 | } |
841 | | |
842 | | int odb_read_object_info_extended(struct object_database *odb, |
843 | | const struct object_id *oid, |
844 | | struct object_info *oi, |
845 | | unsigned flags) |
846 | 0 | { |
847 | 0 | int ret; |
848 | |
|
849 | 0 | if (oid->algo && (hash_algo_by_ptr(odb->repo->hash_algo) != oid->algo)) |
850 | 0 | return oid_object_info_convert(odb->repo, oid, oi, flags); |
851 | | |
852 | 0 | obj_read_lock(); |
853 | 0 | ret = do_oid_object_info_extended(odb, oid, oi, flags); |
854 | 0 | obj_read_unlock(); |
855 | 0 | return ret; |
856 | 0 | } |
857 | | |
858 | | |
859 | | /* returns enum object_type or negative */ |
860 | | int odb_read_object_info(struct object_database *odb, |
861 | | const struct object_id *oid, |
862 | | unsigned long *sizep) |
863 | 0 | { |
864 | 0 | enum object_type type; |
865 | 0 | struct object_info oi = OBJECT_INFO_INIT; |
866 | |
|
867 | 0 | oi.typep = &type; |
868 | 0 | oi.sizep = sizep; |
869 | 0 | if (odb_read_object_info_extended(odb, oid, &oi, |
870 | 0 | OBJECT_INFO_LOOKUP_REPLACE) < 0) |
871 | 0 | return -1; |
872 | 0 | return type; |
873 | 0 | } |
874 | | |
875 | | int odb_pretend_object(struct object_database *odb, |
876 | | void *buf, unsigned long len, enum object_type type, |
877 | | struct object_id *oid) |
878 | 0 | { |
879 | 0 | struct cached_object_entry *co; |
880 | 0 | char *co_buf; |
881 | |
|
882 | 0 | hash_object_file(odb->repo->hash_algo, buf, len, type, oid); |
883 | 0 | if (odb_has_object(odb, oid, 0) || |
884 | 0 | find_cached_object(odb, oid)) |
885 | 0 | return 0; |
886 | | |
887 | 0 | ALLOC_GROW(odb->cached_objects, |
888 | 0 | odb->cached_object_nr + 1, odb->cached_object_alloc); |
889 | 0 | co = &odb->cached_objects[odb->cached_object_nr++]; |
890 | 0 | co->value.size = len; |
891 | 0 | co->value.type = type; |
892 | 0 | co_buf = xmalloc(len); |
893 | 0 | memcpy(co_buf, buf, len); |
894 | 0 | co->value.buf = co_buf; |
895 | 0 | oidcpy(&co->oid, oid); |
896 | 0 | return 0; |
897 | 0 | } |
898 | | |
899 | | void *odb_read_object(struct object_database *odb, |
900 | | const struct object_id *oid, |
901 | | enum object_type *type, |
902 | | unsigned long *size) |
903 | 0 | { |
904 | 0 | struct object_info oi = OBJECT_INFO_INIT; |
905 | 0 | unsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE; |
906 | 0 | void *data; |
907 | |
|
908 | 0 | oi.typep = type; |
909 | 0 | oi.sizep = size; |
910 | 0 | oi.contentp = &data; |
911 | 0 | if (odb_read_object_info_extended(odb, oid, &oi, flags)) |
912 | 0 | return NULL; |
913 | | |
914 | 0 | return data; |
915 | 0 | } |
916 | | |
917 | | void *odb_read_object_peeled(struct object_database *odb, |
918 | | const struct object_id *oid, |
919 | | enum object_type required_type, |
920 | | unsigned long *size, |
921 | | struct object_id *actual_oid_return) |
922 | 0 | { |
923 | 0 | enum object_type type; |
924 | 0 | void *buffer; |
925 | 0 | unsigned long isize; |
926 | 0 | struct object_id actual_oid; |
927 | |
|
928 | 0 | oidcpy(&actual_oid, oid); |
929 | 0 | while (1) { |
930 | 0 | int ref_length = -1; |
931 | 0 | const char *ref_type = NULL; |
932 | |
|
933 | 0 | buffer = odb_read_object(odb, &actual_oid, &type, &isize); |
934 | 0 | if (!buffer) |
935 | 0 | return NULL; |
936 | 0 | if (type == required_type) { |
937 | 0 | *size = isize; |
938 | 0 | if (actual_oid_return) |
939 | 0 | oidcpy(actual_oid_return, &actual_oid); |
940 | 0 | return buffer; |
941 | 0 | } |
942 | | /* Handle references */ |
943 | 0 | else if (type == OBJ_COMMIT) |
944 | 0 | ref_type = "tree "; |
945 | 0 | else if (type == OBJ_TAG) |
946 | 0 | ref_type = "object "; |
947 | 0 | else { |
948 | 0 | free(buffer); |
949 | 0 | return NULL; |
950 | 0 | } |
951 | 0 | ref_length = strlen(ref_type); |
952 | |
|
953 | 0 | if (ref_length + odb->repo->hash_algo->hexsz > isize || |
954 | 0 | memcmp(buffer, ref_type, ref_length) || |
955 | 0 | get_oid_hex_algop((char *) buffer + ref_length, &actual_oid, |
956 | 0 | odb->repo->hash_algo)) { |
957 | 0 | free(buffer); |
958 | 0 | return NULL; |
959 | 0 | } |
960 | 0 | free(buffer); |
961 | | /* Now we have the ID of the referred-to object in |
962 | | * actual_oid. Check again. */ |
963 | 0 | } |
964 | 0 | } |
965 | | |
966 | | int odb_has_object(struct object_database *odb, const struct object_id *oid, |
967 | | unsigned flags) |
968 | 0 | { |
969 | 0 | unsigned object_info_flags = 0; |
970 | |
|
971 | 0 | if (!startup_info->have_repository) |
972 | 0 | return 0; |
973 | 0 | if (!(flags & HAS_OBJECT_RECHECK_PACKED)) |
974 | 0 | object_info_flags |= OBJECT_INFO_QUICK; |
975 | 0 | if (!(flags & HAS_OBJECT_FETCH_PROMISOR)) |
976 | 0 | object_info_flags |= OBJECT_INFO_SKIP_FETCH_OBJECT; |
977 | |
|
978 | 0 | return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0; |
979 | 0 | } |
980 | | |
981 | | int odb_freshen_object(struct object_database *odb, |
982 | | const struct object_id *oid) |
983 | 0 | { |
984 | 0 | struct odb_source *source; |
985 | |
|
986 | 0 | odb_prepare_alternates(odb); |
987 | 0 | for (source = odb->sources; source; source = source->next) { |
988 | 0 | if (packfile_store_freshen_object(source->packfiles, oid)) |
989 | 0 | return 1; |
990 | | |
991 | 0 | if (odb_source_loose_freshen_object(source, oid)) |
992 | 0 | return 1; |
993 | 0 | } |
994 | | |
995 | 0 | return 0; |
996 | 0 | } |
997 | | |
998 | | void odb_assert_oid_type(struct object_database *odb, |
999 | | const struct object_id *oid, enum object_type expect) |
1000 | 0 | { |
1001 | 0 | enum object_type type = odb_read_object_info(odb, oid, NULL); |
1002 | 0 | if (type < 0) |
1003 | 0 | die(_("%s is not a valid object"), oid_to_hex(oid)); |
1004 | 0 | if (type != expect) |
1005 | 0 | die(_("%s is not a valid '%s' object"), oid_to_hex(oid), |
1006 | 0 | type_name(expect)); |
1007 | 0 | } |
1008 | | |
1009 | | int odb_write_object_ext(struct object_database *odb, |
1010 | | const void *buf, unsigned long len, |
1011 | | enum object_type type, |
1012 | | struct object_id *oid, |
1013 | | struct object_id *compat_oid, |
1014 | | unsigned flags) |
1015 | 0 | { |
1016 | 0 | return odb_source_loose_write_object(odb->sources, buf, len, type, |
1017 | 0 | oid, compat_oid, flags); |
1018 | 0 | } |
1019 | | |
1020 | | int odb_write_object_stream(struct object_database *odb, |
1021 | | struct odb_write_stream *stream, size_t len, |
1022 | | struct object_id *oid) |
1023 | 0 | { |
1024 | 0 | return odb_source_loose_write_stream(odb->sources, stream, len, oid); |
1025 | 0 | } |
1026 | | |
1027 | | static void odb_update_commondir(const char *name UNUSED, |
1028 | | const char *old_cwd, |
1029 | | const char *new_cwd, |
1030 | | void *cb_data) |
1031 | 0 | { |
1032 | 0 | struct object_database *odb = cb_data; |
1033 | 0 | struct tmp_objdir *tmp_objdir; |
1034 | 0 | struct odb_source *source; |
1035 | |
|
1036 | 0 | tmp_objdir = tmp_objdir_unapply_primary_odb(); |
1037 | | |
1038 | | /* |
1039 | | * In theory, we only have to do this for the primary object source, as |
1040 | | * alternates' paths are always resolved to an absolute path. |
1041 | | */ |
1042 | 0 | for (source = odb->sources; source; source = source->next) { |
1043 | 0 | char *path; |
1044 | |
|
1045 | 0 | if (is_absolute_path(source->path)) |
1046 | 0 | continue; |
1047 | | |
1048 | 0 | path = reparent_relative_path(old_cwd, new_cwd, |
1049 | 0 | source->path); |
1050 | |
|
1051 | 0 | free(source->path); |
1052 | 0 | source->path = path; |
1053 | 0 | } |
1054 | |
|
1055 | 0 | if (tmp_objdir) |
1056 | 0 | tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd); |
1057 | 0 | } |
1058 | | |
1059 | | struct object_database *odb_new(struct repository *repo, |
1060 | | const char *primary_source, |
1061 | | const char *secondary_sources) |
1062 | 0 | { |
1063 | 0 | struct object_database *o = xmalloc(sizeof(*o)); |
1064 | 0 | char *to_free = NULL; |
1065 | |
|
1066 | 0 | memset(o, 0, sizeof(*o)); |
1067 | 0 | o->repo = repo; |
1068 | 0 | pthread_mutex_init(&o->replace_mutex, NULL); |
1069 | 0 | string_list_init_dup(&o->submodule_source_paths); |
1070 | |
|
1071 | 0 | if (!primary_source) |
1072 | 0 | primary_source = to_free = xstrfmt("%s/objects", repo->commondir); |
1073 | 0 | o->sources = odb_source_new(o, primary_source, true); |
1074 | 0 | o->sources_tail = &o->sources->next; |
1075 | 0 | o->alternate_db = xstrdup_or_null(secondary_sources); |
1076 | |
|
1077 | 0 | free(to_free); |
1078 | |
|
1079 | 0 | chdir_notify_register(NULL, odb_update_commondir, o); |
1080 | |
|
1081 | 0 | return o; |
1082 | 0 | } |
1083 | | |
1084 | | void odb_close(struct object_database *o) |
1085 | 0 | { |
1086 | 0 | struct odb_source *source; |
1087 | 0 | for (source = o->sources; source; source = source->next) |
1088 | 0 | packfile_store_close(source->packfiles); |
1089 | 0 | close_commit_graph(o); |
1090 | 0 | } |
1091 | | |
1092 | | static void odb_free_sources(struct object_database *o) |
1093 | 0 | { |
1094 | 0 | while (o->sources) { |
1095 | 0 | struct odb_source *next; |
1096 | |
|
1097 | 0 | next = o->sources->next; |
1098 | 0 | odb_source_free(o->sources); |
1099 | 0 | o->sources = next; |
1100 | 0 | } |
1101 | 0 | kh_destroy_odb_path_map(o->source_by_path); |
1102 | 0 | o->source_by_path = NULL; |
1103 | 0 | } |
1104 | | |
1105 | | void odb_free(struct object_database *o) |
1106 | 0 | { |
1107 | 0 | if (!o) |
1108 | 0 | return; |
1109 | | |
1110 | 0 | free(o->alternate_db); |
1111 | |
|
1112 | 0 | oidmap_clear(&o->replace_map, 1); |
1113 | 0 | pthread_mutex_destroy(&o->replace_mutex); |
1114 | |
|
1115 | 0 | odb_close(o); |
1116 | 0 | odb_free_sources(o); |
1117 | |
|
1118 | 0 | for (size_t i = 0; i < o->cached_object_nr; i++) |
1119 | 0 | free((char *) o->cached_objects[i].value.buf); |
1120 | 0 | free(o->cached_objects); |
1121 | |
|
1122 | 0 | string_list_clear(&o->submodule_source_paths, 0); |
1123 | |
|
1124 | 0 | chdir_notify_unregister(NULL, odb_update_commondir, o); |
1125 | |
|
1126 | 0 | free(o); |
1127 | 0 | } |
1128 | | |
1129 | | void odb_reprepare(struct object_database *o) |
1130 | 0 | { |
1131 | 0 | struct odb_source *source; |
1132 | |
|
1133 | 0 | obj_read_lock(); |
1134 | | |
1135 | | /* |
1136 | | * Reprepare alt odbs, in case the alternates file was modified |
1137 | | * during the course of this process. This only _adds_ odbs to |
1138 | | * the linked list, so existing odbs will continue to exist for |
1139 | | * the lifetime of the process. |
1140 | | */ |
1141 | 0 | o->loaded_alternates = 0; |
1142 | 0 | odb_prepare_alternates(o); |
1143 | |
|
1144 | 0 | for (source = o->sources; source; source = source->next) { |
1145 | 0 | odb_source_loose_reprepare(source); |
1146 | 0 | packfile_store_reprepare(source->packfiles); |
1147 | 0 | } |
1148 | |
|
1149 | 0 | o->approximate_object_count_valid = 0; |
1150 | |
|
1151 | 0 | obj_read_unlock(); |
1152 | 0 | } |
1153 | | |
1154 | | struct odb_transaction *odb_transaction_begin(struct object_database *odb) |
1155 | 0 | { |
1156 | 0 | if (odb->transaction) |
1157 | 0 | return NULL; |
1158 | | |
1159 | 0 | odb->transaction = odb_transaction_files_begin(odb->sources); |
1160 | |
|
1161 | 0 | return odb->transaction; |
1162 | 0 | } |
1163 | | |
1164 | | void odb_transaction_commit(struct odb_transaction *transaction) |
1165 | 0 | { |
1166 | 0 | if (!transaction) |
1167 | 0 | return; |
1168 | | |
1169 | | /* |
1170 | | * Ensure the transaction ending matches the pending transaction. |
1171 | | */ |
1172 | 0 | ASSERT(transaction == transaction->source->odb->transaction); |
1173 | | |
1174 | 0 | transaction->commit(transaction); |
1175 | | transaction->source->odb->transaction = NULL; |
1176 | 0 | free(transaction); |
1177 | 0 | } |