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