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