]>
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" |
ad4b5beb | 17 | #include "pkt.h" |
9c82357b | 18 | |
8171998f CMN |
19 | #include <regex.h> |
20 | ||
eb0bd77a | 21 | static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var, bool is_fetch) |
9c82357b | 22 | { |
9c82357b | 23 | int error; |
4376f7f6 | 24 | const char *val; |
9c82357b | 25 | |
29e948de | 26 | if ((error = git_config_get_string(&val, cfg, var)) < 0) |
9c82357b CMN |
27 | return error; |
28 | ||
eb0bd77a | 29 | return git_refspec__parse(refspec, val, is_fetch); |
9c82357b CMN |
30 | } |
31 | ||
24f2f94e CMN |
32 | static int download_tags_value(git_remote *remote, git_config *cfg) |
33 | { | |
34 | const char *val; | |
35 | git_buf buf = GIT_BUF_INIT; | |
36 | int error; | |
37 | ||
38 | if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSET) | |
39 | return 0; | |
40 | ||
41 | /* This is the default, let's see if we need to change it */ | |
42 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; | |
43 | if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) | |
44 | return -1; | |
45 | ||
46 | error = git_config_get_string(&val, cfg, git_buf_cstr(&buf)); | |
47 | git_buf_free(&buf); | |
48 | if (!error && !strcmp(val, "--no-tags")) | |
49 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; | |
3230a44f CMN |
50 | else if (!error && !strcmp(val, "--tags")) |
51 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; | |
24f2f94e CMN |
52 | |
53 | if (error == GIT_ENOTFOUND) | |
54 | error = 0; | |
55 | ||
56 | return error; | |
57 | } | |
58 | ||
baaa8a44 | 59 | int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
778e1c73 CMN |
60 | { |
61 | git_remote *remote; | |
62 | ||
4bef3565 VM |
63 | /* name is optional */ |
64 | assert(out && repo && url); | |
617bfdf4 | 65 | |
778e1c73 | 66 | remote = git__malloc(sizeof(git_remote)); |
4376f7f6 | 67 | GITERR_CHECK_ALLOC(remote); |
778e1c73 CMN |
68 | |
69 | memset(remote, 0x0, sizeof(git_remote)); | |
70 | remote->repo = repo; | |
250b95b2 | 71 | remote->check_cert = 1; |
617bfdf4 | 72 | |
4376f7f6 CMN |
73 | if (git_vector_init(&remote->refs, 32, NULL) < 0) |
74 | return -1; | |
d88d4311 | 75 | |
778e1c73 | 76 | remote->url = git__strdup(url); |
4376f7f6 | 77 | GITERR_CHECK_ALLOC(remote->url); |
778e1c73 | 78 | |
617bfdf4 CMN |
79 | if (name != NULL) { |
80 | remote->name = git__strdup(name); | |
4376f7f6 | 81 | GITERR_CHECK_ALLOC(remote->name); |
617bfdf4 CMN |
82 | } |
83 | ||
baaa8a44 | 84 | if (fetch != NULL) { |
eb0bd77a | 85 | if (git_refspec__parse(&remote->fetch, fetch, true) < 0) |
baaa8a44 CMN |
86 | goto on_error; |
87 | } | |
88 | ||
c648d4a8 CMN |
89 | /* A remote without a name doesn't download tags */ |
90 | if (!name) { | |
91 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; | |
92 | } | |
93 | ||
778e1c73 | 94 | *out = remote; |
4376f7f6 | 95 | return 0; |
baaa8a44 CMN |
96 | |
97 | on_error: | |
98 | git_remote_free(remote); | |
99 | return -1; | |
778e1c73 CMN |
100 | } |
101 | ||
9462c471 | 102 | int git_remote_load(git_remote **out, git_repository *repo, const char *name) |
9c82357b CMN |
103 | { |
104 | git_remote *remote; | |
f0f3a18a | 105 | git_buf buf = GIT_BUF_INIT; |
9c82357b | 106 | const char *val; |
4376f7f6 | 107 | int error = 0; |
9462c471 | 108 | git_config *config; |
9c82357b | 109 | |
9462c471 VM |
110 | assert(out && repo && name); |
111 | ||
4376f7f6 CMN |
112 | if (git_repository_config__weakptr(&config, repo) < 0) |
113 | return -1; | |
4bef3565 | 114 | |
9c82357b | 115 | remote = git__malloc(sizeof(git_remote)); |
4376f7f6 | 116 | GITERR_CHECK_ALLOC(remote); |
9c82357b CMN |
117 | |
118 | memset(remote, 0x0, sizeof(git_remote)); | |
250b95b2 | 119 | remote->check_cert = 1; |
9c82357b | 120 | remote->name = git__strdup(name); |
4376f7f6 | 121 | GITERR_CHECK_ALLOC(remote->name); |
9c82357b | 122 | |
2fb9d6de | 123 | if (git_vector_init(&remote->refs, 32, NULL) < 0) { |
124 | error = -1; | |
125 | goto cleanup; | |
126 | } | |
d88d4311 | 127 | |
2fb9d6de | 128 | if (git_buf_printf(&buf, "remote.%s.url", name) < 0) { |
129 | error = -1; | |
130 | goto cleanup; | |
131 | } | |
9c82357b | 132 | |
29e948de | 133 | if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0) |
9c82357b | 134 | goto cleanup; |
9c82357b | 135 | |
9462c471 | 136 | remote->repo = repo; |
9c82357b | 137 | remote->url = git__strdup(val); |
4376f7f6 | 138 | GITERR_CHECK_ALLOC(remote->url); |
9c82357b | 139 | |
3ed4b501 SC |
140 | git_buf_clear(&buf); |
141 | if (git_buf_printf(&buf, "remote.%s.pushurl", name) < 0) { | |
142 | error = -1; | |
143 | goto cleanup; | |
144 | } | |
145 | ||
146 | error = git_config_get_string(&val, config, git_buf_cstr(&buf)); | |
147 | if (error == GIT_ENOTFOUND) | |
148 | error = 0; | |
149 | ||
150 | if (error < 0) { | |
151 | error = -1; | |
152 | goto cleanup; | |
153 | } | |
154 | ||
155 | if (val) { | |
156 | remote->pushurl = git__strdup(val); | |
157 | GITERR_CHECK_ALLOC(remote->pushurl); | |
158 | } | |
159 | ||
f0f3a18a | 160 | git_buf_clear(&buf); |
2fb9d6de | 161 | if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) { |
162 | error = -1; | |
163 | goto cleanup; | |
164 | } | |
9c82357b | 165 | |
eb0bd77a | 166 | error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf), true); |
904b67e6 | 167 | if (error == GIT_ENOTFOUND) |
4376f7f6 | 168 | error = 0; |
9554cd51 | 169 | |
4376f7f6 CMN |
170 | if (error < 0) { |
171 | error = -1; | |
9c82357b | 172 | goto cleanup; |
2dc31040 | 173 | } |
9c82357b | 174 | |
f0f3a18a | 175 | git_buf_clear(&buf); |
2fb9d6de | 176 | if (git_buf_printf(&buf, "remote.%s.push", name) < 0) { |
177 | error = -1; | |
178 | goto cleanup; | |
179 | } | |
9c82357b | 180 | |
eb0bd77a | 181 | error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf), false); |
904b67e6 | 182 | if (error == GIT_ENOTFOUND) |
4376f7f6 | 183 | error = 0; |
9c82357b | 184 | |
4376f7f6 CMN |
185 | if (error < 0) { |
186 | error = -1; | |
9c82357b | 187 | goto cleanup; |
4376f7f6 | 188 | } |
9c82357b | 189 | |
24f2f94e CMN |
190 | if (download_tags_value(remote, config) < 0) |
191 | goto cleanup; | |
192 | ||
9c82357b CMN |
193 | *out = remote; |
194 | ||
195 | cleanup: | |
f0f3a18a | 196 | git_buf_free(&buf); |
9462c471 | 197 | |
4376f7f6 | 198 | if (error < 0) |
9c82357b CMN |
199 | git_remote_free(remote); |
200 | ||
201 | return error; | |
202 | } | |
203 | ||
e497b16c | 204 | static int ensure_remote_name_is_valid(const char *name) |
205 | { | |
206 | git_buf buf = GIT_BUF_INIT; | |
207 | git_refspec refspec; | |
208 | int error = -1; | |
209 | ||
210 | if (!name || *name == '\0') | |
211 | goto cleanup; | |
212 | ||
213 | git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", name); | |
214 | error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); | |
215 | ||
216 | git_buf_free(&buf); | |
217 | git_refspec__free(&refspec); | |
218 | ||
219 | cleanup: | |
220 | if (error) | |
221 | giterr_set( | |
222 | GITERR_CONFIG, | |
223 | "'%s' is not a valid remote name.", name); | |
224 | ||
225 | return error; | |
226 | } | |
227 | ||
4fe5520a | 228 | static int update_config_refspec( |
229 | git_config *config, | |
230 | const char *remote_name, | |
231 | const git_refspec *refspec, | |
232 | int git_direction) | |
233 | { | |
234 | git_buf name = GIT_BUF_INIT, value = GIT_BUF_INIT; | |
235 | int error = -1; | |
236 | ||
237 | if (refspec->src == NULL || refspec->dst == NULL) | |
238 | return 0; | |
239 | ||
fb39b3a5 | 240 | if (git_buf_printf( |
4fe5520a | 241 | &name, |
242 | "remote.%s.%s", | |
243 | remote_name, | |
fb39b3a5 | 244 | git_direction == GIT_DIR_FETCH ? "fetch" : "push") < 0) |
245 | goto cleanup; | |
4fe5520a | 246 | |
fb39b3a5 | 247 | if (git_refspec__serialize(&value, refspec) < 0) |
4fe5520a | 248 | goto cleanup; |
249 | ||
250 | error = git_config_set_string( | |
251 | config, | |
252 | git_buf_cstr(&name), | |
253 | git_buf_cstr(&value)); | |
254 | ||
255 | cleanup: | |
256 | git_buf_free(&name); | |
257 | git_buf_free(&value); | |
258 | ||
259 | return error; | |
260 | } | |
261 | ||
89e5ed98 CMN |
262 | int git_remote_save(const git_remote *remote) |
263 | { | |
218c88a9 | 264 | int error; |
89e5ed98 | 265 | git_config *config; |
218c88a9 | 266 | const char *tagopt = NULL; |
4fe5520a | 267 | git_buf buf = GIT_BUF_INIT; |
89e5ed98 | 268 | |
e497b16c | 269 | assert(remote); |
270 | ||
271 | if (ensure_remote_name_is_valid(remote->name) < 0) | |
272 | return -1; | |
89e5ed98 | 273 | |
4376f7f6 CMN |
274 | if (git_repository_config__weakptr(&config, remote->repo) < 0) |
275 | return -1; | |
89e5ed98 | 276 | |
cb020f0d | 277 | if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0) |
4376f7f6 | 278 | return -1; |
89e5ed98 | 279 | |
4376f7f6 CMN |
280 | if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) { |
281 | git_buf_free(&buf); | |
282 | return -1; | |
283 | } | |
89e5ed98 | 284 | |
413d5563 SC |
285 | git_buf_clear(&buf); |
286 | if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0) | |
287 | return -1; | |
3ed4b501 | 288 | |
413d5563 | 289 | if (remote->pushurl) { |
3ed4b501 SC |
290 | if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) { |
291 | git_buf_free(&buf); | |
292 | return -1; | |
293 | } | |
413d5563 SC |
294 | } else { |
295 | int error = git_config_delete(config, git_buf_cstr(&buf)); | |
296 | if (error == GIT_ENOTFOUND) { | |
297 | error = 0; | |
298 | } | |
299 | if (error < 0) { | |
300 | git_buf_free(&buf); | |
301 | return -1; | |
302 | } | |
3ed4b501 SC |
303 | } |
304 | ||
4fe5520a | 305 | if (update_config_refspec( |
306 | config, | |
307 | remote->name, | |
308 | &remote->fetch, | |
309 | GIT_DIR_FETCH) < 0) | |
4376f7f6 | 310 | goto on_error; |
89e5ed98 | 311 | |
4fe5520a | 312 | if (update_config_refspec( |
313 | config, | |
314 | remote->name, | |
315 | &remote->push, | |
316 | GIT_DIR_PUSH) < 0) | |
4376f7f6 | 317 | goto on_error; |
89e5ed98 | 318 | |
218c88a9 CMN |
319 | /* |
320 | * What action to take depends on the old and new values. This | |
321 | * is describes by the table below. tagopt means whether the | |
322 | * is already a value set in the config | |
323 | * | |
324 | * AUTO ALL or NONE | |
325 | * +-----------------------+ | |
326 | * tagopt | remove | set | | |
327 | * +---------+-------------| | |
328 | * !tagopt | nothing | set | | |
329 | * +---------+-------------+ | |
330 | */ | |
331 | ||
332 | git_buf_clear(&buf); | |
333 | if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) | |
334 | goto on_error; | |
335 | ||
336 | error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf)); | |
337 | if (error < 0 && error != GIT_ENOTFOUND) | |
338 | goto on_error; | |
339 | ||
340 | if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { | |
341 | if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0) | |
342 | goto on_error; | |
343 | } else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) { | |
344 | if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0) | |
345 | goto on_error; | |
346 | } else if (tagopt) { | |
347 | if (git_config_delete(config, git_buf_cstr(&buf)) < 0) | |
348 | goto on_error; | |
349 | } | |
350 | ||
89e5ed98 | 351 | git_buf_free(&buf); |
4376f7f6 CMN |
352 | |
353 | return 0; | |
354 | ||
355 | on_error: | |
356 | git_buf_free(&buf); | |
4376f7f6 | 357 | return -1; |
89e5ed98 CMN |
358 | } |
359 | ||
4bef3565 | 360 | const char *git_remote_name(git_remote *remote) |
9c82357b | 361 | { |
4bef3565 | 362 | assert(remote); |
9c82357b CMN |
363 | return remote->name; |
364 | } | |
365 | ||
4bef3565 | 366 | const char *git_remote_url(git_remote *remote) |
9c82357b | 367 | { |
4bef3565 | 368 | assert(remote); |
9c82357b CMN |
369 | return remote->url; |
370 | } | |
371 | ||
76501590 SC |
372 | int git_remote_set_url(git_remote *remote, const char* url) |
373 | { | |
374 | assert(remote); | |
375 | assert(url); | |
376 | ||
377 | git__free(remote->url); | |
378 | remote->url = git__strdup(url); | |
379 | GITERR_CHECK_ALLOC(remote->url); | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | const char *git_remote_pushurl(git_remote *remote) | |
385 | { | |
386 | assert(remote); | |
387 | return remote->pushurl; | |
388 | } | |
389 | ||
390 | int git_remote_set_pushurl(git_remote *remote, const char* url) | |
391 | { | |
392 | assert(remote); | |
393 | ||
394 | git__free(remote->pushurl); | |
395 | if (url) { | |
396 | remote->pushurl = git__strdup(url); | |
397 | GITERR_CHECK_ALLOC(remote->pushurl); | |
398 | } else { | |
399 | remote->pushurl = NULL; | |
400 | } | |
401 | return 0; | |
402 | } | |
403 | ||
bcb8c007 CMN |
404 | int git_remote_set_fetchspec(git_remote *remote, const char *spec) |
405 | { | |
bcb8c007 CMN |
406 | git_refspec refspec; |
407 | ||
408 | assert(remote && spec); | |
409 | ||
eb0bd77a | 410 | if (git_refspec__parse(&refspec, spec, true) < 0) |
4376f7f6 | 411 | return -1; |
bcb8c007 | 412 | |
eb0bd77a | 413 | git_refspec__free(&remote->fetch); |
bcb8c007 CMN |
414 | remote->fetch.src = refspec.src; |
415 | remote->fetch.dst = refspec.dst; | |
416 | ||
4376f7f6 | 417 | return 0; |
bcb8c007 CMN |
418 | } |
419 | ||
4bef3565 | 420 | const git_refspec *git_remote_fetchspec(git_remote *remote) |
9c82357b | 421 | { |
4bef3565 | 422 | assert(remote); |
9c82357b CMN |
423 | return &remote->fetch; |
424 | } | |
425 | ||
bcb8c007 CMN |
426 | int git_remote_set_pushspec(git_remote *remote, const char *spec) |
427 | { | |
bcb8c007 CMN |
428 | git_refspec refspec; |
429 | ||
430 | assert(remote && spec); | |
431 | ||
eb0bd77a | 432 | if (git_refspec__parse(&refspec, spec, false) < 0) |
4376f7f6 | 433 | return -1; |
bcb8c007 | 434 | |
eb0bd77a | 435 | git_refspec__free(&remote->push); |
bcb8c007 CMN |
436 | remote->push.src = refspec.src; |
437 | remote->push.dst = refspec.dst; | |
438 | ||
4376f7f6 | 439 | return 0; |
bcb8c007 CMN |
440 | } |
441 | ||
4bef3565 | 442 | const git_refspec *git_remote_pushspec(git_remote *remote) |
9c82357b | 443 | { |
4bef3565 | 444 | assert(remote); |
9c82357b CMN |
445 | return &remote->push; |
446 | } | |
447 | ||
eff5b499 SC |
448 | const char* git_remote__urlfordirection(git_remote *remote, int direction) |
449 | { | |
450 | assert(remote); | |
451 | ||
452 | if (direction == GIT_DIR_FETCH) { | |
453 | return remote->url; | |
454 | } | |
455 | ||
456 | if (direction == GIT_DIR_PUSH) { | |
457 | return remote->pushurl ? remote->pushurl : remote->url; | |
458 | } | |
459 | ||
460 | return NULL; | |
461 | } | |
462 | ||
0ac2726f | 463 | int git_remote_connect(git_remote *remote, int direction) |
9ba49bb5 | 464 | { |
9ba49bb5 | 465 | git_transport *t; |
c0c39025 | 466 | const char *url; |
9ba49bb5 | 467 | |
4bef3565 VM |
468 | assert(remote); |
469 | ||
c0c39025 | 470 | url = git_remote__urlfordirection(remote, direction); |
eff5b499 SC |
471 | if (url == NULL ) |
472 | return -1; | |
473 | ||
474 | if (git_transport_new(&t, url) < 0) | |
4376f7f6 | 475 | return -1; |
9ba49bb5 | 476 | |
e03e71da CMN |
477 | t->progress_cb = remote->callbacks.progress; |
478 | t->cb_data = remote->callbacks.data; | |
479 | ||
250b95b2 | 480 | t->check_cert = remote->check_cert; |
4376f7f6 CMN |
481 | if (t->connect(t, direction) < 0) { |
482 | goto on_error; | |
9ba49bb5 CMN |
483 | } |
484 | ||
485 | remote->transport = t; | |
486 | ||
4376f7f6 | 487 | return 0; |
9ba49bb5 | 488 | |
4376f7f6 CMN |
489 | on_error: |
490 | t->free(t); | |
491 | return -1; | |
9ba49bb5 CMN |
492 | } |
493 | ||
d88d4311 | 494 | int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) |
9ba49bb5 | 495 | { |
ad4b5beb CMN |
496 | git_vector *refs = &remote->transport->refs; |
497 | unsigned int i; | |
498 | git_pkt *p = NULL; | |
499 | ||
d88d4311 VM |
500 | assert(remote); |
501 | ||
4376f7f6 CMN |
502 | if (!remote->transport || !remote->transport->connected) { |
503 | giterr_set(GITERR_NET, "The remote is not connected"); | |
504 | return -1; | |
505 | } | |
d88d4311 | 506 | |
ad4b5beb CMN |
507 | git_vector_foreach(refs, i, p) { |
508 | git_pkt_ref *pkt = NULL; | |
509 | ||
510 | if (p->type != GIT_PKT_REF) | |
511 | continue; | |
512 | ||
513 | pkt = (git_pkt_ref *)p; | |
514 | ||
e4607392 | 515 | if (list_cb(&pkt->head, payload)) |
d8d28e2e | 516 | return GIT_EUSER; |
ad4b5beb CMN |
517 | } |
518 | ||
519 | return 0; | |
9ba49bb5 CMN |
520 | } |
521 | ||
216863c4 BS |
522 | int git_remote_download( |
523 | git_remote *remote, | |
7d222e13 | 524 | git_transfer_progress_callback progress_cb, |
216863c4 | 525 | void *progress_payload) |
48a65a07 | 526 | { |
95057b85 CMN |
527 | int error; |
528 | ||
1e3b8ed5 | 529 | assert(remote); |
4bef3565 | 530 | |
95057b85 | 531 | if ((error = git_fetch_negotiate(remote)) < 0) |
4376f7f6 | 532 | return error; |
95057b85 | 533 | |
1e3b8ed5 | 534 | return git_fetch_download_pack(remote, progress_cb, progress_payload); |
48a65a07 CMN |
535 | } |
536 | ||
b3aaa7a7 | 537 | int git_remote_update_tips(git_remote *remote) |
441f57c2 | 538 | { |
a37ddf7e | 539 | int error = 0, autotag; |
517bda19 | 540 | unsigned int i = 0; |
97769280 | 541 | git_buf refname = GIT_BUF_INIT; |
f184836b | 542 | git_oid old; |
a37ddf7e CMN |
543 | git_pkt *pkt; |
544 | git_odb *odb; | |
3f035860 | 545 | git_vector *refs; |
441f57c2 CMN |
546 | git_remote_head *head; |
547 | git_reference *ref; | |
3f035860 | 548 | struct git_refspec *spec; |
a37ddf7e | 549 | git_refspec tagspec; |
441f57c2 | 550 | |
4bef3565 VM |
551 | assert(remote); |
552 | ||
a37ddf7e | 553 | refs = &remote->transport->refs; |
3f035860 VM |
554 | spec = &remote->fetch; |
555 | ||
d88d4311 | 556 | if (refs->length == 0) |
e172cf08 | 557 | return 0; |
517bda19 | 558 | |
acd17006 | 559 | if (git_repository_odb__weakptr(&odb, remote->repo) < 0) |
a37ddf7e CMN |
560 | return -1; |
561 | ||
3230a44f | 562 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
a37ddf7e CMN |
563 | return -1; |
564 | ||
517bda19 | 565 | /* HEAD is only allowed to be the first in the list */ |
a37ddf7e CMN |
566 | pkt = refs->contents[0]; |
567 | head = &((git_pkt_ref *)pkt)->head; | |
517bda19 | 568 | if (!strcmp(head->name, GIT_HEAD_FILE)) { |
4376f7f6 CMN |
569 | if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0) |
570 | return -1; | |
585a2eb7 | 571 | |
4376f7f6 | 572 | i = 1; |
585a2eb7 | 573 | git_reference_free(ref); |
517bda19 CMN |
574 | } |
575 | ||
d88d4311 | 576 | for (; i < refs->length; ++i) { |
a37ddf7e | 577 | git_pkt *pkt = refs->contents[i]; |
9063be1f | 578 | autotag = 0; |
517bda19 | 579 | |
a37ddf7e CMN |
580 | if (pkt->type == GIT_PKT_REF) |
581 | head = &((git_pkt_ref *)pkt)->head; | |
582 | else | |
583 | continue; | |
584 | ||
585 | /* Ignore malformed ref names (which also saves us from tag^{} */ | |
586 | if (!git_reference_is_valid_name(head->name)) | |
587 | continue; | |
588 | ||
589 | if (git_refspec_src_matches(spec, head->name)) { | |
590 | if (git_refspec_transform_r(&refname, spec, head->name) < 0) | |
591 | goto on_error; | |
592 | } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { | |
3230a44f CMN |
593 | |
594 | if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL) | |
595 | autotag = 1; | |
a37ddf7e CMN |
596 | |
597 | if (!git_refspec_src_matches(&tagspec, head->name)) | |
598 | continue; | |
599 | ||
600 | git_buf_clear(&refname); | |
601 | if (git_buf_puts(&refname, head->name) < 0) | |
602 | goto on_error; | |
603 | } else { | |
604 | continue; | |
605 | } | |
606 | ||
607 | if (autotag && !git_odb_exists(odb, &head->oid)) | |
608 | continue; | |
f184836b CMN |
609 | |
610 | error = git_reference_name_to_oid(&old, remote->repo, refname.ptr); | |
904b67e6 | 611 | if (error < 0 && error != GIT_ENOTFOUND) |
f184836b CMN |
612 | goto on_error; |
613 | ||
904b67e6 | 614 | if (error == GIT_ENOTFOUND) |
f184836b CMN |
615 | memset(&old, 0, GIT_OID_RAWSZ); |
616 | ||
617 | if (!git_oid_cmp(&old, &head->oid)) | |
618 | continue; | |
441f57c2 | 619 | |
a37ddf7e CMN |
620 | /* In autotag mode, don't overwrite any locally-existing tags */ |
621 | error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, !autotag); | |
622 | if (error < 0 && error != GIT_EEXISTS) | |
944d250f | 623 | goto on_error; |
39157563 CMN |
624 | |
625 | git_reference_free(ref); | |
f184836b | 626 | |
b3aaa7a7 CMN |
627 | if (remote->callbacks.update_tips != NULL) { |
628 | if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.data) < 0) | |
f184836b CMN |
629 | goto on_error; |
630 | } | |
441f57c2 CMN |
631 | } |
632 | ||
a37ddf7e | 633 | git_refspec__free(&tagspec); |
97769280 | 634 | git_buf_free(&refname); |
f184836b CMN |
635 | return 0; |
636 | ||
637 | on_error: | |
a37ddf7e | 638 | git_refspec__free(&tagspec); |
f184836b CMN |
639 | git_buf_free(&refname); |
640 | return -1; | |
97769280 | 641 | |
441f57c2 CMN |
642 | } |
643 | ||
6ac3b707 CMN |
644 | int git_remote_connected(git_remote *remote) |
645 | { | |
4bef3565 | 646 | assert(remote); |
6ac3b707 CMN |
647 | return remote->transport == NULL ? 0 : remote->transport->connected; |
648 | } | |
649 | ||
f0d2ddbb CMN |
650 | void git_remote_stop(git_remote *remote) |
651 | { | |
652 | git_atomic_set(&remote->transport->cancel, 1); | |
653 | } | |
654 | ||
4cf01e9a CMN |
655 | void git_remote_disconnect(git_remote *remote) |
656 | { | |
4bef3565 VM |
657 | assert(remote); |
658 | ||
42ea35c0 | 659 | if (remote->transport != NULL && remote->transport->connected) |
4cf01e9a | 660 | remote->transport->close(remote->transport); |
4cf01e9a CMN |
661 | } |
662 | ||
9c82357b CMN |
663 | void git_remote_free(git_remote *remote) |
664 | { | |
2aae2188 CMN |
665 | if (remote == NULL) |
666 | return; | |
667 | ||
42ea35c0 MS |
668 | if (remote->transport != NULL) { |
669 | git_remote_disconnect(remote); | |
670 | ||
671 | remote->transport->free(remote->transport); | |
672 | remote->transport = NULL; | |
673 | } | |
674 | ||
675 | git_vector_free(&remote->refs); | |
676 | ||
3665ba8e CMN |
677 | git_refspec__free(&remote->fetch); |
678 | git_refspec__free(&remote->push); | |
3286c408 | 679 | git__free(remote->url); |
3ed4b501 | 680 | git__free(remote->pushurl); |
3286c408 | 681 | git__free(remote->name); |
3286c408 | 682 | git__free(remote); |
9c82357b | 683 | } |
8171998f CMN |
684 | |
685 | struct cb_data { | |
686 | git_vector *list; | |
687 | regex_t *preg; | |
688 | }; | |
689 | ||
a1abe66a | 690 | static int remote_list_cb(const git_config_entry *entry, void *data_) |
8171998f CMN |
691 | { |
692 | struct cb_data *data = (struct cb_data *)data_; | |
693 | size_t nmatch = 2; | |
694 | regmatch_t pmatch[2]; | |
a1abe66a | 695 | const char *name = entry->name; |
8171998f CMN |
696 | |
697 | if (!regexec(data->preg, name, nmatch, pmatch, 0)) { | |
698 | char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so); | |
4376f7f6 | 699 | GITERR_CHECK_ALLOC(remote_name); |
8171998f | 700 | |
4376f7f6 CMN |
701 | if (git_vector_insert(data->list, remote_name) < 0) |
702 | return -1; | |
8171998f CMN |
703 | } |
704 | ||
4376f7f6 | 705 | return 0; |
8171998f CMN |
706 | } |
707 | ||
708 | int git_remote_list(git_strarray *remotes_list, git_repository *repo) | |
709 | { | |
710 | git_config *cfg; | |
711 | git_vector list; | |
712 | regex_t preg; | |
713 | struct cb_data data; | |
714 | int error; | |
715 | ||
4376f7f6 CMN |
716 | if (git_repository_config__weakptr(&cfg, repo) < 0) |
717 | return -1; | |
8171998f | 718 | |
4376f7f6 CMN |
719 | if (git_vector_init(&list, 4, NULL) < 0) |
720 | return -1; | |
8171998f | 721 | |
4376f7f6 CMN |
722 | if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) { |
723 | giterr_set(GITERR_OS, "Remote catch regex failed to compile"); | |
724 | return -1; | |
725 | } | |
8171998f CMN |
726 | |
727 | data.list = &list; | |
728 | data.preg = &preg; | |
729 | error = git_config_foreach(cfg, remote_list_cb, &data); | |
730 | regfree(&preg); | |
4376f7f6 | 731 | if (error < 0) { |
8171998f CMN |
732 | size_t i; |
733 | char *elem; | |
734 | git_vector_foreach(&list, i, elem) { | |
2bc8fa02 | 735 | git__free(elem); |
8171998f CMN |
736 | } |
737 | ||
738 | git_vector_free(&list); | |
e4607392 RB |
739 | |
740 | /* cb error is converted to GIT_EUSER by git_config_foreach */ | |
741 | if (error == GIT_EUSER) | |
742 | error = -1; | |
743 | ||
8171998f CMN |
744 | return error; |
745 | } | |
746 | ||
747 | remotes_list->strings = (char **)list.contents; | |
748 | remotes_list->count = list.length; | |
749 | ||
4376f7f6 | 750 | return 0; |
8171998f | 751 | } |
a209a025 CMN |
752 | |
753 | int git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url) | |
754 | { | |
755 | git_buf buf = GIT_BUF_INIT; | |
a209a025 | 756 | |
d27bf665 | 757 | if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) |
baaa8a44 | 758 | return -1; |
a209a025 | 759 | |
baaa8a44 | 760 | if (git_remote_new(out, repo, name, url, git_buf_cstr(&buf)) < 0) |
a209a025 CMN |
761 | goto on_error; |
762 | ||
763 | git_buf_free(&buf); | |
764 | ||
765 | if (git_remote_save(*out) < 0) | |
baaa8a44 | 766 | goto on_error; |
a209a025 CMN |
767 | |
768 | return 0; | |
769 | ||
770 | on_error: | |
771 | git_buf_free(&buf); | |
772 | git_remote_free(*out); | |
773 | return -1; | |
774 | } | |
250b95b2 CMN |
775 | |
776 | void git_remote_check_cert(git_remote *remote, int check) | |
777 | { | |
778 | assert(remote); | |
779 | ||
780 | remote->check_cert = check; | |
781 | } | |
b3aaa7a7 CMN |
782 | |
783 | void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks) | |
784 | { | |
785 | assert(remote && callbacks); | |
786 | ||
787 | memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks)); | |
e03e71da CMN |
788 | |
789 | if (remote->transport) { | |
790 | remote->transport->progress_cb = remote->callbacks.progress; | |
791 | remote->transport->cb_data = remote->callbacks.data; | |
792 | } | |
b3aaa7a7 | 793 | } |
f70e466f | 794 | |
67dad09b | 795 | const git_transfer_progress* git_remote_stats(git_remote *remote) |
d57c47dc BS |
796 | { |
797 | assert(remote); | |
798 | return &remote->stats; | |
799 | } | |
800 | ||
f70e466f CMN |
801 | int git_remote_autotag(git_remote *remote) |
802 | { | |
803 | return remote->download_tags; | |
804 | } | |
805 | ||
806 | void git_remote_set_autotag(git_remote *remote, int value) | |
807 | { | |
808 | remote->download_tags = value; | |
809 | } | |
fcccf304 | 810 | |
811 | static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) | |
812 | { | |
813 | int error; | |
814 | git_remote *remote; | |
815 | ||
816 | error = git_remote_load(&remote, repo, name); | |
817 | ||
818 | if (error == GIT_ENOTFOUND) | |
819 | return 0; | |
820 | ||
821 | if (error < 0) | |
822 | return error; | |
823 | ||
824 | git_remote_free(remote); | |
825 | ||
826 | giterr_set( | |
827 | GITERR_CONFIG, | |
828 | "Remote '%s' already exists.", name); | |
829 | ||
830 | return GIT_EEXISTS; | |
831 | } | |
832 | ||
833 | static int rename_remote_config_section( | |
834 | git_repository *repo, | |
835 | const char *old_name, | |
836 | const char *new_name) | |
837 | { | |
838 | git_buf old_section_name = GIT_BUF_INIT, | |
839 | new_section_name = GIT_BUF_INIT; | |
840 | int error = -1; | |
841 | ||
842 | if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) | |
843 | goto cleanup; | |
844 | ||
845 | if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0) | |
846 | goto cleanup; | |
847 | ||
848 | error = git_config_rename_section( | |
849 | repo, | |
850 | git_buf_cstr(&old_section_name), | |
851 | git_buf_cstr(&new_section_name)); | |
852 | ||
853 | cleanup: | |
854 | git_buf_free(&old_section_name); | |
855 | git_buf_free(&new_section_name); | |
856 | ||
857 | return error; | |
858 | } | |
859 | ||
860 | struct update_data | |
861 | { | |
862 | git_config *config; | |
863 | const char *old_remote_name; | |
864 | const char *new_remote_name; | |
865 | }; | |
866 | ||
867 | static int update_config_entries_cb( | |
868 | const git_config_entry *entry, | |
869 | void *payload) | |
870 | { | |
871 | struct update_data *data = (struct update_data *)payload; | |
872 | ||
873 | if (strcmp(entry->value, data->old_remote_name)) | |
874 | return 0; | |
875 | ||
876 | return git_config_set_string( | |
877 | data->config, | |
878 | entry->name, | |
879 | data->new_remote_name); | |
880 | } | |
881 | ||
882 | static int update_branch_remote_config_entry( | |
883 | git_repository *repo, | |
884 | const char *old_name, | |
885 | const char *new_name) | |
886 | { | |
887 | git_config *config; | |
888 | struct update_data data; | |
889 | ||
890 | if (git_repository_config__weakptr(&config, repo) < 0) | |
891 | return -1; | |
892 | ||
893 | data.config = config; | |
894 | data.old_remote_name = old_name; | |
895 | data.new_remote_name = new_name; | |
896 | ||
897 | return git_config_foreach_match( | |
898 | config, | |
899 | "branch\\..+\\.remote", | |
900 | update_config_entries_cb, &data); | |
901 | } | |
902 | ||
903 | static int rename_cb(const char *ref, void *data) | |
904 | { | |
905 | if (git__prefixcmp(ref, GIT_REFS_REMOTES_DIR)) | |
906 | return 0; | |
907 | ||
908 | return git_vector_insert((git_vector *)data, git__strdup(ref)); | |
909 | } | |
910 | ||
911 | static int rename_one_remote_reference( | |
912 | git_repository *repo, | |
913 | const char *reference_name, | |
914 | const char *old_remote_name, | |
915 | const char *new_remote_name) | |
916 | { | |
917 | int error; | |
918 | git_buf new_name = GIT_BUF_INIT; | |
919 | git_reference *reference = NULL; | |
920 | ||
921 | if (git_buf_printf( | |
922 | &new_name, | |
923 | GIT_REFS_REMOTES_DIR "%s%s", | |
924 | new_remote_name, | |
925 | reference_name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0) | |
926 | return -1; | |
927 | ||
928 | if (git_reference_lookup(&reference, repo, reference_name) < 0) | |
929 | goto cleanup; | |
930 | ||
931 | error = git_reference_rename(reference, git_buf_cstr(&new_name), 0); | |
932 | ||
933 | cleanup: | |
934 | git_reference_free(reference); | |
935 | git_buf_free(&new_name); | |
936 | return error; | |
937 | } | |
938 | ||
939 | static int rename_remote_references( | |
940 | git_repository *repo, | |
941 | const char *old_name, | |
942 | const char *new_name) | |
943 | { | |
944 | git_vector refnames; | |
945 | int error = -1; | |
946 | unsigned int i; | |
947 | char *name; | |
948 | ||
949 | if (git_vector_init(&refnames, 8, NULL) < 0) | |
950 | goto cleanup; | |
951 | ||
952 | if (git_reference_foreach( | |
953 | repo, | |
954 | GIT_REF_LISTALL, | |
955 | rename_cb, | |
956 | &refnames) < 0) | |
957 | goto cleanup; | |
958 | ||
959 | git_vector_foreach(&refnames, i, name) { | |
960 | if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0) | |
961 | goto cleanup; | |
962 | } | |
963 | ||
964 | error = 0; | |
965 | cleanup: | |
966 | git_vector_foreach(&refnames, i, name) { | |
967 | git__free(name); | |
968 | } | |
969 | ||
970 | git_vector_free(&refnames); | |
971 | return error; | |
972 | } | |
973 | ||
974 | static int rename_fetch_refspecs( | |
975 | git_remote *remote, | |
976 | const char *new_name, | |
977 | int (*callback)(const char *problematic_refspec, void *payload), | |
978 | void *payload) | |
979 | { | |
980 | git_config *config; | |
981 | const git_refspec *fetch_refspec; | |
982 | git_buf dst_prefix = GIT_BUF_INIT, serialized = GIT_BUF_INIT; | |
983 | const char* pos; | |
984 | int error = -1; | |
985 | ||
986 | fetch_refspec = git_remote_fetchspec(remote); | |
987 | ||
988 | /* Is there a refspec to deal with? */ | |
989 | if (fetch_refspec->src == NULL && | |
990 | fetch_refspec->dst == NULL) | |
991 | return 0; | |
992 | ||
993 | if (git_refspec__serialize(&serialized, fetch_refspec) < 0) | |
994 | goto cleanup; | |
995 | ||
996 | /* Is it an in-memory remote? */ | |
997 | if (remote->name == '\0') { | |
998 | error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0; | |
999 | goto cleanup; | |
1000 | } | |
1001 | ||
1002 | if (git_buf_printf(&dst_prefix, ":refs/remotes/%s/", remote->name) < 0) | |
1003 | goto cleanup; | |
1004 | ||
1005 | pos = strstr(git_buf_cstr(&serialized), git_buf_cstr(&dst_prefix)); | |
1006 | ||
1007 | /* Does the dst part of the refspec follow the extected standard format? */ | |
1008 | if (!pos) { | |
1009 | error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0; | |
1010 | goto cleanup; | |
1011 | } | |
1012 | ||
1013 | if (git_buf_splice( | |
1014 | &serialized, | |
1015 | pos - git_buf_cstr(&serialized) + strlen(":refs/remotes/"), | |
1016 | strlen(remote->name), new_name, | |
1017 | strlen(new_name)) < 0) | |
1018 | goto cleanup; | |
1019 | ||
1020 | git_refspec__free(&remote->fetch); | |
1021 | ||
1022 | if (git_refspec__parse(&remote->fetch, git_buf_cstr(&serialized), true) < 0) | |
1023 | goto cleanup; | |
1024 | ||
1025 | if (git_repository_config__weakptr(&config, remote->repo) < 0) | |
1026 | goto cleanup; | |
1027 | ||
1028 | error = update_config_refspec(config, new_name, &remote->fetch, GIT_DIR_FETCH); | |
1029 | ||
1030 | cleanup: | |
1031 | git_buf_free(&serialized); | |
1032 | git_buf_free(&dst_prefix); | |
1033 | return error; | |
1034 | } | |
1035 | ||
1036 | int git_remote_rename( | |
1037 | git_remote *remote, | |
1038 | const char *new_name, | |
1039 | int (*callback)(const char *problematic_refspec, void *payload), | |
1040 | void *payload) | |
1041 | { | |
1042 | int error; | |
1043 | ||
1044 | assert(remote && new_name); | |
1045 | ||
1046 | if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0) | |
1047 | return error; | |
1048 | ||
1049 | if ((error = ensure_remote_name_is_valid(new_name)) < 0) | |
1050 | return error; | |
1051 | ||
1052 | if (!remote->name) { | |
1053 | if ((error = rename_fetch_refspecs( | |
1054 | remote, | |
1055 | new_name, | |
1056 | callback, | |
1057 | payload)) < 0) | |
1058 | return error; | |
1059 | ||
1060 | remote->name = git__strdup(new_name); | |
1061 | ||
1062 | return git_remote_save(remote); | |
1063 | } | |
1064 | ||
1065 | if ((error = rename_remote_config_section( | |
1066 | remote->repo, | |
1067 | remote->name, | |
1068 | new_name)) < 0) | |
1069 | return error; | |
1070 | ||
1071 | if ((error = update_branch_remote_config_entry( | |
1072 | remote->repo, | |
1073 | remote->name, | |
1074 | new_name)) < 0) | |
1075 | return error; | |
1076 | ||
1077 | if ((error = rename_remote_references( | |
1078 | remote->repo, | |
1079 | remote->name, | |
1080 | new_name)) < 0) | |
1081 | return error; | |
1082 | ||
1083 | if ((error = rename_fetch_refspecs( | |
1084 | remote, | |
1085 | new_name, | |
1086 | callback, | |
1087 | payload)) < 0) | |
1088 | return error; | |
1089 | ||
1090 | git__free(remote->name); | |
1091 | remote->name = git__strdup(new_name); | |
1092 | ||
1093 | return 0; | |
1094 | } |