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 struct promisor_remote_config {
9 	struct promisor_remote *promisors;
10 	struct promisor_remote **promisors_tail;
11 };
12 
fetch_objects(struct repository * repo,const char * remote_name,const struct object_id * oids,int oid_nr)13 static int fetch_objects(struct repository *repo,
14 			 const char *remote_name,
15 			 const struct object_id *oids,
16 			 int oid_nr)
17 {
18 	struct child_process child = CHILD_PROCESS_INIT;
19 	int i;
20 	FILE *child_in;
21 
22 	child.git_cmd = 1;
23 	child.in = -1;
24 	if (repo != the_repository)
25 		prepare_other_repo_env(&child.env_array, repo->gitdir);
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 	trace2_data_intmax("promisor", repo, "fetch_count", oid_nr);
35 
36 	for (i = 0; i < oid_nr; i++) {
37 		if (fputs(oid_to_hex(&oids[i]), child_in) < 0)
38 			die_errno(_("promisor-remote: could not write to fetch subprocess"));
39 		if (fputc('\n', child_in) < 0)
40 			die_errno(_("promisor-remote: could not write to fetch subprocess"));
41 	}
42 
43 	if (fclose(child_in) < 0)
44 		die_errno(_("promisor-remote: could not close stdin to fetch subprocess"));
45 	return finish_command(&child) ? -1 : 0;
46 }
47 
promisor_remote_new(struct promisor_remote_config * config,const char * remote_name)48 static struct promisor_remote *promisor_remote_new(struct promisor_remote_config *config,
49 						   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 	*config->promisors_tail = r;
62 	config->promisors_tail = &r->next;
63 
64 	return r;
65 }
66 
promisor_remote_lookup(struct promisor_remote_config * config,const char * remote_name,struct promisor_remote ** previous)67 static struct promisor_remote *promisor_remote_lookup(struct promisor_remote_config *config,
68 						      const char *remote_name,
69 						      struct promisor_remote **previous)
70 {
71 	struct promisor_remote *r, *p;
72 
73 	for (p = NULL, r = config->promisors; r; p = r, r = r->next)
74 		if (!strcmp(r->name, remote_name)) {
75 			if (previous)
76 				*previous = p;
77 			return r;
78 		}
79 
80 	return NULL;
81 }
82 
promisor_remote_move_to_tail(struct promisor_remote_config * config,struct promisor_remote * r,struct promisor_remote * previous)83 static void promisor_remote_move_to_tail(struct promisor_remote_config *config,
84 					 struct promisor_remote *r,
85 					 struct promisor_remote *previous)
86 {
87 	if (r->next == NULL)
88 		return;
89 
90 	if (previous)
91 		previous->next = r->next;
92 	else
93 		config->promisors = r->next ? r->next : r;
94 	r->next = NULL;
95 	*config->promisors_tail = r;
96 	config->promisors_tail = &r->next;
97 }
98 
promisor_remote_config(const char * var,const char * value,void * data)99 static int promisor_remote_config(const char *var, const char *value, void *data)
100 {
101 	struct promisor_remote_config *config = data;
102 	const char *name;
103 	size_t namelen;
104 	const char *subkey;
105 
106 	if (parse_config_key(var, "remote", &name, &namelen, &subkey) < 0)
107 		return 0;
108 
109 	if (!strcmp(subkey, "promisor")) {
110 		char *remote_name;
111 
112 		if (!git_config_bool(var, value))
113 			return 0;
114 
115 		remote_name = xmemdupz(name, namelen);
116 
117 		if (!promisor_remote_lookup(config, remote_name, NULL))
118 			promisor_remote_new(config, remote_name);
119 
120 		free(remote_name);
121 		return 0;
122 	}
123 	if (!strcmp(subkey, "partialclonefilter")) {
124 		struct promisor_remote *r;
125 		char *remote_name = xmemdupz(name, namelen);
126 
127 		r = promisor_remote_lookup(config, remote_name, NULL);
128 		if (!r)
129 			r = promisor_remote_new(config, remote_name);
130 
131 		free(remote_name);
132 
133 		if (!r)
134 			return 0;
135 
136 		return git_config_string(&r->partial_clone_filter, var, value);
137 	}
138 
139 	return 0;
140 }
141 
promisor_remote_init(struct repository * r)142 static void promisor_remote_init(struct repository *r)
143 {
144 	struct promisor_remote_config *config;
145 
146 	if (r->promisor_remote_config)
147 		return;
148 	config = r->promisor_remote_config =
149 		xcalloc(sizeof(*r->promisor_remote_config), 1);
150 	config->promisors_tail = &config->promisors;
151 
152 	repo_config(r, promisor_remote_config, config);
153 
154 	if (r->repository_format_partial_clone) {
155 		struct promisor_remote *o, *previous;
156 
157 		o = promisor_remote_lookup(config,
158 					   r->repository_format_partial_clone,
159 					   &previous);
160 		if (o)
161 			promisor_remote_move_to_tail(config, o, previous);
162 		else
163 			promisor_remote_new(config, r->repository_format_partial_clone);
164 	}
165 }
166 
promisor_remote_clear(struct promisor_remote_config * config)167 void promisor_remote_clear(struct promisor_remote_config *config)
168 {
169 	while (config->promisors) {
170 		struct promisor_remote *r = config->promisors;
171 		config->promisors = config->promisors->next;
172 		free(r);
173 	}
174 
175 	config->promisors_tail = &config->promisors;
176 }
177 
repo_promisor_remote_reinit(struct repository * r)178 void repo_promisor_remote_reinit(struct repository *r)
179 {
180 	promisor_remote_clear(r->promisor_remote_config);
181 	FREE_AND_NULL(r->promisor_remote_config);
182 	promisor_remote_init(r);
183 }
184 
repo_promisor_remote_find(struct repository * r,const char * remote_name)185 struct promisor_remote *repo_promisor_remote_find(struct repository *r,
186 						  const char *remote_name)
187 {
188 	promisor_remote_init(r);
189 
190 	if (!remote_name)
191 		return r->promisor_remote_config->promisors;
192 
193 	return promisor_remote_lookup(r->promisor_remote_config, remote_name, NULL);
194 }
195 
repo_has_promisor_remote(struct repository * r)196 int repo_has_promisor_remote(struct repository *r)
197 {
198 	return !!repo_promisor_remote_find(r, NULL);
199 }
200 
remove_fetched_oids(struct repository * repo,struct object_id ** oids,int oid_nr,int to_free)201 static int remove_fetched_oids(struct repository *repo,
202 			       struct object_id **oids,
203 			       int oid_nr, int to_free)
204 {
205 	int i, remaining_nr = 0;
206 	int *remaining = xcalloc(oid_nr, sizeof(*remaining));
207 	struct object_id *old_oids = *oids;
208 	struct object_id *new_oids;
209 
210 	for (i = 0; i < oid_nr; i++)
211 		if (oid_object_info_extended(repo, &old_oids[i], NULL,
212 					     OBJECT_INFO_SKIP_FETCH_OBJECT)) {
213 			remaining[i] = 1;
214 			remaining_nr++;
215 		}
216 
217 	if (remaining_nr) {
218 		int j = 0;
219 		CALLOC_ARRAY(new_oids, remaining_nr);
220 		for (i = 0; i < oid_nr; i++)
221 			if (remaining[i])
222 				oidcpy(&new_oids[j++], &old_oids[i]);
223 		*oids = new_oids;
224 		if (to_free)
225 			free(old_oids);
226 	}
227 
228 	free(remaining);
229 
230 	return remaining_nr;
231 }
232 
promisor_remote_get_direct(struct repository * repo,const struct object_id * oids,int oid_nr)233 int promisor_remote_get_direct(struct repository *repo,
234 			       const struct object_id *oids,
235 			       int oid_nr)
236 {
237 	struct promisor_remote *r;
238 	struct object_id *remaining_oids = (struct object_id *)oids;
239 	int remaining_nr = oid_nr;
240 	int to_free = 0;
241 	int res = -1;
242 
243 	if (oid_nr == 0)
244 		return 0;
245 
246 	promisor_remote_init(repo);
247 
248 	for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
249 		if (fetch_objects(repo, r->name, remaining_oids, remaining_nr) < 0) {
250 			if (remaining_nr == 1)
251 				continue;
252 			remaining_nr = remove_fetched_oids(repo, &remaining_oids,
253 							 remaining_nr, to_free);
254 			if (remaining_nr) {
255 				to_free = 1;
256 				continue;
257 			}
258 		}
259 		res = 0;
260 		break;
261 	}
262 
263 	if (to_free)
264 		free(remaining_oids);
265 
266 	return res;
267 }
268