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