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