/src/git/promisor-remote.c
Line | Count | Source (jump to first uncovered line) |
1 | | #define USE_THE_REPOSITORY_VARIABLE |
2 | | |
3 | | #include "git-compat-util.h" |
4 | | #include "gettext.h" |
5 | | #include "hex.h" |
6 | | #include "object-store-ll.h" |
7 | | #include "promisor-remote.h" |
8 | | #include "config.h" |
9 | | #include "trace2.h" |
10 | | #include "transport.h" |
11 | | #include "strvec.h" |
12 | | #include "packfile.h" |
13 | | #include "environment.h" |
14 | | |
15 | | struct promisor_remote_config { |
16 | | struct promisor_remote *promisors; |
17 | | struct promisor_remote **promisors_tail; |
18 | | }; |
19 | | |
20 | | static int fetch_objects(struct repository *repo, |
21 | | const char *remote_name, |
22 | | const struct object_id *oids, |
23 | | int oid_nr) |
24 | 0 | { |
25 | 0 | struct child_process child = CHILD_PROCESS_INIT; |
26 | 0 | int i; |
27 | 0 | FILE *child_in; |
28 | 0 | int quiet; |
29 | |
|
30 | 0 | if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0)) { |
31 | 0 | static int warning_shown; |
32 | 0 | if (!warning_shown) { |
33 | 0 | warning_shown = 1; |
34 | 0 | warning(_("lazy fetching disabled; some objects may not be available")); |
35 | 0 | } |
36 | 0 | return -1; |
37 | 0 | } |
38 | | |
39 | 0 | child.git_cmd = 1; |
40 | 0 | child.in = -1; |
41 | 0 | if (repo != the_repository) |
42 | 0 | prepare_other_repo_env(&child.env, repo->gitdir); |
43 | 0 | strvec_pushl(&child.args, "-c", "fetch.negotiationAlgorithm=noop", |
44 | 0 | "fetch", remote_name, "--no-tags", |
45 | 0 | "--no-write-fetch-head", "--recurse-submodules=no", |
46 | 0 | "--filter=blob:none", "--stdin", NULL); |
47 | 0 | if (!git_config_get_bool("promisor.quiet", &quiet) && quiet) |
48 | 0 | strvec_push(&child.args, "--quiet"); |
49 | 0 | if (start_command(&child)) |
50 | 0 | die(_("promisor-remote: unable to fork off fetch subprocess")); |
51 | 0 | child_in = xfdopen(child.in, "w"); |
52 | |
|
53 | 0 | trace2_data_intmax("promisor", repo, "fetch_count", oid_nr); |
54 | |
|
55 | 0 | for (i = 0; i < oid_nr; i++) { |
56 | 0 | if (fputs(oid_to_hex(&oids[i]), child_in) < 0) |
57 | 0 | die_errno(_("promisor-remote: could not write to fetch subprocess")); |
58 | 0 | if (fputc('\n', child_in) < 0) |
59 | 0 | die_errno(_("promisor-remote: could not write to fetch subprocess")); |
60 | 0 | } |
61 | | |
62 | 0 | if (fclose(child_in) < 0) |
63 | 0 | die_errno(_("promisor-remote: could not close stdin to fetch subprocess")); |
64 | 0 | return finish_command(&child) ? -1 : 0; |
65 | 0 | } |
66 | | |
67 | | static struct promisor_remote *promisor_remote_new(struct promisor_remote_config *config, |
68 | | const char *remote_name) |
69 | 0 | { |
70 | 0 | struct promisor_remote *r; |
71 | |
|
72 | 0 | if (*remote_name == '/') { |
73 | 0 | warning(_("promisor remote name cannot begin with '/': %s"), |
74 | 0 | remote_name); |
75 | 0 | return NULL; |
76 | 0 | } |
77 | | |
78 | 0 | FLEX_ALLOC_STR(r, name, remote_name); |
79 | |
|
80 | 0 | *config->promisors_tail = r; |
81 | 0 | config->promisors_tail = &r->next; |
82 | |
|
83 | 0 | return r; |
84 | 0 | } |
85 | | |
86 | | static struct promisor_remote *promisor_remote_lookup(struct promisor_remote_config *config, |
87 | | const char *remote_name, |
88 | | struct promisor_remote **previous) |
89 | 0 | { |
90 | 0 | struct promisor_remote *r, *p; |
91 | |
|
92 | 0 | for (p = NULL, r = config->promisors; r; p = r, r = r->next) |
93 | 0 | if (!strcmp(r->name, remote_name)) { |
94 | 0 | if (previous) |
95 | 0 | *previous = p; |
96 | 0 | return r; |
97 | 0 | } |
98 | | |
99 | 0 | return NULL; |
100 | 0 | } |
101 | | |
102 | | static void promisor_remote_move_to_tail(struct promisor_remote_config *config, |
103 | | struct promisor_remote *r, |
104 | | struct promisor_remote *previous) |
105 | 0 | { |
106 | 0 | if (!r->next) |
107 | 0 | return; |
108 | | |
109 | 0 | if (previous) |
110 | 0 | previous->next = r->next; |
111 | 0 | else |
112 | 0 | config->promisors = r->next ? r->next : r; |
113 | 0 | r->next = NULL; |
114 | 0 | *config->promisors_tail = r; |
115 | 0 | config->promisors_tail = &r->next; |
116 | 0 | } |
117 | | |
118 | | static int promisor_remote_config(const char *var, const char *value, |
119 | | const struct config_context *ctx UNUSED, |
120 | | void *data) |
121 | 0 | { |
122 | 0 | struct promisor_remote_config *config = data; |
123 | 0 | const char *name; |
124 | 0 | size_t namelen; |
125 | 0 | const char *subkey; |
126 | |
|
127 | 0 | if (parse_config_key(var, "remote", &name, &namelen, &subkey) < 0) |
128 | 0 | return 0; |
129 | | |
130 | 0 | if (!strcmp(subkey, "promisor")) { |
131 | 0 | char *remote_name; |
132 | |
|
133 | 0 | if (!git_config_bool(var, value)) |
134 | 0 | return 0; |
135 | | |
136 | 0 | remote_name = xmemdupz(name, namelen); |
137 | |
|
138 | 0 | if (!promisor_remote_lookup(config, remote_name, NULL)) |
139 | 0 | promisor_remote_new(config, remote_name); |
140 | |
|
141 | 0 | free(remote_name); |
142 | 0 | return 0; |
143 | 0 | } |
144 | 0 | if (!strcmp(subkey, "partialclonefilter")) { |
145 | 0 | struct promisor_remote *r; |
146 | 0 | char *remote_name = xmemdupz(name, namelen); |
147 | |
|
148 | 0 | r = promisor_remote_lookup(config, remote_name, NULL); |
149 | 0 | if (!r) |
150 | 0 | r = promisor_remote_new(config, remote_name); |
151 | |
|
152 | 0 | free(remote_name); |
153 | |
|
154 | 0 | if (!r) |
155 | 0 | return 0; |
156 | | |
157 | 0 | return git_config_string(&r->partial_clone_filter, var, value); |
158 | 0 | } |
159 | | |
160 | 0 | return 0; |
161 | 0 | } |
162 | | |
163 | | static void promisor_remote_init(struct repository *r) |
164 | 0 | { |
165 | 0 | struct promisor_remote_config *config; |
166 | |
|
167 | 0 | if (r->promisor_remote_config) |
168 | 0 | return; |
169 | 0 | config = r->promisor_remote_config = |
170 | 0 | xcalloc(1, sizeof(*r->promisor_remote_config)); |
171 | 0 | config->promisors_tail = &config->promisors; |
172 | |
|
173 | 0 | repo_config(r, promisor_remote_config, config); |
174 | |
|
175 | 0 | if (r->repository_format_partial_clone) { |
176 | 0 | struct promisor_remote *o, *previous; |
177 | |
|
178 | 0 | o = promisor_remote_lookup(config, |
179 | 0 | r->repository_format_partial_clone, |
180 | 0 | &previous); |
181 | 0 | if (o) |
182 | 0 | promisor_remote_move_to_tail(config, o, previous); |
183 | 0 | else |
184 | 0 | promisor_remote_new(config, r->repository_format_partial_clone); |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | void promisor_remote_clear(struct promisor_remote_config *config) |
189 | 0 | { |
190 | 0 | while (config->promisors) { |
191 | 0 | struct promisor_remote *r = config->promisors; |
192 | 0 | config->promisors = config->promisors->next; |
193 | 0 | free(r); |
194 | 0 | } |
195 | |
|
196 | 0 | config->promisors_tail = &config->promisors; |
197 | 0 | } |
198 | | |
199 | | void repo_promisor_remote_reinit(struct repository *r) |
200 | 0 | { |
201 | 0 | promisor_remote_clear(r->promisor_remote_config); |
202 | 0 | FREE_AND_NULL(r->promisor_remote_config); |
203 | 0 | promisor_remote_init(r); |
204 | 0 | } |
205 | | |
206 | | struct promisor_remote *repo_promisor_remote_find(struct repository *r, |
207 | | const char *remote_name) |
208 | 0 | { |
209 | 0 | promisor_remote_init(r); |
210 | |
|
211 | 0 | if (!remote_name) |
212 | 0 | return r->promisor_remote_config->promisors; |
213 | | |
214 | 0 | return promisor_remote_lookup(r->promisor_remote_config, remote_name, NULL); |
215 | 0 | } |
216 | | |
217 | | int repo_has_promisor_remote(struct repository *r) |
218 | 0 | { |
219 | 0 | return !!repo_promisor_remote_find(r, NULL); |
220 | 0 | } |
221 | | |
222 | | static int remove_fetched_oids(struct repository *repo, |
223 | | struct object_id **oids, |
224 | | int oid_nr, int to_free) |
225 | 0 | { |
226 | 0 | int i, remaining_nr = 0; |
227 | 0 | int *remaining = xcalloc(oid_nr, sizeof(*remaining)); |
228 | 0 | struct object_id *old_oids = *oids; |
229 | 0 | struct object_id *new_oids; |
230 | |
|
231 | 0 | for (i = 0; i < oid_nr; i++) |
232 | 0 | if (oid_object_info_extended(repo, &old_oids[i], NULL, |
233 | 0 | OBJECT_INFO_SKIP_FETCH_OBJECT)) { |
234 | 0 | remaining[i] = 1; |
235 | 0 | remaining_nr++; |
236 | 0 | } |
237 | |
|
238 | 0 | if (remaining_nr) { |
239 | 0 | int j = 0; |
240 | 0 | CALLOC_ARRAY(new_oids, remaining_nr); |
241 | 0 | for (i = 0; i < oid_nr; i++) |
242 | 0 | if (remaining[i]) |
243 | 0 | oidcpy(&new_oids[j++], &old_oids[i]); |
244 | 0 | *oids = new_oids; |
245 | 0 | if (to_free) |
246 | 0 | free(old_oids); |
247 | 0 | } |
248 | |
|
249 | 0 | free(remaining); |
250 | |
|
251 | 0 | return remaining_nr; |
252 | 0 | } |
253 | | |
254 | | void promisor_remote_get_direct(struct repository *repo, |
255 | | const struct object_id *oids, |
256 | | int oid_nr) |
257 | 0 | { |
258 | 0 | struct promisor_remote *r; |
259 | 0 | struct object_id *remaining_oids = (struct object_id *)oids; |
260 | 0 | int remaining_nr = oid_nr; |
261 | 0 | int to_free = 0; |
262 | 0 | int i; |
263 | |
|
264 | 0 | if (oid_nr == 0) |
265 | 0 | return; |
266 | | |
267 | 0 | promisor_remote_init(repo); |
268 | |
|
269 | 0 | for (r = repo->promisor_remote_config->promisors; r; r = r->next) { |
270 | 0 | if (fetch_objects(repo, r->name, remaining_oids, remaining_nr) < 0) { |
271 | 0 | if (remaining_nr == 1) |
272 | 0 | continue; |
273 | 0 | remaining_nr = remove_fetched_oids(repo, &remaining_oids, |
274 | 0 | remaining_nr, to_free); |
275 | 0 | if (remaining_nr) { |
276 | 0 | to_free = 1; |
277 | 0 | continue; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | goto all_fetched; |
281 | 0 | } |
282 | | |
283 | 0 | for (i = 0; i < remaining_nr; i++) { |
284 | 0 | if (is_promisor_object(&remaining_oids[i])) |
285 | 0 | die(_("could not fetch %s from promisor remote"), |
286 | 0 | oid_to_hex(&remaining_oids[i])); |
287 | 0 | } |
288 | | |
289 | 0 | all_fetched: |
290 | 0 | if (to_free) |
291 | 0 | free(remaining_oids); |
292 | 0 | } |