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