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