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