]>
Commit | Line | Data |
---|---|---|
9c82357b | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
9c82357b | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
9c82357b CMN |
6 | */ |
7 | ||
9c82357b CMN |
8 | #include "git2/config.h" |
9 | #include "git2/types.h" | |
c07d9c95 | 10 | #include "git2/oid.h" |
1ffd0806 | 11 | #include "git2/net.h" |
9c82357b | 12 | |
157cef10 | 13 | #include "common.h" |
9c82357b CMN |
14 | #include "config.h" |
15 | #include "repository.h" | |
16 | #include "remote.h" | |
e1d88030 | 17 | #include "fetch.h" |
441f57c2 | 18 | #include "refs.h" |
b0f6e45d ET |
19 | #include "refspec.h" |
20 | #include "fetchhead.h" | |
fe794b2e | 21 | #include "push.h" |
9c82357b | 22 | |
af613ecd | 23 | static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); |
1ef3f0ce | 24 | static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name); |
af613ecd | 25 | |
3f894205 | 26 | static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch) |
9c82357b | 27 | { |
4330ab26 | 28 | git_refspec *spec; |
9c82357b | 29 | |
4330ab26 CMN |
30 | spec = git__calloc(1, sizeof(git_refspec)); |
31 | GITERR_CHECK_ALLOC(spec); | |
32 | ||
1be680c4 CMN |
33 | if (git_refspec__parse(spec, string, is_fetch) < 0) { |
34 | git__free(spec); | |
35 | return -1; | |
36 | } | |
4330ab26 CMN |
37 | |
38 | spec->push = !is_fetch; | |
3f894205 | 39 | if (git_vector_insert(vector, spec) < 0) { |
1be680c4 CMN |
40 | git_refspec__free(spec); |
41 | git__free(spec); | |
42 | return -1; | |
43 | } | |
9c82357b | 44 | |
4330ab26 | 45 | return 0; |
9c82357b CMN |
46 | } |
47 | ||
3f894205 CMN |
48 | static int add_refspec(git_remote *remote, const char *string, bool is_fetch) |
49 | { | |
50 | return add_refspec_to(&remote->refspecs, string, is_fetch); | |
51 | } | |
52 | ||
24f2f94e CMN |
53 | static int download_tags_value(git_remote *remote, git_config *cfg) |
54 | { | |
9a97f49e | 55 | git_config_entry *ce; |
24f2f94e CMN |
56 | git_buf buf = GIT_BUF_INIT; |
57 | int error; | |
58 | ||
df50512a | 59 | /* The 0 value is the default (auto), let's see if we need to change it */ |
24f2f94e CMN |
60 | if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) |
61 | return -1; | |
62 | ||
9f77b3f6 | 63 | error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); |
24f2f94e | 64 | git_buf_free(&buf); |
24f2f94e | 65 | |
9f77b3f6 RB |
66 | if (!error && ce && ce->value) { |
67 | if (!strcmp(ce->value, "--no-tags")) | |
68 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; | |
69 | else if (!strcmp(ce->value, "--tags")) | |
70 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; | |
bf6bebe2 | 71 | } |
24f2f94e | 72 | |
9a97f49e | 73 | git_config_entry_free(ce); |
24f2f94e CMN |
74 | return error; |
75 | } | |
76 | ||
032ba9e4 | 77 | static int ensure_remote_name_is_valid(const char *name) |
78 | { | |
2bca5b67 | 79 | int error = 0; |
032ba9e4 | 80 | |
2bca5b67 | 81 | if (!git_remote_is_valid_name(name)) { |
032ba9e4 | 82 | giterr_set( |
83 | GITERR_CONFIG, | |
183aa4f8 | 84 | "'%s' is not a valid remote name.", name ? name : "(null)"); |
032ba9e4 | 85 | error = GIT_EINVALIDSPEC; |
86 | } | |
87 | ||
88 | return error; | |
89 | } | |
90 | ||
41698f22 CMN |
91 | #if 0 |
92 | /* We could export this as a helper */ | |
80fc7d6b | 93 | static int get_check_cert(int *out, git_repository *repo) |
af6dab7e PK |
94 | { |
95 | git_config *cfg; | |
96 | const char *val; | |
80fc7d6b ET |
97 | int error = 0; |
98 | ||
99 | assert(out && repo); | |
af6dab7e | 100 | |
80fc7d6b ET |
101 | /* By default, we *DO* want to verify the certificate. */ |
102 | *out = 1; | |
af6dab7e PK |
103 | |
104 | /* Go through the possible sources for SSL verification settings, from | |
105 | * most specific to least specific. */ | |
106 | ||
107 | /* GIT_SSL_NO_VERIFY environment variable */ | |
8f2a3d62 | 108 | if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL) |
80fc7d6b | 109 | return git_config_parse_bool(out, val); |
af6dab7e PK |
110 | |
111 | /* http.sslVerify config setting */ | |
80fc7d6b ET |
112 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) |
113 | return error; | |
af6dab7e | 114 | |
9f77b3f6 | 115 | *out = git_config__get_bool_force(cfg, "http.sslverify", 1); |
80fc7d6b | 116 | return 0; |
af6dab7e | 117 | } |
41698f22 | 118 | #endif |
af6dab7e | 119 | |
12f32d91 ET |
120 | static int canonicalize_url(git_buf *out, const char *in) |
121 | { | |
5b0c6306 | 122 | #ifdef GIT_WIN32 |
12f32d91 ET |
123 | const char *c; |
124 | ||
12f32d91 ET |
125 | /* Given a UNC path like \\server\path, we need to convert this |
126 | * to //server/path for compatibility with core git. | |
127 | */ | |
128 | if (in[0] == '\\' && in[1] == '\\' && | |
129 | (git__isalpha(in[2]) || git__isdigit(in[2]))) { | |
130 | for (c = in; *c; c++) | |
131 | git_buf_putc(out, *c == '\\' ? '/' : *c); | |
132 | ||
133 | return git_buf_oom(out) ? -1 : 0; | |
134 | } | |
135 | #endif | |
136 | ||
137 | return git_buf_puts(out, in); | |
138 | } | |
139 | ||
874dcb25 | 140 | static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
778e1c73 CMN |
141 | { |
142 | git_remote *remote; | |
a8846da7 | 143 | git_config *config = NULL; |
12f32d91 | 144 | git_buf canonical_url = GIT_BUF_INIT, fetchbuf = GIT_BUF_INIT; |
44f36f6e | 145 | int error = -1; |
778e1c73 | 146 | |
4bef3565 | 147 | /* name is optional */ |
874dcb25 | 148 | assert(out && repo && url); |
617bfdf4 | 149 | |
59bccf33 | 150 | remote = git__calloc(1, sizeof(git_remote)); |
4376f7f6 | 151 | GITERR_CHECK_ALLOC(remote); |
778e1c73 | 152 | |
778e1c73 | 153 | remote->repo = repo; |
b0f6e45d | 154 | remote->update_fetchhead = 1; |
617bfdf4 | 155 | |
12f32d91 ET |
156 | if (git_vector_init(&remote->refs, 32, NULL) < 0 || |
157 | canonicalize_url(&canonical_url, url) < 0) | |
44f36f6e | 158 | goto on_error; |
d88d4311 | 159 | |
12f32d91 | 160 | remote->url = git_buf_detach(&canonical_url); |
778e1c73 | 161 | |
617bfdf4 CMN |
162 | if (name != NULL) { |
163 | remote->name = git__strdup(name); | |
4376f7f6 | 164 | GITERR_CHECK_ALLOC(remote->name); |
617bfdf4 CMN |
165 | } |
166 | ||
baaa8a44 | 167 | if (fetch != NULL) { |
4330ab26 | 168 | if (add_refspec(remote, fetch, true) < 0) |
baaa8a44 | 169 | goto on_error; |
d3cd7da5 | 170 | |
1ef3f0ce DC |
171 | if ((error = git_repository_config_snapshot(&config, repo)) < 0) |
172 | goto on_error; | |
173 | ||
174 | if (lookup_remote_prune_config(remote, config, name) < 0) | |
175 | goto on_error; | |
176 | ||
d3cd7da5 POL |
177 | /* Move the data over to where the matching functions can find them */ |
178 | if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) | |
179 | goto on_error; | |
baaa8a44 CMN |
180 | } |
181 | ||
215af2cc | 182 | if (!name) |
183 | /* A remote without a name doesn't download tags */ | |
c648d4a8 | 184 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; |
c648d4a8 | 185 | |
778e1c73 | 186 | *out = remote; |
a8846da7 | 187 | error = 0; |
baaa8a44 CMN |
188 | |
189 | on_error: | |
a8846da7 ET |
190 | if (error) |
191 | git_remote_free(remote); | |
192 | ||
1ef3f0ce | 193 | git_config_free(config); |
44f36f6e | 194 | git_buf_free(&fetchbuf); |
12f32d91 | 195 | git_buf_free(&canonical_url); |
44f36f6e | 196 | return error; |
778e1c73 CMN |
197 | } |
198 | ||
592f466c BS |
199 | static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) |
200 | { | |
201 | int error; | |
202 | git_remote *remote; | |
203 | ||
209425ce | 204 | error = git_remote_lookup(&remote, repo, name); |
592f466c BS |
205 | |
206 | if (error == GIT_ENOTFOUND) | |
207 | return 0; | |
208 | ||
209 | if (error < 0) | |
210 | return error; | |
211 | ||
212 | git_remote_free(remote); | |
213 | ||
214 | giterr_set( | |
215 | GITERR_CONFIG, | |
216 | "Remote '%s' already exists.", name); | |
217 | ||
218 | return GIT_EEXISTS; | |
219 | } | |
220 | ||
f19304d2 | 221 | |
874dcb25 BS |
222 | int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url) |
223 | { | |
224 | git_buf buf = GIT_BUF_INIT; | |
225 | int error; | |
226 | ||
874dcb25 BS |
227 | if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) |
228 | return -1; | |
229 | ||
a68e217f | 230 | error = git_remote_create_with_fetchspec(out, repo, name, url, git_buf_cstr(&buf)); |
874dcb25 BS |
231 | git_buf_free(&buf); |
232 | ||
a68e217f | 233 | return error; |
874dcb25 BS |
234 | } |
235 | ||
40b99d05 VG |
236 | int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
237 | { | |
40b99d05 VG |
238 | git_remote *remote = NULL; |
239 | int error; | |
240 | ||
241 | if ((error = ensure_remote_name_is_valid(name)) < 0) | |
242 | return error; | |
243 | ||
244 | if ((error = ensure_remote_doesnot_exist(repo, name)) < 0) | |
245 | return error; | |
874dcb25 | 246 | |
0fe522d1 | 247 | if (create_internal(&remote, repo, name, url, fetch) < 0) |
874dcb25 BS |
248 | goto on_error; |
249 | ||
c5193e3c | 250 | if (git_remote_save(remote) < 0) |
874dcb25 BS |
251 | goto on_error; |
252 | ||
c5193e3c | 253 | *out = remote; |
254 | ||
874dcb25 BS |
255 | return 0; |
256 | ||
257 | on_error: | |
c5193e3c | 258 | git_remote_free(remote); |
874dcb25 BS |
259 | return -1; |
260 | } | |
261 | ||
fd536d29 | 262 | int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url, const char *fetch) |
874dcb25 BS |
263 | { |
264 | int error; | |
265 | git_remote *remote; | |
266 | ||
79000951 | 267 | if ((error = create_internal(&remote, repo, NULL, url, fetch)) < 0) |
874dcb25 BS |
268 | return error; |
269 | ||
874dcb25 BS |
270 | *out = remote; |
271 | return 0; | |
272 | } | |
273 | ||
991b2840 | 274 | int git_remote_dup(git_remote **dest, git_remote *source) |
40ef47dd | 275 | { |
991b2840 AS |
276 | int error = 0; |
277 | git_strarray refspecs = { 0 }; | |
40ef47dd AS |
278 | git_remote *remote = git__calloc(1, sizeof(git_remote)); |
279 | GITERR_CHECK_ALLOC(remote); | |
280 | ||
40ef47dd AS |
281 | if (source->name != NULL) { |
282 | remote->name = git__strdup(source->name); | |
283 | GITERR_CHECK_ALLOC(remote->name); | |
284 | } | |
285 | ||
286 | if (source->url != NULL) { | |
287 | remote->url = git__strdup(source->url); | |
288 | GITERR_CHECK_ALLOC(remote->url); | |
289 | } | |
290 | ||
291 | if (source->pushurl != NULL) { | |
292 | remote->pushurl = git__strdup(source->pushurl); | |
1697cd6f | 293 | GITERR_CHECK_ALLOC(remote->pushurl); |
40ef47dd AS |
294 | } |
295 | ||
1697cd6f PK |
296 | remote->transport_cb = source->transport_cb; |
297 | remote->transport_cb_payload = source->transport_cb_payload; | |
40ef47dd | 298 | remote->repo = source->repo; |
40ef47dd | 299 | remote->download_tags = source->download_tags; |
40ef47dd | 300 | remote->update_fetchhead = source->update_fetchhead; |
5f473947 | 301 | remote->prune_refs = source->prune_refs; |
40ef47dd | 302 | |
991b2840 AS |
303 | if (git_vector_init(&remote->refs, 32, NULL) < 0 || |
304 | git_vector_init(&remote->refspecs, 2, NULL) < 0 || | |
305 | git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { | |
306 | error = -1; | |
307 | goto cleanup; | |
40ef47dd AS |
308 | } |
309 | ||
991b2840 AS |
310 | if ((error = git_remote_get_fetch_refspecs(&refspecs, source)) < 0 || |
311 | (error = git_remote_set_fetch_refspecs(remote, &refspecs)) < 0) | |
312 | goto cleanup; | |
313 | ||
314 | git_strarray_free(&refspecs); | |
315 | ||
316 | if ((error = git_remote_get_push_refspecs(&refspecs, source)) < 0 || | |
317 | (error = git_remote_set_push_refspecs(remote, &refspecs)) < 0) | |
318 | goto cleanup; | |
319 | ||
40ef47dd AS |
320 | *dest = remote; |
321 | ||
991b2840 AS |
322 | cleanup: |
323 | ||
324 | git_strarray_free(&refspecs); | |
325 | ||
326 | if (error < 0) | |
327 | git__free(remote); | |
328 | ||
329 | return error; | |
40ef47dd AS |
330 | } |
331 | ||
4330ab26 CMN |
332 | struct refspec_cb_data { |
333 | git_remote *remote; | |
334 | int fetch; | |
335 | }; | |
336 | ||
337 | static int refspec_cb(const git_config_entry *entry, void *payload) | |
338 | { | |
96869a4e | 339 | struct refspec_cb_data *data = (struct refspec_cb_data *)payload; |
25e0b157 | 340 | return add_refspec(data->remote, entry->value, data->fetch); |
4330ab26 CMN |
341 | } |
342 | ||
bf6bebe2 | 343 | static int get_optional_config( |
c9ffa84b | 344 | bool *found, git_config *config, git_buf *buf, |
345 | git_config_foreach_cb cb, void *payload) | |
bf6bebe2 RB |
346 | { |
347 | int error = 0; | |
348 | const char *key = git_buf_cstr(buf); | |
349 | ||
350 | if (git_buf_oom(buf)) | |
351 | return -1; | |
352 | ||
353 | if (cb != NULL) | |
4efa3290 | 354 | error = git_config_get_multivar_foreach(config, key, NULL, cb, payload); |
bf6bebe2 RB |
355 | else |
356 | error = git_config_get_string(payload, config, key); | |
357 | ||
c9ffa84b | 358 | if (found) |
359 | *found = !error; | |
360 | ||
bf6bebe2 RB |
361 | if (error == GIT_ENOTFOUND) { |
362 | giterr_clear(); | |
363 | error = 0; | |
364 | } | |
365 | ||
bf6bebe2 RB |
366 | return error; |
367 | } | |
368 | ||
209425ce | 369 | int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) |
9c82357b CMN |
370 | { |
371 | git_remote *remote; | |
f0f3a18a | 372 | git_buf buf = GIT_BUF_INIT; |
9c82357b | 373 | const char *val; |
4376f7f6 | 374 | int error = 0; |
9462c471 | 375 | git_config *config; |
96869a4e | 376 | struct refspec_cb_data data = { NULL }; |
c9ffa84b | 377 | bool optional_setting_found = false, found; |
4330ab26 | 378 | |
9462c471 VM |
379 | assert(out && repo && name); |
380 | ||
032ba9e4 | 381 | if ((error = ensure_remote_name_is_valid(name)) < 0) |
382 | return error; | |
383 | ||
ac99d86b | 384 | if ((error = git_repository_config_snapshot(&config, repo)) < 0) |
29c4cb09 | 385 | return error; |
4bef3565 | 386 | |
392702ee | 387 | remote = git__calloc(1, sizeof(git_remote)); |
4376f7f6 | 388 | GITERR_CHECK_ALLOC(remote); |
9c82357b | 389 | |
b0f6e45d | 390 | remote->update_fetchhead = 1; |
9c82357b | 391 | remote->name = git__strdup(name); |
4376f7f6 | 392 | GITERR_CHECK_ALLOC(remote->name); |
9c82357b | 393 | |
25e0b157 RB |
394 | if (git_vector_init(&remote->refs, 32, NULL) < 0 || |
395 | git_vector_init(&remote->refspecs, 2, NULL) < 0 || | |
2cdd5c57 | 396 | git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 || |
25e0b157 | 397 | git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { |
2fb9d6de | 398 | error = -1; |
399 | goto cleanup; | |
400 | } | |
d88d4311 | 401 | |
25e0b157 | 402 | if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) |
2fb9d6de | 403 | goto cleanup; |
9c82357b | 404 | |
c9ffa84b | 405 | if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) |
9c82357b | 406 | goto cleanup; |
bf6bebe2 | 407 | |
c9ffa84b | 408 | optional_setting_found |= found; |
409 | ||
9462c471 | 410 | remote->repo = repo; |
c9ffa84b | 411 | |
412 | if (found && strlen(val) > 0) { | |
413 | remote->url = git__strdup(val); | |
414 | GITERR_CHECK_ALLOC(remote->url); | |
415 | } | |
9c82357b | 416 | |
bf6bebe2 | 417 | val = NULL; |
3ed4b501 | 418 | git_buf_clear(&buf); |
bf6bebe2 | 419 | git_buf_printf(&buf, "remote.%s.pushurl", name); |
3ed4b501 | 420 | |
c9ffa84b | 421 | if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) |
3ed4b501 | 422 | goto cleanup; |
3ed4b501 | 423 | |
c9ffa84b | 424 | optional_setting_found |= found; |
425 | ||
426 | if (!optional_setting_found) { | |
427 | error = GIT_ENOTFOUND; | |
d723dbed | 428 | giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name); |
3ed4b501 | 429 | goto cleanup; |
c9ffa84b | 430 | } |
3ed4b501 | 431 | |
c9ffa84b | 432 | if (found && strlen(val) > 0) { |
3ed4b501 SC |
433 | remote->pushurl = git__strdup(val); |
434 | GITERR_CHECK_ALLOC(remote->pushurl); | |
435 | } | |
436 | ||
4330ab26 CMN |
437 | data.remote = remote; |
438 | data.fetch = true; | |
96869a4e | 439 | |
f0f3a18a | 440 | git_buf_clear(&buf); |
bf6bebe2 RB |
441 | git_buf_printf(&buf, "remote.%s.fetch", name); |
442 | ||
c9ffa84b | 443 | if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) |
2fb9d6de | 444 | goto cleanup; |
9c82357b | 445 | |
4330ab26 | 446 | data.fetch = false; |
bf6bebe2 RB |
447 | git_buf_clear(&buf); |
448 | git_buf_printf(&buf, "remote.%s.push", name); | |
9c82357b | 449 | |
c9ffa84b | 450 | if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) |
9c82357b CMN |
451 | goto cleanup; |
452 | ||
24f2f94e CMN |
453 | if (download_tags_value(remote, config) < 0) |
454 | goto cleanup; | |
455 | ||
1ef3f0ce DC |
456 | if ((error = lookup_remote_prune_config(remote, config, name)) < 0) |
457 | goto cleanup; | |
458 | ||
459 | /* Move the data over to where the matching functions can find them */ | |
460 | if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) | |
461 | goto cleanup; | |
462 | ||
463 | *out = remote; | |
464 | ||
465 | cleanup: | |
466 | git_config_free(config); | |
467 | git_buf_free(&buf); | |
468 | ||
469 | if (error < 0) | |
470 | git_remote_free(remote); | |
471 | ||
472 | return error; | |
473 | } | |
474 | ||
475 | static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name) | |
476 | { | |
477 | git_buf buf = GIT_BUF_INIT; | |
478 | int error = 0; | |
479 | ||
5f473947 L |
480 | git_buf_printf(&buf, "remote.%s.prune", name); |
481 | ||
482 | if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) { | |
483 | if (error == GIT_ENOTFOUND) { | |
484 | giterr_clear(); | |
485 | ||
486 | if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) { | |
487 | if (error == GIT_ENOTFOUND) { | |
488 | giterr_clear(); | |
66b71ea5 | 489 | error = 0; |
5f473947 L |
490 | } |
491 | } | |
492 | } | |
493 | } | |
494 | ||
f0f3a18a | 495 | git_buf_free(&buf); |
9c82357b CMN |
496 | return error; |
497 | } | |
498 | ||
4330ab26 | 499 | static int update_config_refspec(const git_remote *remote, git_config *config, int direction) |
4fe5520a | 500 | { |
4330ab26 | 501 | git_buf name = GIT_BUF_INIT; |
66566516 | 502 | unsigned int push; |
4330ab26 CMN |
503 | const char *dir; |
504 | size_t i; | |
bf6bebe2 | 505 | int error = 0; |
3793fa9b | 506 | const char *cname; |
4fe5520a | 507 | |
4330ab26 CMN |
508 | push = direction == GIT_DIRECTION_PUSH; |
509 | dir = push ? "push" : "fetch"; | |
4fe5520a | 510 | |
4330ab26 CMN |
511 | if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0) |
512 | return -1; | |
3793fa9b | 513 | cname = git_buf_cstr(&name); |
4fe5520a | 514 | |
4330ab26 | 515 | /* Clear out the existing config */ |
bf6bebe2 | 516 | while (!error) |
3793fa9b | 517 | error = git_config_delete_multivar(config, cname, ".*"); |
4fe5520a | 518 | |
4330ab26 CMN |
519 | if (error != GIT_ENOTFOUND) |
520 | return error; | |
521 | ||
1be680c4 | 522 | for (i = 0; i < remote->refspecs.length; i++) { |
4330ab26 | 523 | git_refspec *spec = git_vector_get(&remote->refspecs, i); |
4330ab26 CMN |
524 | |
525 | if (spec->push != push) | |
526 | continue; | |
527 | ||
376454d0 DRT |
528 | // "$^" is a unmatcheable regexp: it will not match anything at all, so |
529 | // all values will be considered new and we will not replace any | |
530 | // present value. | |
bf6bebe2 | 531 | if ((error = git_config_set_multivar( |
376454d0 | 532 | config, cname, "$^", spec->string)) < 0) { |
4330ab26 CMN |
533 | goto cleanup; |
534 | } | |
535 | } | |
536 | ||
537 | giterr_clear(); | |
538 | error = 0; | |
4fe5520a | 539 | |
540 | cleanup: | |
541 | git_buf_free(&name); | |
4fe5520a | 542 | |
543 | return error; | |
544 | } | |
545 | ||
89e5ed98 CMN |
546 | int git_remote_save(const git_remote *remote) |
547 | { | |
218c88a9 | 548 | int error; |
8286300a | 549 | git_config *cfg; |
218c88a9 | 550 | const char *tagopt = NULL; |
4fe5520a | 551 | git_buf buf = GIT_BUF_INIT; |
9a97f49e | 552 | git_config_entry *existing = NULL; |
89e5ed98 | 553 | |
e497b16c | 554 | assert(remote); |
555 | ||
c07b52df | 556 | if (!remote->name) { |
fd536d29 | 557 | giterr_set(GITERR_INVALID, "Can't save an anonymous remote."); |
874dcb25 | 558 | return GIT_EINVALIDSPEC; |
a71c27cc BS |
559 | } |
560 | ||
032ba9e4 | 561 | if ((error = ensure_remote_name_is_valid(remote->name)) < 0) |
562 | return error; | |
89e5ed98 | 563 | |
8286300a RB |
564 | if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) |
565 | return error; | |
89e5ed98 | 566 | |
8286300a RB |
567 | if ((error = git_buf_printf(&buf, "remote.%s.url", remote->name)) < 0) |
568 | return error; | |
89e5ed98 | 569 | |
8286300a RB |
570 | /* after this point, buffer is allocated so end with cleanup */ |
571 | ||
572 | if ((error = git_config_set_string( | |
573 | cfg, git_buf_cstr(&buf), remote->url)) < 0) | |
574 | goto cleanup; | |
89e5ed98 | 575 | |
413d5563 | 576 | git_buf_clear(&buf); |
8286300a RB |
577 | if ((error = git_buf_printf(&buf, "remote.%s.pushurl", remote->name)) < 0) |
578 | goto cleanup; | |
3ed4b501 | 579 | |
8286300a RB |
580 | if ((error = git_config__update_entry( |
581 | cfg, git_buf_cstr(&buf), remote->pushurl, true, false)) < 0) | |
582 | goto cleanup; | |
3ed4b501 | 583 | |
8286300a RB |
584 | if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_FETCH)) < 0) |
585 | goto cleanup; | |
89e5ed98 | 586 | |
8286300a RB |
587 | if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_PUSH)) < 0) |
588 | goto cleanup; | |
89e5ed98 | 589 | |
218c88a9 CMN |
590 | /* |
591 | * What action to take depends on the old and new values. This | |
592 | * is describes by the table below. tagopt means whether the | |
593 | * is already a value set in the config | |
594 | * | |
595 | * AUTO ALL or NONE | |
596 | * +-----------------------+ | |
597 | * tagopt | remove | set | | |
598 | * +---------+-------------| | |
599 | * !tagopt | nothing | set | | |
600 | * +---------+-------------+ | |
601 | */ | |
602 | ||
603 | git_buf_clear(&buf); | |
8286300a RB |
604 | if ((error = git_buf_printf(&buf, "remote.%s.tagopt", remote->name)) < 0) |
605 | goto cleanup; | |
218c88a9 | 606 | |
8286300a RB |
607 | if ((error = git_config__lookup_entry( |
608 | &existing, cfg, git_buf_cstr(&buf), false)) < 0) | |
609 | goto cleanup; | |
218c88a9 | 610 | |
8286300a RB |
611 | if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) |
612 | tagopt = "--tags"; | |
613 | else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) | |
614 | tagopt = "--no-tags"; | |
615 | else if (existing != NULL) | |
616 | tagopt = NULL; | |
4376f7f6 | 617 | |
8286300a RB |
618 | error = git_config__update_entry( |
619 | cfg, git_buf_cstr(&buf), tagopt, true, false); | |
4376f7f6 | 620 | |
8286300a | 621 | cleanup: |
9a97f49e | 622 | git_config_entry_free(existing); |
4376f7f6 | 623 | git_buf_free(&buf); |
8286300a | 624 | return error; |
89e5ed98 CMN |
625 | } |
626 | ||
df705148 | 627 | const char *git_remote_name(const git_remote *remote) |
9c82357b | 628 | { |
4bef3565 | 629 | assert(remote); |
9c82357b CMN |
630 | return remote->name; |
631 | } | |
632 | ||
85e1eded ES |
633 | git_repository *git_remote_owner(const git_remote *remote) |
634 | { | |
635 | assert(remote); | |
636 | return remote->repo; | |
637 | } | |
638 | ||
df705148 | 639 | const char *git_remote_url(const git_remote *remote) |
9c82357b | 640 | { |
4bef3565 | 641 | assert(remote); |
9c82357b CMN |
642 | return remote->url; |
643 | } | |
644 | ||
76501590 SC |
645 | int git_remote_set_url(git_remote *remote, const char* url) |
646 | { | |
647 | assert(remote); | |
648 | assert(url); | |
649 | ||
650 | git__free(remote->url); | |
651 | remote->url = git__strdup(url); | |
652 | GITERR_CHECK_ALLOC(remote->url); | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
df705148 | 657 | const char *git_remote_pushurl(const git_remote *remote) |
76501590 SC |
658 | { |
659 | assert(remote); | |
660 | return remote->pushurl; | |
661 | } | |
662 | ||
663 | int git_remote_set_pushurl(git_remote *remote, const char* url) | |
664 | { | |
665 | assert(remote); | |
666 | ||
667 | git__free(remote->pushurl); | |
668 | if (url) { | |
669 | remote->pushurl = git__strdup(url); | |
670 | GITERR_CHECK_ALLOC(remote->pushurl); | |
671 | } else { | |
672 | remote->pushurl = NULL; | |
673 | } | |
674 | return 0; | |
675 | } | |
676 | ||
eff5b499 SC |
677 | const char* git_remote__urlfordirection(git_remote *remote, int direction) |
678 | { | |
679 | assert(remote); | |
680 | ||
b83c92dd | 681 | assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH); |
682 | ||
df705148 | 683 | if (direction == GIT_DIRECTION_FETCH) { |
eff5b499 SC |
684 | return remote->url; |
685 | } | |
686 | ||
df705148 | 687 | if (direction == GIT_DIRECTION_PUSH) { |
eff5b499 SC |
688 | return remote->pushurl ? remote->pushurl : remote->url; |
689 | } | |
690 | ||
691 | return NULL; | |
692 | } | |
693 | ||
df705148 | 694 | int git_remote_connect(git_remote *remote, git_direction direction) |
9ba49bb5 | 695 | { |
9ba49bb5 | 696 | git_transport *t; |
c0c39025 | 697 | const char *url; |
41fb1ca0 | 698 | int flags = GIT_TRANSPORTFLAGS_NONE; |
80fc7d6b | 699 | int error; |
9ba49bb5 | 700 | |
4bef3565 VM |
701 | assert(remote); |
702 | ||
41fb1ca0 PK |
703 | t = remote->transport; |
704 | ||
c0c39025 | 705 | url = git_remote__urlfordirection(remote, direction); |
80fc7d6b | 706 | if (url == NULL) { |
44bc0c6a | 707 | giterr_set(GITERR_INVALID, |
708 | "Malformed remote '%s' - missing URL", remote->name); | |
eff5b499 | 709 | return -1; |
44bc0c6a | 710 | } |
eff5b499 | 711 | |
1697cd6f PK |
712 | /* If we don't have a transport object yet, and the caller specified a |
713 | * custom transport factory, use that */ | |
714 | if (!t && remote->transport_cb && | |
715 | (error = remote->transport_cb(&t, remote, remote->transport_cb_payload)) < 0) | |
716 | return error; | |
717 | ||
718 | /* If we still don't have a transport, then use the global | |
719 | * transport registrations which map URI schemes to transport factories */ | |
80fc7d6b ET |
720 | if (!t && (error = git_transport_new(&t, remote, url)) < 0) |
721 | return error; | |
9ba49bb5 | 722 | |
41fb1ca0 | 723 | if (t->set_callbacks && |
9b940586 | 724 | (error = t->set_callbacks(t, remote->callbacks.sideband_progress, NULL, remote->callbacks.certificate_check, remote->callbacks.payload)) < 0) |
41fb1ca0 | 725 | goto on_error; |
613d5eb9 | 726 | |
25e0b157 | 727 | if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0) |
4376f7f6 | 728 | goto on_error; |
9ba49bb5 CMN |
729 | |
730 | remote->transport = t; | |
731 | ||
4376f7f6 | 732 | return 0; |
9ba49bb5 | 733 | |
4376f7f6 CMN |
734 | on_error: |
735 | t->free(t); | |
613d5eb9 PK |
736 | |
737 | if (t == remote->transport) | |
738 | remote->transport = NULL; | |
739 | ||
80fc7d6b | 740 | return error; |
9ba49bb5 CMN |
741 | } |
742 | ||
359dce72 | 743 | int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote) |
9ba49bb5 | 744 | { |
d88d4311 VM |
745 | assert(remote); |
746 | ||
dc8adda4 | 747 | if (!remote->transport) { |
699dfcc3 | 748 | giterr_set(GITERR_NET, "this remote has never connected"); |
dc8adda4 JG |
749 | return -1; |
750 | } | |
751 | ||
359dce72 | 752 | return remote->transport->ls(out, size, remote->transport); |
9ba49bb5 CMN |
753 | } |
754 | ||
613d5eb9 PK |
755 | int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) |
756 | { | |
757 | git_config *cfg; | |
9a97f49e | 758 | git_config_entry *ce = NULL; |
9f77b3f6 | 759 | const char *val = NULL; |
80fc7d6b | 760 | int error; |
613d5eb9 PK |
761 | |
762 | assert(remote); | |
763 | ||
a71c27cc | 764 | if (!proxy_url || !remote->repo) |
613d5eb9 PK |
765 | return -1; |
766 | ||
767 | *proxy_url = NULL; | |
768 | ||
80fc7d6b ET |
769 | if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) |
770 | return error; | |
613d5eb9 PK |
771 | |
772 | /* Go through the possible sources for proxy configuration, from most specific | |
773 | * to least specific. */ | |
774 | ||
775 | /* remote.<name>.proxy config setting */ | |
9f77b3f6 | 776 | if (remote->name && remote->name[0]) { |
613d5eb9 PK |
777 | git_buf buf = GIT_BUF_INIT; |
778 | ||
80fc7d6b ET |
779 | if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) |
780 | return error; | |
613d5eb9 | 781 | |
9f77b3f6 RB |
782 | error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); |
783 | git_buf_free(&buf); | |
613d5eb9 | 784 | |
9f77b3f6 | 785 | if (error < 0) |
80fc7d6b | 786 | return error; |
613d5eb9 | 787 | |
9f77b3f6 RB |
788 | if (ce && ce->value) { |
789 | val = ce->value; | |
790 | goto found; | |
791 | } | |
613d5eb9 PK |
792 | } |
793 | ||
794 | /* http.proxy config setting */ | |
9f77b3f6 | 795 | if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) |
80fc7d6b | 796 | return error; |
9f77b3f6 RB |
797 | if (ce && ce->value) { |
798 | val = ce->value; | |
799 | goto found; | |
800 | } | |
613d5eb9 PK |
801 | |
802 | /* HTTP_PROXY / HTTPS_PROXY environment variables */ | |
803 | val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY"); | |
804 | ||
9f77b3f6 RB |
805 | found: |
806 | if (val && val[0]) { | |
613d5eb9 PK |
807 | *proxy_url = git__strdup(val); |
808 | GITERR_CHECK_ALLOC(*proxy_url); | |
613d5eb9 | 809 | } |
9a97f49e | 810 | git_config_entry_free(ce); |
613d5eb9 PK |
811 | |
812 | return 0; | |
813 | } | |
814 | ||
af613ecd CMN |
815 | /* DWIM `refspecs` based on `refs` and append the output to `out` */ |
816 | static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs) | |
d8488457 | 817 | { |
af613ecd | 818 | size_t i; |
d8488457 | 819 | git_refspec *spec; |
d8488457 CMN |
820 | |
821 | git_vector_foreach(refspecs, i, spec) { | |
af613ecd CMN |
822 | if (git_refspec__dwim_one(out, spec, refs) < 0) |
823 | return -1; | |
824 | } | |
d8488457 | 825 | |
af613ecd CMN |
826 | return 0; |
827 | } | |
d8488457 | 828 | |
af613ecd CMN |
829 | static void free_refspecs(git_vector *vec) |
830 | { | |
831 | size_t i; | |
832 | git_refspec *spec; | |
d8488457 | 833 | |
af613ecd CMN |
834 | git_vector_foreach(vec, i, spec) { |
835 | git_refspec__free(spec); | |
836 | git__free(spec); | |
d8488457 CMN |
837 | } |
838 | ||
af613ecd | 839 | git_vector_clear(vec); |
d8488457 CMN |
840 | } |
841 | ||
842 | static int remote_head_cmp(const void *_a, const void *_b) | |
843 | { | |
844 | const git_remote_head *a = (git_remote_head *) _a; | |
845 | const git_remote_head *b = (git_remote_head *) _b; | |
846 | ||
847 | return git__strcmp_cb(a->name, b->name); | |
848 | } | |
849 | ||
877cde76 CMN |
850 | static int ls_to_vector(git_vector *out, git_remote *remote) |
851 | { | |
852 | git_remote_head **heads; | |
853 | size_t heads_len, i; | |
854 | ||
855 | if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) | |
856 | return -1; | |
857 | ||
858 | if (git_vector_init(out, heads_len, remote_head_cmp) < 0) | |
859 | return -1; | |
860 | ||
861 | for (i = 0; i < heads_len; i++) { | |
862 | if (git_vector_insert(out, heads[i]) < 0) | |
863 | return -1; | |
864 | } | |
865 | ||
866 | return 0; | |
867 | } | |
868 | ||
3f894205 | 869 | int git_remote_download(git_remote *remote, const git_strarray *refspecs) |
48a65a07 | 870 | { |
eecc1772 | 871 | int error = -1; |
3f894205 CMN |
872 | size_t i; |
873 | git_vector refs, specs, *to_active; | |
95057b85 | 874 | |
1e3b8ed5 | 875 | assert(remote); |
4bef3565 | 876 | |
877cde76 | 877 | if (ls_to_vector(&refs, remote) < 0) |
d8488457 | 878 | return -1; |
d8488457 | 879 | |
3f894205 CMN |
880 | if ((git_vector_init(&specs, 0, NULL)) < 0) |
881 | goto on_error; | |
882 | ||
c5837cad | 883 | remote->passed_refspecs = 0; |
8e398e4c | 884 | if (!refspecs || !refspecs->count) { |
3f894205 CMN |
885 | to_active = &remote->refspecs; |
886 | } else { | |
887 | for (i = 0; i < refspecs->count; i++) { | |
888 | if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0) | |
889 | goto on_error; | |
890 | } | |
891 | ||
892 | to_active = &specs; | |
c5837cad | 893 | remote->passed_refspecs = 1; |
3f894205 CMN |
894 | } |
895 | ||
2cdd5c57 CMN |
896 | free_refspecs(&remote->passive_refspecs); |
897 | if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0) | |
898 | goto on_error; | |
af613ecd | 899 | |
2cdd5c57 | 900 | free_refspecs(&remote->active_refspecs); |
3f894205 | 901 | error = dwim_refspecs(&remote->active_refspecs, to_active, &refs); |
2cdd5c57 | 902 | |
877cde76 | 903 | git_vector_free(&refs); |
3f894205 CMN |
904 | free_refspecs(&specs); |
905 | git_vector_free(&specs); | |
877cde76 CMN |
906 | |
907 | if (error < 0) | |
25e0b157 | 908 | return error; |
d8488457 | 909 | |
c070ac64 POL |
910 | if (remote->push) { |
911 | git_push_free(remote->push); | |
912 | remote->push = NULL; | |
913 | } | |
914 | ||
95057b85 | 915 | if ((error = git_fetch_negotiate(remote)) < 0) |
4376f7f6 | 916 | return error; |
95057b85 | 917 | |
d31402a3 | 918 | return git_fetch_download_pack(remote); |
3f894205 CMN |
919 | |
920 | on_error: | |
921 | git_vector_free(&refs); | |
922 | free_refspecs(&specs); | |
923 | git_vector_free(&specs); | |
924 | return error; | |
48a65a07 CMN |
925 | } |
926 | ||
c3ab1e5a BS |
927 | int git_remote_fetch( |
928 | git_remote *remote, | |
3f894205 | 929 | const git_strarray *refspecs, |
c3ab1e5a | 930 | const char *reflog_message) |
fe3a40a4 CMN |
931 | { |
932 | int error; | |
db55bb73 | 933 | git_buf reflog_msg_buf = GIT_BUF_INIT; |
fe3a40a4 CMN |
934 | |
935 | /* Connect and download everything */ | |
25e0b157 | 936 | if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) |
fe3a40a4 CMN |
937 | return error; |
938 | ||
3f894205 | 939 | error = git_remote_download(remote, refspecs); |
fe3a40a4 CMN |
940 | |
941 | /* We don't need to be connected anymore */ | |
942 | git_remote_disconnect(remote); | |
943 | ||
06a8f5c3 BL |
944 | /* If the download failed, return the error */ |
945 | if (error != 0) | |
946 | return error; | |
947 | ||
db55bb73 BS |
948 | /* Default reflog message */ |
949 | if (reflog_message) | |
950 | git_buf_sets(&reflog_msg_buf, reflog_message); | |
951 | else { | |
952 | git_buf_printf(&reflog_msg_buf, "fetch %s", | |
953 | remote->name ? remote->name : remote->url); | |
954 | } | |
955 | ||
fe3a40a4 | 956 | /* Create "remote/foo" branches for all remote branches */ |
659cf202 | 957 | error = git_remote_update_tips(remote, git_buf_cstr(&reflog_msg_buf)); |
db55bb73 | 958 | git_buf_free(&reflog_msg_buf); |
8c13eaed CMN |
959 | if (error < 0) |
960 | return error; | |
961 | ||
962 | if (remote->prune_refs) | |
963 | error = git_remote_prune(remote); | |
964 | ||
db55bb73 | 965 | return error; |
fe3a40a4 CMN |
966 | } |
967 | ||
b0f6e45d ET |
968 | static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) |
969 | { | |
970 | unsigned int i; | |
971 | git_remote_head *remote_ref; | |
972 | ||
973 | assert(update_heads && fetchspec_src); | |
974 | ||
975 | *out = NULL; | |
7d4b65f6 | 976 | |
977 | git_vector_foreach(update_heads, i, remote_ref) { | |
978 | if (strcmp(remote_ref->name, fetchspec_src) == 0) { | |
979 | *out = remote_ref; | |
980 | break; | |
b0f6e45d ET |
981 | } |
982 | } | |
983 | ||
984 | return 0; | |
985 | } | |
986 | ||
2c9b9c8b CMN |
987 | static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name) |
988 | { | |
989 | int error = 0; | |
990 | git_repository *repo; | |
991 | git_buf upstream_remote = GIT_BUF_INIT; | |
992 | git_buf upstream_name = GIT_BUF_INIT; | |
993 | ||
994 | repo = git_remote_owner(remote); | |
995 | ||
996 | if ((!git_reference__is_branch(ref_name)) || | |
997 | !git_remote_name(remote) || | |
998 | (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) || | |
999 | git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || | |
1000 | (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || | |
1001 | !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || | |
1002 | (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) { | |
1003 | /* Not an error if there is no upstream */ | |
1004 | if (error == GIT_ENOTFOUND) { | |
1005 | giterr_clear(); | |
1006 | error = 0; | |
1007 | } | |
1008 | ||
1009 | *update = 0; | |
1010 | } else { | |
1011 | *update = 1; | |
1012 | } | |
1013 | ||
1014 | git_buf_free(&upstream_remote); | |
1015 | git_buf_free(&upstream_name); | |
1016 | return error; | |
1017 | } | |
1018 | ||
f49819aa | 1019 | static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref) |
b0f6e45d ET |
1020 | { |
1021 | git_reference *resolved_ref = NULL; | |
b0f6e45d | 1022 | git_buf remote_name = GIT_BUF_INIT; |
f49819aa | 1023 | git_config *config = NULL; |
e235db02 | 1024 | const char *ref_name; |
2c9b9c8b | 1025 | int error = 0, update; |
b0f6e45d | 1026 | |
4330ab26 | 1027 | assert(out && spec && ref); |
b0f6e45d ET |
1028 | |
1029 | *out = NULL; | |
1030 | ||
67d4997a CMN |
1031 | error = git_reference_resolve(&resolved_ref, ref); |
1032 | ||
1033 | /* If we're in an unborn branch, let's pretend nothing happened */ | |
1034 | if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REF_SYMBOLIC) { | |
1035 | ref_name = git_reference_symbolic_target(ref); | |
1036 | error = 0; | |
1037 | } else { | |
1038 | ref_name = git_reference_name(resolved_ref); | |
1039 | } | |
1040 | ||
2c9b9c8b | 1041 | if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0) |
b0f6e45d | 1042 | goto cleanup; |
b0f6e45d | 1043 | |
2c9b9c8b CMN |
1044 | if (update) |
1045 | error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); | |
b0f6e45d ET |
1046 | |
1047 | cleanup: | |
f7fcb18f | 1048 | git_buf_free(&remote_name); |
b0f6e45d | 1049 | git_reference_free(resolved_ref); |
f49819aa | 1050 | git_config_free(config); |
b0f6e45d ET |
1051 | return error; |
1052 | } | |
1053 | ||
4330ab26 | 1054 | static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads) |
b0f6e45d | 1055 | { |
b0f6e45d ET |
1056 | git_reference *head_ref = NULL; |
1057 | git_fetchhead_ref *fetchhead_ref; | |
1058 | git_remote_head *remote_ref, *merge_remote_ref; | |
1059 | git_vector fetchhead_refs; | |
1060 | bool include_all_fetchheads; | |
1061 | unsigned int i = 0; | |
1062 | int error = 0; | |
1063 | ||
1064 | assert(remote); | |
1065 | ||
404eadb0 CMN |
1066 | /* no heads, nothing to do */ |
1067 | if (update_heads->length == 0) | |
1068 | return 0; | |
1069 | ||
b0f6e45d ET |
1070 | if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0) |
1071 | return -1; | |
1072 | ||
1073 | /* Iff refspec is * (but not subdir slash star), include tags */ | |
1074 | include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0); | |
1075 | ||
1076 | /* Determine what to merge: if refspec was a wildcard, just use HEAD */ | |
1077 | if (git_refspec_is_wildcard(spec)) { | |
1078 | if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 || | |
f49819aa | 1079 | (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0) |
b0f6e45d ET |
1080 | goto cleanup; |
1081 | } else { | |
1082 | /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */ | |
1083 | if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0) | |
1084 | goto cleanup; | |
1085 | } | |
1086 | ||
1087 | /* Create the FETCH_HEAD file */ | |
7d4b65f6 | 1088 | git_vector_foreach(update_heads, i, remote_ref) { |
b0f6e45d ET |
1089 | int merge_this_fetchhead = (merge_remote_ref == remote_ref); |
1090 | ||
1091 | if (!include_all_fetchheads && | |
1092 | !git_refspec_src_matches(spec, remote_ref->name) && | |
1093 | !merge_this_fetchhead) | |
1094 | continue; | |
1095 | ||
1096 | if (git_fetchhead_ref_create(&fetchhead_ref, | |
1097 | &remote_ref->oid, | |
1098 | merge_this_fetchhead, | |
1099 | remote_ref->name, | |
1100 | git_remote_url(remote)) < 0) | |
1101 | goto cleanup; | |
1102 | ||
1103 | if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0) | |
1104 | goto cleanup; | |
1105 | } | |
1106 | ||
1107 | git_fetchhead_write(remote->repo, &fetchhead_refs); | |
1108 | ||
1109 | cleanup: | |
1110 | for (i = 0; i < fetchhead_refs.length; ++i) | |
1111 | git_fetchhead_ref_free(fetchhead_refs.contents[i]); | |
1112 | ||
1113 | git_vector_free(&fetchhead_refs); | |
1114 | git_reference_free(head_ref); | |
1115 | ||
1116 | return error; | |
1117 | } | |
1118 | ||
59ff8b67 CMN |
1119 | /** |
1120 | * Generate a list of candidates for pruning by getting a list of | |
1121 | * references which match the rhs of an active refspec. | |
1122 | */ | |
1123 | static int prune_candidates(git_vector *candidates, git_remote *remote) | |
5f473947 L |
1124 | { |
1125 | git_strarray arr = { 0 }; | |
59ff8b67 | 1126 | size_t i; |
5f473947 L |
1127 | int error; |
1128 | ||
1129 | if ((error = git_reference_list(&arr, remote->repo)) < 0) | |
1130 | return error; | |
1131 | ||
59ff8b67 CMN |
1132 | for (i = 0; i < arr.count; i++) { |
1133 | const char *refname = arr.strings[i]; | |
1134 | char *refname_dup; | |
1135 | ||
1136 | if (!git_remote__matching_dst_refspec(remote, refname)) | |
1137 | continue; | |
1138 | ||
1139 | refname_dup = git__strdup(refname); | |
1140 | GITERR_CHECK_ALLOC(refname_dup); | |
1141 | ||
1142 | if ((error = git_vector_insert(candidates, refname_dup)) < 0) | |
1143 | goto out; | |
1144 | } | |
1145 | ||
1146 | out: | |
1147 | git_strarray_free(&arr); | |
1148 | return error; | |
1149 | } | |
1150 | ||
1151 | static int find_head(const void *_a, const void *_b) | |
1152 | { | |
1153 | git_remote_head *a = (git_remote_head *) _a; | |
1154 | git_remote_head *b = (git_remote_head *) _b; | |
1155 | ||
1156 | return strcmp(a->name, b->name); | |
1157 | } | |
1158 | ||
1159 | int git_remote_prune(git_remote *remote) | |
1160 | { | |
1161 | size_t i, j; | |
1162 | git_vector remote_refs = GIT_VECTOR_INIT; | |
1163 | git_vector candidates = GIT_VECTOR_INIT; | |
1164 | const git_refspec *spec; | |
1165 | const char *refname; | |
1166 | int error; | |
1167 | git_oid zero_id = {{ 0 }}; | |
1168 | ||
5f473947 L |
1169 | if ((error = ls_to_vector(&remote_refs, remote)) < 0) |
1170 | goto cleanup; | |
1171 | ||
59ff8b67 | 1172 | git_vector_set_cmp(&remote_refs, find_head); |
5f473947 | 1173 | |
59ff8b67 CMN |
1174 | if ((error = prune_candidates(&candidates, remote)) < 0) |
1175 | goto cleanup; | |
5f473947 | 1176 | |
59ff8b67 CMN |
1177 | /* |
1178 | * Remove those entries from the candidate list for which we | |
1179 | * can find a remote reference in at least one refspec. | |
1180 | */ | |
1181 | git_vector_foreach(&candidates, i, refname) { | |
1182 | git_vector_foreach(&remote->active_refspecs, j, spec) { | |
1183 | git_buf buf = GIT_BUF_INIT; | |
1184 | size_t pos; | |
1185 | char *src_name; | |
1186 | git_remote_head key = {0}; | |
1187 | ||
1188 | if (!git_refspec_dst_matches(spec, refname)) | |
5f473947 L |
1189 | continue; |
1190 | ||
59ff8b67 CMN |
1191 | if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0) |
1192 | goto cleanup; | |
5f473947 | 1193 | |
59ff8b67 CMN |
1194 | key.name = (char *) git_buf_cstr(&buf); |
1195 | error = git_vector_search(&pos, &remote_refs, &key); | |
1196 | git_buf_free(&buf); | |
5f473947 | 1197 | |
59ff8b67 CMN |
1198 | if (error < 0 && error != GIT_ENOTFOUND) |
1199 | goto cleanup; | |
5f473947 | 1200 | |
59ff8b67 | 1201 | if (error == GIT_ENOTFOUND) |
5f473947 L |
1202 | continue; |
1203 | ||
59ff8b67 CMN |
1204 | /* if we did find a source, remove it from the candiates */ |
1205 | if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0) | |
1206 | goto cleanup; | |
1207 | ||
1208 | git__free(src_name); | |
1209 | break; | |
5f473947 L |
1210 | } |
1211 | } | |
1212 | ||
59ff8b67 CMN |
1213 | /* |
1214 | * For those candidates still left in the list, we need to | |
1215 | * remove them. We do not remove symrefs, as those are for | |
1216 | * stuff like origin/HEAD which will never match, but we do | |
1217 | * not want to remove them. | |
1218 | */ | |
1219 | git_vector_foreach(&candidates, i, refname) { | |
1220 | git_reference *ref; | |
1221 | git_oid id; | |
1222 | ||
1223 | if (refname == NULL) | |
1224 | continue; | |
1225 | ||
1226 | error = git_reference_lookup(&ref, remote->repo, refname); | |
1227 | /* as we want it gone, let's not consider this an error */ | |
1228 | if (error == GIT_ENOTFOUND) | |
1229 | continue; | |
1230 | ||
1231 | if (error < 0) | |
1232 | goto cleanup; | |
1233 | ||
1234 | if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { | |
1235 | git_reference_free(ref); | |
1236 | continue; | |
1237 | } | |
1238 | ||
1239 | git_oid_cpy(&id, git_reference_target(ref)); | |
1240 | error = git_reference_delete(ref); | |
1241 | git_reference_free(ref); | |
1242 | if (error < 0) | |
1243 | goto cleanup; | |
1244 | ||
1245 | if (remote->callbacks.update_tips) | |
1246 | error = remote->callbacks.update_tips(refname, &id, &zero_id, remote->callbacks.payload); | |
1247 | ||
1248 | if (error < 0) | |
1249 | goto cleanup; | |
1250 | } | |
1251 | ||
5f473947 | 1252 | cleanup: |
5f473947 | 1253 | git_vector_free(&remote_refs); |
59ff8b67 | 1254 | git_vector_free_deep(&candidates); |
5f473947 L |
1255 | return error; |
1256 | } | |
1257 | ||
c3ab1e5a BS |
1258 | static int update_tips_for_spec( |
1259 | git_remote *remote, | |
1260 | git_refspec *spec, | |
1261 | git_vector *refs, | |
c3ab1e5a | 1262 | const char *log_message) |
441f57c2 | 1263 | { |
a37ddf7e | 1264 | int error = 0, autotag; |
517bda19 | 1265 | unsigned int i = 0; |
97769280 | 1266 | git_buf refname = GIT_BUF_INIT; |
f184836b | 1267 | git_oid old; |
a37ddf7e | 1268 | git_odb *odb; |
441f57c2 CMN |
1269 | git_remote_head *head; |
1270 | git_reference *ref; | |
a37ddf7e | 1271 | git_refspec tagspec; |
4330ab26 | 1272 | git_vector update_heads; |
441f57c2 | 1273 | |
4bef3565 VM |
1274 | assert(remote); |
1275 | ||
acd17006 | 1276 | if (git_repository_odb__weakptr(&odb, remote->repo) < 0) |
a37ddf7e CMN |
1277 | return -1; |
1278 | ||
3230a44f | 1279 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
a37ddf7e CMN |
1280 | return -1; |
1281 | ||
41fb1ca0 | 1282 | /* Make a copy of the transport's refs */ |
4330ab26 | 1283 | if (git_vector_init(&update_heads, 16, NULL) < 0) |
41fb1ca0 | 1284 | return -1; |
585a2eb7 | 1285 | |
4330ab26 CMN |
1286 | for (; i < refs->length; ++i) { |
1287 | head = git_vector_get(refs, i); | |
9063be1f | 1288 | autotag = 0; |
517bda19 | 1289 | |
a37ddf7e CMN |
1290 | /* Ignore malformed ref names (which also saves us from tag^{} */ |
1291 | if (!git_reference_is_valid_name(head->name)) | |
1292 | continue; | |
1293 | ||
e284c451 POL |
1294 | if (git_refspec_src_matches(&tagspec, head->name)) { |
1295 | if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { | |
3230a44f | 1296 | |
e284c451 POL |
1297 | if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO) |
1298 | autotag = 1; | |
a37ddf7e | 1299 | |
e284c451 POL |
1300 | git_buf_clear(&refname); |
1301 | if (git_buf_puts(&refname, head->name) < 0) | |
1302 | goto on_error; | |
1303 | } else { | |
a37ddf7e | 1304 | continue; |
e284c451 POL |
1305 | } |
1306 | } else if (git_refspec_src_matches(spec, head->name) && spec->dst) { | |
1307 | if (git_refspec_transform(&refname, spec, head->name) < 0) | |
a37ddf7e CMN |
1308 | goto on_error; |
1309 | } else { | |
1310 | continue; | |
1311 | } | |
1312 | ||
e284c451 | 1313 | /* In autotag mode, only create tags for objects already in db */ |
a37ddf7e CMN |
1314 | if (autotag && !git_odb_exists(odb, &head->oid)) |
1315 | continue; | |
f184836b | 1316 | |
d908351a | 1317 | if (!autotag && git_vector_insert(&update_heads, head) < 0) |
b0f6e45d ET |
1318 | goto on_error; |
1319 | ||
2508cc66 | 1320 | error = git_reference_name_to_id(&old, remote->repo, refname.ptr); |
904b67e6 | 1321 | if (error < 0 && error != GIT_ENOTFOUND) |
f184836b CMN |
1322 | goto on_error; |
1323 | ||
d908351a | 1324 | if (error == GIT_ENOTFOUND) { |
f184836b CMN |
1325 | memset(&old, 0, GIT_OID_RAWSZ); |
1326 | ||
d908351a L |
1327 | if (autotag && git_vector_insert(&update_heads, head) < 0) |
1328 | goto on_error; | |
1329 | } | |
1330 | ||
b7f167da | 1331 | if (!git_oid__cmp(&old, &head->oid)) |
f184836b | 1332 | continue; |
441f57c2 | 1333 | |
a37ddf7e | 1334 | /* In autotag mode, don't overwrite any locally-existing tags */ |
c3ab1e5a | 1335 | error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, |
659cf202 | 1336 | log_message); |
a37ddf7e | 1337 | if (error < 0 && error != GIT_EEXISTS) |
944d250f | 1338 | goto on_error; |
39157563 CMN |
1339 | |
1340 | git_reference_free(ref); | |
f184836b | 1341 | |
b3aaa7a7 | 1342 | if (remote->callbacks.update_tips != NULL) { |
df705148 | 1343 | if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.payload) < 0) |
f184836b CMN |
1344 | goto on_error; |
1345 | } | |
441f57c2 CMN |
1346 | } |
1347 | ||
b0f6e45d | 1348 | if (git_remote_update_fetchhead(remote) && |
4330ab26 | 1349 | (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0) |
b0f6e45d ET |
1350 | goto on_error; |
1351 | ||
b0f6e45d | 1352 | git_vector_free(&update_heads); |
a37ddf7e | 1353 | git_refspec__free(&tagspec); |
97769280 | 1354 | git_buf_free(&refname); |
f184836b CMN |
1355 | return 0; |
1356 | ||
1357 | on_error: | |
b0f6e45d | 1358 | git_vector_free(&update_heads); |
a37ddf7e | 1359 | git_refspec__free(&tagspec); |
f184836b CMN |
1360 | git_buf_free(&refname); |
1361 | return -1; | |
97769280 | 1362 | |
441f57c2 CMN |
1363 | } |
1364 | ||
c5837cad CMN |
1365 | /** |
1366 | * Iteration over the three vectors, with a pause whenever we find a match | |
1367 | * | |
1368 | * On each stop, we store the iteration stat in the inout i,j,k | |
1369 | * parameters, and return the currently matching passive refspec as | |
1370 | * well as the head which we matched. | |
1371 | */ | |
1372 | static int next_head(const git_remote *remote, git_vector *refs, | |
1373 | git_refspec **out_spec, git_remote_head **out_head, | |
1374 | size_t *out_i, size_t *out_j, size_t *out_k) | |
1375 | { | |
1376 | const git_vector *active, *passive; | |
1377 | git_remote_head *head; | |
1378 | git_refspec *spec, *passive_spec; | |
1379 | size_t i, j, k; | |
1380 | ||
1381 | active = &remote->active_refspecs; | |
1382 | passive = &remote->passive_refspecs; | |
1383 | ||
1384 | i = *out_i; | |
1385 | j = *out_j; | |
1386 | k = *out_k; | |
1387 | ||
1388 | for (; i < refs->length; i++) { | |
1389 | head = git_vector_get(refs, i); | |
1390 | ||
1391 | if (!git_reference_is_valid_name(head->name)) | |
1392 | continue; | |
1393 | ||
1394 | for (; j < active->length; j++) { | |
1395 | spec = git_vector_get(active, j); | |
1396 | ||
1397 | if (!git_refspec_src_matches(spec, head->name)) | |
1398 | continue; | |
1399 | ||
1400 | for (; k < passive->length; k++) { | |
1401 | passive_spec = git_vector_get(passive, k); | |
1402 | ||
1403 | if (!git_refspec_src_matches(passive_spec, head->name)) | |
1404 | continue; | |
1405 | ||
1406 | *out_spec = passive_spec; | |
1407 | *out_head = head; | |
1408 | *out_i = i; | |
1409 | *out_j = j; | |
1410 | *out_k = k + 1; | |
1411 | return 0; | |
1412 | ||
1413 | } | |
1414 | k = 0; | |
1415 | } | |
1416 | j = 0; | |
1417 | } | |
1418 | ||
1419 | return GIT_ITEROVER; | |
1420 | } | |
1421 | ||
659cf202 | 1422 | static int opportunistic_updates(const git_remote *remote, git_vector *refs, const char *msg) |
c5837cad CMN |
1423 | { |
1424 | size_t i, j, k; | |
1425 | git_refspec *spec; | |
1426 | git_remote_head *head; | |
1427 | git_reference *ref; | |
1428 | git_buf refname = GIT_BUF_INIT; | |
1429 | int error; | |
1430 | ||
1431 | i = j = k = 0; | |
1432 | ||
1433 | while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) { | |
1434 | /* | |
1435 | * If we got here, there is a refspec which was used | |
1436 | * for fetching which matches the source of one of the | |
1437 | * passive refspecs, so we should update that | |
1438 | * remote-tracking branch, but not add it to | |
1439 | * FETCH_HEAD | |
1440 | */ | |
1441 | ||
1442 | if ((error = git_refspec_transform(&refname, spec, head->name)) < 0) | |
1443 | return error; | |
1444 | ||
659cf202 | 1445 | error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg); |
c5837cad | 1446 | git_buf_free(&refname); |
cdd71711 | 1447 | git_reference_free(ref); |
c5837cad CMN |
1448 | |
1449 | if (error < 0) | |
1450 | return error; | |
1451 | } | |
1452 | ||
1453 | return 0; | |
1454 | } | |
1455 | ||
c3ab1e5a BS |
1456 | int git_remote_update_tips( |
1457 | git_remote *remote, | |
c3ab1e5a | 1458 | const char *reflog_message) |
4330ab26 | 1459 | { |
505b5d0c | 1460 | git_refspec *spec, tagspec; |
4330ab26 | 1461 | git_vector refs; |
505b5d0c | 1462 | int error; |
4330ab26 CMN |
1463 | size_t i; |
1464 | ||
fe794b2e CMN |
1465 | /* push has its own logic hidden away in the push object */ |
1466 | if (remote->push) { | |
412a3808 | 1467 | return git_push_update_tips(remote->push); |
fe794b2e CMN |
1468 | } |
1469 | ||
505b5d0c CMN |
1470 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
1471 | return -1; | |
1472 | ||
877cde76 CMN |
1473 | |
1474 | if ((error = ls_to_vector(&refs, remote)) < 0) | |
505b5d0c CMN |
1475 | goto out; |
1476 | ||
1477 | if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { | |
659cf202 | 1478 | if ((error = update_tips_for_spec(remote, &tagspec, &refs, reflog_message)) < 0) |
e284c451 | 1479 | goto out; |
505b5d0c | 1480 | } |
4330ab26 | 1481 | |
af613ecd | 1482 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1483 | if (spec->push) |
1484 | continue; | |
1485 | ||
659cf202 | 1486 | if ((error = update_tips_for_spec(remote, spec, &refs, reflog_message)) < 0) |
505b5d0c | 1487 | goto out; |
4330ab26 CMN |
1488 | } |
1489 | ||
c5837cad CMN |
1490 | /* only try to do opportunisitic updates if the refpec lists differ */ |
1491 | if (remote->passed_refspecs) | |
659cf202 | 1492 | error = opportunistic_updates(remote, &refs, reflog_message); |
c5837cad | 1493 | |
505b5d0c | 1494 | out: |
877cde76 | 1495 | git_vector_free(&refs); |
505b5d0c | 1496 | git_refspec__free(&tagspec); |
505b5d0c | 1497 | return error; |
4330ab26 CMN |
1498 | } |
1499 | ||
11f6ad5f | 1500 | int git_remote_connected(const git_remote *remote) |
6ac3b707 | 1501 | { |
4bef3565 | 1502 | assert(remote); |
41fb1ca0 PK |
1503 | |
1504 | if (!remote->transport || !remote->transport->is_connected) | |
1505 | return 0; | |
1506 | ||
1507 | /* Ask the transport if it's connected. */ | |
613d5eb9 | 1508 | return remote->transport->is_connected(remote->transport); |
6ac3b707 CMN |
1509 | } |
1510 | ||
f0d2ddbb CMN |
1511 | void git_remote_stop(git_remote *remote) |
1512 | { | |
613d5eb9 PK |
1513 | assert(remote); |
1514 | ||
1515 | if (remote->transport && remote->transport->cancel) | |
41fb1ca0 | 1516 | remote->transport->cancel(remote->transport); |
f0d2ddbb CMN |
1517 | } |
1518 | ||
4cf01e9a CMN |
1519 | void git_remote_disconnect(git_remote *remote) |
1520 | { | |
4bef3565 VM |
1521 | assert(remote); |
1522 | ||
41fb1ca0 PK |
1523 | if (git_remote_connected(remote)) |
1524 | remote->transport->close(remote->transport); | |
4cf01e9a CMN |
1525 | } |
1526 | ||
9c82357b CMN |
1527 | void git_remote_free(git_remote *remote) |
1528 | { | |
2aae2188 CMN |
1529 | if (remote == NULL) |
1530 | return; | |
1531 | ||
42ea35c0 MS |
1532 | if (remote->transport != NULL) { |
1533 | git_remote_disconnect(remote); | |
1534 | ||
1535 | remote->transport->free(remote->transport); | |
1536 | remote->transport = NULL; | |
1537 | } | |
1538 | ||
1539 | git_vector_free(&remote->refs); | |
1540 | ||
af613ecd | 1541 | free_refspecs(&remote->refspecs); |
4330ab26 CMN |
1542 | git_vector_free(&remote->refspecs); |
1543 | ||
af613ecd CMN |
1544 | free_refspecs(&remote->active_refspecs); |
1545 | git_vector_free(&remote->active_refspecs); | |
1546 | ||
2cdd5c57 CMN |
1547 | free_refspecs(&remote->passive_refspecs); |
1548 | git_vector_free(&remote->passive_refspecs); | |
1549 | ||
fe794b2e | 1550 | git_push_free(remote->push); |
3286c408 | 1551 | git__free(remote->url); |
3ed4b501 | 1552 | git__free(remote->pushurl); |
3286c408 | 1553 | git__free(remote->name); |
3286c408 | 1554 | git__free(remote); |
9c82357b | 1555 | } |
8171998f | 1556 | |
106c12f1 | 1557 | static int remote_list_cb(const git_config_entry *entry, void *payload) |
8171998f | 1558 | { |
25e0b157 | 1559 | git_vector *list = payload; |
106c12f1 RB |
1560 | const char *name = entry->name + strlen("remote."); |
1561 | size_t namelen = strlen(name); | |
1562 | char *remote_name; | |
8171998f | 1563 | |
106c12f1 | 1564 | /* we know name matches "remote.<stuff>.(push)?url" */ |
8171998f | 1565 | |
106c12f1 RB |
1566 | if (!strcmp(&name[namelen - 4], ".url")) |
1567 | remote_name = git__strndup(name, namelen - 4); /* strip ".url" */ | |
1568 | else | |
1569 | remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */ | |
25e0b157 | 1570 | GITERR_CHECK_ALLOC(remote_name); |
8171998f | 1571 | |
25e0b157 | 1572 | return git_vector_insert(list, remote_name); |
8171998f CMN |
1573 | } |
1574 | ||
1575 | int git_remote_list(git_strarray *remotes_list, git_repository *repo) | |
1576 | { | |
8171998f | 1577 | int error; |
96869a4e | 1578 | git_config *cfg; |
25e0b157 | 1579 | git_vector list = GIT_VECTOR_INIT; |
8171998f | 1580 | |
96869a4e RB |
1581 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) |
1582 | return error; | |
8171998f | 1583 | |
25e0b157 | 1584 | if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) |
96869a4e | 1585 | return error; |
8171998f | 1586 | |
106c12f1 | 1587 | error = git_config_foreach_match( |
25e0b157 | 1588 | cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); |
8171998f | 1589 | |
4376f7f6 | 1590 | if (error < 0) { |
9cfce273 | 1591 | git_vector_free_deep(&list); |
8171998f CMN |
1592 | return error; |
1593 | } | |
1594 | ||
25e0b157 | 1595 | git_vector_uniq(&list, git__free); |
aec87f71 | 1596 | |
25e0b157 RB |
1597 | remotes_list->strings = |
1598 | (char **)git_vector_detach(&remotes_list->count, NULL, &list); | |
8171998f | 1599 | |
4376f7f6 | 1600 | return 0; |
8171998f | 1601 | } |
a209a025 | 1602 | |
0e0cf787 | 1603 | int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks) |
b3aaa7a7 CMN |
1604 | { |
1605 | assert(remote && callbacks); | |
1606 | ||
c7231c45 | 1607 | GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
9267ff58 | 1608 | |
b3aaa7a7 | 1609 | memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks)); |
e03e71da | 1610 | |
41fb1ca0 | 1611 | if (remote->transport && remote->transport->set_callbacks) |
0e0cf787 | 1612 | return remote->transport->set_callbacks(remote->transport, |
98020d3a | 1613 | remote->callbacks.sideband_progress, |
41fb1ca0 | 1614 | NULL, |
9b940586 | 1615 | remote->callbacks.certificate_check, |
df705148 | 1616 | remote->callbacks.payload); |
9267ff58 BS |
1617 | |
1618 | return 0; | |
41fb1ca0 PK |
1619 | } |
1620 | ||
2efd7df6 CMN |
1621 | const git_remote_callbacks *git_remote_get_callbacks(git_remote *remote) |
1622 | { | |
1623 | assert(remote); | |
1624 | ||
1625 | return &remote->callbacks; | |
1626 | } | |
1627 | ||
1697cd6f PK |
1628 | int git_remote_set_transport( |
1629 | git_remote *remote, | |
1630 | git_transport_cb transport_cb, | |
1631 | void *payload) | |
41fb1ca0 | 1632 | { |
1697cd6f | 1633 | assert(remote); |
10711769 | 1634 | |
e03e71da | 1635 | if (remote->transport) { |
41fb1ca0 PK |
1636 | giterr_set(GITERR_NET, "A transport is already bound to this remote"); |
1637 | return -1; | |
e03e71da | 1638 | } |
41fb1ca0 | 1639 | |
1697cd6f PK |
1640 | remote->transport_cb = transport_cb; |
1641 | remote->transport_cb_payload = payload; | |
41fb1ca0 | 1642 | return 0; |
b3aaa7a7 | 1643 | } |
f70e466f | 1644 | |
67dad09b | 1645 | const git_transfer_progress* git_remote_stats(git_remote *remote) |
d57c47dc BS |
1646 | { |
1647 | assert(remote); | |
1648 | return &remote->stats; | |
1649 | } | |
1650 | ||
11f6ad5f | 1651 | git_remote_autotag_option_t git_remote_autotag(const git_remote *remote) |
f70e466f CMN |
1652 | { |
1653 | return remote->download_tags; | |
1654 | } | |
1655 | ||
f4a62c30 | 1656 | void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value) |
f70e466f CMN |
1657 | { |
1658 | remote->download_tags = value; | |
1659 | } | |
fcccf304 | 1660 | |
5f473947 L |
1661 | int git_remote_prune_refs(const git_remote *remote) |
1662 | { | |
1663 | return remote->prune_refs; | |
1664 | } | |
1665 | ||
fcccf304 | 1666 | static int rename_remote_config_section( |
1667 | git_repository *repo, | |
1668 | const char *old_name, | |
1669 | const char *new_name) | |
1670 | { | |
1671 | git_buf old_section_name = GIT_BUF_INIT, | |
1672 | new_section_name = GIT_BUF_INIT; | |
1673 | int error = -1; | |
1674 | ||
1675 | if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) | |
1676 | goto cleanup; | |
1677 | ||
40e48ea4 | 1678 | if (new_name && |
1679 | (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)) | |
1680 | goto cleanup; | |
fcccf304 | 1681 | |
1682 | error = git_config_rename_section( | |
1683 | repo, | |
1684 | git_buf_cstr(&old_section_name), | |
40e48ea4 | 1685 | new_name ? git_buf_cstr(&new_section_name) : NULL); |
fcccf304 | 1686 | |
1687 | cleanup: | |
1688 | git_buf_free(&old_section_name); | |
1689 | git_buf_free(&new_section_name); | |
1690 | ||
1691 | return error; | |
1692 | } | |
1693 | ||
96869a4e | 1694 | struct update_data { |
fcccf304 | 1695 | git_config *config; |
1696 | const char *old_remote_name; | |
1697 | const char *new_remote_name; | |
1698 | }; | |
1699 | ||
1700 | static int update_config_entries_cb( | |
1701 | const git_config_entry *entry, | |
1702 | void *payload) | |
1703 | { | |
1704 | struct update_data *data = (struct update_data *)payload; | |
1705 | ||
1706 | if (strcmp(entry->value, data->old_remote_name)) | |
1707 | return 0; | |
1708 | ||
25e0b157 RB |
1709 | return git_config_set_string( |
1710 | data->config, entry->name, data->new_remote_name); | |
fcccf304 | 1711 | } |
1712 | ||
1713 | static int update_branch_remote_config_entry( | |
1714 | git_repository *repo, | |
1715 | const char *old_name, | |
1716 | const char *new_name) | |
1717 | { | |
96869a4e RB |
1718 | int error; |
1719 | struct update_data data = { NULL }; | |
fcccf304 | 1720 | |
96869a4e RB |
1721 | if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) |
1722 | return error; | |
fcccf304 | 1723 | |
fcccf304 | 1724 | data.old_remote_name = old_name; |
1725 | data.new_remote_name = new_name; | |
1726 | ||
25e0b157 | 1727 | return git_config_foreach_match( |
96869a4e | 1728 | data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); |
fcccf304 | 1729 | } |
1730 | ||
fcccf304 | 1731 | static int rename_one_remote_reference( |
d1544564 | 1732 | git_reference *reference_in, |
fcccf304 | 1733 | const char *old_remote_name, |
1734 | const char *new_remote_name) | |
1735 | { | |
96869a4e | 1736 | int error; |
d1544564 CMN |
1737 | git_reference *ref = NULL, *dummy = NULL; |
1738 | git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT; | |
fcccf304 | 1739 | git_buf new_name = GIT_BUF_INIT; |
ccf6ce5c | 1740 | git_buf log_message = GIT_BUF_INIT; |
d1544564 CMN |
1741 | size_t pfx_len; |
1742 | const char *target; | |
fcccf304 | 1743 | |
d1544564 CMN |
1744 | if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0) |
1745 | return error; | |
1746 | ||
1747 | pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1; | |
1748 | git_buf_puts(&new_name, namespace.ptr); | |
1749 | if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0) | |
ccf6ce5c | 1750 | goto cleanup; |
fcccf304 | 1751 | |
ccf6ce5c BS |
1752 | if ((error = git_buf_printf(&log_message, |
1753 | "renamed remote %s to %s", | |
1754 | old_remote_name, new_remote_name)) < 0) | |
1755 | goto cleanup; | |
fcccf304 | 1756 | |
d1544564 | 1757 | if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1, |
659cf202 | 1758 | git_buf_cstr(&log_message))) < 0) |
d1544564 CMN |
1759 | goto cleanup; |
1760 | ||
1761 | if (git_reference_type(ref) != GIT_REF_SYMBOLIC) | |
1762 | goto cleanup; | |
1763 | ||
1764 | /* Handle refs like origin/HEAD -> origin/master */ | |
1765 | target = git_reference_symbolic_target(ref); | |
1766 | if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0) | |
1767 | goto cleanup; | |
1768 | ||
1769 | if (git__prefixcmp(target, old_namespace.ptr)) | |
1770 | goto cleanup; | |
1771 | ||
1772 | git_buf_clear(&new_name); | |
1773 | git_buf_puts(&new_name, namespace.ptr); | |
1774 | if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0) | |
1775 | goto cleanup; | |
1776 | ||
1777 | error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name), | |
659cf202 | 1778 | git_buf_cstr(&log_message)); |
d1544564 CMN |
1779 | |
1780 | git_reference_free(dummy); | |
ccf6ce5c BS |
1781 | |
1782 | cleanup: | |
d1544564 CMN |
1783 | git_reference_free(reference_in); |
1784 | git_reference_free(ref); | |
1785 | git_buf_free(&namespace); | |
1786 | git_buf_free(&old_namespace); | |
fcccf304 | 1787 | git_buf_free(&new_name); |
ccf6ce5c | 1788 | git_buf_free(&log_message); |
fcccf304 | 1789 | return error; |
1790 | } | |
1791 | ||
1792 | static int rename_remote_references( | |
1793 | git_repository *repo, | |
1794 | const char *old_name, | |
1795 | const char *new_name) | |
1796 | { | |
96869a4e | 1797 | int error; |
a52ab4b8 | 1798 | git_buf buf = GIT_BUF_INIT; |
56960b83 | 1799 | git_reference *ref; |
9bd89d96 | 1800 | git_reference_iterator *iter; |
fcccf304 | 1801 | |
a52ab4b8 | 1802 | if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0) |
96869a4e | 1803 | return error; |
fcccf304 | 1804 | |
a52ab4b8 CMN |
1805 | error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf)); |
1806 | git_buf_free(&buf); | |
9bd89d96 | 1807 | |
a52ab4b8 CMN |
1808 | if (error < 0) |
1809 | return error; | |
1810 | ||
1811 | while ((error = git_reference_next(&ref, iter)) == 0) { | |
96869a4e RB |
1812 | if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) |
1813 | break; | |
9bd89d96 CMN |
1814 | } |
1815 | ||
1816 | git_reference_iterator_free(iter); | |
fcccf304 | 1817 | |
96869a4e | 1818 | return (error == GIT_ITEROVER) ? 0 : error; |
fcccf304 | 1819 | } |
1820 | ||
72bca13e | 1821 | static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name) |
fcccf304 | 1822 | { |
1823 | git_config *config; | |
4330ab26 | 1824 | git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; |
1be680c4 | 1825 | const git_refspec *spec; |
4330ab26 | 1826 | size_t i; |
25e0b157 | 1827 | int error = 0; |
fcccf304 | 1828 | |
dab89f9b | 1829 | if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) |
25e0b157 RB |
1830 | return error; |
1831 | ||
72bca13e CMN |
1832 | if ((error = git_vector_init(problems, 1, NULL)) < 0) |
1833 | return error; | |
1834 | ||
25e0b157 RB |
1835 | if ((error = git_buf_printf( |
1836 | &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0) | |
1837 | return error; | |
dab89f9b | 1838 | |
1be680c4 | 1839 | git_vector_foreach(&remote->refspecs, i, spec) { |
4330ab26 CMN |
1840 | if (spec->push) |
1841 | continue; | |
fcccf304 | 1842 | |
dab89f9b | 1843 | /* Does the dst part of the refspec follow the expected format? */ |
5a49ff9f | 1844 | if (strcmp(git_buf_cstr(&base), spec->string)) { |
72bca13e | 1845 | char *dup; |
fcccf304 | 1846 | |
72bca13e CMN |
1847 | dup = git__strdup(spec->string); |
1848 | GITERR_CHECK_ALLOC(dup); | |
1849 | ||
1850 | if ((error = git_vector_insert(problems, dup)) < 0) | |
25e0b157 | 1851 | break; |
c7b3e1b3 | 1852 | |
4330ab26 CMN |
1853 | continue; |
1854 | } | |
fcccf304 | 1855 | |
4330ab26 | 1856 | /* If we do want to move it to the new section */ |
fcccf304 | 1857 | |
dab89f9b RB |
1858 | git_buf_clear(&val); |
1859 | git_buf_clear(&var); | |
fcccf304 | 1860 | |
dab89f9b RB |
1861 | if (git_buf_printf( |
1862 | &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 || | |
1863 | git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) | |
1864 | { | |
1865 | error = -1; | |
25e0b157 | 1866 | break; |
dab89f9b | 1867 | } |
fcccf304 | 1868 | |
dab89f9b RB |
1869 | if ((error = git_config_set_string( |
1870 | config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) | |
25e0b157 | 1871 | break; |
4330ab26 | 1872 | } |
fcccf304 | 1873 | |
4330ab26 CMN |
1874 | git_buf_free(&base); |
1875 | git_buf_free(&var); | |
1876 | git_buf_free(&val); | |
72bca13e CMN |
1877 | |
1878 | if (error < 0) { | |
1879 | char *str; | |
1880 | git_vector_foreach(problems, i, str) | |
1881 | git__free(str); | |
1882 | ||
1883 | git_vector_free(problems); | |
1884 | } | |
1885 | ||
fcccf304 | 1886 | return error; |
1887 | } | |
1888 | ||
46c8f7f8 | 1889 | int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name) |
fcccf304 | 1890 | { |
1891 | int error; | |
46c8f7f8 | 1892 | git_vector problem_refspecs = GIT_VECTOR_INIT; |
64bcf567 | 1893 | git_remote *remote = NULL; |
fcccf304 | 1894 | |
46c8f7f8 | 1895 | assert(out && repo && name && new_name); |
fcccf304 | 1896 | |
209425ce | 1897 | if ((error = git_remote_lookup(&remote, repo, name)) < 0) |
cce27d82 | 1898 | return error; |
79000951 | 1899 | |
fcccf304 | 1900 | if ((error = ensure_remote_name_is_valid(new_name)) < 0) |
46c8f7f8 | 1901 | goto cleanup; |
fcccf304 | 1902 | |
46c8f7f8 CMN |
1903 | if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0) |
1904 | goto cleanup; | |
fcccf304 | 1905 | |
46c8f7f8 CMN |
1906 | if ((error = rename_remote_config_section(repo, name, new_name)) < 0) |
1907 | goto cleanup; | |
fcccf304 | 1908 | |
46c8f7f8 CMN |
1909 | if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0) |
1910 | goto cleanup; | |
fcccf304 | 1911 | |
46c8f7f8 CMN |
1912 | if ((error = rename_remote_references(repo, name, new_name)) < 0) |
1913 | goto cleanup; | |
fcccf304 | 1914 | |
72bca13e | 1915 | if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0) |
46c8f7f8 | 1916 | goto cleanup; |
fcccf304 | 1917 | |
72bca13e CMN |
1918 | out->count = problem_refspecs.length; |
1919 | out->strings = (char **) problem_refspecs.contents; | |
1920 | ||
46c8f7f8 CMN |
1921 | cleanup: |
1922 | if (error < 0) | |
1923 | git_vector_free(&problem_refspecs); | |
fcccf304 | 1924 | |
46c8f7f8 CMN |
1925 | git_remote_free(remote); |
1926 | return error; | |
fcccf304 | 1927 | } |
b0f6e45d ET |
1928 | |
1929 | int git_remote_update_fetchhead(git_remote *remote) | |
1930 | { | |
8f2a3d62 | 1931 | return (remote->update_fetchhead != 0); |
b0f6e45d ET |
1932 | } |
1933 | ||
1934 | void git_remote_set_update_fetchhead(git_remote *remote, int value) | |
1935 | { | |
8f2a3d62 | 1936 | remote->update_fetchhead = (value != 0); |
b0f6e45d | 1937 | } |
2bca5b67 | 1938 | |
1939 | int git_remote_is_valid_name( | |
1940 | const char *remote_name) | |
1941 | { | |
1942 | git_buf buf = GIT_BUF_INIT; | |
1943 | git_refspec refspec; | |
1944 | int error = -1; | |
1945 | ||
1946 | if (!remote_name || *remote_name == '\0') | |
1947 | return 0; | |
1948 | ||
1949 | git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name); | |
1950 | error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); | |
1951 | ||
1952 | git_buf_free(&buf); | |
1953 | git_refspec__free(&refspec); | |
1954 | ||
1955 | giterr_clear(); | |
1956 | return error == 0; | |
1957 | } | |
4330ab26 CMN |
1958 | |
1959 | git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname) | |
1960 | { | |
1961 | git_refspec *spec; | |
1962 | size_t i; | |
1963 | ||
af613ecd | 1964 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1965 | if (spec->push) |
1966 | continue; | |
1967 | ||
1968 | if (git_refspec_src_matches(spec, refname)) | |
1969 | return spec; | |
1970 | } | |
1971 | ||
1972 | return NULL; | |
1973 | } | |
1974 | ||
1975 | git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname) | |
1976 | { | |
1977 | git_refspec *spec; | |
1978 | size_t i; | |
1979 | ||
af613ecd | 1980 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1981 | if (spec->push) |
1982 | continue; | |
1983 | ||
1984 | if (git_refspec_dst_matches(spec, refname)) | |
1985 | return spec; | |
1986 | } | |
1987 | ||
1988 | return NULL; | |
1989 | } | |
1990 | ||
1991 | void git_remote_clear_refspecs(git_remote *remote) | |
1992 | { | |
1993 | git_refspec *spec; | |
4330ab26 CMN |
1994 | size_t i; |
1995 | ||
1996 | git_vector_foreach(&remote->refspecs, i, spec) { | |
1997 | git_refspec__free(spec); | |
1be680c4 | 1998 | git__free(spec); |
4330ab26 CMN |
1999 | } |
2000 | git_vector_clear(&remote->refspecs); | |
4330ab26 CMN |
2001 | } |
2002 | ||
bc6374ea | 2003 | int git_remote_add_fetch(git_remote *remote, const char *refspec) |
4330ab26 | 2004 | { |
c300d84a | 2005 | return add_refspec(remote, refspec, true); |
4330ab26 CMN |
2006 | } |
2007 | ||
bc6374ea | 2008 | int git_remote_add_push(git_remote *remote, const char *refspec) |
4330ab26 | 2009 | { |
c300d84a | 2010 | return add_refspec(remote, refspec, false); |
266af6d8 CMN |
2011 | } |
2012 | ||
2013 | static int set_refspecs(git_remote *remote, git_strarray *array, int push) | |
2014 | { | |
2015 | git_vector *vec = &remote->refspecs; | |
2016 | git_refspec *spec; | |
2017 | size_t i; | |
2018 | ||
2019 | /* Start by removing any refspecs of the same type */ | |
2020 | for (i = 0; i < vec->length; i++) { | |
2021 | spec = git_vector_get(vec, i); | |
2022 | if (spec->push != push) | |
2023 | continue; | |
2024 | ||
2025 | git_refspec__free(spec); | |
2026 | git__free(spec); | |
2027 | git_vector_remove(vec, i); | |
2028 | i--; | |
2029 | } | |
2030 | ||
2031 | /* And now we add the new ones */ | |
2032 | ||
2033 | for (i = 0; i < array->count; i++) { | |
2034 | if (add_refspec(remote, array->strings[i], !push) < 0) | |
2035 | return -1; | |
2036 | } | |
2037 | ||
c300d84a | 2038 | return 0; |
266af6d8 CMN |
2039 | } |
2040 | ||
2041 | int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array) | |
2042 | { | |
2043 | return set_refspecs(remote, array, false); | |
2044 | } | |
2045 | ||
2046 | int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array) | |
2047 | { | |
2048 | return set_refspecs(remote, array, true); | |
4330ab26 | 2049 | } |
bc6374ea | 2050 | |
11f6ad5f | 2051 | static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push) |
bc6374ea CMN |
2052 | { |
2053 | size_t i; | |
2054 | git_vector refspecs; | |
2055 | git_refspec *spec; | |
2056 | char *dup; | |
2057 | ||
2058 | if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0) | |
2059 | return -1; | |
2060 | ||
2061 | git_vector_foreach(&remote->refspecs, i, spec) { | |
2062 | if (spec->push != push) | |
2063 | continue; | |
2064 | ||
1be680c4 | 2065 | if ((dup = git__strdup(spec->string)) == NULL) |
bc6374ea | 2066 | goto on_error; |
bc6374ea CMN |
2067 | |
2068 | if (git_vector_insert(&refspecs, dup) < 0) { | |
2069 | git__free(dup); | |
2070 | goto on_error; | |
2071 | } | |
2072 | } | |
2073 | ||
2074 | array->strings = (char **)refspecs.contents; | |
2075 | array->count = refspecs.length; | |
2076 | ||
2077 | return 0; | |
2078 | ||
2079 | on_error: | |
9cfce273 | 2080 | git_vector_free_deep(&refspecs); |
bc6374ea CMN |
2081 | |
2082 | return -1; | |
2083 | } | |
2084 | ||
11f6ad5f | 2085 | int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote) |
bc6374ea CMN |
2086 | { |
2087 | return copy_refspecs(array, remote, false); | |
2088 | } | |
2089 | ||
11f6ad5f | 2090 | int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote) |
bc6374ea CMN |
2091 | { |
2092 | return copy_refspecs(array, remote, true); | |
2093 | } | |
1ffd0806 | 2094 | |
11f6ad5f | 2095 | size_t git_remote_refspec_count(const git_remote *remote) |
1ffd0806 CMN |
2096 | { |
2097 | return remote->refspecs.length; | |
2098 | } | |
2099 | ||
11f6ad5f | 2100 | const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) |
1ffd0806 CMN |
2101 | { |
2102 | return git_vector_get(&remote->refspecs, n); | |
2103 | } | |
b9f81997 | 2104 | |
bc91347b | 2105 | int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version) |
b9f81997 | 2106 | { |
bc91347b RB |
2107 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
2108 | opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT); | |
2109 | return 0; | |
b9f81997 | 2110 | } |
40e48ea4 | 2111 | |
5cdac19c CMN |
2112 | /* asserts a branch.<foo>.remote format */ |
2113 | static const char *name_offset(size_t *len_out, const char *name) | |
40e48ea4 | 2114 | { |
5cdac19c CMN |
2115 | size_t prefix_len; |
2116 | const char *dot; | |
40e48ea4 | 2117 | |
5cdac19c CMN |
2118 | prefix_len = strlen("remote."); |
2119 | dot = strchr(name + prefix_len, '.'); | |
40e48ea4 | 2120 | |
5cdac19c | 2121 | assert(dot); |
40e48ea4 | 2122 | |
5cdac19c CMN |
2123 | *len_out = dot - name - prefix_len; |
2124 | return name + prefix_len; | |
40e48ea4 | 2125 | } |
2126 | ||
2127 | static int remove_branch_config_related_entries( | |
2128 | git_repository *repo, | |
2129 | const char *remote_name) | |
2130 | { | |
2131 | int error; | |
2132 | git_config *config; | |
5cdac19c CMN |
2133 | git_config_entry *entry; |
2134 | git_config_iterator *iter; | |
2135 | git_buf buf = GIT_BUF_INIT; | |
40e48ea4 | 2136 | |
2137 | if ((error = git_repository_config__weakptr(&config, repo)) < 0) | |
2138 | return error; | |
2139 | ||
5cdac19c | 2140 | if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0) |
40e48ea4 | 2141 | return error; |
2142 | ||
5cdac19c CMN |
2143 | /* find any branches with us as upstream and remove that config */ |
2144 | while ((error = git_config_next(&entry, iter)) == 0) { | |
2145 | const char *branch; | |
2146 | size_t branch_len; | |
40e48ea4 | 2147 | |
5cdac19c CMN |
2148 | if (strcmp(remote_name, entry->value)) |
2149 | continue; | |
40e48ea4 | 2150 | |
5cdac19c | 2151 | branch = name_offset(&branch_len, entry->name); |
40e48ea4 | 2152 | |
5cdac19c CMN |
2153 | git_buf_clear(&buf); |
2154 | if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0) | |
2155 | break; | |
2156 | ||
2157 | if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) | |
2158 | break; | |
2159 | ||
2160 | git_buf_clear(&buf); | |
2161 | if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0) | |
2162 | break; | |
2163 | ||
2164 | if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) | |
2165 | break; | |
40e48ea4 | 2166 | } |
2167 | ||
5cdac19c CMN |
2168 | if (error == GIT_ITEROVER) |
2169 | error = 0; | |
2170 | ||
2171 | git_buf_free(&buf); | |
2172 | git_config_iterator_free(iter); | |
40e48ea4 | 2173 | return error; |
2174 | } | |
2175 | ||
8a9419aa | 2176 | static int remove_refs(git_repository *repo, const git_refspec *spec) |
ec8a949a | 2177 | { |
8a9419aa CMN |
2178 | git_reference_iterator *iter = NULL; |
2179 | git_vector refs; | |
ec8a949a | 2180 | const char *name; |
8a9419aa | 2181 | char *dup; |
ec8a949a | 2182 | int error; |
8a9419aa | 2183 | size_t i; |
ec8a949a | 2184 | |
8a9419aa | 2185 | if ((error = git_vector_init(&refs, 8, NULL)) < 0) |
ec8a949a CMN |
2186 | return error; |
2187 | ||
8a9419aa CMN |
2188 | if ((error = git_reference_iterator_new(&iter, repo)) < 0) |
2189 | goto cleanup; | |
2190 | ||
ec8a949a | 2191 | while ((error = git_reference_next_name(&name, iter)) == 0) { |
8a9419aa CMN |
2192 | if (!git_refspec_dst_matches(spec, name)) |
2193 | continue; | |
2194 | ||
2195 | dup = git__strdup(name); | |
2196 | if (!dup) { | |
2197 | error = -1; | |
2198 | goto cleanup; | |
2199 | } | |
ec8a949a | 2200 | |
8a9419aa CMN |
2201 | if ((error = git_vector_insert(&refs, dup)) < 0) |
2202 | goto cleanup; | |
2203 | } | |
ec8a949a CMN |
2204 | if (error == GIT_ITEROVER) |
2205 | error = 0; | |
8a9419aa CMN |
2206 | if (error < 0) |
2207 | goto cleanup; | |
2208 | ||
2209 | git_vector_foreach(&refs, i, name) { | |
2210 | if ((error = git_reference_remove(repo, name)) < 0) | |
2211 | break; | |
2212 | } | |
ec8a949a | 2213 | |
8a9419aa CMN |
2214 | cleanup: |
2215 | git_reference_iterator_free(iter); | |
2216 | git_vector_foreach(&refs, i, dup) { | |
2217 | git__free(dup); | |
2218 | } | |
2219 | git_vector_free(&refs); | |
ec8a949a CMN |
2220 | return error; |
2221 | } | |
2222 | ||
2223 | static int remove_remote_tracking(git_repository *repo, const char *remote_name) | |
2224 | { | |
2225 | git_remote *remote; | |
2226 | int error; | |
2227 | size_t i, count; | |
2228 | ||
2229 | /* we want to use what's on the config, regardless of changes to the instance in memory */ | |
209425ce | 2230 | if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0) |
ec8a949a CMN |
2231 | return error; |
2232 | ||
2233 | count = git_remote_refspec_count(remote); | |
2234 | for (i = 0; i < count; i++) { | |
2235 | const git_refspec *refspec = git_remote_get_refspec(remote, i); | |
2236 | ||
2237 | /* shouldn't ever actually happen */ | |
2238 | if (refspec == NULL) | |
2239 | continue; | |
2240 | ||
8a9419aa | 2241 | if ((error = remove_refs(repo, refspec)) < 0) |
ec8a949a CMN |
2242 | break; |
2243 | } | |
2244 | ||
2245 | git_remote_free(remote); | |
2246 | return error; | |
2247 | } | |
2248 | ||
262eec23 | 2249 | int git_remote_delete(git_repository *repo, const char *name) |
40e48ea4 | 2250 | { |
2251 | int error; | |
40e48ea4 | 2252 | |
262eec23 | 2253 | assert(repo && name); |
ec8a949a | 2254 | |
262eec23 CMN |
2255 | if ((error = remove_branch_config_related_entries(repo, name)) < 0 || |
2256 | (error = remove_remote_tracking(repo, name)) < 0 || | |
2257 | (error = rename_remote_config_section(repo, name, NULL)) < 0) | |
ec8a949a CMN |
2258 | return error; |
2259 | ||
40e48ea4 | 2260 | return 0; |
2261 | } | |
d22db24f CMN |
2262 | |
2263 | int git_remote_default_branch(git_buf *out, git_remote *remote) | |
2264 | { | |
2265 | const git_remote_head **heads; | |
2266 | const git_remote_head *guess = NULL; | |
2267 | const git_oid *head_id; | |
2268 | size_t heads_len, i; | |
2269 | int error; | |
2270 | ||
dc8adda4 JG |
2271 | assert(out); |
2272 | ||
d22db24f CMN |
2273 | if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0) |
2274 | return error; | |
2275 | ||
2276 | if (heads_len == 0) | |
2277 | return GIT_ENOTFOUND; | |
2278 | ||
0cdaa376 CMN |
2279 | if (strcmp(heads[0]->name, GIT_HEAD_FILE)) |
2280 | return GIT_ENOTFOUND; | |
2281 | ||
d22db24f CMN |
2282 | git_buf_sanitize(out); |
2283 | /* the first one must be HEAD so if that has the symref info, we're done */ | |
2284 | if (heads[0]->symref_target) | |
2285 | return git_buf_puts(out, heads[0]->symref_target); | |
2286 | ||
2287 | /* | |
2288 | * If there's no symref information, we have to look over them | |
2289 | * and guess. We return the first match unless the master | |
2290 | * branch is a candidate. Then we return the master branch. | |
2291 | */ | |
2292 | head_id = &heads[0]->oid; | |
2293 | ||
2294 | for (i = 1; i < heads_len; i++) { | |
2295 | if (git_oid_cmp(head_id, &heads[i]->oid)) | |
2296 | continue; | |
2297 | ||
38952604 CMN |
2298 | if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR)) |
2299 | continue; | |
2300 | ||
d22db24f CMN |
2301 | if (!guess) { |
2302 | guess = heads[i]; | |
2303 | continue; | |
2304 | } | |
2305 | ||
2306 | if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) { | |
2307 | guess = heads[i]; | |
2308 | break; | |
2309 | } | |
2310 | } | |
2311 | ||
2312 | if (!guess) | |
2313 | return GIT_ENOTFOUND; | |
2314 | ||
2315 | return git_buf_puts(out, guess->name); | |
2316 | } | |
3149547b | 2317 | |
fe794b2e | 2318 | int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) |
3149547b | 2319 | { |
3149547b | 2320 | size_t i; |
fe794b2e CMN |
2321 | int error; |
2322 | git_push *push; | |
64e3e6d4 | 2323 | git_refspec *spec; |
fe794b2e | 2324 | git_remote_callbacks *cbs; |
3149547b | 2325 | |
fe794b2e | 2326 | assert(remote); |
3149547b | 2327 | |
fe794b2e CMN |
2328 | if (!git_remote_connected(remote) && |
2329 | (error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0) | |
2330 | goto cleanup; | |
2331 | ||
2332 | if (remote->push) { | |
2333 | git_push_free(remote->push); | |
2334 | remote->push = NULL; | |
2335 | } | |
2336 | ||
2337 | if ((error = git_push_new(&remote->push, remote)) < 0) | |
3149547b CMN |
2338 | return error; |
2339 | ||
fe794b2e | 2340 | push = remote->push; |
3149547b CMN |
2341 | |
2342 | if (opts && (error = git_push_set_options(push, opts)) < 0) | |
2343 | goto cleanup; | |
2344 | ||
64e3e6d4 CMN |
2345 | if (refspecs && refspecs->count > 0) { |
2346 | for (i = 0; i < refspecs->count; i++) { | |
2347 | if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0) | |
2348 | goto cleanup; | |
2349 | } | |
2350 | } else { | |
2351 | git_vector_foreach(&remote->refspecs, i, spec) { | |
2352 | if (!spec->push) | |
2353 | continue; | |
2354 | if ((error = git_push_add_refspec(push, spec->string)) < 0) | |
2355 | goto cleanup; | |
2356 | } | |
3149547b CMN |
2357 | } |
2358 | ||
2359 | cbs = &remote->callbacks; | |
2360 | if ((error = git_push_set_callbacks(push, | |
2361 | cbs->pack_progress, cbs->payload, | |
2362 | cbs->push_transfer_progress, cbs->payload)) < 0) | |
2363 | goto cleanup; | |
2364 | ||
2365 | if ((error = git_push_finish(push)) < 0) | |
2366 | goto cleanup; | |
2367 | ||
52ee0e8e CMN |
2368 | if (cbs->push_update_reference && |
2369 | (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0) | |
3149547b CMN |
2370 | goto cleanup; |
2371 | ||
3149547b | 2372 | cleanup: |
fe794b2e CMN |
2373 | return error; |
2374 | } | |
2375 | ||
412a3808 | 2376 | int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) |
fe794b2e CMN |
2377 | { |
2378 | int error; | |
2379 | ||
2380 | assert(remote && refspecs); | |
2381 | ||
2382 | if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0) | |
2383 | return error; | |
2384 | ||
2385 | if ((error = git_remote_upload(remote, refspecs, opts)) < 0) | |
2386 | return error; | |
2387 | ||
412a3808 | 2388 | error = git_remote_update_tips(remote, NULL); |
fe794b2e | 2389 | |
3149547b | 2390 | git_remote_disconnect(remote); |
3149547b CMN |
2391 | return error; |
2392 | } |