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