]>
Commit | Line | Data |
---|---|---|
9c82357b | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
9c82357b | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
9c82357b CMN |
6 | */ |
7 | ||
eae0bfdc PP |
8 | #include "remote.h" |
9 | ||
9c82357b CMN |
10 | #include "git2/config.h" |
11 | #include "git2/types.h" | |
c07d9c95 | 12 | #include "git2/oid.h" |
1ffd0806 | 13 | #include "git2/net.h" |
9c82357b CMN |
14 | |
15 | #include "config.h" | |
16 | #include "repository.h" | |
e1d88030 | 17 | #include "fetch.h" |
441f57c2 | 18 | #include "refs.h" |
b0f6e45d ET |
19 | #include "refspec.h" |
20 | #include "fetchhead.h" | |
fe794b2e | 21 | #include "push.h" |
9c82357b | 22 | |
22261344 CMN |
23 | #define CONFIG_URL_FMT "remote.%s.url" |
24 | #define CONFIG_PUSHURL_FMT "remote.%s.pushurl" | |
25 | #define CONFIG_FETCH_FMT "remote.%s.fetch" | |
77254990 | 26 | #define CONFIG_PUSH_FMT "remote.%s.push" |
35a8a8c5 | 27 | #define CONFIG_TAGOPT_FMT "remote.%s.tagopt" |
22261344 | 28 | |
af613ecd | 29 | static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); |
1ef3f0ce | 30 | static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name); |
ec0c4c40 | 31 | char *apply_insteadof(git_config *config, const char *url, int direction); |
af613ecd | 32 | |
3f894205 | 33 | static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch) |
9c82357b | 34 | { |
4330ab26 | 35 | git_refspec *spec; |
9c82357b | 36 | |
4330ab26 | 37 | spec = git__calloc(1, sizeof(git_refspec)); |
ac3d33df | 38 | GIT_ERROR_CHECK_ALLOC(spec); |
4330ab26 | 39 | |
1be680c4 CMN |
40 | if (git_refspec__parse(spec, string, is_fetch) < 0) { |
41 | git__free(spec); | |
42 | return -1; | |
43 | } | |
4330ab26 CMN |
44 | |
45 | spec->push = !is_fetch; | |
3f894205 | 46 | if (git_vector_insert(vector, spec) < 0) { |
ac3d33df | 47 | git_refspec__dispose(spec); |
1be680c4 CMN |
48 | git__free(spec); |
49 | return -1; | |
50 | } | |
9c82357b | 51 | |
4330ab26 | 52 | return 0; |
9c82357b CMN |
53 | } |
54 | ||
3f894205 CMN |
55 | static int add_refspec(git_remote *remote, const char *string, bool is_fetch) |
56 | { | |
57 | return add_refspec_to(&remote->refspecs, string, is_fetch); | |
58 | } | |
59 | ||
24f2f94e CMN |
60 | static int download_tags_value(git_remote *remote, git_config *cfg) |
61 | { | |
9a97f49e | 62 | git_config_entry *ce; |
24f2f94e CMN |
63 | git_buf buf = GIT_BUF_INIT; |
64 | int error; | |
65 | ||
24f2f94e CMN |
66 | if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) |
67 | return -1; | |
68 | ||
9f77b3f6 | 69 | error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); |
ac3d33df | 70 | git_buf_dispose(&buf); |
24f2f94e | 71 | |
9f77b3f6 RB |
72 | if (!error && ce && ce->value) { |
73 | if (!strcmp(ce->value, "--no-tags")) | |
74 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; | |
75 | else if (!strcmp(ce->value, "--tags")) | |
76 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; | |
bf6bebe2 | 77 | } |
24f2f94e | 78 | |
9a97f49e | 79 | git_config_entry_free(ce); |
24f2f94e CMN |
80 | return error; |
81 | } | |
82 | ||
032ba9e4 | 83 | static int ensure_remote_name_is_valid(const char *name) |
84 | { | |
2bca5b67 | 85 | int error = 0; |
032ba9e4 | 86 | |
2bca5b67 | 87 | if (!git_remote_is_valid_name(name)) { |
ac3d33df JK |
88 | git_error_set( |
89 | GIT_ERROR_CONFIG, | |
183aa4f8 | 90 | "'%s' is not a valid remote name.", name ? name : "(null)"); |
032ba9e4 | 91 | error = GIT_EINVALIDSPEC; |
92 | } | |
93 | ||
94 | return error; | |
95 | } | |
96 | ||
77254990 CMN |
97 | static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch) |
98 | { | |
99 | git_config *cfg; | |
100 | git_buf var = GIT_BUF_INIT; | |
c6e942fb | 101 | git_refspec spec; |
77254990 CMN |
102 | const char *fmt; |
103 | int error; | |
104 | ||
105 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) | |
106 | return error; | |
107 | ||
108 | fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT; | |
109 | ||
110 | if ((error = ensure_remote_name_is_valid(name)) < 0) | |
111 | return error; | |
112 | ||
c6e942fb | 113 | if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0) { |
ac3d33df | 114 | if (git_error_last()->klass != GIT_ERROR_NOMEMORY) |
c6e942fb CMN |
115 | error = GIT_EINVALIDSPEC; |
116 | ||
117 | return error; | |
118 | } | |
119 | ||
ac3d33df | 120 | git_refspec__dispose(&spec); |
c6e942fb | 121 | |
77254990 CMN |
122 | if ((error = git_buf_printf(&var, fmt, name)) < 0) |
123 | return error; | |
124 | ||
125 | /* | |
126 | * "$^" is a unmatcheable regexp: it will not match anything at all, so | |
127 | * all values will be considered new and we will not replace any | |
128 | * present value. | |
129 | */ | |
130 | if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) { | |
131 | goto cleanup; | |
132 | } | |
133 | ||
134 | cleanup: | |
ac3d33df | 135 | git_buf_dispose(&var); |
77254990 CMN |
136 | return 0; |
137 | } | |
138 | ||
12f32d91 ET |
139 | static int canonicalize_url(git_buf *out, const char *in) |
140 | { | |
47a40d1d | 141 | if (in == NULL || strlen(in) == 0) { |
ac3d33df | 142 | git_error_set(GIT_ERROR_INVALID, "cannot set empty URL"); |
47a40d1d CMN |
143 | return GIT_EINVALIDSPEC; |
144 | } | |
12f32d91 | 145 | |
47a40d1d | 146 | #ifdef GIT_WIN32 |
12f32d91 ET |
147 | /* Given a UNC path like \\server\path, we need to convert this |
148 | * to //server/path for compatibility with core git. | |
149 | */ | |
150 | if (in[0] == '\\' && in[1] == '\\' && | |
151 | (git__isalpha(in[2]) || git__isdigit(in[2]))) { | |
47a40d1d | 152 | const char *c; |
12f32d91 ET |
153 | for (c = in; *c; c++) |
154 | git_buf_putc(out, *c == '\\' ? '/' : *c); | |
155 | ||
156 | return git_buf_oom(out) ? -1 : 0; | |
157 | } | |
158 | #endif | |
159 | ||
160 | return git_buf_puts(out, in); | |
161 | } | |
162 | ||
ac3d33df | 163 | static int default_fetchspec_for_name(git_buf *buf, const char *name) |
778e1c73 | 164 | { |
ac3d33df JK |
165 | if (git_buf_printf(buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) |
166 | return -1; | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) | |
172 | { | |
173 | int error; | |
778e1c73 | 174 | git_remote *remote; |
ac3d33df JK |
175 | |
176 | error = git_remote_lookup(&remote, repo, name); | |
177 | ||
178 | if (error == GIT_ENOTFOUND) | |
179 | return 0; | |
180 | ||
181 | if (error < 0) | |
182 | return error; | |
183 | ||
184 | git_remote_free(remote); | |
185 | ||
186 | git_error_set(GIT_ERROR_CONFIG, "remote '%s' already exists", name); | |
187 | ||
188 | return GIT_EEXISTS; | |
189 | } | |
190 | ||
0c9c969a | 191 | int git_remote_create_options_init(git_remote_create_options *opts, unsigned int version) |
ac3d33df JK |
192 | { |
193 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( | |
194 | opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT); | |
195 | return 0; | |
196 | } | |
197 | ||
0c9c969a UG |
198 | int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version) |
199 | { | |
200 | return git_remote_create_options_init(opts, version); | |
201 | } | |
202 | ||
ac3d33df JK |
203 | int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts) |
204 | { | |
205 | git_remote *remote = NULL; | |
4dbcf0e6 | 206 | git_config *config_ro = NULL, *config_rw; |
22261344 CMN |
207 | git_buf canonical_url = GIT_BUF_INIT; |
208 | git_buf var = GIT_BUF_INIT; | |
ac3d33df JK |
209 | git_buf specbuf = GIT_BUF_INIT; |
210 | const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT; | |
44f36f6e | 211 | int error = -1; |
778e1c73 | 212 | |
eae0bfdc | 213 | assert(out && url); |
617bfdf4 | 214 | |
ac3d33df JK |
215 | if (!opts) { |
216 | opts = &dummy_opts; | |
217 | } | |
218 | ||
219 | GIT_ERROR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options"); | |
220 | ||
221 | if (opts->name != NULL) { | |
222 | if ((error = ensure_remote_name_is_valid(opts->name)) < 0) | |
223 | return error; | |
224 | ||
225 | if (opts->repository && | |
226 | (error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0) | |
227 | return error; | |
228 | } | |
229 | ||
230 | if (opts->repository) { | |
231 | if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0) | |
232 | goto on_error; | |
233 | } | |
22261344 | 234 | |
59bccf33 | 235 | remote = git__calloc(1, sizeof(git_remote)); |
ac3d33df | 236 | GIT_ERROR_CHECK_ALLOC(remote); |
778e1c73 | 237 | |
ac3d33df | 238 | remote->repo = opts->repository; |
617bfdf4 | 239 | |
ac3d33df | 240 | if ((error = git_vector_init(&remote->refs, 8, NULL)) < 0 || |
d0cb11e7 | 241 | (error = canonicalize_url(&canonical_url, url)) < 0) |
44f36f6e | 242 | goto on_error; |
d88d4311 | 243 | |
ac3d33df | 244 | if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) { |
eae0bfdc PP |
245 | remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); |
246 | } else { | |
247 | remote->url = git__strdup(canonical_url.ptr); | |
248 | } | |
ac3d33df | 249 | GIT_ERROR_CHECK_ALLOC(remote->url); |
22261344 | 250 | |
ac3d33df JK |
251 | if (opts->name != NULL) { |
252 | remote->name = git__strdup(opts->name); | |
253 | GIT_ERROR_CHECK_ALLOC(remote->name); | |
22261344 | 254 | |
ac3d33df JK |
255 | if (opts->repository && |
256 | ((error = git_buf_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 || | |
257 | (error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 || | |
258 | (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)) | |
22261344 | 259 | goto on_error; |
617bfdf4 CMN |
260 | } |
261 | ||
ac3d33df JK |
262 | if (opts->fetchspec != NULL || |
263 | (opts->name && !(opts->flags & GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC))) { | |
264 | const char *fetch = NULL; | |
265 | if (opts->fetchspec) { | |
266 | fetch = opts->fetchspec; | |
267 | } else { | |
268 | if ((error = default_fetchspec_for_name(&specbuf, opts->name)) < 0) | |
269 | goto on_error; | |
77254990 | 270 | |
ac3d33df JK |
271 | fetch = git_buf_cstr(&specbuf); |
272 | } | |
273 | ||
274 | if ((error = add_refspec(remote, fetch, true)) < 0) | |
baaa8a44 | 275 | goto on_error; |
d3cd7da5 | 276 | |
ac3d33df JK |
277 | /* only write for named remotes with a repository */ |
278 | if (opts->repository && opts->name && | |
279 | ((error = write_add_refspec(opts->repository, opts->name, fetch, true)) < 0 || | |
280 | (error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0)) | |
1ef3f0ce DC |
281 | goto on_error; |
282 | ||
d3cd7da5 | 283 | /* Move the data over to where the matching functions can find them */ |
69f0032b | 284 | if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0) |
d3cd7da5 | 285 | goto on_error; |
baaa8a44 CMN |
286 | } |
287 | ||
35a8a8c5 | 288 | /* A remote without a name doesn't download tags */ |
ac3d33df | 289 | if (!opts->name) |
c648d4a8 | 290 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; |
35a8a8c5 CMN |
291 | else |
292 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; | |
293 | ||
c648d4a8 | 294 | |
ac3d33df | 295 | git_buf_dispose(&var); |
22261344 | 296 | |
778e1c73 | 297 | *out = remote; |
a8846da7 | 298 | error = 0; |
baaa8a44 CMN |
299 | |
300 | on_error: | |
a8846da7 ET |
301 | if (error) |
302 | git_remote_free(remote); | |
303 | ||
4dbcf0e6 | 304 | git_config_free(config_ro); |
ac3d33df JK |
305 | git_buf_dispose(&specbuf); |
306 | git_buf_dispose(&canonical_url); | |
307 | git_buf_dispose(&var); | |
44f36f6e | 308 | return error; |
778e1c73 CMN |
309 | } |
310 | ||
ac3d33df | 311 | int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url) |
592f466c | 312 | { |
ac3d33df | 313 | git_buf buf = GIT_BUF_INIT; |
592f466c | 314 | int error; |
ac3d33df | 315 | git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT; |
592f466c | 316 | |
ac3d33df JK |
317 | /* Those 2 tests are duplicated here because of backward-compatibility */ |
318 | if ((error = ensure_remote_name_is_valid(name)) < 0) | |
592f466c BS |
319 | return error; |
320 | ||
ac3d33df JK |
321 | if (canonicalize_url(&buf, url) < 0) |
322 | return GIT_ERROR; | |
592f466c | 323 | |
ac3d33df | 324 | git_buf_clear(&buf); |
f19304d2 | 325 | |
ac3d33df JK |
326 | opts.repository = repo; |
327 | opts.name = name; | |
874dcb25 | 328 | |
ac3d33df | 329 | error = git_remote_create_with_opts(out, url, &opts); |
874dcb25 | 330 | |
ac3d33df | 331 | git_buf_dispose(&buf); |
874dcb25 | 332 | |
a68e217f | 333 | return error; |
874dcb25 BS |
334 | } |
335 | ||
40b99d05 VG |
336 | int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) |
337 | { | |
40b99d05 | 338 | int error; |
ac3d33df | 339 | git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT; |
40b99d05 VG |
340 | |
341 | if ((error = ensure_remote_name_is_valid(name)) < 0) | |
342 | return error; | |
343 | ||
ac3d33df JK |
344 | opts.repository = repo; |
345 | opts.name = name; | |
346 | opts.fetchspec = fetch; | |
347 | opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC; | |
c5193e3c | 348 | |
ac3d33df | 349 | return git_remote_create_with_opts(out, url, &opts); |
874dcb25 BS |
350 | } |
351 | ||
ae5b9362 | 352 | int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url) |
874dcb25 | 353 | { |
ac3d33df JK |
354 | git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT; |
355 | ||
356 | opts.repository = repo; | |
357 | ||
358 | return git_remote_create_with_opts(out, url, &opts); | |
874dcb25 BS |
359 | } |
360 | ||
eae0bfdc PP |
361 | int git_remote_create_detached(git_remote **out, const char *url) |
362 | { | |
ac3d33df | 363 | return git_remote_create_with_opts(out, url, NULL); |
eae0bfdc PP |
364 | } |
365 | ||
991b2840 | 366 | int git_remote_dup(git_remote **dest, git_remote *source) |
40ef47dd | 367 | { |
70f7484d | 368 | size_t i; |
991b2840 | 369 | int error = 0; |
70f7484d | 370 | git_refspec *spec; |
40ef47dd | 371 | git_remote *remote = git__calloc(1, sizeof(git_remote)); |
ac3d33df | 372 | GIT_ERROR_CHECK_ALLOC(remote); |
40ef47dd | 373 | |
40ef47dd AS |
374 | if (source->name != NULL) { |
375 | remote->name = git__strdup(source->name); | |
ac3d33df | 376 | GIT_ERROR_CHECK_ALLOC(remote->name); |
40ef47dd AS |
377 | } |
378 | ||
379 | if (source->url != NULL) { | |
380 | remote->url = git__strdup(source->url); | |
ac3d33df | 381 | GIT_ERROR_CHECK_ALLOC(remote->url); |
40ef47dd AS |
382 | } |
383 | ||
384 | if (source->pushurl != NULL) { | |
385 | remote->pushurl = git__strdup(source->pushurl); | |
ac3d33df | 386 | GIT_ERROR_CHECK_ALLOC(remote->pushurl); |
40ef47dd AS |
387 | } |
388 | ||
389 | remote->repo = source->repo; | |
40ef47dd | 390 | remote->download_tags = source->download_tags; |
5f473947 | 391 | remote->prune_refs = source->prune_refs; |
40ef47dd | 392 | |
991b2840 AS |
393 | if (git_vector_init(&remote->refs, 32, NULL) < 0 || |
394 | git_vector_init(&remote->refspecs, 2, NULL) < 0 || | |
395 | git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { | |
396 | error = -1; | |
397 | goto cleanup; | |
40ef47dd AS |
398 | } |
399 | ||
70f7484d CMN |
400 | git_vector_foreach(&source->refspecs, i, spec) { |
401 | if ((error = add_refspec(remote, spec->string, !spec->push)) < 0) | |
402 | goto cleanup; | |
403 | } | |
991b2840 | 404 | |
40ef47dd AS |
405 | *dest = remote; |
406 | ||
991b2840 AS |
407 | cleanup: |
408 | ||
991b2840 AS |
409 | if (error < 0) |
410 | git__free(remote); | |
411 | ||
412 | return error; | |
40ef47dd AS |
413 | } |
414 | ||
4330ab26 CMN |
415 | struct refspec_cb_data { |
416 | git_remote *remote; | |
417 | int fetch; | |
418 | }; | |
419 | ||
420 | static int refspec_cb(const git_config_entry *entry, void *payload) | |
421 | { | |
96869a4e | 422 | struct refspec_cb_data *data = (struct refspec_cb_data *)payload; |
25e0b157 | 423 | return add_refspec(data->remote, entry->value, data->fetch); |
4330ab26 CMN |
424 | } |
425 | ||
bf6bebe2 | 426 | static int get_optional_config( |
c9ffa84b | 427 | bool *found, git_config *config, git_buf *buf, |
428 | git_config_foreach_cb cb, void *payload) | |
bf6bebe2 RB |
429 | { |
430 | int error = 0; | |
431 | const char *key = git_buf_cstr(buf); | |
432 | ||
433 | if (git_buf_oom(buf)) | |
434 | return -1; | |
435 | ||
436 | if (cb != NULL) | |
4efa3290 | 437 | error = git_config_get_multivar_foreach(config, key, NULL, cb, payload); |
bf6bebe2 RB |
438 | else |
439 | error = git_config_get_string(payload, config, key); | |
440 | ||
c9ffa84b | 441 | if (found) |
442 | *found = !error; | |
443 | ||
bf6bebe2 | 444 | if (error == GIT_ENOTFOUND) { |
ac3d33df | 445 | git_error_clear(); |
bf6bebe2 RB |
446 | error = 0; |
447 | } | |
448 | ||
bf6bebe2 RB |
449 | return error; |
450 | } | |
451 | ||
209425ce | 452 | int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) |
9c82357b | 453 | { |
6c7cee42 | 454 | git_remote *remote = NULL; |
f0f3a18a | 455 | git_buf buf = GIT_BUF_INIT; |
9c82357b | 456 | const char *val; |
4376f7f6 | 457 | int error = 0; |
9462c471 | 458 | git_config *config; |
96869a4e | 459 | struct refspec_cb_data data = { NULL }; |
c9ffa84b | 460 | bool optional_setting_found = false, found; |
4330ab26 | 461 | |
9462c471 VM |
462 | assert(out && repo && name); |
463 | ||
032ba9e4 | 464 | if ((error = ensure_remote_name_is_valid(name)) < 0) |
465 | return error; | |
466 | ||
ac99d86b | 467 | if ((error = git_repository_config_snapshot(&config, repo)) < 0) |
29c4cb09 | 468 | return error; |
4bef3565 | 469 | |
392702ee | 470 | remote = git__calloc(1, sizeof(git_remote)); |
ac3d33df | 471 | GIT_ERROR_CHECK_ALLOC(remote); |
9c82357b | 472 | |
9c82357b | 473 | remote->name = git__strdup(name); |
ac3d33df | 474 | GIT_ERROR_CHECK_ALLOC(remote->name); |
9c82357b | 475 | |
25e0b157 RB |
476 | if (git_vector_init(&remote->refs, 32, NULL) < 0 || |
477 | git_vector_init(&remote->refspecs, 2, NULL) < 0 || | |
2cdd5c57 | 478 | git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 || |
25e0b157 | 479 | git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { |
2fb9d6de | 480 | error = -1; |
481 | goto cleanup; | |
482 | } | |
d88d4311 | 483 | |
25e0b157 | 484 | if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) |
2fb9d6de | 485 | goto cleanup; |
9c82357b | 486 | |
c9ffa84b | 487 | if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) |
9c82357b | 488 | goto cleanup; |
bf6bebe2 | 489 | |
c9ffa84b | 490 | optional_setting_found |= found; |
491 | ||
9462c471 | 492 | remote->repo = repo; |
35a8a8c5 | 493 | remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; |
c9ffa84b | 494 | |
495 | if (found && strlen(val) > 0) { | |
ec0c4c40 | 496 | remote->url = apply_insteadof(config, val, GIT_DIRECTION_FETCH); |
ac3d33df | 497 | GIT_ERROR_CHECK_ALLOC(remote->url); |
c9ffa84b | 498 | } |
9c82357b | 499 | |
bf6bebe2 | 500 | val = NULL; |
3ed4b501 | 501 | git_buf_clear(&buf); |
bf6bebe2 | 502 | git_buf_printf(&buf, "remote.%s.pushurl", name); |
3ed4b501 | 503 | |
c9ffa84b | 504 | if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) |
3ed4b501 | 505 | goto cleanup; |
3ed4b501 | 506 | |
c9ffa84b | 507 | optional_setting_found |= found; |
508 | ||
509 | if (!optional_setting_found) { | |
510 | error = GIT_ENOTFOUND; | |
ac3d33df | 511 | git_error_set(GIT_ERROR_CONFIG, "remote '%s' does not exist", name); |
3ed4b501 | 512 | goto cleanup; |
c9ffa84b | 513 | } |
3ed4b501 | 514 | |
c9ffa84b | 515 | if (found && strlen(val) > 0) { |
ec0c4c40 | 516 | remote->pushurl = apply_insteadof(config, val, GIT_DIRECTION_PUSH); |
ac3d33df | 517 | GIT_ERROR_CHECK_ALLOC(remote->pushurl); |
3ed4b501 SC |
518 | } |
519 | ||
4330ab26 CMN |
520 | data.remote = remote; |
521 | data.fetch = true; | |
96869a4e | 522 | |
f0f3a18a | 523 | git_buf_clear(&buf); |
bf6bebe2 RB |
524 | git_buf_printf(&buf, "remote.%s.fetch", name); |
525 | ||
c9ffa84b | 526 | if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) |
2fb9d6de | 527 | goto cleanup; |
9c82357b | 528 | |
4330ab26 | 529 | data.fetch = false; |
bf6bebe2 RB |
530 | git_buf_clear(&buf); |
531 | git_buf_printf(&buf, "remote.%s.push", name); | |
9c82357b | 532 | |
c9ffa84b | 533 | if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) |
9c82357b CMN |
534 | goto cleanup; |
535 | ||
6c7cee42 | 536 | if ((error = download_tags_value(remote, config)) < 0) |
24f2f94e CMN |
537 | goto cleanup; |
538 | ||
1ef3f0ce DC |
539 | if ((error = lookup_remote_prune_config(remote, config, name)) < 0) |
540 | goto cleanup; | |
541 | ||
542 | /* Move the data over to where the matching functions can find them */ | |
69f0032b | 543 | if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0) |
1ef3f0ce DC |
544 | goto cleanup; |
545 | ||
546 | *out = remote; | |
547 | ||
548 | cleanup: | |
549 | git_config_free(config); | |
ac3d33df | 550 | git_buf_dispose(&buf); |
1ef3f0ce DC |
551 | |
552 | if (error < 0) | |
553 | git_remote_free(remote); | |
554 | ||
555 | return error; | |
556 | } | |
557 | ||
558 | static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name) | |
559 | { | |
560 | git_buf buf = GIT_BUF_INIT; | |
561 | int error = 0; | |
562 | ||
5f473947 L |
563 | git_buf_printf(&buf, "remote.%s.prune", name); |
564 | ||
565 | if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) { | |
566 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 567 | git_error_clear(); |
5f473947 L |
568 | |
569 | if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) { | |
570 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 571 | git_error_clear(); |
66b71ea5 | 572 | error = 0; |
5f473947 L |
573 | } |
574 | } | |
575 | } | |
576 | } | |
577 | ||
ac3d33df | 578 | git_buf_dispose(&buf); |
9c82357b CMN |
579 | return error; |
580 | } | |
581 | ||
df705148 | 582 | const char *git_remote_name(const git_remote *remote) |
9c82357b | 583 | { |
4bef3565 | 584 | assert(remote); |
9c82357b CMN |
585 | return remote->name; |
586 | } | |
587 | ||
85e1eded ES |
588 | git_repository *git_remote_owner(const git_remote *remote) |
589 | { | |
590 | assert(remote); | |
591 | return remote->repo; | |
592 | } | |
593 | ||
df705148 | 594 | const char *git_remote_url(const git_remote *remote) |
9c82357b | 595 | { |
4bef3565 | 596 | assert(remote); |
9c82357b CMN |
597 | return remote->url; |
598 | } | |
599 | ||
22261344 | 600 | static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url) |
76501590 | 601 | { |
22261344 CMN |
602 | git_config *cfg; |
603 | git_buf buf = GIT_BUF_INIT, canonical_url = GIT_BUF_INIT; | |
604 | int error; | |
76501590 | 605 | |
22261344 | 606 | assert(repo && remote); |
76501590 | 607 | |
22261344 CMN |
608 | if ((error = ensure_remote_name_is_valid(remote)) < 0) |
609 | return error; | |
610 | ||
611 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) | |
612 | return error; | |
613 | ||
614 | if ((error = git_buf_printf(&buf, pattern, remote)) < 0) | |
615 | return error; | |
616 | ||
617 | if (url) { | |
618 | if ((error = canonicalize_url(&canonical_url, url)) < 0) | |
619 | goto cleanup; | |
620 | ||
621 | error = git_config_set_string(cfg, buf.ptr, url); | |
622 | } else { | |
623 | error = git_config_delete_entry(cfg, buf.ptr); | |
624 | } | |
625 | ||
626 | cleanup: | |
ac3d33df JK |
627 | git_buf_dispose(&canonical_url); |
628 | git_buf_dispose(&buf); | |
22261344 CMN |
629 | |
630 | return error; | |
631 | } | |
632 | ||
633 | int git_remote_set_url(git_repository *repo, const char *remote, const char *url) | |
634 | { | |
635 | return set_url(repo, remote, CONFIG_URL_FMT, url); | |
76501590 SC |
636 | } |
637 | ||
df705148 | 638 | const char *git_remote_pushurl(const git_remote *remote) |
76501590 SC |
639 | { |
640 | assert(remote); | |
641 | return remote->pushurl; | |
642 | } | |
643 | ||
22261344 | 644 | int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url) |
76501590 | 645 | { |
22261344 | 646 | return set_url(repo, remote, CONFIG_PUSHURL_FMT, url); |
76501590 SC |
647 | } |
648 | ||
0c9c969a | 649 | static int resolve_url(git_buf *resolved_url, const char *url, int direction, const git_remote_callbacks *callbacks) |
eff5b499 | 650 | { |
0c9c969a UG |
651 | int status; |
652 | ||
653 | if (callbacks && callbacks->resolve_url) { | |
654 | git_buf_clear(resolved_url); | |
655 | status = callbacks->resolve_url(resolved_url, url, direction, callbacks->payload); | |
656 | if (status != GIT_PASSTHROUGH) { | |
657 | git_error_set_after_callback_function(status, "git_resolve_url_cb"); | |
658 | git_buf_sanitize(resolved_url); | |
659 | return status; | |
660 | } | |
661 | } | |
eff5b499 | 662 | |
0c9c969a UG |
663 | return git_buf_sets(resolved_url, url); |
664 | } | |
665 | ||
666 | int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks) | |
667 | { | |
668 | const char *url = NULL; | |
669 | ||
670 | assert(remote); | |
b83c92dd | 671 | assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH); |
672 | ||
df705148 | 673 | if (direction == GIT_DIRECTION_FETCH) { |
0c9c969a UG |
674 | url = remote->url; |
675 | } else if (direction == GIT_DIRECTION_PUSH) { | |
676 | url = remote->pushurl ? remote->pushurl : remote->url; | |
eff5b499 SC |
677 | } |
678 | ||
0c9c969a UG |
679 | if (!url) { |
680 | git_error_set(GIT_ERROR_INVALID, | |
681 | "malformed remote '%s' - missing %s URL", | |
682 | remote->name ? remote->name : "(anonymous)", | |
683 | direction == GIT_DIRECTION_FETCH ? "fetch" : "push"); | |
684 | return GIT_EINVALID; | |
eff5b499 | 685 | } |
0c9c969a | 686 | return resolve_url(url_out, url, direction, callbacks); |
eff5b499 SC |
687 | } |
688 | ||
8f0104ec CMN |
689 | int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs) |
690 | { | |
691 | if (!t->set_callbacks || !cbs) | |
692 | return 0; | |
693 | ||
694 | return t->set_callbacks(t, cbs->sideband_progress, NULL, | |
695 | cbs->certificate_check, cbs->payload); | |
696 | } | |
697 | ||
d16c1b97 | 698 | static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers) |
4f2b6093 | 699 | { |
d7375662 | 700 | if (!t->set_custom_headers) |
4f2b6093 MB |
701 | return 0; |
702 | ||
703 | return t->set_custom_headers(t, custom_headers); | |
704 | } | |
705 | ||
ac3d33df | 706 | int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn) |
9ba49bb5 | 707 | { |
9ba49bb5 | 708 | git_transport *t; |
0c9c969a | 709 | git_buf url = GIT_BUF_INIT; |
41fb1ca0 | 710 | int flags = GIT_TRANSPORTFLAGS_NONE; |
80fc7d6b | 711 | int error; |
8f0104ec | 712 | void *payload = NULL; |
0c9c969a | 713 | git_credential_acquire_cb credentials = NULL; |
058b753c | 714 | git_transport_cb transport = NULL; |
9ba49bb5 | 715 | |
4bef3565 VM |
716 | assert(remote); |
717 | ||
8f0104ec | 718 | if (callbacks) { |
ac3d33df | 719 | GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
8f0104ec | 720 | credentials = callbacks->credentials; |
058b753c | 721 | transport = callbacks->transport; |
8f0104ec CMN |
722 | payload = callbacks->payload; |
723 | } | |
724 | ||
ac3d33df JK |
725 | if (conn->proxy) |
726 | GIT_ERROR_CHECK_VERSION(conn->proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); | |
07bd3e57 | 727 | |
41fb1ca0 PK |
728 | t = remote->transport; |
729 | ||
0c9c969a UG |
730 | if ((error = git_remote__urlfordirection(&url, remote, direction, callbacks)) < 0) |
731 | goto on_error; | |
eff5b499 | 732 | |
1697cd6f PK |
733 | /* If we don't have a transport object yet, and the caller specified a |
734 | * custom transport factory, use that */ | |
058b753c CMN |
735 | if (!t && transport && |
736 | (error = transport(&t, remote, payload)) < 0) | |
0c9c969a | 737 | goto on_error; |
1697cd6f PK |
738 | |
739 | /* If we still don't have a transport, then use the global | |
740 | * transport registrations which map URI schemes to transport factories */ | |
0c9c969a UG |
741 | if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0) |
742 | goto on_error; | |
9ba49bb5 | 743 | |
ac3d33df | 744 | if ((error = set_transport_custom_headers(t, conn->custom_headers)) != 0) |
4f2b6093 MB |
745 | goto on_error; |
746 | ||
8f0104ec | 747 | if ((error = set_transport_callbacks(t, callbacks)) < 0 || |
0c9c969a | 748 | (error = t->connect(t, url.ptr, credentials, payload, conn->proxy, direction, flags)) != 0) |
4376f7f6 | 749 | goto on_error; |
9ba49bb5 CMN |
750 | |
751 | remote->transport = t; | |
752 | ||
0c9c969a UG |
753 | git_buf_dispose(&url); |
754 | ||
4376f7f6 | 755 | return 0; |
9ba49bb5 | 756 | |
4376f7f6 | 757 | on_error: |
0c9c969a UG |
758 | if (t) |
759 | t->free(t); | |
760 | ||
761 | git_buf_dispose(&url); | |
613d5eb9 PK |
762 | |
763 | if (t == remote->transport) | |
764 | remote->transport = NULL; | |
765 | ||
80fc7d6b | 766 | return error; |
9ba49bb5 CMN |
767 | } |
768 | ||
ac3d33df JK |
769 | int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers) |
770 | { | |
771 | git_remote_connection_opts conn; | |
772 | ||
773 | conn.proxy = proxy; | |
774 | conn.custom_headers = custom_headers; | |
775 | ||
776 | return git_remote__connect(remote, direction, callbacks, &conn); | |
777 | } | |
778 | ||
359dce72 | 779 | int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote) |
9ba49bb5 | 780 | { |
d88d4311 VM |
781 | assert(remote); |
782 | ||
dc8adda4 | 783 | if (!remote->transport) { |
ac3d33df | 784 | git_error_set(GIT_ERROR_NET, "this remote has never connected"); |
dc8adda4 JG |
785 | return -1; |
786 | } | |
787 | ||
359dce72 | 788 | return remote->transport->ls(out, size, remote->transport); |
9ba49bb5 CMN |
789 | } |
790 | ||
613d5eb9 PK |
791 | int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) |
792 | { | |
793 | git_config *cfg; | |
9a97f49e | 794 | git_config_entry *ce = NULL; |
e069c621 | 795 | git_buf val = GIT_BUF_INIT; |
80fc7d6b | 796 | int error; |
613d5eb9 PK |
797 | |
798 | assert(remote); | |
799 | ||
a71c27cc | 800 | if (!proxy_url || !remote->repo) |
613d5eb9 PK |
801 | return -1; |
802 | ||
803 | *proxy_url = NULL; | |
804 | ||
80fc7d6b ET |
805 | if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) |
806 | return error; | |
613d5eb9 PK |
807 | |
808 | /* Go through the possible sources for proxy configuration, from most specific | |
809 | * to least specific. */ | |
810 | ||
811 | /* remote.<name>.proxy config setting */ | |
9f77b3f6 | 812 | if (remote->name && remote->name[0]) { |
613d5eb9 PK |
813 | git_buf buf = GIT_BUF_INIT; |
814 | ||
80fc7d6b ET |
815 | if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) |
816 | return error; | |
613d5eb9 | 817 | |
9f77b3f6 | 818 | error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); |
ac3d33df | 819 | git_buf_dispose(&buf); |
613d5eb9 | 820 | |
9f77b3f6 | 821 | if (error < 0) |
80fc7d6b | 822 | return error; |
613d5eb9 | 823 | |
9f77b3f6 | 824 | if (ce && ce->value) { |
e069c621 | 825 | *proxy_url = git__strdup(ce->value); |
9f77b3f6 RB |
826 | goto found; |
827 | } | |
613d5eb9 PK |
828 | } |
829 | ||
830 | /* http.proxy config setting */ | |
9f77b3f6 | 831 | if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) |
80fc7d6b | 832 | return error; |
e069c621 | 833 | |
9f77b3f6 | 834 | if (ce && ce->value) { |
e069c621 | 835 | *proxy_url = git__strdup(ce->value); |
9f77b3f6 RB |
836 | goto found; |
837 | } | |
613d5eb9 | 838 | |
2af282d8 CB |
839 | /* http_proxy / https_proxy environment variables */ |
840 | error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy"); | |
5f3276c7 | 841 | |
61189a11 CB |
842 | /* try uppercase environment variables */ |
843 | if (error == GIT_ENOTFOUND) | |
2af282d8 | 844 | error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); |
5f3276c7 | 845 | |
e069c621 ET |
846 | if (error < 0) { |
847 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 848 | git_error_clear(); |
e069c621 ET |
849 | error = 0; |
850 | } | |
851 | ||
852 | return error; | |
613d5eb9 | 853 | } |
e069c621 ET |
854 | |
855 | *proxy_url = git_buf_detach(&val); | |
856 | ||
857 | found: | |
ac3d33df | 858 | GIT_ERROR_CHECK_ALLOC(*proxy_url); |
9a97f49e | 859 | git_config_entry_free(ce); |
613d5eb9 PK |
860 | |
861 | return 0; | |
862 | } | |
863 | ||
af613ecd CMN |
864 | /* DWIM `refspecs` based on `refs` and append the output to `out` */ |
865 | static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs) | |
d8488457 | 866 | { |
af613ecd | 867 | size_t i; |
d8488457 | 868 | git_refspec *spec; |
d8488457 CMN |
869 | |
870 | git_vector_foreach(refspecs, i, spec) { | |
af613ecd CMN |
871 | if (git_refspec__dwim_one(out, spec, refs) < 0) |
872 | return -1; | |
873 | } | |
d8488457 | 874 | |
af613ecd CMN |
875 | return 0; |
876 | } | |
d8488457 | 877 | |
af613ecd CMN |
878 | static void free_refspecs(git_vector *vec) |
879 | { | |
880 | size_t i; | |
881 | git_refspec *spec; | |
d8488457 | 882 | |
af613ecd | 883 | git_vector_foreach(vec, i, spec) { |
ac3d33df | 884 | git_refspec__dispose(spec); |
af613ecd | 885 | git__free(spec); |
d8488457 CMN |
886 | } |
887 | ||
af613ecd | 888 | git_vector_clear(vec); |
d8488457 CMN |
889 | } |
890 | ||
891 | static int remote_head_cmp(const void *_a, const void *_b) | |
892 | { | |
893 | const git_remote_head *a = (git_remote_head *) _a; | |
894 | const git_remote_head *b = (git_remote_head *) _b; | |
895 | ||
896 | return git__strcmp_cb(a->name, b->name); | |
897 | } | |
898 | ||
877cde76 CMN |
899 | static int ls_to_vector(git_vector *out, git_remote *remote) |
900 | { | |
901 | git_remote_head **heads; | |
902 | size_t heads_len, i; | |
903 | ||
904 | if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) | |
905 | return -1; | |
906 | ||
907 | if (git_vector_init(out, heads_len, remote_head_cmp) < 0) | |
908 | return -1; | |
909 | ||
910 | for (i = 0; i < heads_len; i++) { | |
911 | if (git_vector_insert(out, heads[i]) < 0) | |
912 | return -1; | |
913 | } | |
914 | ||
915 | return 0; | |
916 | } | |
917 | ||
8f0104ec | 918 | int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts) |
48a65a07 | 919 | { |
eecc1772 | 920 | int error = -1; |
3f894205 | 921 | size_t i; |
2785544f | 922 | git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT; |
8f0104ec | 923 | const git_remote_callbacks *cbs = NULL; |
c49126c8 | 924 | const git_strarray *custom_headers = NULL; |
07bd3e57 | 925 | const git_proxy_options *proxy = NULL; |
95057b85 | 926 | |
1e3b8ed5 | 927 | assert(remote); |
4bef3565 | 928 | |
eae0bfdc | 929 | if (!remote->repo) { |
ac3d33df | 930 | git_error_set(GIT_ERROR_INVALID, "cannot download detached remote"); |
eae0bfdc PP |
931 | return -1; |
932 | } | |
933 | ||
8f0104ec | 934 | if (opts) { |
ac3d33df | 935 | GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
8f0104ec | 936 | cbs = &opts->callbacks; |
c49126c8 | 937 | custom_headers = &opts->custom_headers; |
ac3d33df | 938 | GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); |
07bd3e57 | 939 | proxy = &opts->proxy_opts; |
8f0104ec CMN |
940 | } |
941 | ||
942 | if (!git_remote_connected(remote) && | |
07bd3e57 | 943 | (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0) |
8f0104ec CMN |
944 | goto on_error; |
945 | ||
877cde76 | 946 | if (ls_to_vector(&refs, remote) < 0) |
d8488457 | 947 | return -1; |
d8488457 | 948 | |
3f894205 CMN |
949 | if ((git_vector_init(&specs, 0, NULL)) < 0) |
950 | goto on_error; | |
951 | ||
c5837cad | 952 | remote->passed_refspecs = 0; |
8e398e4c | 953 | if (!refspecs || !refspecs->count) { |
3f894205 CMN |
954 | to_active = &remote->refspecs; |
955 | } else { | |
956 | for (i = 0; i < refspecs->count; i++) { | |
957 | if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0) | |
958 | goto on_error; | |
959 | } | |
960 | ||
961 | to_active = &specs; | |
c5837cad | 962 | remote->passed_refspecs = 1; |
3f894205 CMN |
963 | } |
964 | ||
2cdd5c57 CMN |
965 | free_refspecs(&remote->passive_refspecs); |
966 | if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0) | |
967 | goto on_error; | |
af613ecd | 968 | |
2cdd5c57 | 969 | free_refspecs(&remote->active_refspecs); |
3f894205 | 970 | error = dwim_refspecs(&remote->active_refspecs, to_active, &refs); |
2cdd5c57 | 971 | |
877cde76 | 972 | git_vector_free(&refs); |
3f894205 CMN |
973 | free_refspecs(&specs); |
974 | git_vector_free(&specs); | |
877cde76 CMN |
975 | |
976 | if (error < 0) | |
25e0b157 | 977 | return error; |
d8488457 | 978 | |
c070ac64 POL |
979 | if (remote->push) { |
980 | git_push_free(remote->push); | |
981 | remote->push = NULL; | |
982 | } | |
983 | ||
35a8a8c5 | 984 | if ((error = git_fetch_negotiate(remote, opts)) < 0) |
4376f7f6 | 985 | return error; |
95057b85 | 986 | |
8f0104ec | 987 | return git_fetch_download_pack(remote, cbs); |
3f894205 CMN |
988 | |
989 | on_error: | |
990 | git_vector_free(&refs); | |
991 | free_refspecs(&specs); | |
992 | git_vector_free(&specs); | |
993 | return error; | |
48a65a07 CMN |
994 | } |
995 | ||
c3ab1e5a BS |
996 | int git_remote_fetch( |
997 | git_remote *remote, | |
3f894205 | 998 | const git_strarray *refspecs, |
8f0104ec | 999 | const git_fetch_options *opts, |
c3ab1e5a | 1000 | const char *reflog_message) |
fe3a40a4 | 1001 | { |
3eff2a57 | 1002 | int error, update_fetchhead = 1; |
35a8a8c5 | 1003 | git_remote_autotag_option_t tagopt = remote->download_tags; |
6fb373a0 | 1004 | bool prune = false; |
db55bb73 | 1005 | git_buf reflog_msg_buf = GIT_BUF_INIT; |
8f0104ec | 1006 | const git_remote_callbacks *cbs = NULL; |
ac3d33df | 1007 | git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT; |
8f0104ec CMN |
1008 | |
1009 | if (opts) { | |
ac3d33df | 1010 | GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
8f0104ec | 1011 | cbs = &opts->callbacks; |
ac3d33df | 1012 | conn.custom_headers = &opts->custom_headers; |
3eff2a57 | 1013 | update_fetchhead = opts->update_fetchhead; |
35a8a8c5 | 1014 | tagopt = opts->download_tags; |
ac3d33df JK |
1015 | GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); |
1016 | conn.proxy = &opts->proxy_opts; | |
8f0104ec | 1017 | } |
fe3a40a4 CMN |
1018 | |
1019 | /* Connect and download everything */ | |
ac3d33df | 1020 | if ((error = git_remote__connect(remote, GIT_DIRECTION_FETCH, cbs, &conn)) != 0) |
fe3a40a4 CMN |
1021 | return error; |
1022 | ||
8f0104ec | 1023 | error = git_remote_download(remote, refspecs, opts); |
fe3a40a4 CMN |
1024 | |
1025 | /* We don't need to be connected anymore */ | |
1026 | git_remote_disconnect(remote); | |
1027 | ||
06a8f5c3 BL |
1028 | /* If the download failed, return the error */ |
1029 | if (error != 0) | |
1030 | return error; | |
1031 | ||
db55bb73 BS |
1032 | /* Default reflog message */ |
1033 | if (reflog_message) | |
1034 | git_buf_sets(&reflog_msg_buf, reflog_message); | |
1035 | else { | |
1036 | git_buf_printf(&reflog_msg_buf, "fetch %s", | |
1037 | remote->name ? remote->name : remote->url); | |
1038 | } | |
1039 | ||
fe3a40a4 | 1040 | /* Create "remote/foo" branches for all remote branches */ |
35a8a8c5 | 1041 | error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_buf_cstr(&reflog_msg_buf)); |
ac3d33df | 1042 | git_buf_dispose(&reflog_msg_buf); |
8c13eaed CMN |
1043 | if (error < 0) |
1044 | return error; | |
1045 | ||
6fb373a0 CMN |
1046 | if (opts && opts->prune == GIT_FETCH_PRUNE) |
1047 | prune = true; | |
c2418f46 | 1048 | else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs) |
6fb373a0 CMN |
1049 | prune = true; |
1050 | else if (opts && opts->prune == GIT_FETCH_NO_PRUNE) | |
1051 | prune = false; | |
1052 | else | |
1053 | prune = remote->prune_refs; | |
1054 | ||
1055 | if (prune) | |
8f0104ec | 1056 | error = git_remote_prune(remote, cbs); |
8c13eaed | 1057 | |
db55bb73 | 1058 | return error; |
fe3a40a4 CMN |
1059 | } |
1060 | ||
b0f6e45d ET |
1061 | static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) |
1062 | { | |
1063 | unsigned int i; | |
1064 | git_remote_head *remote_ref; | |
1065 | ||
1066 | assert(update_heads && fetchspec_src); | |
1067 | ||
1068 | *out = NULL; | |
7d4b65f6 | 1069 | |
1070 | git_vector_foreach(update_heads, i, remote_ref) { | |
1071 | if (strcmp(remote_ref->name, fetchspec_src) == 0) { | |
1072 | *out = remote_ref; | |
1073 | break; | |
b0f6e45d ET |
1074 | } |
1075 | } | |
1076 | ||
1077 | return 0; | |
1078 | } | |
1079 | ||
2c9b9c8b CMN |
1080 | static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name) |
1081 | { | |
1082 | int error = 0; | |
1083 | git_repository *repo; | |
1084 | git_buf upstream_remote = GIT_BUF_INIT; | |
1085 | git_buf upstream_name = GIT_BUF_INIT; | |
1086 | ||
1087 | repo = git_remote_owner(remote); | |
1088 | ||
1089 | if ((!git_reference__is_branch(ref_name)) || | |
1090 | !git_remote_name(remote) || | |
1091 | (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) || | |
1092 | git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || | |
1093 | (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || | |
1094 | !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || | |
1095 | (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) { | |
1096 | /* Not an error if there is no upstream */ | |
1097 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 1098 | git_error_clear(); |
2c9b9c8b CMN |
1099 | error = 0; |
1100 | } | |
1101 | ||
1102 | *update = 0; | |
1103 | } else { | |
1104 | *update = 1; | |
1105 | } | |
1106 | ||
ac3d33df JK |
1107 | git_buf_dispose(&upstream_remote); |
1108 | git_buf_dispose(&upstream_name); | |
2c9b9c8b CMN |
1109 | return error; |
1110 | } | |
1111 | ||
f49819aa | 1112 | static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref) |
b0f6e45d ET |
1113 | { |
1114 | git_reference *resolved_ref = NULL; | |
b0f6e45d | 1115 | git_buf remote_name = GIT_BUF_INIT; |
f49819aa | 1116 | git_config *config = NULL; |
e235db02 | 1117 | const char *ref_name; |
2c9b9c8b | 1118 | int error = 0, update; |
b0f6e45d | 1119 | |
4330ab26 | 1120 | assert(out && spec && ref); |
b0f6e45d ET |
1121 | |
1122 | *out = NULL; | |
1123 | ||
67d4997a CMN |
1124 | error = git_reference_resolve(&resolved_ref, ref); |
1125 | ||
1126 | /* If we're in an unborn branch, let's pretend nothing happened */ | |
ac3d33df | 1127 | if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { |
67d4997a CMN |
1128 | ref_name = git_reference_symbolic_target(ref); |
1129 | error = 0; | |
1130 | } else { | |
1131 | ref_name = git_reference_name(resolved_ref); | |
1132 | } | |
1133 | ||
2c9b9c8b | 1134 | if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0) |
b0f6e45d | 1135 | goto cleanup; |
b0f6e45d | 1136 | |
2c9b9c8b CMN |
1137 | if (update) |
1138 | error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); | |
b0f6e45d ET |
1139 | |
1140 | cleanup: | |
ac3d33df | 1141 | git_buf_dispose(&remote_name); |
b0f6e45d | 1142 | git_reference_free(resolved_ref); |
f49819aa | 1143 | git_config_free(config); |
b0f6e45d ET |
1144 | return error; |
1145 | } | |
1146 | ||
4330ab26 | 1147 | static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads) |
b0f6e45d | 1148 | { |
b0f6e45d ET |
1149 | git_reference *head_ref = NULL; |
1150 | git_fetchhead_ref *fetchhead_ref; | |
1151 | git_remote_head *remote_ref, *merge_remote_ref; | |
1152 | git_vector fetchhead_refs; | |
1153 | bool include_all_fetchheads; | |
1154 | unsigned int i = 0; | |
1155 | int error = 0; | |
1156 | ||
1157 | assert(remote); | |
1158 | ||
404eadb0 CMN |
1159 | /* no heads, nothing to do */ |
1160 | if (update_heads->length == 0) | |
1161 | return 0; | |
1162 | ||
b0f6e45d ET |
1163 | if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0) |
1164 | return -1; | |
1165 | ||
1166 | /* Iff refspec is * (but not subdir slash star), include tags */ | |
1167 | include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0); | |
1168 | ||
1169 | /* Determine what to merge: if refspec was a wildcard, just use HEAD */ | |
1170 | if (git_refspec_is_wildcard(spec)) { | |
1171 | if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 || | |
f49819aa | 1172 | (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0) |
b0f6e45d ET |
1173 | goto cleanup; |
1174 | } else { | |
1175 | /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */ | |
1176 | if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0) | |
1177 | goto cleanup; | |
1178 | } | |
1179 | ||
1180 | /* Create the FETCH_HEAD file */ | |
7d4b65f6 | 1181 | git_vector_foreach(update_heads, i, remote_ref) { |
b0f6e45d ET |
1182 | int merge_this_fetchhead = (merge_remote_ref == remote_ref); |
1183 | ||
1184 | if (!include_all_fetchheads && | |
1185 | !git_refspec_src_matches(spec, remote_ref->name) && | |
1186 | !merge_this_fetchhead) | |
1187 | continue; | |
1188 | ||
1189 | if (git_fetchhead_ref_create(&fetchhead_ref, | |
1190 | &remote_ref->oid, | |
1191 | merge_this_fetchhead, | |
1192 | remote_ref->name, | |
1193 | git_remote_url(remote)) < 0) | |
1194 | goto cleanup; | |
1195 | ||
1196 | if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0) | |
1197 | goto cleanup; | |
1198 | } | |
1199 | ||
1200 | git_fetchhead_write(remote->repo, &fetchhead_refs); | |
1201 | ||
1202 | cleanup: | |
1203 | for (i = 0; i < fetchhead_refs.length; ++i) | |
1204 | git_fetchhead_ref_free(fetchhead_refs.contents[i]); | |
1205 | ||
1206 | git_vector_free(&fetchhead_refs); | |
1207 | git_reference_free(head_ref); | |
1208 | ||
1209 | return error; | |
1210 | } | |
1211 | ||
59ff8b67 CMN |
1212 | /** |
1213 | * Generate a list of candidates for pruning by getting a list of | |
1214 | * references which match the rhs of an active refspec. | |
1215 | */ | |
1216 | static int prune_candidates(git_vector *candidates, git_remote *remote) | |
5f473947 L |
1217 | { |
1218 | git_strarray arr = { 0 }; | |
59ff8b67 | 1219 | size_t i; |
5f473947 L |
1220 | int error; |
1221 | ||
1222 | if ((error = git_reference_list(&arr, remote->repo)) < 0) | |
1223 | return error; | |
1224 | ||
59ff8b67 CMN |
1225 | for (i = 0; i < arr.count; i++) { |
1226 | const char *refname = arr.strings[i]; | |
1227 | char *refname_dup; | |
1228 | ||
1229 | if (!git_remote__matching_dst_refspec(remote, refname)) | |
1230 | continue; | |
1231 | ||
1232 | refname_dup = git__strdup(refname); | |
ac3d33df | 1233 | GIT_ERROR_CHECK_ALLOC(refname_dup); |
59ff8b67 CMN |
1234 | |
1235 | if ((error = git_vector_insert(candidates, refname_dup)) < 0) | |
1236 | goto out; | |
1237 | } | |
1238 | ||
1239 | out: | |
1240 | git_strarray_free(&arr); | |
1241 | return error; | |
1242 | } | |
1243 | ||
1244 | static int find_head(const void *_a, const void *_b) | |
1245 | { | |
1246 | git_remote_head *a = (git_remote_head *) _a; | |
1247 | git_remote_head *b = (git_remote_head *) _b; | |
1248 | ||
1249 | return strcmp(a->name, b->name); | |
1250 | } | |
1251 | ||
8f0104ec | 1252 | int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks) |
59ff8b67 CMN |
1253 | { |
1254 | size_t i, j; | |
1255 | git_vector remote_refs = GIT_VECTOR_INIT; | |
1256 | git_vector candidates = GIT_VECTOR_INIT; | |
1257 | const git_refspec *spec; | |
1258 | const char *refname; | |
1259 | int error; | |
1260 | git_oid zero_id = {{ 0 }}; | |
1261 | ||
8f0104ec | 1262 | if (callbacks) |
ac3d33df | 1263 | GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
8f0104ec | 1264 | |
5f473947 L |
1265 | if ((error = ls_to_vector(&remote_refs, remote)) < 0) |
1266 | goto cleanup; | |
1267 | ||
59ff8b67 | 1268 | git_vector_set_cmp(&remote_refs, find_head); |
5f473947 | 1269 | |
59ff8b67 CMN |
1270 | if ((error = prune_candidates(&candidates, remote)) < 0) |
1271 | goto cleanup; | |
5f473947 | 1272 | |
59ff8b67 CMN |
1273 | /* |
1274 | * Remove those entries from the candidate list for which we | |
1275 | * can find a remote reference in at least one refspec. | |
1276 | */ | |
1277 | git_vector_foreach(&candidates, i, refname) { | |
1278 | git_vector_foreach(&remote->active_refspecs, j, spec) { | |
1279 | git_buf buf = GIT_BUF_INIT; | |
1280 | size_t pos; | |
1281 | char *src_name; | |
1282 | git_remote_head key = {0}; | |
1283 | ||
1284 | if (!git_refspec_dst_matches(spec, refname)) | |
5f473947 L |
1285 | continue; |
1286 | ||
59ff8b67 CMN |
1287 | if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0) |
1288 | goto cleanup; | |
5f473947 | 1289 | |
59ff8b67 | 1290 | key.name = (char *) git_buf_cstr(&buf); |
6c7cee42 | 1291 | error = git_vector_bsearch(&pos, &remote_refs, &key); |
ac3d33df | 1292 | git_buf_dispose(&buf); |
5f473947 | 1293 | |
59ff8b67 CMN |
1294 | if (error < 0 && error != GIT_ENOTFOUND) |
1295 | goto cleanup; | |
5f473947 | 1296 | |
59ff8b67 | 1297 | if (error == GIT_ENOTFOUND) |
5f473947 L |
1298 | continue; |
1299 | ||
59ff8b67 CMN |
1300 | /* if we did find a source, remove it from the candiates */ |
1301 | if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0) | |
1302 | goto cleanup; | |
1303 | ||
1304 | git__free(src_name); | |
1305 | break; | |
5f473947 L |
1306 | } |
1307 | } | |
1308 | ||
59ff8b67 CMN |
1309 | /* |
1310 | * For those candidates still left in the list, we need to | |
1311 | * remove them. We do not remove symrefs, as those are for | |
1312 | * stuff like origin/HEAD which will never match, but we do | |
1313 | * not want to remove them. | |
1314 | */ | |
1315 | git_vector_foreach(&candidates, i, refname) { | |
1316 | git_reference *ref; | |
1317 | git_oid id; | |
1318 | ||
1319 | if (refname == NULL) | |
1320 | continue; | |
1321 | ||
1322 | error = git_reference_lookup(&ref, remote->repo, refname); | |
1323 | /* as we want it gone, let's not consider this an error */ | |
1324 | if (error == GIT_ENOTFOUND) | |
1325 | continue; | |
1326 | ||
1327 | if (error < 0) | |
1328 | goto cleanup; | |
1329 | ||
ac3d33df | 1330 | if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { |
59ff8b67 CMN |
1331 | git_reference_free(ref); |
1332 | continue; | |
1333 | } | |
1334 | ||
1335 | git_oid_cpy(&id, git_reference_target(ref)); | |
1336 | error = git_reference_delete(ref); | |
1337 | git_reference_free(ref); | |
1338 | if (error < 0) | |
1339 | goto cleanup; | |
1340 | ||
8f0104ec CMN |
1341 | if (callbacks && callbacks->update_tips) |
1342 | error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload); | |
59ff8b67 CMN |
1343 | |
1344 | if (error < 0) | |
1345 | goto cleanup; | |
1346 | } | |
1347 | ||
5f473947 | 1348 | cleanup: |
5f473947 | 1349 | git_vector_free(&remote_refs); |
59ff8b67 | 1350 | git_vector_free_deep(&candidates); |
5f473947 L |
1351 | return error; |
1352 | } | |
1353 | ||
c3ab1e5a BS |
1354 | static int update_tips_for_spec( |
1355 | git_remote *remote, | |
8f0104ec | 1356 | const git_remote_callbacks *callbacks, |
3eff2a57 | 1357 | int update_fetchhead, |
35a8a8c5 | 1358 | git_remote_autotag_option_t tagopt, |
c3ab1e5a BS |
1359 | git_refspec *spec, |
1360 | git_vector *refs, | |
c3ab1e5a | 1361 | const char *log_message) |
441f57c2 | 1362 | { |
a37ddf7e | 1363 | int error = 0, autotag; |
517bda19 | 1364 | unsigned int i = 0; |
97769280 | 1365 | git_buf refname = GIT_BUF_INIT; |
f184836b | 1366 | git_oid old; |
a37ddf7e | 1367 | git_odb *odb; |
441f57c2 CMN |
1368 | git_remote_head *head; |
1369 | git_reference *ref; | |
a37ddf7e | 1370 | git_refspec tagspec; |
4330ab26 | 1371 | git_vector update_heads; |
441f57c2 | 1372 | |
4bef3565 VM |
1373 | assert(remote); |
1374 | ||
acd17006 | 1375 | if (git_repository_odb__weakptr(&odb, remote->repo) < 0) |
a37ddf7e CMN |
1376 | return -1; |
1377 | ||
3230a44f | 1378 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
a37ddf7e CMN |
1379 | return -1; |
1380 | ||
41fb1ca0 | 1381 | /* Make a copy of the transport's refs */ |
4330ab26 | 1382 | if (git_vector_init(&update_heads, 16, NULL) < 0) |
41fb1ca0 | 1383 | return -1; |
585a2eb7 | 1384 | |
4330ab26 CMN |
1385 | for (; i < refs->length; ++i) { |
1386 | head = git_vector_get(refs, i); | |
9063be1f | 1387 | autotag = 0; |
e3e017d4 | 1388 | git_buf_clear(&refname); |
517bda19 | 1389 | |
a37ddf7e CMN |
1390 | /* Ignore malformed ref names (which also saves us from tag^{} */ |
1391 | if (!git_reference_is_valid_name(head->name)) | |
1392 | continue; | |
1393 | ||
e3e017d4 | 1394 | /* If we have a tag, see if the auto-follow rules say to update it */ |
e284c451 | 1395 | if (git_refspec_src_matches(&tagspec, head->name)) { |
35a8a8c5 | 1396 | if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { |
3230a44f | 1397 | |
35a8a8c5 | 1398 | if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO) |
e284c451 | 1399 | autotag = 1; |
a37ddf7e | 1400 | |
e284c451 POL |
1401 | git_buf_clear(&refname); |
1402 | if (git_buf_puts(&refname, head->name) < 0) | |
1403 | goto on_error; | |
e284c451 | 1404 | } |
e3e017d4 CMN |
1405 | } |
1406 | ||
1407 | /* If we didn't want to auto-follow the tag, check if the refspec matches */ | |
1408 | if (!autotag && git_refspec_src_matches(spec, head->name)) { | |
23aa7c90 CMN |
1409 | if (spec->dst) { |
1410 | if (git_refspec_transform(&refname, spec, head->name) < 0) | |
1411 | goto on_error; | |
1412 | } else { | |
1413 | /* | |
1414 | * no rhs mans store it in FETCH_HEAD, even if we don't | |
1415 | update anything else. | |
1416 | */ | |
1417 | if ((error = git_vector_insert(&update_heads, head)) < 0) | |
1418 | goto on_error; | |
1419 | ||
1420 | continue; | |
1421 | } | |
e3e017d4 CMN |
1422 | } |
1423 | ||
1424 | /* If we still don't have a refname, we don't want it */ | |
1425 | if (git_buf_len(&refname) == 0) { | |
a37ddf7e CMN |
1426 | continue; |
1427 | } | |
1428 | ||
e284c451 | 1429 | /* In autotag mode, only create tags for objects already in db */ |
a37ddf7e CMN |
1430 | if (autotag && !git_odb_exists(odb, &head->oid)) |
1431 | continue; | |
f184836b | 1432 | |
d908351a | 1433 | if (!autotag && git_vector_insert(&update_heads, head) < 0) |
b0f6e45d ET |
1434 | goto on_error; |
1435 | ||
2508cc66 | 1436 | error = git_reference_name_to_id(&old, remote->repo, refname.ptr); |
904b67e6 | 1437 | if (error < 0 && error != GIT_ENOTFOUND) |
f184836b CMN |
1438 | goto on_error; |
1439 | ||
d908351a | 1440 | if (error == GIT_ENOTFOUND) { |
f184836b CMN |
1441 | memset(&old, 0, GIT_OID_RAWSZ); |
1442 | ||
d908351a L |
1443 | if (autotag && git_vector_insert(&update_heads, head) < 0) |
1444 | goto on_error; | |
1445 | } | |
1446 | ||
b7f167da | 1447 | if (!git_oid__cmp(&old, &head->oid)) |
f184836b | 1448 | continue; |
441f57c2 | 1449 | |
a37ddf7e | 1450 | /* In autotag mode, don't overwrite any locally-existing tags */ |
ac3d33df | 1451 | error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, |
659cf202 | 1452 | log_message); |
7f9673e4 JH |
1453 | |
1454 | if (error == GIT_EEXISTS) | |
1455 | continue; | |
1456 | ||
1457 | if (error < 0) | |
944d250f | 1458 | goto on_error; |
39157563 CMN |
1459 | |
1460 | git_reference_free(ref); | |
f184836b | 1461 | |
8f0104ec CMN |
1462 | if (callbacks && callbacks->update_tips != NULL) { |
1463 | if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0) | |
f184836b CMN |
1464 | goto on_error; |
1465 | } | |
441f57c2 CMN |
1466 | } |
1467 | ||
3eff2a57 | 1468 | if (update_fetchhead && |
4330ab26 | 1469 | (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0) |
b0f6e45d ET |
1470 | goto on_error; |
1471 | ||
b0f6e45d | 1472 | git_vector_free(&update_heads); |
ac3d33df JK |
1473 | git_refspec__dispose(&tagspec); |
1474 | git_buf_dispose(&refname); | |
f184836b CMN |
1475 | return 0; |
1476 | ||
1477 | on_error: | |
b0f6e45d | 1478 | git_vector_free(&update_heads); |
ac3d33df JK |
1479 | git_refspec__dispose(&tagspec); |
1480 | git_buf_dispose(&refname); | |
f184836b | 1481 | return -1; |
97769280 | 1482 | |
441f57c2 CMN |
1483 | } |
1484 | ||
c5837cad CMN |
1485 | /** |
1486 | * Iteration over the three vectors, with a pause whenever we find a match | |
1487 | * | |
1488 | * On each stop, we store the iteration stat in the inout i,j,k | |
1489 | * parameters, and return the currently matching passive refspec as | |
1490 | * well as the head which we matched. | |
1491 | */ | |
1492 | static int next_head(const git_remote *remote, git_vector *refs, | |
1493 | git_refspec **out_spec, git_remote_head **out_head, | |
1494 | size_t *out_i, size_t *out_j, size_t *out_k) | |
1495 | { | |
1496 | const git_vector *active, *passive; | |
1497 | git_remote_head *head; | |
1498 | git_refspec *spec, *passive_spec; | |
1499 | size_t i, j, k; | |
1500 | ||
1501 | active = &remote->active_refspecs; | |
1502 | passive = &remote->passive_refspecs; | |
1503 | ||
1504 | i = *out_i; | |
1505 | j = *out_j; | |
1506 | k = *out_k; | |
1507 | ||
1508 | for (; i < refs->length; i++) { | |
1509 | head = git_vector_get(refs, i); | |
1510 | ||
1511 | if (!git_reference_is_valid_name(head->name)) | |
1512 | continue; | |
1513 | ||
1514 | for (; j < active->length; j++) { | |
1515 | spec = git_vector_get(active, j); | |
1516 | ||
1517 | if (!git_refspec_src_matches(spec, head->name)) | |
1518 | continue; | |
1519 | ||
1520 | for (; k < passive->length; k++) { | |
1521 | passive_spec = git_vector_get(passive, k); | |
1522 | ||
1523 | if (!git_refspec_src_matches(passive_spec, head->name)) | |
1524 | continue; | |
1525 | ||
1526 | *out_spec = passive_spec; | |
1527 | *out_head = head; | |
1528 | *out_i = i; | |
1529 | *out_j = j; | |
1530 | *out_k = k + 1; | |
1531 | return 0; | |
1532 | ||
1533 | } | |
1534 | k = 0; | |
1535 | } | |
1536 | j = 0; | |
1537 | } | |
1538 | ||
1539 | return GIT_ITEROVER; | |
1540 | } | |
1541 | ||
9566ce43 CMN |
1542 | static int opportunistic_updates(const git_remote *remote, const git_remote_callbacks *callbacks, |
1543 | git_vector *refs, const char *msg) | |
c5837cad CMN |
1544 | { |
1545 | size_t i, j, k; | |
1546 | git_refspec *spec; | |
1547 | git_remote_head *head; | |
1548 | git_reference *ref; | |
1549 | git_buf refname = GIT_BUF_INIT; | |
9566ce43 | 1550 | int error = 0; |
c5837cad CMN |
1551 | |
1552 | i = j = k = 0; | |
1553 | ||
1554 | while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) { | |
9566ce43 | 1555 | git_oid old = {{ 0 }}; |
c5837cad CMN |
1556 | /* |
1557 | * If we got here, there is a refspec which was used | |
1558 | * for fetching which matches the source of one of the | |
1559 | * passive refspecs, so we should update that | |
1560 | * remote-tracking branch, but not add it to | |
1561 | * FETCH_HEAD | |
1562 | */ | |
1563 | ||
9566ce43 | 1564 | git_buf_clear(&refname); |
c5837cad | 1565 | if ((error = git_refspec_transform(&refname, spec, head->name)) < 0) |
9566ce43 | 1566 | goto cleanup; |
c5837cad | 1567 | |
9566ce43 CMN |
1568 | error = git_reference_name_to_id(&old, remote->repo, refname.ptr); |
1569 | if (error < 0 && error != GIT_ENOTFOUND) | |
1570 | goto cleanup; | |
1571 | ||
1572 | if (!git_oid_cmp(&old, &head->oid)) | |
1573 | continue; | |
c5837cad | 1574 | |
9566ce43 CMN |
1575 | /* If we did find a current reference, make sure we haven't lost a race */ |
1576 | if (error) | |
1577 | error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg); | |
1578 | else | |
1579 | error = git_reference_create_matching(&ref, remote->repo, refname.ptr, &head->oid, true, &old, msg); | |
1580 | git_reference_free(ref); | |
c5837cad | 1581 | if (error < 0) |
9566ce43 CMN |
1582 | goto cleanup; |
1583 | ||
1584 | if (callbacks && callbacks->update_tips != NULL) { | |
1585 | if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0) | |
1586 | goto cleanup; | |
1587 | } | |
c5837cad CMN |
1588 | } |
1589 | ||
9566ce43 CMN |
1590 | if (error == GIT_ITEROVER) |
1591 | error = 0; | |
1592 | ||
1593 | cleanup: | |
ac3d33df | 1594 | git_buf_dispose(&refname); |
9566ce43 | 1595 | return error; |
c5837cad CMN |
1596 | } |
1597 | ||
eae0bfdc PP |
1598 | static int truncate_fetch_head(const char *gitdir) |
1599 | { | |
1600 | git_buf path = GIT_BUF_INIT; | |
1601 | int error; | |
1602 | ||
1603 | if ((error = git_buf_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0) | |
1604 | return error; | |
1605 | ||
1606 | error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE); | |
ac3d33df | 1607 | git_buf_dispose(&path); |
eae0bfdc PP |
1608 | |
1609 | return error; | |
1610 | } | |
1611 | ||
c3ab1e5a BS |
1612 | int git_remote_update_tips( |
1613 | git_remote *remote, | |
8f0104ec | 1614 | const git_remote_callbacks *callbacks, |
3eff2a57 | 1615 | int update_fetchhead, |
35a8a8c5 | 1616 | git_remote_autotag_option_t download_tags, |
c3ab1e5a | 1617 | const char *reflog_message) |
4330ab26 | 1618 | { |
505b5d0c | 1619 | git_refspec *spec, tagspec; |
cdedef40 | 1620 | git_vector refs = GIT_VECTOR_INIT; |
35a8a8c5 | 1621 | git_remote_autotag_option_t tagopt; |
505b5d0c | 1622 | int error; |
4330ab26 CMN |
1623 | size_t i; |
1624 | ||
fe794b2e CMN |
1625 | /* push has its own logic hidden away in the push object */ |
1626 | if (remote->push) { | |
8f0104ec | 1627 | return git_push_update_tips(remote->push, callbacks); |
fe794b2e CMN |
1628 | } |
1629 | ||
505b5d0c CMN |
1630 | if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) |
1631 | return -1; | |
1632 | ||
877cde76 CMN |
1633 | |
1634 | if ((error = ls_to_vector(&refs, remote)) < 0) | |
505b5d0c CMN |
1635 | goto out; |
1636 | ||
c2418f46 | 1637 | if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED) |
35a8a8c5 CMN |
1638 | tagopt = remote->download_tags; |
1639 | else | |
1640 | tagopt = download_tags; | |
1641 | ||
eae0bfdc PP |
1642 | if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0) |
1643 | goto out; | |
1644 | ||
35a8a8c5 CMN |
1645 | if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { |
1646 | if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0) | |
e284c451 | 1647 | goto out; |
505b5d0c | 1648 | } |
4330ab26 | 1649 | |
af613ecd | 1650 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
1651 | if (spec->push) |
1652 | continue; | |
1653 | ||
35a8a8c5 | 1654 | if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0) |
505b5d0c | 1655 | goto out; |
4330ab26 CMN |
1656 | } |
1657 | ||
c5837cad CMN |
1658 | /* only try to do opportunisitic updates if the refpec lists differ */ |
1659 | if (remote->passed_refspecs) | |
9566ce43 | 1660 | error = opportunistic_updates(remote, callbacks, &refs, reflog_message); |
c5837cad | 1661 | |
505b5d0c | 1662 | out: |
877cde76 | 1663 | git_vector_free(&refs); |
ac3d33df | 1664 | git_refspec__dispose(&tagspec); |
505b5d0c | 1665 | return error; |
4330ab26 CMN |
1666 | } |
1667 | ||
11f6ad5f | 1668 | int git_remote_connected(const git_remote *remote) |
6ac3b707 | 1669 | { |
4bef3565 | 1670 | assert(remote); |
41fb1ca0 PK |
1671 | |
1672 | if (!remote->transport || !remote->transport->is_connected) | |
1673 | return 0; | |
1674 | ||
1675 | /* Ask the transport if it's connected. */ | |
613d5eb9 | 1676 | return remote->transport->is_connected(remote->transport); |
6ac3b707 CMN |
1677 | } |
1678 | ||
0c9c969a | 1679 | int git_remote_stop(git_remote *remote) |
f0d2ddbb | 1680 | { |
613d5eb9 PK |
1681 | assert(remote); |
1682 | ||
1683 | if (remote->transport && remote->transport->cancel) | |
41fb1ca0 | 1684 | remote->transport->cancel(remote->transport); |
0c9c969a UG |
1685 | |
1686 | return 0; | |
f0d2ddbb CMN |
1687 | } |
1688 | ||
0c9c969a | 1689 | int git_remote_disconnect(git_remote *remote) |
4cf01e9a | 1690 | { |
4bef3565 VM |
1691 | assert(remote); |
1692 | ||
41fb1ca0 PK |
1693 | if (git_remote_connected(remote)) |
1694 | remote->transport->close(remote->transport); | |
0c9c969a UG |
1695 | |
1696 | return 0; | |
4cf01e9a CMN |
1697 | } |
1698 | ||
9c82357b CMN |
1699 | void git_remote_free(git_remote *remote) |
1700 | { | |
2aae2188 CMN |
1701 | if (remote == NULL) |
1702 | return; | |
1703 | ||
42ea35c0 MS |
1704 | if (remote->transport != NULL) { |
1705 | git_remote_disconnect(remote); | |
1706 | ||
1707 | remote->transport->free(remote->transport); | |
1708 | remote->transport = NULL; | |
1709 | } | |
1710 | ||
1711 | git_vector_free(&remote->refs); | |
1712 | ||
af613ecd | 1713 | free_refspecs(&remote->refspecs); |
4330ab26 CMN |
1714 | git_vector_free(&remote->refspecs); |
1715 | ||
af613ecd CMN |
1716 | free_refspecs(&remote->active_refspecs); |
1717 | git_vector_free(&remote->active_refspecs); | |
1718 | ||
2cdd5c57 CMN |
1719 | free_refspecs(&remote->passive_refspecs); |
1720 | git_vector_free(&remote->passive_refspecs); | |
1721 | ||
fe794b2e | 1722 | git_push_free(remote->push); |
3286c408 | 1723 | git__free(remote->url); |
3ed4b501 | 1724 | git__free(remote->pushurl); |
3286c408 | 1725 | git__free(remote->name); |
3286c408 | 1726 | git__free(remote); |
9c82357b | 1727 | } |
8171998f | 1728 | |
106c12f1 | 1729 | static int remote_list_cb(const git_config_entry *entry, void *payload) |
8171998f | 1730 | { |
25e0b157 | 1731 | git_vector *list = payload; |
106c12f1 RB |
1732 | const char *name = entry->name + strlen("remote."); |
1733 | size_t namelen = strlen(name); | |
1734 | char *remote_name; | |
8171998f | 1735 | |
106c12f1 | 1736 | /* we know name matches "remote.<stuff>.(push)?url" */ |
8171998f | 1737 | |
106c12f1 RB |
1738 | if (!strcmp(&name[namelen - 4], ".url")) |
1739 | remote_name = git__strndup(name, namelen - 4); /* strip ".url" */ | |
1740 | else | |
1741 | remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */ | |
ac3d33df | 1742 | GIT_ERROR_CHECK_ALLOC(remote_name); |
8171998f | 1743 | |
25e0b157 | 1744 | return git_vector_insert(list, remote_name); |
8171998f CMN |
1745 | } |
1746 | ||
1747 | int git_remote_list(git_strarray *remotes_list, git_repository *repo) | |
1748 | { | |
8171998f | 1749 | int error; |
96869a4e | 1750 | git_config *cfg; |
25e0b157 | 1751 | git_vector list = GIT_VECTOR_INIT; |
8171998f | 1752 | |
96869a4e RB |
1753 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) |
1754 | return error; | |
8171998f | 1755 | |
25e0b157 | 1756 | if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) |
96869a4e | 1757 | return error; |
8171998f | 1758 | |
106c12f1 | 1759 | error = git_config_foreach_match( |
25e0b157 | 1760 | cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); |
8171998f | 1761 | |
4376f7f6 | 1762 | if (error < 0) { |
9cfce273 | 1763 | git_vector_free_deep(&list); |
8171998f CMN |
1764 | return error; |
1765 | } | |
1766 | ||
25e0b157 | 1767 | git_vector_uniq(&list, git__free); |
aec87f71 | 1768 | |
25e0b157 RB |
1769 | remotes_list->strings = |
1770 | (char **)git_vector_detach(&remotes_list->count, NULL, &list); | |
8171998f | 1771 | |
4376f7f6 | 1772 | return 0; |
8171998f | 1773 | } |
a209a025 | 1774 | |
0c9c969a | 1775 | const git_indexer_progress *git_remote_stats(git_remote *remote) |
d57c47dc BS |
1776 | { |
1777 | assert(remote); | |
1778 | return &remote->stats; | |
1779 | } | |
1780 | ||
11f6ad5f | 1781 | git_remote_autotag_option_t git_remote_autotag(const git_remote *remote) |
f70e466f CMN |
1782 | { |
1783 | return remote->download_tags; | |
1784 | } | |
1785 | ||
35a8a8c5 | 1786 | int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value) |
f70e466f | 1787 | { |
35a8a8c5 CMN |
1788 | git_buf var = GIT_BUF_INIT; |
1789 | git_config *config; | |
1790 | int error; | |
1791 | ||
1792 | assert(repo && remote); | |
1793 | ||
1794 | if ((error = ensure_remote_name_is_valid(remote)) < 0) | |
1795 | return error; | |
1796 | ||
1797 | if ((error = git_repository_config__weakptr(&config, repo)) < 0) | |
1798 | return error; | |
1799 | ||
1800 | if ((error = git_buf_printf(&var, CONFIG_TAGOPT_FMT, remote))) | |
1801 | return error; | |
1802 | ||
1803 | switch (value) { | |
1804 | case GIT_REMOTE_DOWNLOAD_TAGS_NONE: | |
1805 | error = git_config_set_string(config, var.ptr, "--no-tags"); | |
1806 | break; | |
1807 | case GIT_REMOTE_DOWNLOAD_TAGS_ALL: | |
1808 | error = git_config_set_string(config, var.ptr, "--tags"); | |
1809 | break; | |
1810 | case GIT_REMOTE_DOWNLOAD_TAGS_AUTO: | |
1811 | error = git_config_delete_entry(config, var.ptr); | |
1812 | if (error == GIT_ENOTFOUND) | |
1813 | error = 0; | |
1814 | break; | |
1815 | default: | |
ac3d33df | 1816 | git_error_set(GIT_ERROR_INVALID, "invalid value for the tagopt setting"); |
35a8a8c5 CMN |
1817 | error = -1; |
1818 | } | |
1819 | ||
ac3d33df | 1820 | git_buf_dispose(&var); |
35a8a8c5 | 1821 | return error; |
f70e466f | 1822 | } |
fcccf304 | 1823 | |
5f473947 L |
1824 | int git_remote_prune_refs(const git_remote *remote) |
1825 | { | |
1826 | return remote->prune_refs; | |
1827 | } | |
1828 | ||
fcccf304 | 1829 | static int rename_remote_config_section( |
1830 | git_repository *repo, | |
1831 | const char *old_name, | |
1832 | const char *new_name) | |
1833 | { | |
1834 | git_buf old_section_name = GIT_BUF_INIT, | |
1835 | new_section_name = GIT_BUF_INIT; | |
1836 | int error = -1; | |
1837 | ||
1838 | if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) | |
1839 | goto cleanup; | |
1840 | ||
40e48ea4 | 1841 | if (new_name && |
1842 | (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)) | |
1843 | goto cleanup; | |
fcccf304 | 1844 | |
1845 | error = git_config_rename_section( | |
1846 | repo, | |
1847 | git_buf_cstr(&old_section_name), | |
40e48ea4 | 1848 | new_name ? git_buf_cstr(&new_section_name) : NULL); |
fcccf304 | 1849 | |
1850 | cleanup: | |
ac3d33df JK |
1851 | git_buf_dispose(&old_section_name); |
1852 | git_buf_dispose(&new_section_name); | |
fcccf304 | 1853 | |
1854 | return error; | |
1855 | } | |
1856 | ||
96869a4e | 1857 | struct update_data { |
fcccf304 | 1858 | git_config *config; |
1859 | const char *old_remote_name; | |
1860 | const char *new_remote_name; | |
1861 | }; | |
1862 | ||
1863 | static int update_config_entries_cb( | |
1864 | const git_config_entry *entry, | |
1865 | void *payload) | |
1866 | { | |
1867 | struct update_data *data = (struct update_data *)payload; | |
1868 | ||
1869 | if (strcmp(entry->value, data->old_remote_name)) | |
1870 | return 0; | |
1871 | ||
25e0b157 RB |
1872 | return git_config_set_string( |
1873 | data->config, entry->name, data->new_remote_name); | |
fcccf304 | 1874 | } |
1875 | ||
1876 | static int update_branch_remote_config_entry( | |
1877 | git_repository *repo, | |
1878 | const char *old_name, | |
1879 | const char *new_name) | |
1880 | { | |
96869a4e RB |
1881 | int error; |
1882 | struct update_data data = { NULL }; | |
fcccf304 | 1883 | |
96869a4e RB |
1884 | if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) |
1885 | return error; | |
fcccf304 | 1886 | |
fcccf304 | 1887 | data.old_remote_name = old_name; |
1888 | data.new_remote_name = new_name; | |
1889 | ||
25e0b157 | 1890 | return git_config_foreach_match( |
96869a4e | 1891 | data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); |
fcccf304 | 1892 | } |
1893 | ||
fcccf304 | 1894 | static int rename_one_remote_reference( |
d1544564 | 1895 | git_reference *reference_in, |
fcccf304 | 1896 | const char *old_remote_name, |
1897 | const char *new_remote_name) | |
1898 | { | |
96869a4e | 1899 | int error; |
d1544564 CMN |
1900 | git_reference *ref = NULL, *dummy = NULL; |
1901 | git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT; | |
fcccf304 | 1902 | git_buf new_name = GIT_BUF_INIT; |
ccf6ce5c | 1903 | git_buf log_message = GIT_BUF_INIT; |
d1544564 CMN |
1904 | size_t pfx_len; |
1905 | const char *target; | |
fcccf304 | 1906 | |
d1544564 CMN |
1907 | if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0) |
1908 | return error; | |
1909 | ||
1910 | pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1; | |
1911 | git_buf_puts(&new_name, namespace.ptr); | |
1912 | if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0) | |
ccf6ce5c | 1913 | goto cleanup; |
fcccf304 | 1914 | |
ccf6ce5c BS |
1915 | if ((error = git_buf_printf(&log_message, |
1916 | "renamed remote %s to %s", | |
1917 | old_remote_name, new_remote_name)) < 0) | |
1918 | goto cleanup; | |
fcccf304 | 1919 | |
d1544564 | 1920 | if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1, |
659cf202 | 1921 | git_buf_cstr(&log_message))) < 0) |
d1544564 CMN |
1922 | goto cleanup; |
1923 | ||
ac3d33df | 1924 | if (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC) |
d1544564 CMN |
1925 | goto cleanup; |
1926 | ||
1927 | /* Handle refs like origin/HEAD -> origin/master */ | |
1928 | target = git_reference_symbolic_target(ref); | |
1929 | if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0) | |
1930 | goto cleanup; | |
1931 | ||
1932 | if (git__prefixcmp(target, old_namespace.ptr)) | |
1933 | goto cleanup; | |
1934 | ||
1935 | git_buf_clear(&new_name); | |
1936 | git_buf_puts(&new_name, namespace.ptr); | |
1937 | if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0) | |
1938 | goto cleanup; | |
1939 | ||
1940 | error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name), | |
659cf202 | 1941 | git_buf_cstr(&log_message)); |
d1544564 CMN |
1942 | |
1943 | git_reference_free(dummy); | |
ccf6ce5c BS |
1944 | |
1945 | cleanup: | |
d1544564 CMN |
1946 | git_reference_free(reference_in); |
1947 | git_reference_free(ref); | |
ac3d33df JK |
1948 | git_buf_dispose(&namespace); |
1949 | git_buf_dispose(&old_namespace); | |
1950 | git_buf_dispose(&new_name); | |
1951 | git_buf_dispose(&log_message); | |
fcccf304 | 1952 | return error; |
1953 | } | |
1954 | ||
1955 | static int rename_remote_references( | |
1956 | git_repository *repo, | |
1957 | const char *old_name, | |
1958 | const char *new_name) | |
1959 | { | |
96869a4e | 1960 | int error; |
a52ab4b8 | 1961 | git_buf buf = GIT_BUF_INIT; |
56960b83 | 1962 | git_reference *ref; |
9bd89d96 | 1963 | git_reference_iterator *iter; |
fcccf304 | 1964 | |
a52ab4b8 | 1965 | if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0) |
96869a4e | 1966 | return error; |
fcccf304 | 1967 | |
a52ab4b8 | 1968 | error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf)); |
ac3d33df | 1969 | git_buf_dispose(&buf); |
9bd89d96 | 1970 | |
a52ab4b8 CMN |
1971 | if (error < 0) |
1972 | return error; | |
1973 | ||
1974 | while ((error = git_reference_next(&ref, iter)) == 0) { | |
96869a4e RB |
1975 | if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) |
1976 | break; | |
9bd89d96 CMN |
1977 | } |
1978 | ||
1979 | git_reference_iterator_free(iter); | |
fcccf304 | 1980 | |
96869a4e | 1981 | return (error == GIT_ITEROVER) ? 0 : error; |
fcccf304 | 1982 | } |
1983 | ||
72bca13e | 1984 | static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name) |
fcccf304 | 1985 | { |
1986 | git_config *config; | |
4330ab26 | 1987 | git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; |
1be680c4 | 1988 | const git_refspec *spec; |
4330ab26 | 1989 | size_t i; |
25e0b157 | 1990 | int error = 0; |
fcccf304 | 1991 | |
dab89f9b | 1992 | if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) |
25e0b157 RB |
1993 | return error; |
1994 | ||
72bca13e CMN |
1995 | if ((error = git_vector_init(problems, 1, NULL)) < 0) |
1996 | return error; | |
1997 | ||
ac3d33df | 1998 | if ((error = default_fetchspec_for_name(&base, remote->name)) < 0) |
25e0b157 | 1999 | return error; |
dab89f9b | 2000 | |
1be680c4 | 2001 | git_vector_foreach(&remote->refspecs, i, spec) { |
4330ab26 CMN |
2002 | if (spec->push) |
2003 | continue; | |
fcccf304 | 2004 | |
dab89f9b | 2005 | /* Does the dst part of the refspec follow the expected format? */ |
5a49ff9f | 2006 | if (strcmp(git_buf_cstr(&base), spec->string)) { |
72bca13e | 2007 | char *dup; |
fcccf304 | 2008 | |
72bca13e | 2009 | dup = git__strdup(spec->string); |
ac3d33df | 2010 | GIT_ERROR_CHECK_ALLOC(dup); |
72bca13e CMN |
2011 | |
2012 | if ((error = git_vector_insert(problems, dup)) < 0) | |
25e0b157 | 2013 | break; |
c7b3e1b3 | 2014 | |
4330ab26 CMN |
2015 | continue; |
2016 | } | |
fcccf304 | 2017 | |
4330ab26 | 2018 | /* If we do want to move it to the new section */ |
fcccf304 | 2019 | |
dab89f9b RB |
2020 | git_buf_clear(&val); |
2021 | git_buf_clear(&var); | |
fcccf304 | 2022 | |
ac3d33df | 2023 | if (default_fetchspec_for_name(&val, new_name) < 0 || |
dab89f9b RB |
2024 | git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) |
2025 | { | |
2026 | error = -1; | |
25e0b157 | 2027 | break; |
dab89f9b | 2028 | } |
fcccf304 | 2029 | |
dab89f9b RB |
2030 | if ((error = git_config_set_string( |
2031 | config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) | |
25e0b157 | 2032 | break; |
4330ab26 | 2033 | } |
fcccf304 | 2034 | |
ac3d33df JK |
2035 | git_buf_dispose(&base); |
2036 | git_buf_dispose(&var); | |
2037 | git_buf_dispose(&val); | |
72bca13e CMN |
2038 | |
2039 | if (error < 0) { | |
2040 | char *str; | |
2041 | git_vector_foreach(problems, i, str) | |
2042 | git__free(str); | |
2043 | ||
2044 | git_vector_free(problems); | |
2045 | } | |
2046 | ||
fcccf304 | 2047 | return error; |
2048 | } | |
2049 | ||
46c8f7f8 | 2050 | int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name) |
fcccf304 | 2051 | { |
2052 | int error; | |
46c8f7f8 | 2053 | git_vector problem_refspecs = GIT_VECTOR_INIT; |
64bcf567 | 2054 | git_remote *remote = NULL; |
fcccf304 | 2055 | |
46c8f7f8 | 2056 | assert(out && repo && name && new_name); |
fcccf304 | 2057 | |
209425ce | 2058 | if ((error = git_remote_lookup(&remote, repo, name)) < 0) |
cce27d82 | 2059 | return error; |
79000951 | 2060 | |
fcccf304 | 2061 | if ((error = ensure_remote_name_is_valid(new_name)) < 0) |
46c8f7f8 | 2062 | goto cleanup; |
fcccf304 | 2063 | |
46c8f7f8 CMN |
2064 | if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0) |
2065 | goto cleanup; | |
fcccf304 | 2066 | |
46c8f7f8 CMN |
2067 | if ((error = rename_remote_config_section(repo, name, new_name)) < 0) |
2068 | goto cleanup; | |
fcccf304 | 2069 | |
46c8f7f8 CMN |
2070 | if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0) |
2071 | goto cleanup; | |
fcccf304 | 2072 | |
46c8f7f8 CMN |
2073 | if ((error = rename_remote_references(repo, name, new_name)) < 0) |
2074 | goto cleanup; | |
fcccf304 | 2075 | |
72bca13e | 2076 | if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0) |
46c8f7f8 | 2077 | goto cleanup; |
fcccf304 | 2078 | |
72bca13e CMN |
2079 | out->count = problem_refspecs.length; |
2080 | out->strings = (char **) problem_refspecs.contents; | |
2081 | ||
46c8f7f8 CMN |
2082 | cleanup: |
2083 | if (error < 0) | |
2084 | git_vector_free(&problem_refspecs); | |
fcccf304 | 2085 | |
46c8f7f8 CMN |
2086 | git_remote_free(remote); |
2087 | return error; | |
fcccf304 | 2088 | } |
b0f6e45d | 2089 | |
2bca5b67 | 2090 | int git_remote_is_valid_name( |
2091 | const char *remote_name) | |
2092 | { | |
2093 | git_buf buf = GIT_BUF_INIT; | |
2094 | git_refspec refspec; | |
2095 | int error = -1; | |
2096 | ||
2097 | if (!remote_name || *remote_name == '\0') | |
2098 | return 0; | |
2099 | ||
2100 | git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name); | |
2101 | error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true); | |
2102 | ||
ac3d33df JK |
2103 | git_buf_dispose(&buf); |
2104 | git_refspec__dispose(&refspec); | |
2bca5b67 | 2105 | |
ac3d33df | 2106 | git_error_clear(); |
2bca5b67 | 2107 | return error == 0; |
2108 | } | |
4330ab26 CMN |
2109 | |
2110 | git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname) | |
2111 | { | |
2112 | git_refspec *spec; | |
2113 | size_t i; | |
2114 | ||
af613ecd | 2115 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
2116 | if (spec->push) |
2117 | continue; | |
2118 | ||
2119 | if (git_refspec_src_matches(spec, refname)) | |
2120 | return spec; | |
2121 | } | |
2122 | ||
2123 | return NULL; | |
2124 | } | |
2125 | ||
2126 | git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname) | |
2127 | { | |
2128 | git_refspec *spec; | |
2129 | size_t i; | |
2130 | ||
af613ecd | 2131 | git_vector_foreach(&remote->active_refspecs, i, spec) { |
4330ab26 CMN |
2132 | if (spec->push) |
2133 | continue; | |
2134 | ||
2135 | if (git_refspec_dst_matches(spec, refname)) | |
2136 | return spec; | |
2137 | } | |
2138 | ||
2139 | return NULL; | |
2140 | } | |
2141 | ||
77254990 | 2142 | int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec) |
4330ab26 | 2143 | { |
77254990 | 2144 | return write_add_refspec(repo, remote, refspec, true); |
4330ab26 CMN |
2145 | } |
2146 | ||
77254990 | 2147 | int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec) |
4330ab26 | 2148 | { |
77254990 | 2149 | return write_add_refspec(repo, remote, refspec, false); |
266af6d8 CMN |
2150 | } |
2151 | ||
11f6ad5f | 2152 | static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push) |
bc6374ea CMN |
2153 | { |
2154 | size_t i; | |
2155 | git_vector refspecs; | |
2156 | git_refspec *spec; | |
2157 | char *dup; | |
2158 | ||
2159 | if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0) | |
2160 | return -1; | |
2161 | ||
2162 | git_vector_foreach(&remote->refspecs, i, spec) { | |
2163 | if (spec->push != push) | |
2164 | continue; | |
2165 | ||
1be680c4 | 2166 | if ((dup = git__strdup(spec->string)) == NULL) |
bc6374ea | 2167 | goto on_error; |
bc6374ea CMN |
2168 | |
2169 | if (git_vector_insert(&refspecs, dup) < 0) { | |
2170 | git__free(dup); | |
2171 | goto on_error; | |
2172 | } | |
2173 | } | |
2174 | ||
2175 | array->strings = (char **)refspecs.contents; | |
2176 | array->count = refspecs.length; | |
2177 | ||
2178 | return 0; | |
2179 | ||
2180 | on_error: | |
9cfce273 | 2181 | git_vector_free_deep(&refspecs); |
bc6374ea CMN |
2182 | |
2183 | return -1; | |
2184 | } | |
2185 | ||
11f6ad5f | 2186 | int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote) |
bc6374ea CMN |
2187 | { |
2188 | return copy_refspecs(array, remote, false); | |
2189 | } | |
2190 | ||
11f6ad5f | 2191 | int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote) |
bc6374ea CMN |
2192 | { |
2193 | return copy_refspecs(array, remote, true); | |
2194 | } | |
1ffd0806 | 2195 | |
11f6ad5f | 2196 | size_t git_remote_refspec_count(const git_remote *remote) |
1ffd0806 CMN |
2197 | { |
2198 | return remote->refspecs.length; | |
2199 | } | |
2200 | ||
11f6ad5f | 2201 | const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) |
1ffd0806 CMN |
2202 | { |
2203 | return git_vector_get(&remote->refspecs, n); | |
2204 | } | |
b9f81997 | 2205 | |
bc91347b | 2206 | int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version) |
b9f81997 | 2207 | { |
bc91347b RB |
2208 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
2209 | opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT); | |
2210 | return 0; | |
b9f81997 | 2211 | } |
40e48ea4 | 2212 | |
5cdac19c CMN |
2213 | /* asserts a branch.<foo>.remote format */ |
2214 | static const char *name_offset(size_t *len_out, const char *name) | |
40e48ea4 | 2215 | { |
5cdac19c CMN |
2216 | size_t prefix_len; |
2217 | const char *dot; | |
40e48ea4 | 2218 | |
5cdac19c CMN |
2219 | prefix_len = strlen("remote."); |
2220 | dot = strchr(name + prefix_len, '.'); | |
40e48ea4 | 2221 | |
5cdac19c | 2222 | assert(dot); |
40e48ea4 | 2223 | |
5cdac19c CMN |
2224 | *len_out = dot - name - prefix_len; |
2225 | return name + prefix_len; | |
40e48ea4 | 2226 | } |
2227 | ||
2228 | static int remove_branch_config_related_entries( | |
2229 | git_repository *repo, | |
2230 | const char *remote_name) | |
2231 | { | |
2232 | int error; | |
2233 | git_config *config; | |
5cdac19c CMN |
2234 | git_config_entry *entry; |
2235 | git_config_iterator *iter; | |
2236 | git_buf buf = GIT_BUF_INIT; | |
40e48ea4 | 2237 | |
2238 | if ((error = git_repository_config__weakptr(&config, repo)) < 0) | |
2239 | return error; | |
2240 | ||
5cdac19c | 2241 | if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0) |
40e48ea4 | 2242 | return error; |
2243 | ||
5cdac19c CMN |
2244 | /* find any branches with us as upstream and remove that config */ |
2245 | while ((error = git_config_next(&entry, iter)) == 0) { | |
2246 | const char *branch; | |
2247 | size_t branch_len; | |
40e48ea4 | 2248 | |
5cdac19c CMN |
2249 | if (strcmp(remote_name, entry->value)) |
2250 | continue; | |
40e48ea4 | 2251 | |
5cdac19c | 2252 | branch = name_offset(&branch_len, entry->name); |
40e48ea4 | 2253 | |
5cdac19c CMN |
2254 | git_buf_clear(&buf); |
2255 | if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0) | |
2256 | break; | |
2257 | ||
d81cb2e4 DT |
2258 | if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) { |
2259 | if (error != GIT_ENOTFOUND) | |
2260 | break; | |
ac3d33df | 2261 | git_error_clear(); |
d81cb2e4 | 2262 | } |
5cdac19c CMN |
2263 | |
2264 | git_buf_clear(&buf); | |
2265 | if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0) | |
2266 | break; | |
2267 | ||
d81cb2e4 DT |
2268 | if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) { |
2269 | if (error != GIT_ENOTFOUND) | |
2270 | break; | |
ac3d33df | 2271 | git_error_clear(); |
d81cb2e4 | 2272 | } |
40e48ea4 | 2273 | } |
2274 | ||
5cdac19c CMN |
2275 | if (error == GIT_ITEROVER) |
2276 | error = 0; | |
2277 | ||
ac3d33df | 2278 | git_buf_dispose(&buf); |
5cdac19c | 2279 | git_config_iterator_free(iter); |
40e48ea4 | 2280 | return error; |
2281 | } | |
2282 | ||
8a9419aa | 2283 | static int remove_refs(git_repository *repo, const git_refspec *spec) |
ec8a949a | 2284 | { |
8a9419aa CMN |
2285 | git_reference_iterator *iter = NULL; |
2286 | git_vector refs; | |
ec8a949a | 2287 | const char *name; |
8a9419aa | 2288 | char *dup; |
ec8a949a | 2289 | int error; |
8a9419aa | 2290 | size_t i; |
ec8a949a | 2291 | |
8a9419aa | 2292 | if ((error = git_vector_init(&refs, 8, NULL)) < 0) |
ec8a949a CMN |
2293 | return error; |
2294 | ||
8a9419aa CMN |
2295 | if ((error = git_reference_iterator_new(&iter, repo)) < 0) |
2296 | goto cleanup; | |
2297 | ||
ec8a949a | 2298 | while ((error = git_reference_next_name(&name, iter)) == 0) { |
8a9419aa CMN |
2299 | if (!git_refspec_dst_matches(spec, name)) |
2300 | continue; | |
2301 | ||
2302 | dup = git__strdup(name); | |
2303 | if (!dup) { | |
2304 | error = -1; | |
2305 | goto cleanup; | |
2306 | } | |
ec8a949a | 2307 | |
8a9419aa CMN |
2308 | if ((error = git_vector_insert(&refs, dup)) < 0) |
2309 | goto cleanup; | |
2310 | } | |
ec8a949a CMN |
2311 | if (error == GIT_ITEROVER) |
2312 | error = 0; | |
8a9419aa CMN |
2313 | if (error < 0) |
2314 | goto cleanup; | |
2315 | ||
2316 | git_vector_foreach(&refs, i, name) { | |
2317 | if ((error = git_reference_remove(repo, name)) < 0) | |
2318 | break; | |
2319 | } | |
ec8a949a | 2320 | |
8a9419aa CMN |
2321 | cleanup: |
2322 | git_reference_iterator_free(iter); | |
2323 | git_vector_foreach(&refs, i, dup) { | |
2324 | git__free(dup); | |
2325 | } | |
2326 | git_vector_free(&refs); | |
ec8a949a CMN |
2327 | return error; |
2328 | } | |
2329 | ||
2330 | static int remove_remote_tracking(git_repository *repo, const char *remote_name) | |
2331 | { | |
2332 | git_remote *remote; | |
2333 | int error; | |
2334 | size_t i, count; | |
2335 | ||
2336 | /* we want to use what's on the config, regardless of changes to the instance in memory */ | |
209425ce | 2337 | if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0) |
ec8a949a CMN |
2338 | return error; |
2339 | ||
2340 | count = git_remote_refspec_count(remote); | |
2341 | for (i = 0; i < count; i++) { | |
2342 | const git_refspec *refspec = git_remote_get_refspec(remote, i); | |
2343 | ||
2344 | /* shouldn't ever actually happen */ | |
2345 | if (refspec == NULL) | |
2346 | continue; | |
2347 | ||
8a9419aa | 2348 | if ((error = remove_refs(repo, refspec)) < 0) |
ec8a949a CMN |
2349 | break; |
2350 | } | |
2351 | ||
2352 | git_remote_free(remote); | |
2353 | return error; | |
2354 | } | |
2355 | ||
262eec23 | 2356 | int git_remote_delete(git_repository *repo, const char *name) |
40e48ea4 | 2357 | { |
2358 | int error; | |
40e48ea4 | 2359 | |
262eec23 | 2360 | assert(repo && name); |
ec8a949a | 2361 | |
262eec23 CMN |
2362 | if ((error = remove_branch_config_related_entries(repo, name)) < 0 || |
2363 | (error = remove_remote_tracking(repo, name)) < 0 || | |
2364 | (error = rename_remote_config_section(repo, name, NULL)) < 0) | |
ec8a949a CMN |
2365 | return error; |
2366 | ||
40e48ea4 | 2367 | return 0; |
2368 | } | |
d22db24f CMN |
2369 | |
2370 | int git_remote_default_branch(git_buf *out, git_remote *remote) | |
2371 | { | |
2372 | const git_remote_head **heads; | |
2373 | const git_remote_head *guess = NULL; | |
2374 | const git_oid *head_id; | |
2375 | size_t heads_len, i; | |
2376 | int error; | |
2377 | ||
dc8adda4 JG |
2378 | assert(out); |
2379 | ||
d22db24f CMN |
2380 | if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0) |
2381 | return error; | |
2382 | ||
2383 | if (heads_len == 0) | |
2384 | return GIT_ENOTFOUND; | |
2385 | ||
0cdaa376 CMN |
2386 | if (strcmp(heads[0]->name, GIT_HEAD_FILE)) |
2387 | return GIT_ENOTFOUND; | |
2388 | ||
d22db24f CMN |
2389 | git_buf_sanitize(out); |
2390 | /* the first one must be HEAD so if that has the symref info, we're done */ | |
2391 | if (heads[0]->symref_target) | |
2392 | return git_buf_puts(out, heads[0]->symref_target); | |
2393 | ||
2394 | /* | |
2395 | * If there's no symref information, we have to look over them | |
2396 | * and guess. We return the first match unless the master | |
2397 | * branch is a candidate. Then we return the master branch. | |
2398 | */ | |
2399 | head_id = &heads[0]->oid; | |
2400 | ||
2401 | for (i = 1; i < heads_len; i++) { | |
2402 | if (git_oid_cmp(head_id, &heads[i]->oid)) | |
2403 | continue; | |
2404 | ||
38952604 CMN |
2405 | if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR)) |
2406 | continue; | |
2407 | ||
d22db24f CMN |
2408 | if (!guess) { |
2409 | guess = heads[i]; | |
2410 | continue; | |
2411 | } | |
2412 | ||
2413 | if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) { | |
2414 | guess = heads[i]; | |
2415 | break; | |
2416 | } | |
2417 | } | |
2418 | ||
2419 | if (!guess) | |
2420 | return GIT_ENOTFOUND; | |
2421 | ||
2422 | return git_buf_puts(out, guess->name); | |
2423 | } | |
3149547b | 2424 | |
fe794b2e | 2425 | int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) |
3149547b | 2426 | { |
3149547b | 2427 | size_t i; |
fe794b2e CMN |
2428 | int error; |
2429 | git_push *push; | |
64e3e6d4 | 2430 | git_refspec *spec; |
8f0104ec | 2431 | const git_remote_callbacks *cbs = NULL; |
ac3d33df | 2432 | git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT; |
3149547b | 2433 | |
fe794b2e | 2434 | assert(remote); |
3149547b | 2435 | |
eae0bfdc | 2436 | if (!remote->repo) { |
ac3d33df | 2437 | git_error_set(GIT_ERROR_INVALID, "cannot download detached remote"); |
eae0bfdc PP |
2438 | return -1; |
2439 | } | |
2440 | ||
d29c5412 | 2441 | if (opts) { |
8f0104ec | 2442 | cbs = &opts->callbacks; |
ac3d33df JK |
2443 | conn.custom_headers = &opts->custom_headers; |
2444 | conn.proxy = &opts->proxy_opts; | |
d29c5412 | 2445 | } |
8f0104ec | 2446 | |
fe794b2e | 2447 | if (!git_remote_connected(remote) && |
ac3d33df | 2448 | (error = git_remote__connect(remote, GIT_DIRECTION_PUSH, cbs, &conn)) < 0) |
fe794b2e CMN |
2449 | goto cleanup; |
2450 | ||
1034f1b5 | 2451 | free_refspecs(&remote->active_refspecs); |
69f0032b | 2452 | if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0) |
1034f1b5 POL |
2453 | goto cleanup; |
2454 | ||
fe794b2e CMN |
2455 | if (remote->push) { |
2456 | git_push_free(remote->push); | |
2457 | remote->push = NULL; | |
2458 | } | |
2459 | ||
2460 | if ((error = git_push_new(&remote->push, remote)) < 0) | |
3149547b CMN |
2461 | return error; |
2462 | ||
fe794b2e | 2463 | push = remote->push; |
3149547b CMN |
2464 | |
2465 | if (opts && (error = git_push_set_options(push, opts)) < 0) | |
2466 | goto cleanup; | |
2467 | ||
64e3e6d4 CMN |
2468 | if (refspecs && refspecs->count > 0) { |
2469 | for (i = 0; i < refspecs->count; i++) { | |
2470 | if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0) | |
2471 | goto cleanup; | |
2472 | } | |
2473 | } else { | |
2474 | git_vector_foreach(&remote->refspecs, i, spec) { | |
2475 | if (!spec->push) | |
2476 | continue; | |
2477 | if ((error = git_push_add_refspec(push, spec->string)) < 0) | |
2478 | goto cleanup; | |
2479 | } | |
3149547b CMN |
2480 | } |
2481 | ||
8f0104ec | 2482 | if ((error = git_push_finish(push, cbs)) < 0) |
3149547b CMN |
2483 | goto cleanup; |
2484 | ||
8f0104ec | 2485 | if (cbs && cbs->push_update_reference && |
52ee0e8e | 2486 | (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0) |
3149547b CMN |
2487 | goto cleanup; |
2488 | ||
3149547b | 2489 | cleanup: |
fe794b2e CMN |
2490 | return error; |
2491 | } | |
2492 | ||
412a3808 | 2493 | int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) |
fe794b2e CMN |
2494 | { |
2495 | int error; | |
8f0104ec | 2496 | const git_remote_callbacks *cbs = NULL; |
d29c5412 | 2497 | const git_strarray *custom_headers = NULL; |
07bd3e57 | 2498 | const git_proxy_options *proxy = NULL; |
8f0104ec | 2499 | |
eae0bfdc PP |
2500 | assert(remote); |
2501 | ||
2502 | if (!remote->repo) { | |
ac3d33df | 2503 | git_error_set(GIT_ERROR_INVALID, "cannot download detached remote"); |
eae0bfdc PP |
2504 | return -1; |
2505 | } | |
2506 | ||
8f0104ec | 2507 | if (opts) { |
ac3d33df | 2508 | GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); |
8f0104ec | 2509 | cbs = &opts->callbacks; |
d29c5412 | 2510 | custom_headers = &opts->custom_headers; |
ac3d33df | 2511 | GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); |
07bd3e57 | 2512 | proxy = &opts->proxy_opts; |
8f0104ec | 2513 | } |
fe794b2e | 2514 | |
90cdf44f | 2515 | assert(remote); |
fe794b2e | 2516 | |
07bd3e57 | 2517 | if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) |
fe794b2e CMN |
2518 | return error; |
2519 | ||
2520 | if ((error = git_remote_upload(remote, refspecs, opts)) < 0) | |
2521 | return error; | |
2522 | ||
35a8a8c5 | 2523 | error = git_remote_update_tips(remote, cbs, 0, 0, NULL); |
fe794b2e | 2524 | |
3149547b | 2525 | git_remote_disconnect(remote); |
3149547b CMN |
2526 | return error; |
2527 | } | |
ec0c4c40 PS |
2528 | |
2529 | #define PREFIX "url" | |
2530 | #define SUFFIX_FETCH "insteadof" | |
2531 | #define SUFFIX_PUSH "pushinsteadof" | |
2532 | ||
2533 | char *apply_insteadof(git_config *config, const char *url, int direction) | |
2534 | { | |
2535 | size_t match_length, prefix_length, suffix_length; | |
2536 | char *replacement = NULL; | |
2537 | const char *regexp; | |
2538 | ||
2539 | git_buf result = GIT_BUF_INIT; | |
2540 | git_config_entry *entry; | |
2541 | git_config_iterator *iter; | |
2542 | ||
2543 | assert(config); | |
2544 | assert(url); | |
2545 | assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH); | |
2546 | ||
2547 | /* Add 1 to prefix/suffix length due to the additional escaped dot */ | |
2548 | prefix_length = strlen(PREFIX) + 1; | |
2549 | if (direction == GIT_DIRECTION_FETCH) { | |
2550 | regexp = PREFIX "\\..*\\." SUFFIX_FETCH; | |
2551 | suffix_length = strlen(SUFFIX_FETCH) + 1; | |
2552 | } else { | |
2553 | regexp = PREFIX "\\..*\\." SUFFIX_PUSH; | |
2554 | suffix_length = strlen(SUFFIX_PUSH) + 1; | |
2555 | } | |
2556 | ||
2785544f CMN |
2557 | if (git_config_iterator_glob_new(&iter, config, regexp) < 0) |
2558 | return NULL; | |
ec0c4c40 PS |
2559 | |
2560 | match_length = 0; | |
2561 | while (git_config_next(&entry, iter) == 0) { | |
2562 | size_t n, replacement_length; | |
2563 | ||
2564 | /* Check if entry value is a prefix of URL */ | |
2565 | if (git__prefixcmp(url, entry->value)) | |
2566 | continue; | |
2567 | /* Check if entry value is longer than previous | |
2568 | * prefixes */ | |
2569 | if ((n = strlen(entry->value)) <= match_length) | |
2570 | continue; | |
2571 | ||
2572 | git__free(replacement); | |
2573 | match_length = n; | |
2574 | ||
2575 | /* Cut off prefix and suffix of the value */ | |
2576 | replacement_length = | |
2577 | strlen(entry->name) - (prefix_length + suffix_length); | |
2578 | replacement = git__strndup(entry->name + prefix_length, | |
2579 | replacement_length); | |
2580 | } | |
2581 | ||
2582 | git_config_iterator_free(iter); | |
2583 | ||
2584 | if (match_length == 0) | |
2585 | return git__strdup(url); | |
2586 | ||
2587 | git_buf_printf(&result, "%s%s", replacement, url + match_length); | |
2588 | ||
2589 | git__free(replacement); | |
2590 | ||
2591 | return result.ptr; | |
2592 | } |