]>
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 | ||
8 | #include "git2/remote.h" | |
9 | #include "git2/config.h" | |
10 | #include "git2/types.h" | |
11 | ||
12 | #include "config.h" | |
13 | #include "repository.h" | |
14 | #include "remote.h" | |
e1d88030 | 15 | #include "fetch.h" |
441f57c2 | 16 | #include "refs.h" |
9c82357b | 17 | |
8171998f CMN |
18 | #include <regex.h> |
19 | ||
9c82357b CMN |
20 | static int refspec_parse(git_refspec *refspec, const char *str) |
21 | { | |
22 | char *delim; | |
23 | ||
24 | memset(refspec, 0x0, sizeof(git_refspec)); | |
25 | ||
26 | if (*str == '+') { | |
27 | refspec->force = 1; | |
28 | str++; | |
29 | } | |
30 | ||
31 | delim = strchr(str, ':'); | |
4376f7f6 CMN |
32 | if (delim == NULL) { |
33 | giterr_set(GITERR_NET, "Invalid refspec, missing ':'"); | |
34 | return -1; | |
35 | } | |
9c82357b CMN |
36 | |
37 | refspec->src = git__strndup(str, delim - str); | |
4376f7f6 | 38 | GITERR_CHECK_ALLOC(refspec->src); |
9c82357b CMN |
39 | |
40 | refspec->dst = git__strdup(delim + 1); | |
4376f7f6 | 41 | GITERR_CHECK_ALLOC(refspec->dst); |
9c82357b | 42 | |
4376f7f6 | 43 | return 0; |
9c82357b CMN |
44 | } |
45 | ||
46 | static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var) | |
47 | { | |
9c82357b | 48 | int error; |
4376f7f6 | 49 | const char *val; |
9c82357b | 50 | |
29e948de | 51 | if ((error = git_config_get_string(&val, cfg, var)) < 0) |
9c82357b CMN |
52 | return error; |
53 | ||
2dc31040 | 54 | return refspec_parse(refspec, val); |
9c82357b CMN |
55 | } |
56 | ||
baaa8a44 | 57 | int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
778e1c73 CMN |
58 | { |
59 | git_remote *remote; | |
60 | ||
4bef3565 VM |
61 | /* name is optional */ |
62 | assert(out && repo && url); | |
617bfdf4 | 63 | |
778e1c73 | 64 | remote = git__malloc(sizeof(git_remote)); |
4376f7f6 | 65 | GITERR_CHECK_ALLOC(remote); |
778e1c73 CMN |
66 | |
67 | memset(remote, 0x0, sizeof(git_remote)); | |
68 | remote->repo = repo; | |
250b95b2 | 69 | remote->check_cert = 1; |
617bfdf4 | 70 | |
4376f7f6 CMN |
71 | if (git_vector_init(&remote->refs, 32, NULL) < 0) |
72 | return -1; | |
d88d4311 | 73 | |
778e1c73 | 74 | remote->url = git__strdup(url); |
4376f7f6 | 75 | GITERR_CHECK_ALLOC(remote->url); |
778e1c73 | 76 | |
617bfdf4 CMN |
77 | if (name != NULL) { |
78 | remote->name = git__strdup(name); | |
4376f7f6 | 79 | GITERR_CHECK_ALLOC(remote->name); |
617bfdf4 CMN |
80 | } |
81 | ||
baaa8a44 CMN |
82 | if (fetch != NULL) { |
83 | if (refspec_parse(&remote->fetch, fetch) < 0) | |
84 | goto on_error; | |
85 | } | |
86 | ||
778e1c73 | 87 | *out = remote; |
4376f7f6 | 88 | return 0; |
baaa8a44 CMN |
89 | |
90 | on_error: | |
91 | git_remote_free(remote); | |
92 | return -1; | |
778e1c73 CMN |
93 | } |
94 | ||
9462c471 | 95 | int git_remote_load(git_remote **out, git_repository *repo, const char *name) |
9c82357b CMN |
96 | { |
97 | git_remote *remote; | |
f0f3a18a | 98 | git_buf buf = GIT_BUF_INIT; |
9c82357b | 99 | const char *val; |
4376f7f6 | 100 | int error = 0; |
9462c471 | 101 | git_config *config; |
9c82357b | 102 | |
9462c471 VM |
103 | assert(out && repo && name); |
104 | ||
4376f7f6 CMN |
105 | if (git_repository_config__weakptr(&config, repo) < 0) |
106 | return -1; | |
4bef3565 | 107 | |
9c82357b | 108 | remote = git__malloc(sizeof(git_remote)); |
4376f7f6 | 109 | GITERR_CHECK_ALLOC(remote); |
9c82357b CMN |
110 | |
111 | memset(remote, 0x0, sizeof(git_remote)); | |
250b95b2 | 112 | remote->check_cert = 1; |
9c82357b | 113 | remote->name = git__strdup(name); |
4376f7f6 | 114 | GITERR_CHECK_ALLOC(remote->name); |
9c82357b | 115 | |
2fb9d6de | 116 | if (git_vector_init(&remote->refs, 32, NULL) < 0) { |
117 | error = -1; | |
118 | goto cleanup; | |
119 | } | |
d88d4311 | 120 | |
2fb9d6de | 121 | if (git_buf_printf(&buf, "remote.%s.url", name) < 0) { |
122 | error = -1; | |
123 | goto cleanup; | |
124 | } | |
9c82357b | 125 | |
29e948de | 126 | if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0) |
9c82357b | 127 | goto cleanup; |
9c82357b | 128 | |
9462c471 | 129 | remote->repo = repo; |
9c82357b | 130 | remote->url = git__strdup(val); |
4376f7f6 | 131 | GITERR_CHECK_ALLOC(remote->url); |
9c82357b | 132 | |
f0f3a18a | 133 | git_buf_clear(&buf); |
2fb9d6de | 134 | if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) { |
135 | error = -1; | |
136 | goto cleanup; | |
137 | } | |
9c82357b | 138 | |
f0f3a18a | 139 | error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf)); |
904b67e6 | 140 | if (error == GIT_ENOTFOUND) |
4376f7f6 | 141 | error = 0; |
9554cd51 | 142 | |
4376f7f6 CMN |
143 | if (error < 0) { |
144 | error = -1; | |
9c82357b | 145 | goto cleanup; |
2dc31040 | 146 | } |
9c82357b | 147 | |
f0f3a18a | 148 | git_buf_clear(&buf); |
2fb9d6de | 149 | if (git_buf_printf(&buf, "remote.%s.push", name) < 0) { |
150 | error = -1; | |
151 | goto cleanup; | |
152 | } | |
9c82357b | 153 | |
f0f3a18a | 154 | error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf)); |
904b67e6 | 155 | if (error == GIT_ENOTFOUND) |
4376f7f6 | 156 | error = 0; |
9c82357b | 157 | |
4376f7f6 CMN |
158 | if (error < 0) { |
159 | error = -1; | |
9c82357b | 160 | goto cleanup; |
4376f7f6 | 161 | } |
9c82357b CMN |
162 | |
163 | *out = remote; | |
164 | ||
165 | cleanup: | |
f0f3a18a | 166 | git_buf_free(&buf); |
9462c471 | 167 | |
4376f7f6 | 168 | if (error < 0) |
9c82357b CMN |
169 | git_remote_free(remote); |
170 | ||
171 | return error; | |
172 | } | |
173 | ||
89e5ed98 CMN |
174 | int git_remote_save(const git_remote *remote) |
175 | { | |
89e5ed98 CMN |
176 | git_config *config; |
177 | git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT; | |
178 | ||
4376f7f6 CMN |
179 | if (git_repository_config__weakptr(&config, remote->repo) < 0) |
180 | return -1; | |
89e5ed98 | 181 | |
cb020f0d | 182 | if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0) |
4376f7f6 | 183 | return -1; |
89e5ed98 | 184 | |
4376f7f6 CMN |
185 | if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) { |
186 | git_buf_free(&buf); | |
187 | return -1; | |
188 | } | |
89e5ed98 | 189 | |
9c94a356 | 190 | if (remote->fetch.src != NULL && remote->fetch.dst != NULL) { |
89e5ed98 CMN |
191 | git_buf_clear(&buf); |
192 | git_buf_clear(&value); | |
4376f7f6 | 193 | git_buf_printf(&buf, "remote.%s.fetch", remote->name); |
d05e2c64 | 194 | if (remote->fetch.force) |
195 | git_buf_putc(&value, '+'); | |
89e5ed98 CMN |
196 | git_buf_printf(&value, "%s:%s", remote->fetch.src, remote->fetch.dst); |
197 | if (git_buf_oom(&buf) || git_buf_oom(&value)) | |
4376f7f6 | 198 | return -1; |
89e5ed98 | 199 | |
4376f7f6 CMN |
200 | if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0) |
201 | goto on_error; | |
89e5ed98 CMN |
202 | } |
203 | ||
9c94a356 | 204 | if (remote->push.src != NULL && remote->push.dst != NULL) { |
89e5ed98 CMN |
205 | git_buf_clear(&buf); |
206 | git_buf_clear(&value); | |
4376f7f6 | 207 | git_buf_printf(&buf, "remote.%s.push", remote->name); |
d05e2c64 | 208 | if (remote->push.force) |
209 | git_buf_putc(&value, '+'); | |
89e5ed98 CMN |
210 | git_buf_printf(&value, "%s:%s", remote->push.src, remote->push.dst); |
211 | if (git_buf_oom(&buf) || git_buf_oom(&value)) | |
4376f7f6 | 212 | return -1; |
89e5ed98 | 213 | |
4376f7f6 CMN |
214 | if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0) |
215 | goto on_error; | |
89e5ed98 CMN |
216 | } |
217 | ||
89e5ed98 CMN |
218 | git_buf_free(&buf); |
219 | git_buf_free(&value); | |
4376f7f6 CMN |
220 | |
221 | return 0; | |
222 | ||
223 | on_error: | |
224 | git_buf_free(&buf); | |
225 | git_buf_free(&value); | |
226 | return -1; | |
89e5ed98 CMN |
227 | } |
228 | ||
4bef3565 | 229 | const char *git_remote_name(git_remote *remote) |
9c82357b | 230 | { |
4bef3565 | 231 | assert(remote); |
9c82357b CMN |
232 | return remote->name; |
233 | } | |
234 | ||
4bef3565 | 235 | const char *git_remote_url(git_remote *remote) |
9c82357b | 236 | { |
4bef3565 | 237 | assert(remote); |
9c82357b CMN |
238 | return remote->url; |
239 | } | |
240 | ||
bcb8c007 CMN |
241 | int git_remote_set_fetchspec(git_remote *remote, const char *spec) |
242 | { | |
bcb8c007 CMN |
243 | git_refspec refspec; |
244 | ||
245 | assert(remote && spec); | |
246 | ||
4376f7f6 CMN |
247 | if (refspec_parse(&refspec, spec) < 0) |
248 | return -1; | |
bcb8c007 CMN |
249 | |
250 | git__free(remote->fetch.src); | |
251 | git__free(remote->fetch.dst); | |
252 | remote->fetch.src = refspec.src; | |
253 | remote->fetch.dst = refspec.dst; | |
254 | ||
4376f7f6 | 255 | return 0; |
bcb8c007 CMN |
256 | } |
257 | ||
4bef3565 | 258 | const git_refspec *git_remote_fetchspec(git_remote *remote) |
9c82357b | 259 | { |
4bef3565 | 260 | assert(remote); |
9c82357b CMN |
261 | return &remote->fetch; |
262 | } | |
263 | ||
bcb8c007 CMN |
264 | int git_remote_set_pushspec(git_remote *remote, const char *spec) |
265 | { | |
bcb8c007 CMN |
266 | git_refspec refspec; |
267 | ||
268 | assert(remote && spec); | |
269 | ||
4376f7f6 CMN |
270 | if (refspec_parse(&refspec, spec) < 0) |
271 | return -1; | |
bcb8c007 CMN |
272 | |
273 | git__free(remote->push.src); | |
274 | git__free(remote->push.dst); | |
275 | remote->push.src = refspec.src; | |
276 | remote->push.dst = refspec.dst; | |
277 | ||
4376f7f6 | 278 | return 0; |
bcb8c007 CMN |
279 | } |
280 | ||
4bef3565 | 281 | const git_refspec *git_remote_pushspec(git_remote *remote) |
9c82357b | 282 | { |
4bef3565 | 283 | assert(remote); |
9c82357b CMN |
284 | return &remote->push; |
285 | } | |
286 | ||
0ac2726f | 287 | int git_remote_connect(git_remote *remote, int direction) |
9ba49bb5 | 288 | { |
9ba49bb5 CMN |
289 | git_transport *t; |
290 | ||
4bef3565 VM |
291 | assert(remote); |
292 | ||
4376f7f6 CMN |
293 | if (git_transport_new(&t, remote->url) < 0) |
294 | return -1; | |
9ba49bb5 | 295 | |
250b95b2 | 296 | t->check_cert = remote->check_cert; |
4376f7f6 CMN |
297 | if (t->connect(t, direction) < 0) { |
298 | goto on_error; | |
9ba49bb5 CMN |
299 | } |
300 | ||
301 | remote->transport = t; | |
302 | ||
4376f7f6 | 303 | return 0; |
9ba49bb5 | 304 | |
4376f7f6 CMN |
305 | on_error: |
306 | t->free(t); | |
307 | return -1; | |
9ba49bb5 CMN |
308 | } |
309 | ||
d88d4311 | 310 | int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) |
9ba49bb5 | 311 | { |
d88d4311 VM |
312 | assert(remote); |
313 | ||
4376f7f6 CMN |
314 | if (!remote->transport || !remote->transport->connected) { |
315 | giterr_set(GITERR_NET, "The remote is not connected"); | |
316 | return -1; | |
317 | } | |
d88d4311 VM |
318 | |
319 | return remote->transport->ls(remote->transport, list_cb, payload); | |
9ba49bb5 CMN |
320 | } |
321 | ||
7a520f5d | 322 | int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats) |
48a65a07 | 323 | { |
95057b85 CMN |
324 | int error; |
325 | ||
7a520f5d | 326 | assert(remote && bytes && stats); |
4bef3565 | 327 | |
95057b85 | 328 | if ((error = git_fetch_negotiate(remote)) < 0) |
4376f7f6 | 329 | return error; |
95057b85 | 330 | |
7a520f5d | 331 | return git_fetch_download_pack(remote, bytes, stats); |
48a65a07 CMN |
332 | } |
333 | ||
f184836b | 334 | int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b)) |
441f57c2 | 335 | { |
4376f7f6 | 336 | int error = 0; |
517bda19 | 337 | unsigned int i = 0; |
97769280 | 338 | git_buf refname = GIT_BUF_INIT; |
f184836b | 339 | git_oid old; |
3f035860 | 340 | git_vector *refs; |
441f57c2 CMN |
341 | git_remote_head *head; |
342 | git_reference *ref; | |
3f035860 | 343 | struct git_refspec *spec; |
441f57c2 | 344 | |
4bef3565 VM |
345 | assert(remote); |
346 | ||
3f035860 VM |
347 | refs = &remote->refs; |
348 | spec = &remote->fetch; | |
349 | ||
d88d4311 | 350 | if (refs->length == 0) |
e172cf08 | 351 | return 0; |
517bda19 CMN |
352 | |
353 | /* HEAD is only allowed to be the first in the list */ | |
d88d4311 | 354 | head = refs->contents[0]; |
517bda19 | 355 | if (!strcmp(head->name, GIT_HEAD_FILE)) { |
4376f7f6 CMN |
356 | if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0) |
357 | return -1; | |
585a2eb7 | 358 | |
4376f7f6 | 359 | i = 1; |
585a2eb7 | 360 | git_reference_free(ref); |
517bda19 CMN |
361 | } |
362 | ||
d88d4311 VM |
363 | for (; i < refs->length; ++i) { |
364 | head = refs->contents[i]; | |
517bda19 | 365 | |
4376f7f6 | 366 | if (git_refspec_transform_r(&refname, spec, head->name) < 0) |
f184836b CMN |
367 | goto on_error; |
368 | ||
369 | error = git_reference_name_to_oid(&old, remote->repo, refname.ptr); | |
904b67e6 | 370 | if (error < 0 && error != GIT_ENOTFOUND) |
f184836b CMN |
371 | goto on_error; |
372 | ||
904b67e6 | 373 | if (error == GIT_ENOTFOUND) |
f184836b CMN |
374 | memset(&old, 0, GIT_OID_RAWSZ); |
375 | ||
376 | if (!git_oid_cmp(&old, &head->oid)) | |
377 | continue; | |
441f57c2 | 378 | |
4376f7f6 | 379 | if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0) |
944d250f | 380 | goto on_error; |
39157563 CMN |
381 | |
382 | git_reference_free(ref); | |
f184836b CMN |
383 | |
384 | if (cb != NULL) { | |
385 | if (cb(refname.ptr, &old, &head->oid) < 0) | |
386 | goto on_error; | |
387 | } | |
441f57c2 CMN |
388 | } |
389 | ||
97769280 | 390 | git_buf_free(&refname); |
f184836b CMN |
391 | return 0; |
392 | ||
393 | on_error: | |
394 | git_buf_free(&refname); | |
395 | return -1; | |
97769280 | 396 | |
441f57c2 CMN |
397 | } |
398 | ||
6ac3b707 CMN |
399 | int git_remote_connected(git_remote *remote) |
400 | { | |
4bef3565 | 401 | assert(remote); |
6ac3b707 CMN |
402 | return remote->transport == NULL ? 0 : remote->transport->connected; |
403 | } | |
404 | ||
4cf01e9a CMN |
405 | void git_remote_disconnect(git_remote *remote) |
406 | { | |
4bef3565 VM |
407 | assert(remote); |
408 | ||
42ea35c0 | 409 | if (remote->transport != NULL && remote->transport->connected) |
4cf01e9a | 410 | remote->transport->close(remote->transport); |
4cf01e9a CMN |
411 | } |
412 | ||
9c82357b CMN |
413 | void git_remote_free(git_remote *remote) |
414 | { | |
2aae2188 CMN |
415 | if (remote == NULL) |
416 | return; | |
417 | ||
42ea35c0 MS |
418 | if (remote->transport != NULL) { |
419 | git_remote_disconnect(remote); | |
420 | ||
421 | remote->transport->free(remote->transport); | |
422 | remote->transport = NULL; | |
423 | } | |
424 | ||
425 | git_vector_free(&remote->refs); | |
426 | ||
3286c408 VM |
427 | git__free(remote->fetch.src); |
428 | git__free(remote->fetch.dst); | |
429 | git__free(remote->push.src); | |
430 | git__free(remote->push.dst); | |
431 | git__free(remote->url); | |
432 | git__free(remote->name); | |
3286c408 | 433 | git__free(remote); |
9c82357b | 434 | } |
8171998f CMN |
435 | |
436 | struct cb_data { | |
437 | git_vector *list; | |
438 | regex_t *preg; | |
439 | }; | |
440 | ||
854eccbb | 441 | static int remote_list_cb(const char *name, const char *value, void *data_) |
8171998f CMN |
442 | { |
443 | struct cb_data *data = (struct cb_data *)data_; | |
444 | size_t nmatch = 2; | |
445 | regmatch_t pmatch[2]; | |
854eccbb | 446 | GIT_UNUSED(value); |
8171998f CMN |
447 | |
448 | if (!regexec(data->preg, name, nmatch, pmatch, 0)) { | |
449 | char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so); | |
4376f7f6 | 450 | GITERR_CHECK_ALLOC(remote_name); |
8171998f | 451 | |
4376f7f6 CMN |
452 | if (git_vector_insert(data->list, remote_name) < 0) |
453 | return -1; | |
8171998f CMN |
454 | } |
455 | ||
4376f7f6 | 456 | return 0; |
8171998f CMN |
457 | } |
458 | ||
459 | int git_remote_list(git_strarray *remotes_list, git_repository *repo) | |
460 | { | |
461 | git_config *cfg; | |
462 | git_vector list; | |
463 | regex_t preg; | |
464 | struct cb_data data; | |
465 | int error; | |
466 | ||
4376f7f6 CMN |
467 | if (git_repository_config__weakptr(&cfg, repo) < 0) |
468 | return -1; | |
8171998f | 469 | |
4376f7f6 CMN |
470 | if (git_vector_init(&list, 4, NULL) < 0) |
471 | return -1; | |
8171998f | 472 | |
4376f7f6 CMN |
473 | if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) { |
474 | giterr_set(GITERR_OS, "Remote catch regex failed to compile"); | |
475 | return -1; | |
476 | } | |
8171998f CMN |
477 | |
478 | data.list = &list; | |
479 | data.preg = &preg; | |
480 | error = git_config_foreach(cfg, remote_list_cb, &data); | |
481 | regfree(&preg); | |
4376f7f6 | 482 | if (error < 0) { |
8171998f CMN |
483 | size_t i; |
484 | char *elem; | |
485 | git_vector_foreach(&list, i, elem) { | |
2bc8fa02 | 486 | git__free(elem); |
8171998f CMN |
487 | } |
488 | ||
489 | git_vector_free(&list); | |
490 | return error; | |
491 | } | |
492 | ||
493 | remotes_list->strings = (char **)list.contents; | |
494 | remotes_list->count = list.length; | |
495 | ||
4376f7f6 | 496 | return 0; |
8171998f | 497 | } |
a209a025 CMN |
498 | |
499 | int git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url) | |
500 | { | |
501 | git_buf buf = GIT_BUF_INIT; | |
a209a025 | 502 | |
d27bf665 | 503 | if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) |
baaa8a44 | 504 | return -1; |
a209a025 | 505 | |
baaa8a44 | 506 | if (git_remote_new(out, repo, name, url, git_buf_cstr(&buf)) < 0) |
a209a025 CMN |
507 | goto on_error; |
508 | ||
509 | git_buf_free(&buf); | |
510 | ||
511 | if (git_remote_save(*out) < 0) | |
baaa8a44 | 512 | goto on_error; |
a209a025 CMN |
513 | |
514 | return 0; | |
515 | ||
516 | on_error: | |
517 | git_buf_free(&buf); | |
518 | git_remote_free(*out); | |
519 | return -1; | |
520 | } | |
250b95b2 CMN |
521 | |
522 | void git_remote_check_cert(git_remote *remote, int check) | |
523 | { | |
524 | assert(remote); | |
525 | ||
526 | remote->check_cert = check; | |
527 | } |