]>
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" | |
9c82357b | 21 | |
af613ecd CMN |
22 | static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); |
23 | ||
4330ab26 | 24 | static int add_refspec(git_remote *remote, const char *string, bool is_fetch) |
9c82357b | 25 | { |
4330ab26 | 26 | git_refspec *spec; |
9c82357b | 27 | |
4330ab26 CMN |
28 | spec = git__calloc(1, sizeof(git_refspec)); |
29 | GITERR_CHECK_ALLOC(spec); | |
30 | ||
1be680c4 CMN |
31 | if (git_refspec__parse(spec, string, is_fetch) < 0) { |
32 | git__free(spec); | |
33 | return -1; | |
34 | } | |
4330ab26 CMN |
35 | |
36 | spec->push = !is_fetch; | |
1be680c4 CMN |
37 | if (git_vector_insert(&remote->refspecs, spec) < 0) { |
38 | git_refspec__free(spec); | |
39 | git__free(spec); | |
40 | return -1; | |
41 | } | |
9c82357b | 42 | |
4330ab26 | 43 | return 0; |
9c82357b CMN |
44 | } |
45 | ||
24f2f94e CMN |
46 | static int download_tags_value(git_remote *remote, git_config *cfg) |
47 | { | |
9f77b3f6 | 48 | const git_config_entry *ce; |
24f2f94e CMN |
49 | git_buf buf = GIT_BUF_INIT; |
50 | int error; | |
51 | ||
df50512a | 52 | /* The 0 value is the default (auto), let's see if we need to change it */ |
24f2f94e CMN |
53 | if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) |
54 | return -1; | |
55 | ||
9f77b3f6 | 56 | error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); |
24f2f94e | 57 | git_buf_free(&buf); |
24f2f94e | 58 | |
9f77b3f6 RB |
59 | if (!error && ce && ce->value) { |
60 | if (!strcmp(ce->value, "--no-tags")) | |
61 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; | |
62 | else if (!strcmp(ce->value, "--tags")) | |
63 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; | |
bf6bebe2 | 64 | } |
24f2f94e CMN |
65 | |
66 | return error; | |
67 | } | |
68 | ||
032ba9e4 | 69 | static int ensure_remote_name_is_valid(const char *name) |
70 | { | |
2bca5b67 | 71 | int error = 0; |
032ba9e4 | 72 | |
2bca5b67 | 73 | if (!git_remote_is_valid_name(name)) { |
032ba9e4 | 74 | giterr_set( |
75 | GITERR_CONFIG, | |
76 | "'%s' is not a valid remote name.", name); | |
77 | error = GIT_EINVALIDSPEC; | |
78 | } | |
79 | ||
80 | return error; | |
81 | } | |
82 | ||
80fc7d6b | 83 | static int get_check_cert(int *out, git_repository *repo) |
af6dab7e PK |
84 | { |
85 | git_config *cfg; | |
86 | const char *val; | |
80fc7d6b ET |
87 | int error = 0; |
88 | ||
89 | assert(out && repo); | |
af6dab7e | 90 | |
80fc7d6b ET |
91 | /* By default, we *DO* want to verify the certificate. */ |
92 | *out = 1; | |
af6dab7e PK |
93 | |
94 | /* Go through the possible sources for SSL verification settings, from | |
95 | * most specific to least specific. */ | |
96 | ||
97 | /* GIT_SSL_NO_VERIFY environment variable */ | |
8f2a3d62 | 98 | if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL) |
80fc7d6b | 99 | return git_config_parse_bool(out, val); |
af6dab7e PK |
100 | |
101 | /* http.sslVerify config setting */ | |
80fc7d6b ET |
102 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) |
103 | return error; | |
af6dab7e | 104 | |
9f77b3f6 | 105 | *out = git_config__get_bool_force(cfg, "http.sslverify", 1); |
80fc7d6b | 106 | return 0; |
af6dab7e PK |
107 | } |
108 | ||
874dcb25 | 109 | static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
778e1c73 CMN |
110 | { |
111 | git_remote *remote; | |
44f36f6e BS |
112 | git_buf fetchbuf = GIT_BUF_INIT; |
113 | int error = -1; | |
778e1c73 | 114 | |
4bef3565 | 115 | /* name is optional */ |
874dcb25 | 116 | assert(out && repo && url); |
617bfdf4 | 117 | |
59bccf33 | 118 | remote = git__calloc(1, sizeof(git_remote)); |
4376f7f6 | 119 | GITERR_CHECK_ALLOC(remote); |
778e1c73 | 120 | |
778e1c73 | 121 | remote->repo = repo; |
b0f6e45d | 122 | remote->update_fetchhead = 1; |
617bfdf4 | 123 | |
80fc7d6b ET |
124 | if (get_check_cert(&remote->check_cert, repo) < 0) |
125 | goto on_error; | |
126 | ||
4376f7f6 | 127 | if (git_vector_init(&remote->refs, 32, NULL) < 0) |
44f36f6e | 128 | goto on_error; |
d88d4311 | 129 | |
778e1c73 | 130 | remote->url = git__strdup(url); |
4376f7f6 | 131 | GITERR_CHECK_ALLOC(remote->url); |
778e1c73 | 132 | |
617bfdf4 CMN |
133 | if (name != NULL) { |
134 | remote->name = git__strdup(name); | |
4376f7f6 | 135 | GITERR_CHECK_ALLOC(remote->name); |
617bfdf4 CMN |
136 | } |
137 | ||
baaa8a44 | 138 | if (fetch != NULL) { |
4330ab26 | 139 | if (add_refspec(remote, fetch, true) < 0) |
baaa8a44 CMN |
140 | goto on_error; |
141 | } | |
142 | ||
215af2cc | 143 | if (!name) |
144 | /* A remote without a name doesn't download tags */ | |
c648d4a8 | 145 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; |
c648d4a8 | 146 | |
778e1c73 | 147 | *out = remote; |
44f36f6e | 148 | git_buf_free(&fetchbuf); |
4376f7f6 | 149 | return 0; |
baaa8a44 CMN |
150 | |
151 | on_error: | |
152 | git_remote_free(remote); | |
44f36f6e BS |
153 | git_buf_free(&fetchbuf); |
154 | return error; | |
778e1c73 CMN |
155 | } |
156 | ||
592f466c BS |
157 | static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) |
158 | { | |
159 | int error; | |
160 | git_remote *remote; | |
161 | ||
162 | error = git_remote_load(&remote, repo, name); | |
163 | ||
164 | if (error == GIT_ENOTFOUND) | |
165 | return 0; | |
166 | ||
167 | if (error < 0) | |
168 | return error; | |
169 | ||
170 | git_remote_free(remote); | |
171 | ||
172 | giterr_set( | |
173 | GITERR_CONFIG, | |
174 | "Remote '%s' already exists.", name); | |
175 | ||
176 | return GIT_EEXISTS; | |
177 | } | |
178 | ||
f19304d2 | 179 | |
874dcb25 BS |
180 | int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url) |
181 | { | |
182 | git_buf buf = GIT_BUF_INIT; | |
c5193e3c | 183 | git_remote *remote = NULL; |
874dcb25 BS |
184 | int error; |
185 | ||
186 | if ((error = ensure_remote_name_is_valid(name)) < 0) | |
187 | return error; | |
188 | ||
f19304d2 | 189 | if ((error = ensure_remote_doesnot_exist(repo, name)) < 0) |
190 | return error; | |
191 | ||
874dcb25 BS |
192 | if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) |
193 | return -1; | |
194 | ||
c5193e3c | 195 | if (create_internal(&remote, repo, name, url, git_buf_cstr(&buf)) < 0) |
874dcb25 BS |
196 | goto on_error; |
197 | ||
198 | git_buf_free(&buf); | |
199 | ||
c5193e3c | 200 | if (git_remote_save(remote) < 0) |
874dcb25 BS |
201 | goto on_error; |
202 | ||
c5193e3c | 203 | *out = remote; |
204 | ||
874dcb25 BS |
205 | return 0; |
206 | ||
207 | on_error: | |
208 | git_buf_free(&buf); | |
c5193e3c | 209 | git_remote_free(remote); |
874dcb25 BS |
210 | return -1; |
211 | } | |
212 | ||
40b99d05 VG |
213 | int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
214 | { | |
40b99d05 VG |
215 | git_remote *remote = NULL; |
216 | int error; | |
217 | ||
218 | if ((error = ensure_remote_name_is_valid(name)) < 0) | |
219 | return error; | |
220 | ||
221 | if ((error = ensure_remote_doesnot_exist(repo, name)) < 0) | |
222 | return error; | |
874dcb25 | 223 | |
0fe522d1 | 224 | if (create_internal(&remote, repo, name, url, fetch) < 0) |
874dcb25 BS |
225 | goto on_error; |
226 | ||
c5193e3c | 227 | if (git_remote_save(remote) < 0) |
874dcb25 BS |
228 | goto on_error; |
229 | ||
c5193e3c | 230 | *out = remote; |
231 | ||
874dcb25 BS |
232 | return 0; |
233 | ||
234 | on_error: | |
c5193e3c | 235 | git_remote_free(remote); |
874dcb25 BS |
236 | return -1; |
237 | } | |
238 | ||
0642c143 | 239 | int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url) |
874dcb25 BS |
240 | { |
241 | int error; | |
242 | git_remote *remote; | |
243 | ||
79000951 | 244 | if ((error = create_internal(&remote, repo, NULL, url, fetch)) < 0) |
874dcb25 BS |
245 | return error; |
246 | ||
874dcb25 BS |
247 | *out = remote; |
248 | return 0; | |
249 | } | |
250 | ||
40ef47dd AS |
251 | int git_remote_dup(git_remote **dest, const git_remote *source) |
252 | { | |
253 | int error; | |
254 | git_remote *remote = git__calloc(1, sizeof(git_remote)); | |
255 | GITERR_CHECK_ALLOC(remote); | |
256 | ||
40ef47dd AS |
257 | if (source->name != NULL) { |
258 | remote->name = git__strdup(source->name); | |
259 | GITERR_CHECK_ALLOC(remote->name); | |
260 | } | |
261 | ||
262 | if (source->url != NULL) { | |
263 | remote->url = git__strdup(source->url); | |
264 | GITERR_CHECK_ALLOC(remote->url); | |
265 | } | |
266 | ||
267 | if (source->pushurl != NULL) { | |
268 | remote->pushurl = git__strdup(source->pushurl); | |
269 | GITERR_CHECK_ALLOC(remote->pushurl); | |
270 | } | |
271 | ||
272 | remote->repo = source->repo; | |
40ef47dd AS |
273 | remote->download_tags = source->download_tags; |
274 | remote->check_cert = source->check_cert; | |
275 | remote->update_fetchhead = source->update_fetchhead; | |
276 | ||
277 | if ((error = git_vector_dup(&remote->refs, &source->refs, NULL)) < 0 || | |
278 | (error = git_vector_dup(&remote->refspecs, &source->refspecs, NULL)) < 0 || | |
279 | (error = git_vector_dup(&remote->active_refspecs, &source->active_refspecs, NULL))) { | |
280 | git__free(remote); | |
281 | return error; | |
282 | } | |
283 | ||
284 | *dest = remote; | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
4330ab26 CMN |
289 | struct refspec_cb_data { |
290 | git_remote *remote; | |
291 | int fetch; | |
292 | }; | |
293 | ||
294 | static int refspec_cb(const git_config_entry *entry, void *payload) | |
295 | { | |
96869a4e | 296 | struct refspec_cb_data *data = (struct refspec_cb_data *)payload; |
25e0b157 | 297 | return add_refspec(data->remote, entry->value, data->fetch); |
4330ab26 CMN |
298 | } |
299 | ||
bf6bebe2 | 300 | static int get_optional_config( |
c9ffa84b | 301 | bool *found, git_config *config, git_buf *buf, |
302 | git_config_foreach_cb cb, void *payload) | |
bf6bebe2 RB |
303 | { |
304 | int error = 0; | |
305 | const char *key = git_buf_cstr(buf); | |
306 | ||
307 | if (git_buf_oom(buf)) | |
308 | return -1; | |
309 | ||
310 | if (cb != NULL) | |
4efa3290 | 311 | error = git_config_get_multivar_foreach(config, key, NULL, cb, payload); |
bf6bebe2 RB |
312 | else |
313 | error = git_config_get_string(payload, config, key); | |
314 | ||
c9ffa84b | 315 | if (found) |
316 | *found = !error; | |
317 | ||
bf6bebe2 RB |
318 | if (error == GIT_ENOTFOUND) { |
319 | giterr_clear(); | |
320 | error = 0; | |
321 | } | |
322 | ||
bf6bebe2 RB |
323 | return error; |
324 | } | |
325 | ||
9462c471 | 326 | int git_remote_load(git_remote **out, git_repository *repo, const char *name) |
9c82357b CMN |
327 | { |
328 | git_remote *remote; | |
f0f3a18a | 329 | git_buf buf = GIT_BUF_INIT; |
9c82357b | 330 | const char *val; |
4376f7f6 | 331 | int error = 0; |
9462c471 | 332 | git_config *config; |
96869a4e | 333 | struct refspec_cb_data data = { NULL }; |
c9ffa84b | 334 | bool optional_setting_found = false, found; |
4330ab26 | 335 | |
9462c471 VM |
336 | assert(out && repo && name); |
337 | ||
032ba9e4 | 338 | if ((error = ensure_remote_name_is_valid(name)) < 0) |
339 | return error; | |
340 | ||
4376f7f6 CMN |
341 | if (git_repository_config__weakptr(&config, repo) < 0) |
342 | return -1; | |
4bef3565 | 343 | |
9c82357b | 344 | remote = git__malloc(sizeof(git_remote)); |
4376f7f6 | 345 | GITERR_CHECK_ALLOC(remote); |
9c82357b CMN |
346 | |
347 | memset(remote, 0x0, sizeof(git_remote)); | |
b0f6e45d | 348 | remote->update_fetchhead = 1; |
9c82357b | 349 | remote->name = git__strdup(name); |
4376f7f6 | 350 | GITERR_CHECK_ALLOC(remote->name); |
9c82357b | 351 | |
80fc7d6b ET |
352 | if ((error = get_check_cert(&remote->check_cert, repo)) < 0) |
353 | goto cleanup; | |
354 | ||
25e0b157 RB |
355 | if (git_vector_init(&remote->refs, 32, NULL) < 0 || |
356 | git_vector_init(&remote->refspecs, 2, NULL) < 0 || | |
357 | git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { | |
2fb9d6de | 358 | error = -1; |
359 | goto cleanup; | |
360 | } | |
d88d4311 | 361 | |
25e0b157 | 362 | if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) |
2fb9d6de | 363 | goto cleanup; |
9c82357b | 364 | |
c9ffa84b | 365 | if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) |
9c82357b | 366 | goto cleanup; |
bf6bebe2 | 367 | |
c9ffa84b | 368 | optional_setting_found |= found; |
369 | ||
9462c471 | 370 | remote->repo = repo; |
c9ffa84b | 371 | |
372 | if (found && strlen(val) > 0) { | |
373 | remote->url = git__strdup(val); | |
374 | GITERR_CHECK_ALLOC(remote->url); | |
375 | } | |
9c82357b | 376 | |
bf6bebe2 | 377 | val = NULL; |
3ed4b501 | 378 | git_buf_clear(&buf); |
bf6bebe2 | 379 | git_buf_printf(&buf, "remote.%s.pushurl", name); |
3ed4b501 | 380 | |
c9ffa84b | 381 | if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) |
3ed4b501 | 382 | goto cleanup; |
3ed4b501 | 383 | |
c9ffa84b | 384 | optional_setting_found |= found; |
385 | ||
386 | if (!optional_setting_found) { | |
387 | error = GIT_ENOTFOUND; | |
3ed4b501 | 388 | goto cleanup; |
c9ffa84b | 389 | } |
3ed4b501 | 390 | |
c9ffa84b | 391 | if (found && strlen(val) > 0) { |
3ed4b501 SC |
392 | remote->pushurl = git__strdup(val); |
393 | GITERR_CHECK_ALLOC(remote->pushurl); | |
394 | } | |
395 | ||
4330ab26 CMN |
396 | data.remote = remote; |
397 | data.fetch = true; | |
96869a4e | 398 | |
f0f3a18a | 399 | git_buf_clear(&buf); |
bf6bebe2 RB |
400 | git_buf_printf(&buf, "remote.%s.fetch", name); |
401 | ||
c9ffa84b | 402 | if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) |
2fb9d6de | 403 | goto cleanup; |
9c82357b | 404 | |
4330ab26 | 405 | data.fetch = false; |
bf6bebe2 RB |
406 | git_buf_clear(&buf); |
407 | git_buf_printf(&buf, "remote.%s.push", name); | |
9c82357b | 408 | |
c9ffa84b | 409 | if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) |
9c82357b CMN |
410 | goto cleanup; |
411 | ||
24f2f94e CMN |
412 | if (download_tags_value(remote, config) < 0) |
413 | goto cleanup; | |
414 | ||
af613ecd CMN |
415 | /* Move the data over to where the matching functions can find them */ |
416 | if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) | |
417 | goto cleanup; | |
418 | ||
9c82357b CMN |
419 | *out = remote; |
420 | ||
421 | cleanup: | |
f0f3a18a | 422 | git_buf_free(&buf); |
9462c471 | 423 | |
4376f7f6 | 424 | if (error < 0) |
9c82357b CMN |
425 | git_remote_free(remote); |
426 | ||
427 | return error; | |
428 | } | |
429 | ||
4330ab26 | 430 | static int update_config_refspec(const git_remote *remote, git_config *config, int direction) |
4fe5520a | 431 | { |
4330ab26 | 432 | git_buf name = GIT_BUF_INIT; |
66566516 | 433 | unsigned int push; |
4330ab26 CMN |
434 | const char *dir; |
435 | size_t i; | |
bf6bebe2 | 436 | int error = 0; |
3793fa9b | 437 | const char *cname; |
4fe5520a | 438 | |
4330ab26 CMN |
439 | push = direction == GIT_DIRECTION_PUSH; |
440 | dir = push ? "push" : "fetch"; | |
4fe5520a | 441 | |
4330ab26 CMN |
442 | if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0) |
443 | return -1; | |
3793fa9b | 444 | cname = git_buf_cstr(&name); |
4fe5520a | 445 | |
4330ab26 | 446 | /* Clear out the existing config */ |
bf6bebe2 | 447 | while (!error) |
3793fa9b | 448 | error = git_config_delete_multivar(config, cname, ".*"); |
4fe5520a | 449 | |
4330ab26 CMN |
450 | if (error != GIT_ENOTFOUND) |
451 | return error; | |
452 | ||
1be680c4 | 453 | for (i = 0; i < remote->refspecs.length; i++) { |
4330ab26 | 454 | git_refspec *spec = git_vector_get(&remote->refspecs, i); |
4330ab26 CMN |
455 | |
456 | if (spec->push != push) | |
457 | continue; | |
458 | ||
376454d0 DRT |
459 | // "$^" is a unmatcheable regexp: it will not match anything at all, so |
460 | // all values will be considered new and we will not replace any | |
461 | // present value. | |
bf6bebe2 | 462 | if ((error = git_config_set_multivar( |
376454d0 | 463 | config, cname, "$^", spec->string)) < 0) { |
4330ab26 CMN |
464 | goto cleanup; |
465 | } | |
466 | } | |
467 | ||
468 | giterr_clear(); | |
469 | error = 0; | |
4fe5520a | 470 | |
471 | cleanup: | |
472 | git_buf_free(&name); | |
4fe5520a | 473 | |
474 | return error; | |
475 | } | |
476 | ||
89e5ed98 CMN |
477 | int git_remote_save(const git_remote *remote) |
478 | { | |
218c88a9 | 479 | int error; |
89e5ed98 | 480 | git_config *config; |
218c88a9 | 481 | const char *tagopt = NULL; |
4fe5520a | 482 | git_buf buf = GIT_BUF_INIT; |
89e5ed98 | 483 | |
e497b16c | 484 | assert(remote); |
485 | ||
c07b52df | 486 | if (!remote->name) { |
874dcb25 BS |
487 | giterr_set(GITERR_INVALID, "Can't save an in-memory remote."); |
488 | return GIT_EINVALIDSPEC; | |
a71c27cc BS |
489 | } |
490 | ||
032ba9e4 | 491 | if ((error = ensure_remote_name_is_valid(remote->name)) < 0) |
492 | return error; | |
89e5ed98 | 493 | |
4376f7f6 CMN |
494 | if (git_repository_config__weakptr(&config, remote->repo) < 0) |
495 | return -1; | |
89e5ed98 | 496 | |
cb020f0d | 497 | if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0) |
4376f7f6 | 498 | return -1; |
89e5ed98 | 499 | |
4376f7f6 CMN |
500 | if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) { |
501 | git_buf_free(&buf); | |
502 | return -1; | |
503 | } | |
89e5ed98 | 504 | |
413d5563 SC |
505 | git_buf_clear(&buf); |
506 | if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0) | |
507 | return -1; | |
3ed4b501 | 508 | |
413d5563 | 509 | if (remote->pushurl) { |
3ed4b501 SC |
510 | if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) { |
511 | git_buf_free(&buf); | |
512 | return -1; | |
513 | } | |
413d5563 | 514 | } else { |
54b2a37a | 515 | int error = git_config_delete_entry(config, git_buf_cstr(&buf)); |
413d5563 SC |
516 | if (error == GIT_ENOTFOUND) { |
517 | error = 0; | |
c48e8700 | 518 | giterr_clear(); |
413d5563 SC |
519 | } |
520 | if (error < 0) { | |
521 | git_buf_free(&buf); | |
9f77b3f6 | 522 | return error; |
413d5563 | 523 | } |
3ed4b501 SC |
524 | } |
525 | ||
4330ab26 CMN |
526 | if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0) |
527 | goto on_error; | |
89e5ed98 | 528 | |
4330ab26 CMN |
529 | if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0) |
530 | goto on_error; | |
89e5ed98 | 531 | |
218c88a9 CMN |
532 | /* |
533 | * What action to take depends on the old and new values. This | |
534 | * is describes by the table below. tagopt means whether the | |
535 | * is already a value set in the config | |
536 | * | |
537 | * AUTO ALL or NONE | |
538 | * +-----------------------+ | |
539 | * tagopt | remove | set | | |
540 | * +---------+-------------| | |
541 | * !tagopt | nothing | set | | |
542 | * +---------+-------------+ | |
543 | */ | |
544 | ||
545 | git_buf_clear(&buf); | |
546 | if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) | |
547 | goto on_error; | |
548 | ||
549 | error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf)); | |
550 | if (error < 0 && error != GIT_ENOTFOUND) | |
551 | goto on_error; | |
552 | ||
553 | if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { | |
554 | if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0) | |
555 | goto on_error; | |
556 | } else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) { | |
557 | if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0) | |
558 | goto on_error; | |
559 | } else if (tagopt) { | |
54b2a37a | 560 | if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) |
218c88a9 CMN |
561 | goto on_error; |
562 | } | |
563 | ||
89e5ed98 | 564 | git_buf_free(&buf); |
4376f7f6 CMN |
565 | |
566 | return 0; | |
567 | ||
568 | on_error: | |
569 | git_buf_free(&buf); | |
4376f7f6 | 570 | return -1; |
89e5ed98 CMN |
571 | } |
572 | ||
df705148 | 573 | const char *git_remote_name(const git_remote *remote) |
9c82357b | 574 | { |
4bef3565 | 575 | assert(remote); |
9c82357b CMN |
576 | return remote->name; |
577 | } | |
578 | ||
85e1eded ES |
579 | git_repository *git_remote_owner(const git_remote *remote) |
580 | { | |
581 | assert(remote); | |
582 | return remote->repo; | |
583 | } | |
584 | ||
df705148 | 585 | const char *git_remote_url(const git_remote *remote) |
9c82357b | 586 | { |
4bef3565 | 587 | assert(remote); |
9c82357b CMN |
588 | return remote->url; |
589 | } | |
590 | ||
76501590 SC |
591 | int git_remote_set_url(git_remote *remote, const char* url) |
592 | { | |
593 | assert(remote); | |
594 | assert(url); | |
595 | ||
596 | git__free(remote->url); | |
597 | remote->url = git__strdup(url); | |
598 | GITERR_CHECK_ALLOC(remote->url); | |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
df705148 | 603 | const char *git_remote_pushurl(const git_remote *remote) |
76501590 SC |
604 | { |
605 | assert(remote); | |
606 | return remote->pushurl; | |
607 | } | |
608 | ||
609 | int git_remote_set_pushurl(git_remote *remote, const char* url) | |
610 | { | |
611 | assert(remote); | |
612 | ||
613 | git__free(remote->pushurl); | |
614 | if (url) { | |
615 | remote->pushurl = git__strdup(url); | |
616 | GITERR_CHECK_ALLOC(remote->pushurl); | |
617 | } else { | |
618 | remote->pushurl = NULL; | |
619 | } | |
620 | return 0; | |
621 | } | |
622 | ||
eff5b499 SC |
623 | const char* git_remote__urlfordirection(git_remote *remote, int direction) |
624 | { | |
625 | assert(remote); | |
626 | ||
b83c92dd | 627 | assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH); |
628 | ||
df705148 | 629 | if (direction == GIT_DIRECTION_FETCH) { |
eff5b499 SC |
630 | return remote->url; |
631 | } | |
632 | ||
df705148 | 633 | if (direction == GIT_DIRECTION_PUSH) { |
eff5b499 SC |
634 | return remote->pushurl ? remote->pushurl : remote->url; |
635 | } | |
636 | ||
637 | return NULL; | |
638 | } | |
639 | ||
df705148 | 640 | int git_remote_connect(git_remote *remote, git_direction direction) |
9ba49bb5 | 641 | { |
9ba49bb5 | 642 | git_transport *t; |
c0c39025 | 643 | const char *url; |
41fb1ca0 | 644 | int flags = GIT_TRANSPORTFLAGS_NONE; |
80fc7d6b | 645 | int error; |
9ba49bb5 | 646 | |
4bef3565 VM |
647 | assert(remote); |
648 | ||
41fb1ca0 PK |
649 | t = remote->transport; |
650 | ||
c0c39025 | 651 | url = git_remote__urlfordirection(remote, direction); |
80fc7d6b | 652 | if (url == NULL) { |
44bc0c6a | 653 | giterr_set(GITERR_INVALID, |
654 | "Malformed remote '%s' - missing URL", remote->name); | |
eff5b499 | 655 | return -1; |
44bc0c6a | 656 | } |
eff5b499 | 657 | |
41fb1ca0 PK |
658 | /* A transport could have been supplied in advance with |
659 | * git_remote_set_transport */ | |
80fc7d6b ET |
660 | if (!t && (error = git_transport_new(&t, remote, url)) < 0) |
661 | return error; | |
9ba49bb5 | 662 | |
41fb1ca0 | 663 | if (t->set_callbacks && |
80fc7d6b | 664 | (error = t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload)) < 0) |
41fb1ca0 | 665 | goto on_error; |
613d5eb9 | 666 | |
41fb1ca0 PK |
667 | if (!remote->check_cert) |
668 | flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT; | |
e03e71da | 669 | |
25e0b157 | 670 | if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0) |
4376f7f6 | 671 | goto on_error; |
9ba49bb5 CMN |
672 | |
673 | remote->transport = t; | |
674 | ||
4376f7f6 | 675 | return 0; |
9ba49bb5 | 676 | |
4376f7f6 CMN |
677 | on_error: |
678 | t->free(t); | |
613d5eb9 PK |
679 | |
680 | if (t == remote->transport) | |
681 | remote->transport = NULL; | |
682 | ||
80fc7d6b | 683 | return error; |
9ba49bb5 CMN |
684 | } |
685 | ||
359dce72 | 686 | int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote) |
9ba49bb5 | 687 | { |
d88d4311 VM |
688 | assert(remote); |
689 | ||
359dce72 | 690 | return remote->transport->ls(out, size, remote->transport); |
9ba49bb5 CMN |
691 | } |
692 | ||
613d5eb9 PK |
693 | int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) |
694 | { | |
695 | git_config *cfg; | |
9f77b3f6 RB |
696 | const git_config_entry *ce; |
697 | const char *val = NULL; | |
80fc7d6b | 698 | int error; |
613d5eb9 PK |
699 | |
700 | assert(remote); | |
701 | ||
a71c27cc | 702 | if (!proxy_url || !remote->repo) |
613d5eb9 PK |
703 | return -1; |
704 | ||
705 | *proxy_url = NULL; | |
706 | ||
80fc7d6b ET |
707 | if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) |
708 | return error; | |
613d5eb9 PK |
709 | |
710 | /* Go through the possible sources for proxy configuration, from most specific | |
711 | * to least specific. */ | |
712 | ||
713 | /* remote.<name>.proxy config setting */ | |
9f77b3f6 | 714 | if (remote->name && remote->name[0]) { |
613d5eb9 PK |
715 | git_buf buf = GIT_BUF_INIT; |
716 | ||
80fc7d6b ET |
717 | if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) |
718 | return error; | |
613d5eb9 | 719 | |
9f77b3f6 RB |
720 | error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); |
721 | git_buf_free(&buf); | |
613d5eb9 | 722 | |
9f77b3f6 | 723 | if (error < 0) |
80fc7d6b | 724 | return error; |
613d5eb9 | 725 | |
9f77b3f6 RB |
726 | if (ce && ce->value) { |
727 | val = ce->value; | |
728 | goto found; | |
729 | } | |
613d5eb9 PK |
730 | } |
731 | ||
732 | /* http.proxy config setting */ | |
9f77b3f6 | 733 | if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) |
80fc7d6b | 734 | return error; |
9f77b3f6 RB |
735 | if (ce && ce->value) { |
736 | val = ce->value; | |
737 | goto found; | |
738 | } | |
613d5eb9 PK |
739 | |
740 | /* HTTP_PROXY / HTTPS_PROXY environment variables */ | |
741 | val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY"); | |
742 | ||
9f77b3f6 RB |
743 | found: |
744 | if (val && val[0]) { | |
613d5eb9 PK |
745 | *proxy_url = git__strdup(val); |
746 | GITERR_CHECK_ALLOC(*proxy_url); | |
613d5eb9 PK |
747 | } |
748 | ||
749 | return 0; | |
750 | } | |
751 | ||
af613ecd CMN |
752 | /* DWIM `refspecs` based on `refs` and append the output to `out` */ |
753 | static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs) | |
d8488457 | 754 | { |
af613ecd | 755 | size_t i; |
d8488457 | 756 | git_refspec *spec; |
d8488457 CMN |
757 | |
758 | git_vector_foreach(refspecs, i, spec) { | |
af613ecd CMN |
759 | if (git_refspec__dwim_one(out, spec, refs) < 0) |
760 | return -1; | |
761 | } | |
d8488457 | 762 | |
af613ecd CMN |
763 | return 0; |
764 | } | |
d8488457 | 765 | |
af613ecd CMN |
766 | static void free_refspecs(git_vector *vec) |
767 | { | |
768 | size_t i; | |
769 | git_refspec *spec; | |
d8488457 | 770 | |
af613ecd CMN |
771 | git_vector_foreach(vec, i, spec) { |
772 | git_refspec__free(spec); | |
773 | git__free(spec); | |
d8488457 CMN |
774 | } |
775 | ||
af613ecd | 776 | git_vector_clear(vec); |
d8488457 CMN |
777 | } |
778 | ||
779 | static int remote_head_cmp(const void *_a, const void *_b) | |
780 | { | |
781 | const git_remote_head *a = (git_remote_head *) _a; | |
782 | const git_remote_head *b = (git_remote_head *) _b; | |
783 | ||
784 | return git__strcmp_cb(a->name, b->name); | |
785 | } | |
786 | ||
877cde76 CMN |
787 | static int ls_to_vector(git_vector *out, git_remote *remote) |
788 | { | |
789 | git_remote_head **heads; | |
790 | size_t heads_len, i; | |
791 | ||
792 | if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) | |
793 | return -1; | |
794 | ||
795 | if (git_vector_init(out, heads_len, remote_head_cmp) < 0) | |
796 | return -1; | |
797 | ||
798 | for (i = 0; i < heads_len; i++) { | |
799 | if (git_vector_insert(out, heads[i]) < 0) | |
800 | return -1; | |
801 | } | |
802 | ||
803 | return 0; | |
804 | } | |
805 | ||
d31402a3 | 806 | int git_remote_download(git_remote *remote) |
48a65a07 | 807 | { |
95057b85 | 808 | int error; |
d8488457 | 809 | git_vector refs; |
95057b85 | 810 | |
1e3b8ed5 | 811 | assert(remote); |
4bef3565 | 812 | |
877cde76 | 813 | if (ls_to_vector(&refs, remote) < 0) |
d8488457 | 814 | return -1; |
d8488457 | 815 | |
af613ecd CMN |
816 | free_refspecs(&remote->active_refspecs); |
817 | ||
877cde76 CMN |
818 | error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs); |
819 | git_vector_free(&refs); | |
820 | ||
821 | if (error < 0) | |
25e0b157 | 822 | return error; |
d8488457 | 823 | |
95057b85 | 824 | if ((error = git_fetch_negotiate(remote)) < 0) |
4376f7f6 | 825 | return error; |
95057b85 | 826 | |
d31402a3 | 827 | return git_fetch_download_pack(remote); |
48a65a07 CMN |
828 | } |
829 | ||
fe3a40a4 CMN |
830 | int git_remote_fetch(git_remote *remote) |
831 | { | |
832 | int error; | |
833 | ||
834 | /* Connect and download everything */ | |
25e0b157 | 835 | if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) |
fe3a40a4 CMN |
836 | return error; |
837 | ||
25e0b157 | 838 | if ((error = git_remote_download(remote)) != 0) |
fe3a40a4 CMN |
839 | return error; |
840 | ||
841 | /* We don't need to be connected anymore */ | |
842 | git_remote_disconnect(remote); | |
843 | ||
844 | /* Create "remote/foo" branches for all remote branches */ | |
845 | return git_remote_update_tips(remote); | |
846 | } | |
847 | ||
b0f6e45d ET |
848 | static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) |
849 | { | |
850 | unsigned int i; | |
851 | git_remote_head *remote_ref; | |
852 | ||
853 | assert(update_heads && fetchspec_src); | |
854 | ||
855 | *out = NULL; | |
7d4b65f6 | 856 | |
857 | git_vector_foreach(update_heads, i, remote_ref) { | |
858 | if (strcmp(remote_ref->name, fetchspec_src) == 0) { | |
859 | *out = remote_ref; | |
860 | break; | |
b0f6e45d ET |
861 | } |
862 | } | |
863 | ||
864 | return 0; | |
865 | } | |
866 | ||
4330ab26 | 867 | static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref) |
b0f6e45d ET |
868 | { |
869 | git_reference *resolved_ref = NULL; | |
870 | git_reference *tracking_ref = NULL; | |
871 | git_buf remote_name = GIT_BUF_INIT; | |
872 | int error = 0; | |
873 | ||
4330ab26 | 874 | assert(out && spec && ref); |
b0f6e45d ET |
875 | |
876 | *out = NULL; | |
877 | ||
878 | if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 || | |
879 | (!git_reference_is_branch(resolved_ref)) || | |
a258d8e3 | 880 | (error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 || |
4330ab26 | 881 | (error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) { |
605da51a | 882 | /* Not an error if HEAD is unborn or no tracking branch */ |
b0f6e45d ET |
883 | if (error == GIT_ENOTFOUND) |
884 | error = 0; | |
885 | ||
886 | goto cleanup; | |
887 | } | |
888 | ||
889 | error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); | |
890 | ||
891 | cleanup: | |
892 | git_reference_free(tracking_ref); | |
893 | git_reference_free(resolved_ref); | |
894 | git_buf_free(&remote_name); | |
895 | return error; | |
896 | } | |
897 | ||
4330ab26 | 898 | static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads) |
b0f6e45d | 899 | { |
b0f6e45d ET |
900 | git_reference *head_ref = NULL; |
901 | git_fetchhead_ref *fetchhead_ref; | |
902 | git_remote_head *remote_ref, *merge_remote_ref; | |
903 | git_vector fetchhead_refs; | |
904 | bool include_all_fetchheads; | |
905 | unsigned int i = 0; | |
906 | int error = 0; | |
907 | ||
908 | assert(remote); | |
909 | ||
404eadb0 CMN |
910 | /* no heads, nothing to do */ |
911 | if (update_heads->length == 0) | |
912 | return 0; | |
913 | ||
b0f6e45d ET |
914 | if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0) |
915 | return -1; | |
916 | ||
917 | /* Iff refspec is * (but not subdir slash star), include tags */ | |
918 | include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0); | |
919 | ||
920 | /* Determine what to merge: if refspec was a wildcard, just use HEAD */ | |
921 | if (git_refspec_is_wildcard(spec)) { | |
922 | if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 || | |
4330ab26 | 923 | (error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0) |
b0f6e45d ET |
924 | goto cleanup; |
925 | } else { | |
926 | /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */ | |
927 | if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0) | |
928 | goto cleanup; | |
929 | } | |
930 | ||
931 | /* Create the FETCH_HEAD file */ | |
7d4b65f6 | 932 | git_vector_foreach(update_heads, i, remote_ref) { |
b0f6e45d ET |
933 | int merge_this_fetchhead = (merge_remote_ref == remote_ref); |
934 | ||
935 | if (!include_all_fetchheads && | |
936 | !git_refspec_src_matches(spec, remote_ref->name) && | |
937 | !merge_this_fetchhead) | |
938 | continue; | |
939 | ||
940 | if (git_fetchhead_ref_create(&fetchhead_ref, | |
941 | &remote_ref->oid, | |
942 | merge_this_fetchhead, | |
943 | remote_ref->name, | |
944 | git_remote_url(remote)) < 0) | |
945 | goto cleanup; | |
946 | ||
947 | if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0) | |
948 | goto cleanup; | |
949 | } | |
950 | ||
951 | git_fetchhead_write(remote->repo, &fetchhead_refs); | |
952 | ||
953 | cleanup: | |
954 | for (i = 0; i < fetchhead_refs.length; ++i) | |
955 | git_fetchhead_ref_free(fetchhead_refs.contents[i]); | |
956 | ||
957 | git_vector_free(&fetchhead_refs); | |
958 | git_reference_free(head_ref); | |
959 | ||
960 | return error; | |
961 | } | |
962 | ||
4330ab26 | 963 | static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs) |
441f57c2 | 964 | { |
a37ddf7e | 965 | int error = 0, autotag; |
517bda19 | 966 | unsigned int i = 0; |
97769280 | 967 | git_buf refname = GIT_BUF_INIT; |
f184836b | 968 | git_oid old; |
a37ddf7e | 969 | git_odb *odb; |
441f57c2 CMN |
970 | git_remote_head *head; |
971 | git_reference *ref; | |
a37ddf7e | 972 | git_refspec tagspec; |
4330ab26 | 973 | git_vector update_heads; |
441f57c2 | 974 | |
4bef3565 VM |
975 | assert(remote); |
976 | ||
acd17006 | 977 | if (git_repository_odb__weakptr(&odb, remote->repo) < 0) |
a37ddf7e CMN |
978 | return -1; |
979 | ||
3230a44f | 980 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
a37ddf7e CMN |
981 | return -1; |
982 | ||
41fb1ca0 | 983 | /* Make a copy of the transport's refs */ |
4330ab26 | 984 | if (git_vector_init(&update_heads, 16, NULL) < 0) |
41fb1ca0 | 985 | return -1; |
585a2eb7 | 986 | |
4330ab26 CMN |
987 | for (; i < refs->length; ++i) { |
988 | head = git_vector_get(refs, i); | |
9063be1f | 989 | autotag = 0; |
517bda19 | 990 | |
a37ddf7e CMN |
991 | /* Ignore malformed ref names (which also saves us from tag^{} */ |
992 | if (!git_reference_is_valid_name(head->name)) | |
993 | continue; | |
994 | ||
d8488457 | 995 | if (git_refspec_src_matches(spec, head->name) && spec->dst) { |
a37ddf7e CMN |
996 | if (git_refspec_transform_r(&refname, spec, head->name) < 0) |
997 | goto on_error; | |
998 | } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { | |
3230a44f CMN |
999 | |
1000 | if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL) | |
1001 | autotag = 1; | |
a37ddf7e CMN |
1002 | |
1003 | if (!git_refspec_src_matches(&tagspec, head->name)) | |
1004 | continue; | |
1005 | ||
1006 | git_buf_clear(&refname); | |
1007 | if (git_buf_puts(&refname, head->name) < 0) | |
1008 | goto on_error; | |
1009 | } else { | |
1010 | continue; | |
1011 | } | |
1012 | ||
1013 | if (autotag && !git_odb_exists(odb, &head->oid)) | |
1014 | continue; | |
f184836b | 1015 | |
b0f6e45d ET |
1016 | if (git_vector_insert(&update_heads, head) < 0) |
1017 | goto on_error; | |
1018 | ||
2508cc66 | 1019 | error = git_reference_name_to_id(&old, remote->repo, refname.ptr); |
904b67e6 | 1020 | if (error < 0 && error != GIT_ENOTFOUND) |
f184836b CMN |
1021 | goto on_error; |
1022 | ||
904b67e6 | 1023 | if (error == GIT_ENOTFOUND) |
f184836b CMN |
1024 | memset(&old, 0, GIT_OID_RAWSZ); |
1025 | ||
b7f167da | 1026 | if (!git_oid__cmp(&old, &head->oid)) |
f184836b | 1027 | continue; |
441f57c2 | 1028 | |
a37ddf7e | 1029 | /* In autotag mode, don't overwrite any locally-existing tags */ |
0b28217b | 1030 | error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, NULL, NULL); |
a37ddf7e | 1031 | if (error < 0 && error != GIT_EEXISTS) |
944d250f | 1032 | goto on_error; |
39157563 CMN |
1033 | |
1034 | git_reference_free(ref); | |
f184836b | 1035 | |
b3aaa7a7 | 1036 | if (remote->callbacks.update_tips != NULL) { |
df705148 | 1037 | if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.payload) < 0) |
f184836b CMN |
1038 | goto on_error; |
1039 | } | |
441f57c2 CMN |
1040 | } |
1041 | ||
b0f6e45d | 1042 | if (git_remote_update_fetchhead(remote) && |
4330ab26 | 1043 | (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0) |
b0f6e45d ET |
1044 | goto on_error; |
1045 | ||
b0f6e45d | 1046 | git_vector_free(&update_heads); |
a37ddf7e | 1047 | git_refspec__free(&tagspec); |
97769280 | 1048 | git_buf_free(&refname); |
f184836b CMN |
1049 | return 0; |
1050 | ||
1051 | on_error: | |
b0f6e45d | 1052 | git_vector_free(&update_heads); |
a37ddf7e | 1053 | git_refspec__free(&tagspec); |
f184836b CMN |
1054 | git_buf_free(&refname); |
1055 | return -1; | |
97769280 | 1056 | |
441f57c2 CMN |
1057 | } |
1058 | ||
4330ab26 CMN |
1059 | int git_remote_update_tips(git_remote *remote) |
1060 | { | |
505b5d0c | 1061 | git_refspec *spec, tagspec; |
4330ab26 | 1062 | git_vector refs; |
505b5d0c | 1063 | int error; |
4330ab26 CMN |
1064 | size_t i; |
1065 | ||
505b5d0c CMN |
1066 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
1067 | return -1; | |
1068 | ||
877cde76 CMN |
1069 | |
1070 | if ((error = ls_to_vector(&refs, remote)) < 0) | |
505b5d0c CMN |
1071 | goto out; |
1072 | ||
1073 | if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { | |
1074 | error = update_tips_for_spec(remote, &tagspec, &refs); | |
1075 | goto out; | |
1076 | } | |
4330ab26 | 1077 | |
af613ecd | 1078 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1079 | if (spec->push) |
1080 | continue; | |
1081 | ||
505b5d0c CMN |
1082 | if ((error = update_tips_for_spec(remote, spec, &refs)) < 0) |
1083 | goto out; | |
4330ab26 CMN |
1084 | } |
1085 | ||
505b5d0c | 1086 | out: |
877cde76 | 1087 | git_vector_free(&refs); |
505b5d0c | 1088 | git_refspec__free(&tagspec); |
505b5d0c | 1089 | return error; |
4330ab26 CMN |
1090 | } |
1091 | ||
11f6ad5f | 1092 | int git_remote_connected(const git_remote *remote) |
6ac3b707 | 1093 | { |
4bef3565 | 1094 | assert(remote); |
41fb1ca0 PK |
1095 | |
1096 | if (!remote->transport || !remote->transport->is_connected) | |
1097 | return 0; | |
1098 | ||
1099 | /* Ask the transport if it's connected. */ | |
613d5eb9 | 1100 | return remote->transport->is_connected(remote->transport); |
6ac3b707 CMN |
1101 | } |
1102 | ||
f0d2ddbb CMN |
1103 | void git_remote_stop(git_remote *remote) |
1104 | { | |
613d5eb9 PK |
1105 | assert(remote); |
1106 | ||
1107 | if (remote->transport && remote->transport->cancel) | |
41fb1ca0 | 1108 | remote->transport->cancel(remote->transport); |
f0d2ddbb CMN |
1109 | } |
1110 | ||
4cf01e9a CMN |
1111 | void git_remote_disconnect(git_remote *remote) |
1112 | { | |
4bef3565 VM |
1113 | assert(remote); |
1114 | ||
41fb1ca0 PK |
1115 | if (git_remote_connected(remote)) |
1116 | remote->transport->close(remote->transport); | |
4cf01e9a CMN |
1117 | } |
1118 | ||
9c82357b CMN |
1119 | void git_remote_free(git_remote *remote) |
1120 | { | |
2aae2188 CMN |
1121 | if (remote == NULL) |
1122 | return; | |
1123 | ||
42ea35c0 MS |
1124 | if (remote->transport != NULL) { |
1125 | git_remote_disconnect(remote); | |
1126 | ||
1127 | remote->transport->free(remote->transport); | |
1128 | remote->transport = NULL; | |
1129 | } | |
1130 | ||
1131 | git_vector_free(&remote->refs); | |
1132 | ||
af613ecd | 1133 | free_refspecs(&remote->refspecs); |
4330ab26 CMN |
1134 | git_vector_free(&remote->refspecs); |
1135 | ||
af613ecd CMN |
1136 | free_refspecs(&remote->active_refspecs); |
1137 | git_vector_free(&remote->active_refspecs); | |
1138 | ||
3286c408 | 1139 | git__free(remote->url); |
3ed4b501 | 1140 | git__free(remote->pushurl); |
3286c408 | 1141 | git__free(remote->name); |
3286c408 | 1142 | git__free(remote); |
9c82357b | 1143 | } |
8171998f | 1144 | |
106c12f1 | 1145 | static int remote_list_cb(const git_config_entry *entry, void *payload) |
8171998f | 1146 | { |
25e0b157 | 1147 | git_vector *list = payload; |
106c12f1 RB |
1148 | const char *name = entry->name + strlen("remote."); |
1149 | size_t namelen = strlen(name); | |
1150 | char *remote_name; | |
8171998f | 1151 | |
106c12f1 | 1152 | /* we know name matches "remote.<stuff>.(push)?url" */ |
8171998f | 1153 | |
106c12f1 RB |
1154 | if (!strcmp(&name[namelen - 4], ".url")) |
1155 | remote_name = git__strndup(name, namelen - 4); /* strip ".url" */ | |
1156 | else | |
1157 | remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */ | |
25e0b157 | 1158 | GITERR_CHECK_ALLOC(remote_name); |
8171998f | 1159 | |
25e0b157 | 1160 | return git_vector_insert(list, remote_name); |
8171998f CMN |
1161 | } |
1162 | ||
1163 | int git_remote_list(git_strarray *remotes_list, git_repository *repo) | |
1164 | { | |
8171998f | 1165 | int error; |
96869a4e | 1166 | git_config *cfg; |
25e0b157 | 1167 | git_vector list = GIT_VECTOR_INIT; |
8171998f | 1168 | |
96869a4e RB |
1169 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) |
1170 | return error; | |
8171998f | 1171 | |
25e0b157 | 1172 | if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) |
96869a4e | 1173 | return error; |
8171998f | 1174 | |
106c12f1 | 1175 | error = git_config_foreach_match( |
25e0b157 | 1176 | cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); |
8171998f | 1177 | |
4376f7f6 | 1178 | if (error < 0) { |
9cfce273 | 1179 | git_vector_free_deep(&list); |
8171998f CMN |
1180 | return error; |
1181 | } | |
1182 | ||
25e0b157 | 1183 | git_vector_uniq(&list, git__free); |
aec87f71 | 1184 | |
25e0b157 RB |
1185 | remotes_list->strings = |
1186 | (char **)git_vector_detach(&remotes_list->count, NULL, &list); | |
8171998f | 1187 | |
4376f7f6 | 1188 | return 0; |
8171998f | 1189 | } |
a209a025 | 1190 | |
250b95b2 CMN |
1191 | void git_remote_check_cert(git_remote *remote, int check) |
1192 | { | |
1193 | assert(remote); | |
1194 | ||
1195 | remote->check_cert = check; | |
1196 | } | |
b3aaa7a7 | 1197 | |
0e0cf787 | 1198 | int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks) |
b3aaa7a7 CMN |
1199 | { |
1200 | assert(remote && callbacks); | |
1201 | ||
c7231c45 | 1202 | GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
9267ff58 | 1203 | |
b3aaa7a7 | 1204 | memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks)); |
e03e71da | 1205 | |
41fb1ca0 | 1206 | if (remote->transport && remote->transport->set_callbacks) |
0e0cf787 | 1207 | return remote->transport->set_callbacks(remote->transport, |
41fb1ca0 PK |
1208 | remote->callbacks.progress, |
1209 | NULL, | |
df705148 | 1210 | remote->callbacks.payload); |
9267ff58 BS |
1211 | |
1212 | return 0; | |
41fb1ca0 PK |
1213 | } |
1214 | ||
1215 | int git_remote_set_transport(git_remote *remote, git_transport *transport) | |
1216 | { | |
1217 | assert(remote && transport); | |
1218 | ||
c7231c45 | 1219 | GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport"); |
10711769 | 1220 | |
e03e71da | 1221 | if (remote->transport) { |
41fb1ca0 PK |
1222 | giterr_set(GITERR_NET, "A transport is already bound to this remote"); |
1223 | return -1; | |
e03e71da | 1224 | } |
41fb1ca0 PK |
1225 | |
1226 | remote->transport = transport; | |
1227 | return 0; | |
b3aaa7a7 | 1228 | } |
f70e466f | 1229 | |
67dad09b | 1230 | const git_transfer_progress* git_remote_stats(git_remote *remote) |
d57c47dc BS |
1231 | { |
1232 | assert(remote); | |
1233 | return &remote->stats; | |
1234 | } | |
1235 | ||
11f6ad5f | 1236 | git_remote_autotag_option_t git_remote_autotag(const git_remote *remote) |
f70e466f CMN |
1237 | { |
1238 | return remote->download_tags; | |
1239 | } | |
1240 | ||
f4a62c30 | 1241 | void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value) |
f70e466f CMN |
1242 | { |
1243 | remote->download_tags = value; | |
1244 | } | |
fcccf304 | 1245 | |
fcccf304 | 1246 | static int rename_remote_config_section( |
1247 | git_repository *repo, | |
1248 | const char *old_name, | |
1249 | const char *new_name) | |
1250 | { | |
1251 | git_buf old_section_name = GIT_BUF_INIT, | |
1252 | new_section_name = GIT_BUF_INIT; | |
1253 | int error = -1; | |
1254 | ||
1255 | if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) | |
1256 | goto cleanup; | |
1257 | ||
1258 | if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0) | |
1259 | goto cleanup; | |
1260 | ||
1261 | error = git_config_rename_section( | |
1262 | repo, | |
1263 | git_buf_cstr(&old_section_name), | |
1264 | git_buf_cstr(&new_section_name)); | |
1265 | ||
1266 | cleanup: | |
1267 | git_buf_free(&old_section_name); | |
1268 | git_buf_free(&new_section_name); | |
1269 | ||
1270 | return error; | |
1271 | } | |
1272 | ||
96869a4e | 1273 | struct update_data { |
fcccf304 | 1274 | git_config *config; |
1275 | const char *old_remote_name; | |
1276 | const char *new_remote_name; | |
1277 | }; | |
1278 | ||
1279 | static int update_config_entries_cb( | |
1280 | const git_config_entry *entry, | |
1281 | void *payload) | |
1282 | { | |
1283 | struct update_data *data = (struct update_data *)payload; | |
1284 | ||
1285 | if (strcmp(entry->value, data->old_remote_name)) | |
1286 | return 0; | |
1287 | ||
25e0b157 RB |
1288 | return git_config_set_string( |
1289 | data->config, entry->name, data->new_remote_name); | |
fcccf304 | 1290 | } |
1291 | ||
1292 | static int update_branch_remote_config_entry( | |
1293 | git_repository *repo, | |
1294 | const char *old_name, | |
1295 | const char *new_name) | |
1296 | { | |
96869a4e RB |
1297 | int error; |
1298 | struct update_data data = { NULL }; | |
fcccf304 | 1299 | |
96869a4e RB |
1300 | if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) |
1301 | return error; | |
fcccf304 | 1302 | |
fcccf304 | 1303 | data.old_remote_name = old_name; |
1304 | data.new_remote_name = new_name; | |
1305 | ||
25e0b157 | 1306 | return git_config_foreach_match( |
96869a4e | 1307 | data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); |
fcccf304 | 1308 | } |
1309 | ||
fcccf304 | 1310 | static int rename_one_remote_reference( |
56960b83 | 1311 | git_reference *reference, |
fcccf304 | 1312 | const char *old_remote_name, |
1313 | const char *new_remote_name) | |
1314 | { | |
96869a4e | 1315 | int error; |
fcccf304 | 1316 | git_buf new_name = GIT_BUF_INIT; |
fcccf304 | 1317 | |
96869a4e | 1318 | error = git_buf_printf( |
fcccf304 | 1319 | &new_name, |
1320 | GIT_REFS_REMOTES_DIR "%s%s", | |
1321 | new_remote_name, | |
96869a4e | 1322 | reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)); |
fcccf304 | 1323 | |
96869a4e RB |
1324 | if (!error) { |
1325 | error = git_reference_rename( | |
1326 | NULL, reference, git_buf_cstr(&new_name), 0); | |
1327 | git_reference_free(reference); | |
1328 | } | |
fcccf304 | 1329 | |
fcccf304 | 1330 | git_buf_free(&new_name); |
1331 | return error; | |
1332 | } | |
1333 | ||
1334 | static int rename_remote_references( | |
1335 | git_repository *repo, | |
1336 | const char *old_name, | |
1337 | const char *new_name) | |
1338 | { | |
96869a4e | 1339 | int error; |
56960b83 | 1340 | git_reference *ref; |
9bd89d96 | 1341 | git_reference_iterator *iter; |
fcccf304 | 1342 | |
96869a4e RB |
1343 | if ((error = git_reference_iterator_new(&iter, repo)) < 0) |
1344 | return error; | |
fcccf304 | 1345 | |
56960b83 | 1346 | while ((error = git_reference_next(&ref, iter)) == 0) { |
2f77d8f1 RB |
1347 | if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { |
1348 | git_reference_free(ref); | |
9bd89d96 | 1349 | continue; |
2f77d8f1 | 1350 | } |
9bd89d96 | 1351 | |
96869a4e RB |
1352 | if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) |
1353 | break; | |
9bd89d96 CMN |
1354 | } |
1355 | ||
1356 | git_reference_iterator_free(iter); | |
fcccf304 | 1357 | |
96869a4e | 1358 | return (error == GIT_ITEROVER) ? 0 : error; |
fcccf304 | 1359 | } |
1360 | ||
1361 | static int rename_fetch_refspecs( | |
1362 | git_remote *remote, | |
1363 | const char *new_name, | |
1364 | int (*callback)(const char *problematic_refspec, void *payload), | |
1365 | void *payload) | |
1366 | { | |
1367 | git_config *config; | |
4330ab26 | 1368 | git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; |
1be680c4 | 1369 | const git_refspec *spec; |
4330ab26 | 1370 | size_t i; |
25e0b157 | 1371 | int error = 0; |
fcccf304 | 1372 | |
dab89f9b | 1373 | if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) |
25e0b157 RB |
1374 | return error; |
1375 | ||
1376 | if ((error = git_buf_printf( | |
1377 | &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0) | |
1378 | return error; | |
dab89f9b | 1379 | |
1be680c4 | 1380 | git_vector_foreach(&remote->refspecs, i, spec) { |
4330ab26 CMN |
1381 | if (spec->push) |
1382 | continue; | |
fcccf304 | 1383 | |
dab89f9b RB |
1384 | /* Every refspec is a problem refspec for an in-memory remote, OR */ |
1385 | /* Does the dst part of the refspec follow the expected format? */ | |
1386 | if (!remote->name || | |
1387 | strcmp(git_buf_cstr(&base), spec->string)) { | |
fcccf304 | 1388 | |
c7b3e1b3 | 1389 | if ((error = callback(spec->string, payload)) != 0) { |
26c1cb91 | 1390 | giterr_set_after_callback(error); |
25e0b157 | 1391 | break; |
c7b3e1b3 RB |
1392 | } |
1393 | ||
4330ab26 CMN |
1394 | continue; |
1395 | } | |
fcccf304 | 1396 | |
4330ab26 | 1397 | /* If we do want to move it to the new section */ |
fcccf304 | 1398 | |
dab89f9b RB |
1399 | git_buf_clear(&val); |
1400 | git_buf_clear(&var); | |
fcccf304 | 1401 | |
dab89f9b RB |
1402 | if (git_buf_printf( |
1403 | &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 || | |
1404 | git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) | |
1405 | { | |
1406 | error = -1; | |
25e0b157 | 1407 | break; |
dab89f9b | 1408 | } |
fcccf304 | 1409 | |
dab89f9b RB |
1410 | if ((error = git_config_set_string( |
1411 | config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) | |
25e0b157 | 1412 | break; |
4330ab26 | 1413 | } |
fcccf304 | 1414 | |
4330ab26 CMN |
1415 | git_buf_free(&base); |
1416 | git_buf_free(&var); | |
1417 | git_buf_free(&val); | |
fcccf304 | 1418 | return error; |
1419 | } | |
1420 | ||
1421 | int git_remote_rename( | |
1422 | git_remote *remote, | |
1423 | const char *new_name, | |
df705148 | 1424 | git_remote_rename_problem_cb callback, |
fcccf304 | 1425 | void *payload) |
1426 | { | |
1427 | int error; | |
1428 | ||
1429 | assert(remote && new_name); | |
1430 | ||
c07b52df | 1431 | if (!remote->name) { |
79000951 BS |
1432 | giterr_set(GITERR_INVALID, "Can't rename an in-memory remote."); |
1433 | return GIT_EINVALIDSPEC; | |
1434 | } | |
1435 | ||
fcccf304 | 1436 | if ((error = ensure_remote_name_is_valid(new_name)) < 0) |
1437 | return error; | |
1438 | ||
a71c27cc BS |
1439 | if (remote->repo) { |
1440 | if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0) | |
fcccf304 | 1441 | return error; |
1442 | ||
a71c27cc BS |
1443 | if (!remote->name) { |
1444 | if ((error = rename_fetch_refspecs( | |
1445 | remote, | |
1446 | new_name, | |
1447 | callback, | |
1448 | payload)) < 0) | |
dab89f9b | 1449 | return error; |
fcccf304 | 1450 | |
a71c27cc | 1451 | remote->name = git__strdup(new_name); |
dab89f9b | 1452 | GITERR_CHECK_ALLOC(remote->name); |
fcccf304 | 1453 | |
a71c27cc BS |
1454 | return git_remote_save(remote); |
1455 | } | |
fcccf304 | 1456 | |
a71c27cc BS |
1457 | if ((error = rename_remote_config_section( |
1458 | remote->repo, | |
1459 | remote->name, | |
1460 | new_name)) < 0) | |
1461 | return error; | |
fcccf304 | 1462 | |
a71c27cc BS |
1463 | if ((error = update_branch_remote_config_entry( |
1464 | remote->repo, | |
1465 | remote->name, | |
1466 | new_name)) < 0) | |
1467 | return error; | |
fcccf304 | 1468 | |
a71c27cc BS |
1469 | if ((error = rename_remote_references( |
1470 | remote->repo, | |
1471 | remote->name, | |
1472 | new_name)) < 0) | |
1473 | return error; | |
1474 | ||
1475 | if ((error = rename_fetch_refspecs( | |
1476 | remote, | |
1477 | new_name, | |
1478 | callback, | |
1479 | payload)) < 0) | |
dab89f9b | 1480 | return error; |
a71c27cc | 1481 | } |
fcccf304 | 1482 | |
1483 | git__free(remote->name); | |
dab89f9b | 1484 | |
fcccf304 | 1485 | remote->name = git__strdup(new_name); |
dab89f9b | 1486 | GITERR_CHECK_ALLOC(remote->name); |
fcccf304 | 1487 | |
1488 | return 0; | |
1489 | } | |
b0f6e45d ET |
1490 | |
1491 | int git_remote_update_fetchhead(git_remote *remote) | |
1492 | { | |
8f2a3d62 | 1493 | return (remote->update_fetchhead != 0); |
b0f6e45d ET |
1494 | } |
1495 | ||
1496 | void git_remote_set_update_fetchhead(git_remote *remote, int value) | |
1497 | { | |
8f2a3d62 | 1498 | remote->update_fetchhead = (value != 0); |
b0f6e45d | 1499 | } |
2bca5b67 | 1500 | |
1501 | int git_remote_is_valid_name( | |
1502 | const char *remote_name) | |
1503 | { | |
1504 | git_buf buf = GIT_BUF_INIT; | |
1505 | git_refspec refspec; | |
1506 | int error = -1; | |
1507 | ||
1508 | if (!remote_name || *remote_name == '\0') | |
1509 | return 0; | |
1510 | ||
1511 | git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name); | |
1512 | error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); | |
1513 | ||
1514 | git_buf_free(&buf); | |
1515 | git_refspec__free(&refspec); | |
1516 | ||
1517 | giterr_clear(); | |
1518 | return error == 0; | |
1519 | } | |
4330ab26 CMN |
1520 | |
1521 | git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname) | |
1522 | { | |
1523 | git_refspec *spec; | |
1524 | size_t i; | |
1525 | ||
af613ecd | 1526 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1527 | if (spec->push) |
1528 | continue; | |
1529 | ||
1530 | if (git_refspec_src_matches(spec, refname)) | |
1531 | return spec; | |
1532 | } | |
1533 | ||
1534 | return NULL; | |
1535 | } | |
1536 | ||
1537 | git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname) | |
1538 | { | |
1539 | git_refspec *spec; | |
1540 | size_t i; | |
1541 | ||
af613ecd | 1542 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1543 | if (spec->push) |
1544 | continue; | |
1545 | ||
1546 | if (git_refspec_dst_matches(spec, refname)) | |
1547 | return spec; | |
1548 | } | |
1549 | ||
1550 | return NULL; | |
1551 | } | |
1552 | ||
1553 | void git_remote_clear_refspecs(git_remote *remote) | |
1554 | { | |
1555 | git_refspec *spec; | |
4330ab26 CMN |
1556 | size_t i; |
1557 | ||
1558 | git_vector_foreach(&remote->refspecs, i, spec) { | |
1559 | git_refspec__free(spec); | |
1be680c4 | 1560 | git__free(spec); |
4330ab26 CMN |
1561 | } |
1562 | git_vector_clear(&remote->refspecs); | |
4330ab26 CMN |
1563 | } |
1564 | ||
266af6d8 CMN |
1565 | static int add_and_dwim(git_remote *remote, const char *str, int push) |
1566 | { | |
1567 | git_refspec *spec; | |
1568 | git_vector *vec; | |
1569 | ||
1570 | if (add_refspec(remote, str, !push) < 0) | |
1571 | return -1; | |
1572 | ||
1573 | vec = &remote->refspecs; | |
1574 | spec = git_vector_get(vec, vec->length - 1); | |
1575 | return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs); | |
1576 | } | |
1577 | ||
bc6374ea | 1578 | int git_remote_add_fetch(git_remote *remote, const char *refspec) |
4330ab26 | 1579 | { |
266af6d8 | 1580 | return add_and_dwim(remote, refspec, false); |
4330ab26 CMN |
1581 | } |
1582 | ||
bc6374ea | 1583 | int git_remote_add_push(git_remote *remote, const char *refspec) |
4330ab26 | 1584 | { |
266af6d8 CMN |
1585 | return add_and_dwim(remote, refspec, true); |
1586 | } | |
1587 | ||
1588 | static int set_refspecs(git_remote *remote, git_strarray *array, int push) | |
1589 | { | |
1590 | git_vector *vec = &remote->refspecs; | |
1591 | git_refspec *spec; | |
1592 | size_t i; | |
1593 | ||
1594 | /* Start by removing any refspecs of the same type */ | |
1595 | for (i = 0; i < vec->length; i++) { | |
1596 | spec = git_vector_get(vec, i); | |
1597 | if (spec->push != push) | |
1598 | continue; | |
1599 | ||
1600 | git_refspec__free(spec); | |
1601 | git__free(spec); | |
1602 | git_vector_remove(vec, i); | |
1603 | i--; | |
1604 | } | |
1605 | ||
1606 | /* And now we add the new ones */ | |
1607 | ||
1608 | for (i = 0; i < array->count; i++) { | |
1609 | if (add_refspec(remote, array->strings[i], !push) < 0) | |
1610 | return -1; | |
1611 | } | |
1612 | ||
1613 | free_refspecs(&remote->active_refspecs); | |
1614 | git_vector_clear(&remote->active_refspecs); | |
1615 | ||
1616 | return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs); | |
1617 | } | |
1618 | ||
1619 | int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array) | |
1620 | { | |
1621 | return set_refspecs(remote, array, false); | |
1622 | } | |
1623 | ||
1624 | int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array) | |
1625 | { | |
1626 | return set_refspecs(remote, array, true); | |
4330ab26 | 1627 | } |
bc6374ea | 1628 | |
11f6ad5f | 1629 | static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push) |
bc6374ea CMN |
1630 | { |
1631 | size_t i; | |
1632 | git_vector refspecs; | |
1633 | git_refspec *spec; | |
1634 | char *dup; | |
1635 | ||
1636 | if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0) | |
1637 | return -1; | |
1638 | ||
1639 | git_vector_foreach(&remote->refspecs, i, spec) { | |
1640 | if (spec->push != push) | |
1641 | continue; | |
1642 | ||
1be680c4 | 1643 | if ((dup = git__strdup(spec->string)) == NULL) |
bc6374ea | 1644 | goto on_error; |
bc6374ea CMN |
1645 | |
1646 | if (git_vector_insert(&refspecs, dup) < 0) { | |
1647 | git__free(dup); | |
1648 | goto on_error; | |
1649 | } | |
1650 | } | |
1651 | ||
1652 | array->strings = (char **)refspecs.contents; | |
1653 | array->count = refspecs.length; | |
1654 | ||
1655 | return 0; | |
1656 | ||
1657 | on_error: | |
9cfce273 | 1658 | git_vector_free_deep(&refspecs); |
bc6374ea CMN |
1659 | |
1660 | return -1; | |
1661 | } | |
1662 | ||
11f6ad5f | 1663 | int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote) |
bc6374ea CMN |
1664 | { |
1665 | return copy_refspecs(array, remote, false); | |
1666 | } | |
1667 | ||
11f6ad5f | 1668 | int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote) |
bc6374ea CMN |
1669 | { |
1670 | return copy_refspecs(array, remote, true); | |
1671 | } | |
1ffd0806 | 1672 | |
11f6ad5f | 1673 | size_t git_remote_refspec_count(const git_remote *remote) |
1ffd0806 CMN |
1674 | { |
1675 | return remote->refspecs.length; | |
1676 | } | |
1677 | ||
11f6ad5f | 1678 | const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) |
1ffd0806 CMN |
1679 | { |
1680 | return git_vector_get(&remote->refspecs, n); | |
1681 | } |