git
/
bundle-uri.c
938 строк · 22.8 Кб
1#define USE_THE_REPOSITORY_VARIABLE
2
3#include "git-compat-util.h"
4#include "bundle-uri.h"
5#include "bundle.h"
6#include "copy.h"
7#include "environment.h"
8#include "gettext.h"
9#include "refs.h"
10#include "run-command.h"
11#include "hashmap.h"
12#include "pkt-line.h"
13#include "config.h"
14#include "fetch-pack.h"
15#include "remote.h"
16
17static struct {
18enum bundle_list_heuristic heuristic;
19const char *name;
20} heuristics[BUNDLE_HEURISTIC__COUNT] = {
21{ BUNDLE_HEURISTIC_NONE, ""},
22{ BUNDLE_HEURISTIC_CREATIONTOKEN, "creationToken" },
23};
24
25static int compare_bundles(const void *hashmap_cmp_fn_data UNUSED,
26const struct hashmap_entry *he1,
27const struct hashmap_entry *he2,
28const void *id)
29{
30const struct remote_bundle_info *e1 =
31container_of(he1, const struct remote_bundle_info, ent);
32const struct remote_bundle_info *e2 =
33container_of(he2, const struct remote_bundle_info, ent);
34
35return strcmp(e1->id, id ? (const char *)id : e2->id);
36}
37
38void init_bundle_list(struct bundle_list *list)
39{
40memset(list, 0, sizeof(*list));
41
42/* Implied defaults. */
43list->mode = BUNDLE_MODE_ALL;
44list->version = 1;
45
46hashmap_init(&list->bundles, compare_bundles, NULL, 0);
47}
48
49static int clear_remote_bundle_info(struct remote_bundle_info *bundle,
50void *data UNUSED)
51{
52FREE_AND_NULL(bundle->id);
53FREE_AND_NULL(bundle->uri);
54FREE_AND_NULL(bundle->file);
55bundle->unbundled = 0;
56return 0;
57}
58
59void clear_bundle_list(struct bundle_list *list)
60{
61if (!list)
62return;
63
64for_all_bundles_in_list(list, clear_remote_bundle_info, NULL);
65hashmap_clear_and_free(&list->bundles, struct remote_bundle_info, ent);
66free(list->baseURI);
67}
68
69int for_all_bundles_in_list(struct bundle_list *list,
70bundle_iterator iter,
71void *data)
72{
73struct remote_bundle_info *info;
74struct hashmap_iter i;
75
76hashmap_for_each_entry(&list->bundles, &i, info, ent) {
77int result = iter(info, data);
78
79if (result)
80return result;
81}
82
83return 0;
84}
85
86static int summarize_bundle(struct remote_bundle_info *info, void *data)
87{
88FILE *fp = data;
89fprintf(fp, "[bundle \"%s\"]\n", info->id);
90fprintf(fp, "\turi = %s\n", info->uri);
91
92if (info->creationToken)
93fprintf(fp, "\tcreationToken = %"PRIu64"\n", info->creationToken);
94return 0;
95}
96
97void print_bundle_list(FILE *fp, struct bundle_list *list)
98{
99const char *mode;
100
101switch (list->mode) {
102case BUNDLE_MODE_ALL:
103mode = "all";
104break;
105
106case BUNDLE_MODE_ANY:
107mode = "any";
108break;
109
110case BUNDLE_MODE_NONE:
111default:
112mode = "<unknown>";
113}
114
115fprintf(fp, "[bundle]\n");
116fprintf(fp, "\tversion = %d\n", list->version);
117fprintf(fp, "\tmode = %s\n", mode);
118
119if (list->heuristic) {
120int i;
121for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
122if (heuristics[i].heuristic == list->heuristic) {
123printf("\theuristic = %s\n",
124heuristics[list->heuristic].name);
125break;
126}
127}
128}
129
130for_all_bundles_in_list(list, summarize_bundle, fp);
131}
132
133/**
134* Given a key-value pair, update the state of the given bundle list.
135* Returns 0 if the key-value pair is understood. Returns -1 if the key
136* is not understood or the value is malformed.
137*/
138static int bundle_list_update(const char *key, const char *value,
139struct bundle_list *list)
140{
141struct strbuf id = STRBUF_INIT;
142struct remote_bundle_info lookup = REMOTE_BUNDLE_INFO_INIT;
143struct remote_bundle_info *bundle;
144const char *subsection, *subkey;
145size_t subsection_len;
146
147if (parse_config_key(key, "bundle", &subsection, &subsection_len, &subkey))
148return -1;
149
150if (!subsection_len) {
151if (!strcmp(subkey, "version")) {
152int version;
153if (!git_parse_int(value, &version))
154return -1;
155if (version != 1)
156return -1;
157
158list->version = version;
159return 0;
160}
161
162if (!strcmp(subkey, "mode")) {
163if (!strcmp(value, "all"))
164list->mode = BUNDLE_MODE_ALL;
165else if (!strcmp(value, "any"))
166list->mode = BUNDLE_MODE_ANY;
167else
168return -1;
169return 0;
170}
171
172if (!strcmp(subkey, "heuristic")) {
173int i;
174for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
175if (heuristics[i].heuristic &&
176heuristics[i].name &&
177!strcmp(value, heuristics[i].name)) {
178list->heuristic = heuristics[i].heuristic;
179return 0;
180}
181}
182
183/* Ignore unknown heuristics. */
184return 0;
185}
186
187/* Ignore other unknown global keys. */
188return 0;
189}
190
191strbuf_add(&id, subsection, subsection_len);
192
193/*
194* Check for an existing bundle with this <id>, or create one
195* if necessary.
196*/
197lookup.id = id.buf;
198hashmap_entry_init(&lookup.ent, strhash(lookup.id));
199if (!(bundle = hashmap_get_entry(&list->bundles, &lookup, ent, NULL))) {
200CALLOC_ARRAY(bundle, 1);
201bundle->id = strbuf_detach(&id, NULL);
202hashmap_entry_init(&bundle->ent, strhash(bundle->id));
203hashmap_add(&list->bundles, &bundle->ent);
204}
205strbuf_release(&id);
206
207if (!strcmp(subkey, "uri")) {
208if (bundle->uri)
209return -1;
210bundle->uri = relative_url(list->baseURI, value, NULL);
211return 0;
212}
213
214if (!strcmp(subkey, "creationtoken")) {
215if (sscanf(value, "%"PRIu64, &bundle->creationToken) != 1)
216warning(_("could not parse bundle list key %s with value '%s'"),
217"creationToken", value);
218return 0;
219}
220
221/*
222* At this point, we ignore any information that we don't
223* understand, assuming it to be hints for a heuristic the client
224* does not currently understand.
225*/
226return 0;
227}
228
229static int config_to_bundle_list(const char *key, const char *value,
230const struct config_context *ctx UNUSED,
231void *data)
232{
233struct bundle_list *list = data;
234return bundle_list_update(key, value, list);
235}
236
237int bundle_uri_parse_config_format(const char *uri,
238const char *filename,
239struct bundle_list *list)
240{
241int result;
242struct config_options opts = {
243.error_action = CONFIG_ERROR_ERROR,
244};
245
246if (!list->baseURI) {
247struct strbuf baseURI = STRBUF_INIT;
248strbuf_addstr(&baseURI, uri);
249
250/*
251* If the URI does not end with a trailing slash, then
252* remove the filename portion of the path. This is
253* important for relative URIs.
254*/
255strbuf_strip_file_from_path(&baseURI);
256list->baseURI = strbuf_detach(&baseURI, NULL);
257}
258result = git_config_from_file_with_options(config_to_bundle_list,
259filename, list,
260CONFIG_SCOPE_UNKNOWN,
261&opts);
262
263if (!result && list->mode == BUNDLE_MODE_NONE) {
264warning(_("bundle list at '%s' has no mode"), uri);
265result = 1;
266}
267
268return result;
269}
270
271static char *find_temp_filename(void)
272{
273int fd;
274struct strbuf name = STRBUF_INIT;
275/*
276* Find a temporary filename that is available. This is briefly
277* racy, but unlikely to collide.
278*/
279fd = odb_mkstemp(&name, "bundles/tmp_uri_XXXXXX");
280if (fd < 0) {
281warning(_("failed to create temporary file"));
282return NULL;
283}
284
285close(fd);
286unlink(name.buf);
287return strbuf_detach(&name, NULL);
288}
289
290static int download_https_uri_to_file(const char *file, const char *uri)
291{
292int result = 0;
293struct child_process cp = CHILD_PROCESS_INIT;
294FILE *child_in = NULL, *child_out = NULL;
295struct strbuf line = STRBUF_INIT;
296int found_get = 0;
297
298strvec_pushl(&cp.args, "git-remote-https", uri, NULL);
299cp.err = -1;
300cp.in = -1;
301cp.out = -1;
302
303if (start_command(&cp))
304return 1;
305
306child_in = fdopen(cp.in, "w");
307if (!child_in) {
308result = 1;
309goto cleanup;
310}
311
312child_out = fdopen(cp.out, "r");
313if (!child_out) {
314result = 1;
315goto cleanup;
316}
317
318fprintf(child_in, "capabilities\n");
319fflush(child_in);
320
321while (!strbuf_getline(&line, child_out)) {
322if (!line.len)
323break;
324if (!strcmp(line.buf, "get"))
325found_get = 1;
326}
327strbuf_release(&line);
328
329if (!found_get) {
330result = error(_("insufficient capabilities"));
331goto cleanup;
332}
333
334fprintf(child_in, "get %s %s\n\n", uri, file);
335
336cleanup:
337if (child_in)
338fclose(child_in);
339if (finish_command(&cp))
340return 1;
341if (child_out)
342fclose(child_out);
343return result;
344}
345
346static int copy_uri_to_file(const char *filename, const char *uri)
347{
348const char *out;
349
350if (starts_with(uri, "https:") ||
351starts_with(uri, "http:"))
352return download_https_uri_to_file(filename, uri);
353
354if (skip_prefix(uri, "file://", &out))
355uri = out;
356
357/* Copy as a file */
358return copy_file(filename, uri, 0);
359}
360
361static int unbundle_from_file(struct repository *r, const char *file)
362{
363int result = 0;
364int bundle_fd;
365struct bundle_header header = BUNDLE_HEADER_INIT;
366struct string_list_item *refname;
367struct strbuf bundle_ref = STRBUF_INIT;
368size_t bundle_prefix_len;
369
370if ((bundle_fd = read_bundle_header(file, &header)) < 0)
371return 1;
372
373/*
374* Skip the reachability walk here, since we will be adding
375* a reachable ref pointing to the new tips, which will reach
376* the prerequisite commits.
377*/
378if ((result = unbundle(r, &header, bundle_fd, NULL,
379VERIFY_BUNDLE_QUIET | (fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0))))
380return 1;
381
382/*
383* Convert all refs/heads/ from the bundle into refs/bundles/
384* in the local repository.
385*/
386strbuf_addstr(&bundle_ref, "refs/bundles/");
387bundle_prefix_len = bundle_ref.len;
388
389for_each_string_list_item(refname, &header.references) {
390struct object_id *oid = refname->util;
391struct object_id old_oid;
392const char *branch_name;
393int has_old;
394
395if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
396continue;
397
398strbuf_setlen(&bundle_ref, bundle_prefix_len);
399strbuf_addstr(&bundle_ref, branch_name);
400
401has_old = !refs_read_ref(get_main_ref_store(the_repository),
402bundle_ref.buf, &old_oid);
403refs_update_ref(get_main_ref_store(the_repository),
404"fetched bundle", bundle_ref.buf, oid,
405has_old ? &old_oid : NULL,
4060, UPDATE_REFS_MSG_ON_ERR);
407}
408
409bundle_header_release(&header);
410return result;
411}
412
413struct bundle_list_context {
414struct repository *r;
415struct bundle_list *list;
416enum bundle_list_mode mode;
417int count;
418int depth;
419};
420
421/*
422* This early definition is necessary because we use indirect recursion:
423*
424* While iterating through a bundle list that was downloaded as part
425* of fetch_bundle_uri_internal(), iterator methods eventually call it
426* again, but with depth + 1.
427*/
428static int fetch_bundle_uri_internal(struct repository *r,
429struct remote_bundle_info *bundle,
430int depth,
431struct bundle_list *list);
432
433static int download_bundle_to_file(struct remote_bundle_info *bundle, void *data)
434{
435int res;
436struct bundle_list_context *ctx = data;
437
438if (ctx->mode == BUNDLE_MODE_ANY && ctx->count)
439return 0;
440
441res = fetch_bundle_uri_internal(ctx->r, bundle, ctx->depth + 1, ctx->list);
442
443/*
444* Only increment count if the download succeeded. If our mode is
445* BUNDLE_MODE_ANY, then we will want to try other URIs in the
446* list in case they work instead.
447*/
448if (!res)
449ctx->count++;
450
451/*
452* To be opportunistic as possible, we continue iterating and
453* download as many bundles as we can, so we can apply the ones
454* that work, even in BUNDLE_MODE_ALL mode.
455*/
456return 0;
457}
458
459struct bundles_for_sorting {
460struct remote_bundle_info **items;
461size_t alloc;
462size_t nr;
463};
464
465static int append_bundle(struct remote_bundle_info *bundle, void *data)
466{
467struct bundles_for_sorting *list = data;
468list->items[list->nr++] = bundle;
469return 0;
470}
471
472/**
473* For use in QSORT() to get a list sorted by creationToken
474* in decreasing order.
475*/
476static int compare_creation_token_decreasing(const void *va, const void *vb)
477{
478const struct remote_bundle_info * const *a = va;
479const struct remote_bundle_info * const *b = vb;
480
481if ((*a)->creationToken > (*b)->creationToken)
482return -1;
483if ((*a)->creationToken < (*b)->creationToken)
484return 1;
485return 0;
486}
487
488static int fetch_bundles_by_token(struct repository *r,
489struct bundle_list *list)
490{
491int cur;
492int move_direction = 0;
493const char *creationTokenStr;
494uint64_t maxCreationToken = 0, newMaxCreationToken = 0;
495struct bundle_list_context ctx = {
496.r = r,
497.list = list,
498.mode = list->mode,
499};
500struct bundles_for_sorting bundles = {
501.alloc = hashmap_get_size(&list->bundles),
502};
503
504ALLOC_ARRAY(bundles.items, bundles.alloc);
505
506for_all_bundles_in_list(list, append_bundle, &bundles);
507
508if (!bundles.nr) {
509free(bundles.items);
510return 0;
511}
512
513QSORT(bundles.items, bundles.nr, compare_creation_token_decreasing);
514
515/*
516* If fetch.bundleCreationToken exists, parses to a uint64t, and
517* is not strictly smaller than the maximum creation token in the
518* bundle list, then do not download any bundles.
519*/
520if (!repo_config_get_value(r,
521"fetch.bundlecreationtoken",
522&creationTokenStr) &&
523sscanf(creationTokenStr, "%"PRIu64, &maxCreationToken) == 1 &&
524bundles.items[0]->creationToken <= maxCreationToken) {
525free(bundles.items);
526return 0;
527}
528
529/*
530* Attempt to download and unbundle the minimum number of bundles by
531* creationToken in decreasing order. If we fail to unbundle (after
532* a successful download) then move to the next non-downloaded bundle
533* and attempt downloading. Once we succeed in applying a bundle,
534* move to the previous unapplied bundle and attempt to unbundle it
535* again.
536*
537* In the case of a fresh clone, we will likely download all of the
538* bundles before successfully unbundling the oldest one, then the
539* rest of the bundles unbundle successfully in increasing order
540* of creationToken.
541*
542* If there are existing objects, then this process may terminate
543* early when all required commits from "new" bundles exist in the
544* repo's object store.
545*/
546cur = 0;
547while (cur >= 0 && cur < bundles.nr) {
548struct remote_bundle_info *bundle = bundles.items[cur];
549
550/*
551* If we need to dig into bundles below the previous
552* creation token value, then likely we are in an erroneous
553* state due to missing or invalid bundles. Halt the process
554* instead of continuing to download extra data.
555*/
556if (bundle->creationToken <= maxCreationToken)
557break;
558
559if (!bundle->file) {
560/*
561* Not downloaded yet. Try downloading.
562*
563* Note that bundle->file is non-NULL if a download
564* was attempted, even if it failed to download.
565*/
566if (fetch_bundle_uri_internal(ctx.r, bundle, ctx.depth + 1, ctx.list)) {
567/* Mark as unbundled so we do not retry. */
568bundle->unbundled = 1;
569
570/* Try looking deeper in the list. */
571move_direction = 1;
572goto move;
573}
574
575/* We expect bundles when using creationTokens. */
576if (!is_bundle(bundle->file, 1)) {
577warning(_("file downloaded from '%s' is not a bundle"),
578bundle->uri);
579break;
580}
581}
582
583if (bundle->file && !bundle->unbundled) {
584/*
585* This was downloaded, but not successfully
586* unbundled. Try unbundling again.
587*/
588if (unbundle_from_file(ctx.r, bundle->file)) {
589/* Try looking deeper in the list. */
590move_direction = 1;
591} else {
592/*
593* Succeeded in unbundle. Retry bundles
594* that previously failed to unbundle.
595*/
596move_direction = -1;
597bundle->unbundled = 1;
598
599if (bundle->creationToken > newMaxCreationToken)
600newMaxCreationToken = bundle->creationToken;
601}
602}
603
604/*
605* Else case: downloaded and unbundled successfully.
606* Skip this by moving in the same direction as the
607* previous step.
608*/
609
610move:
611/* Move in the specified direction and repeat. */
612cur += move_direction;
613}
614
615/*
616* We succeed if the loop terminates because 'cur' drops below
617* zero. The other case is that we terminate because 'cur'
618* reaches the end of the list, so we have a failure no matter
619* which bundles we apply from the list.
620*/
621if (cur < 0) {
622struct strbuf value = STRBUF_INIT;
623strbuf_addf(&value, "%"PRIu64"", newMaxCreationToken);
624if (repo_config_set_multivar_gently(ctx.r,
625"fetch.bundleCreationToken",
626value.buf, NULL, 0))
627warning(_("failed to store maximum creation token"));
628
629strbuf_release(&value);
630}
631
632free(bundles.items);
633return cur >= 0;
634}
635
636static int download_bundle_list(struct repository *r,
637struct bundle_list *local_list,
638struct bundle_list *global_list,
639int depth)
640{
641struct bundle_list_context ctx = {
642.r = r,
643.list = global_list,
644.depth = depth + 1,
645.mode = local_list->mode,
646};
647
648return for_all_bundles_in_list(local_list, download_bundle_to_file, &ctx);
649}
650
651static int fetch_bundle_list_in_config_format(struct repository *r,
652struct bundle_list *global_list,
653struct remote_bundle_info *bundle,
654int depth)
655{
656int result;
657struct bundle_list list_from_bundle;
658
659init_bundle_list(&list_from_bundle);
660
661if ((result = bundle_uri_parse_config_format(bundle->uri,
662bundle->file,
663&list_from_bundle)))
664goto cleanup;
665
666if (list_from_bundle.mode == BUNDLE_MODE_NONE) {
667warning(_("unrecognized bundle mode from URI '%s'"),
668bundle->uri);
669result = -1;
670goto cleanup;
671}
672
673/*
674* If this list uses the creationToken heuristic, then the URIs
675* it advertises are expected to be bundles, not nested lists.
676* We can drop 'global_list' and 'depth'.
677*/
678if (list_from_bundle.heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN) {
679result = fetch_bundles_by_token(r, &list_from_bundle);
680global_list->heuristic = BUNDLE_HEURISTIC_CREATIONTOKEN;
681} else if ((result = download_bundle_list(r, &list_from_bundle,
682global_list, depth)))
683goto cleanup;
684
685cleanup:
686clear_bundle_list(&list_from_bundle);
687return result;
688}
689
690/**
691* This limits the recursion on fetch_bundle_uri_internal() when following
692* bundle lists.
693*/
694static int max_bundle_uri_depth = 4;
695
696/**
697* Recursively download all bundles advertised at the given URI
698* to files. If the file is a bundle, then add it to the given
699* 'list'. Otherwise, expect a bundle list and recurse on the
700* URIs in that list according to the list mode (ANY or ALL).
701*/
702static int fetch_bundle_uri_internal(struct repository *r,
703struct remote_bundle_info *bundle,
704int depth,
705struct bundle_list *list)
706{
707int result = 0;
708struct remote_bundle_info *bcopy;
709
710if (depth >= max_bundle_uri_depth) {
711warning(_("exceeded bundle URI recursion limit (%d)"),
712max_bundle_uri_depth);
713return -1;
714}
715
716if (!bundle->file &&
717!(bundle->file = find_temp_filename())) {
718result = -1;
719goto cleanup;
720}
721
722if ((result = copy_uri_to_file(bundle->file, bundle->uri))) {
723warning(_("failed to download bundle from URI '%s'"), bundle->uri);
724goto cleanup;
725}
726
727if ((result = !is_bundle(bundle->file, 1))) {
728result = fetch_bundle_list_in_config_format(
729r, list, bundle, depth);
730if (result)
731warning(_("file at URI '%s' is not a bundle or bundle list"),
732bundle->uri);
733goto cleanup;
734}
735
736/* Copy the bundle and insert it into the global list. */
737CALLOC_ARRAY(bcopy, 1);
738bcopy->id = xstrdup(bundle->id);
739bcopy->file = xstrdup(bundle->file);
740hashmap_entry_init(&bcopy->ent, strhash(bcopy->id));
741hashmap_add(&list->bundles, &bcopy->ent);
742
743cleanup:
744if (result && bundle->file)
745unlink(bundle->file);
746return result;
747}
748
749/**
750* This loop iterator breaks the loop with nonzero return code on the
751* first successful unbundling of a bundle.
752*/
753static int attempt_unbundle(struct remote_bundle_info *info, void *data)
754{
755struct repository *r = data;
756
757if (!info->file || info->unbundled)
758return 0;
759
760if (!unbundle_from_file(r, info->file)) {
761info->unbundled = 1;
762return 1;
763}
764
765return 0;
766}
767
768static int unbundle_all_bundles(struct repository *r,
769struct bundle_list *list)
770{
771/*
772* Iterate through all bundles looking for ones that can
773* successfully unbundle. If any succeed, then perhaps another
774* will succeed in the next attempt.
775*
776* Keep in mind that a non-zero result for the loop here means
777* the loop terminated early on a successful unbundling, which
778* signals that we can try again.
779*/
780while (for_all_bundles_in_list(list, attempt_unbundle, r)) ;
781
782return 0;
783}
784
785static int unlink_bundle(struct remote_bundle_info *info, void *data UNUSED)
786{
787if (info->file)
788unlink_or_warn(info->file);
789return 0;
790}
791
792int fetch_bundle_uri(struct repository *r, const char *uri,
793int *has_heuristic)
794{
795int result;
796struct bundle_list list;
797struct remote_bundle_info bundle = {
798.uri = xstrdup(uri),
799.id = xstrdup(""),
800};
801
802init_bundle_list(&list);
803
804/*
805* Do not fetch an empty bundle URI. An empty bundle URI
806* could signal that a configured bundle URI has been disabled.
807*/
808if (!*uri) {
809result = 0;
810goto cleanup;
811}
812
813/* If a bundle is added to this global list, then it is required. */
814list.mode = BUNDLE_MODE_ALL;
815
816if ((result = fetch_bundle_uri_internal(r, &bundle, 0, &list)))
817goto cleanup;
818
819result = unbundle_all_bundles(r, &list);
820
821cleanup:
822if (has_heuristic)
823*has_heuristic = (list.heuristic != BUNDLE_HEURISTIC_NONE);
824for_all_bundles_in_list(&list, unlink_bundle, NULL);
825clear_bundle_list(&list);
826clear_remote_bundle_info(&bundle, NULL);
827return result;
828}
829
830int fetch_bundle_list(struct repository *r, struct bundle_list *list)
831{
832int result;
833struct bundle_list global_list;
834
835/*
836* If the creationToken heuristic is used, then the URIs
837* advertised by 'list' are not nested lists and instead
838* direct bundles. We do not need to use global_list.
839*/
840if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
841return fetch_bundles_by_token(r, list);
842
843init_bundle_list(&global_list);
844
845/* If a bundle is added to this global list, then it is required. */
846global_list.mode = BUNDLE_MODE_ALL;
847
848if ((result = download_bundle_list(r, list, &global_list, 0)))
849goto cleanup;
850
851if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
852result = fetch_bundles_by_token(r, list);
853else
854result = unbundle_all_bundles(r, &global_list);
855
856cleanup:
857for_all_bundles_in_list(&global_list, unlink_bundle, NULL);
858clear_bundle_list(&global_list);
859return result;
860}
861
862/**
863* API for serve.c.
864*/
865
866int bundle_uri_advertise(struct repository *r, struct strbuf *value UNUSED)
867{
868static int advertise_bundle_uri = -1;
869
870if (advertise_bundle_uri != -1)
871goto cached;
872
873advertise_bundle_uri = 0;
874repo_config_get_maybe_bool(r, "uploadpack.advertisebundleuris", &advertise_bundle_uri);
875
876cached:
877return advertise_bundle_uri;
878}
879
880static int config_to_packet_line(const char *key, const char *value,
881const struct config_context *ctx UNUSED,
882void *data)
883{
884struct packet_reader *writer = data;
885
886if (starts_with(key, "bundle."))
887packet_write_fmt(writer->fd, "%s=%s", key, value);
888
889return 0;
890}
891
892int bundle_uri_command(struct repository *r,
893struct packet_reader *request)
894{
895struct packet_writer writer;
896packet_writer_init(&writer, 1);
897
898while (packet_reader_read(request) == PACKET_READ_NORMAL)
899die(_("bundle-uri: unexpected argument: '%s'"), request->line);
900if (request->status != PACKET_READ_FLUSH)
901die(_("bundle-uri: expected flush after arguments"));
902
903/*
904* Read all "bundle.*" config lines to the client as key=value
905* packet lines.
906*/
907repo_config(r, config_to_packet_line, &writer);
908
909packet_writer_flush(&writer);
910
911return 0;
912}
913
914/**
915* General API for {transport,connect}.c etc.
916*/
917int bundle_uri_parse_line(struct bundle_list *list, const char *line)
918{
919int result;
920const char *equals;
921struct strbuf key = STRBUF_INIT;
922
923if (!strlen(line))
924return error(_("bundle-uri: got an empty line"));
925
926equals = strchr(line, '=');
927
928if (!equals)
929return error(_("bundle-uri: line is not of the form 'key=value'"));
930if (line == equals || !*(equals + 1))
931return error(_("bundle-uri: line has empty key or value"));
932
933strbuf_add(&key, line, equals - line);
934result = bundle_list_update(key.buf, equals + 1, list);
935strbuf_release(&key);
936
937return result;
938}
939