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