]>
Commit | Line | Data |
---|---|---|
9c82357b | 1 | /* |
bb742ede | 2 | * Copyright (C) 2009-2011 the libgit2 contributors |
9c82357b | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
9c82357b CMN |
6 | */ |
7 | ||
8 | #include "git2/remote.h" | |
9 | #include "git2/config.h" | |
10 | #include "git2/types.h" | |
11 | ||
12 | #include "config.h" | |
13 | #include "repository.h" | |
14 | #include "remote.h" | |
e1d88030 | 15 | #include "fetch.h" |
441f57c2 | 16 | #include "refs.h" |
9c82357b CMN |
17 | |
18 | static int refspec_parse(git_refspec *refspec, const char *str) | |
19 | { | |
20 | char *delim; | |
21 | ||
22 | memset(refspec, 0x0, sizeof(git_refspec)); | |
23 | ||
24 | if (*str == '+') { | |
25 | refspec->force = 1; | |
26 | str++; | |
27 | } | |
28 | ||
29 | delim = strchr(str, ':'); | |
30 | if (delim == NULL) | |
31 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'"); | |
32 | ||
33 | refspec->src = git__strndup(str, delim - str); | |
34 | if (refspec->src == NULL) | |
35 | return GIT_ENOMEM; | |
36 | ||
37 | refspec->dst = git__strdup(delim + 1); | |
38 | if (refspec->dst == NULL) { | |
3286c408 | 39 | git__free(refspec->src); |
9c82357b CMN |
40 | refspec->src = NULL; |
41 | return GIT_ENOMEM; | |
42 | } | |
43 | ||
44 | return GIT_SUCCESS; | |
45 | } | |
46 | ||
47 | static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var) | |
48 | { | |
49 | const char *val; | |
50 | int error; | |
51 | ||
52 | error = git_config_get_string(cfg, var, &val); | |
53 | if (error < GIT_SUCCESS) | |
54 | return error; | |
55 | ||
2dc31040 | 56 | return refspec_parse(refspec, val); |
9c82357b CMN |
57 | } |
58 | ||
617bfdf4 | 59 | int git_remote_new(git_remote **out, git_repository *repo, const char *url, const char *name) |
778e1c73 CMN |
60 | { |
61 | git_remote *remote; | |
62 | ||
4bef3565 VM |
63 | /* name is optional */ |
64 | assert(out && repo && url); | |
617bfdf4 | 65 | |
778e1c73 CMN |
66 | remote = git__malloc(sizeof(git_remote)); |
67 | if (remote == NULL) | |
68 | return GIT_ENOMEM; | |
69 | ||
70 | memset(remote, 0x0, sizeof(git_remote)); | |
71 | remote->repo = repo; | |
617bfdf4 | 72 | |
d88d4311 VM |
73 | if (git_vector_init(&remote->refs, 32, NULL) < 0) { |
74 | git_remote_free(remote); | |
75 | return GIT_ENOMEM; | |
76 | } | |
77 | ||
778e1c73 CMN |
78 | remote->url = git__strdup(url); |
79 | if (remote->url == NULL) { | |
d88d4311 | 80 | git_remote_free(remote); |
778e1c73 CMN |
81 | return GIT_ENOMEM; |
82 | } | |
83 | ||
617bfdf4 CMN |
84 | if (name != NULL) { |
85 | remote->name = git__strdup(name); | |
86 | if (remote->name == NULL) { | |
d88d4311 | 87 | git_remote_free(remote); |
617bfdf4 CMN |
88 | return GIT_ENOMEM; |
89 | } | |
90 | } | |
91 | ||
778e1c73 CMN |
92 | *out = remote; |
93 | return GIT_SUCCESS; | |
94 | } | |
95 | ||
9462c471 | 96 | int git_remote_load(git_remote **out, git_repository *repo, const char *name) |
9c82357b CMN |
97 | { |
98 | git_remote *remote; | |
99 | char *buf = NULL; | |
100 | const char *val; | |
101 | int ret, error, buf_len; | |
9462c471 | 102 | git_config *config; |
9c82357b | 103 | |
9462c471 VM |
104 | assert(out && repo && name); |
105 | ||
106 | error = git_repository_config__weakptr(&config, repo); | |
107 | if (error < GIT_SUCCESS) | |
108 | return error; | |
4bef3565 | 109 | |
9c82357b CMN |
110 | remote = git__malloc(sizeof(git_remote)); |
111 | if (remote == NULL) | |
112 | return GIT_ENOMEM; | |
113 | ||
114 | memset(remote, 0x0, sizeof(git_remote)); | |
115 | remote->name = git__strdup(name); | |
116 | if (remote->name == NULL) { | |
117 | error = GIT_ENOMEM; | |
118 | goto cleanup; | |
119 | } | |
120 | ||
d88d4311 VM |
121 | if (git_vector_init(&remote->refs, 32, NULL) < 0) { |
122 | error = GIT_ENOMEM; | |
123 | goto cleanup; | |
124 | } | |
125 | ||
9c82357b | 126 | /* "fetch" is the longest var name we're interested in */ |
932669b8 | 127 | buf_len = strlen("remote.") + strlen(".fetch") + strlen(name) + 1; |
9c82357b CMN |
128 | buf = git__malloc(buf_len); |
129 | if (buf == NULL) { | |
130 | error = GIT_ENOMEM; | |
131 | goto cleanup; | |
132 | } | |
133 | ||
84dd3820 | 134 | ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "url"); |
9c82357b CMN |
135 | if (ret < 0) { |
136 | error = git__throw(GIT_EOSERR, "Failed to build config var name"); | |
137 | goto cleanup; | |
138 | } | |
139 | ||
9462c471 | 140 | error = git_config_get_string(config, buf, &val); |
9c82357b | 141 | if (error < GIT_SUCCESS) { |
87d9869f | 142 | error = git__rethrow(error, "Remote's url doesn't exist"); |
9c82357b CMN |
143 | goto cleanup; |
144 | } | |
145 | ||
9462c471 | 146 | remote->repo = repo; |
9c82357b CMN |
147 | remote->url = git__strdup(val); |
148 | if (remote->url == NULL) { | |
149 | error = GIT_ENOMEM; | |
150 | goto cleanup; | |
151 | } | |
152 | ||
84dd3820 | 153 | ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "fetch"); |
9c82357b CMN |
154 | if (ret < 0) { |
155 | error = git__throw(GIT_EOSERR, "Failed to build config var name"); | |
156 | goto cleanup; | |
157 | } | |
158 | ||
9462c471 | 159 | error = parse_remote_refspec(config, &remote->fetch, buf); |
2dc31040 CMN |
160 | if (error < GIT_SUCCESS) { |
161 | error = git__rethrow(error, "Failed to get fetch refspec"); | |
9c82357b | 162 | goto cleanup; |
2dc31040 | 163 | } |
9c82357b | 164 | |
84dd3820 | 165 | ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "push"); |
9c82357b CMN |
166 | if (ret < 0) { |
167 | error = git__throw(GIT_EOSERR, "Failed to build config var name"); | |
168 | goto cleanup; | |
169 | } | |
170 | ||
9462c471 | 171 | error = parse_remote_refspec(config, &remote->push, buf); |
2dc31040 CMN |
172 | /* Not finding push is fine */ |
173 | if (error == GIT_ENOTFOUND) | |
174 | error = GIT_SUCCESS; | |
9c82357b | 175 | |
9c82357b CMN |
176 | if (error < GIT_SUCCESS) |
177 | goto cleanup; | |
178 | ||
179 | *out = remote; | |
180 | ||
181 | cleanup: | |
3286c408 | 182 | git__free(buf); |
9462c471 | 183 | |
9c82357b CMN |
184 | if (error < GIT_SUCCESS) |
185 | git_remote_free(remote); | |
186 | ||
187 | return error; | |
188 | } | |
189 | ||
4bef3565 | 190 | const char *git_remote_name(git_remote *remote) |
9c82357b | 191 | { |
4bef3565 | 192 | assert(remote); |
9c82357b CMN |
193 | return remote->name; |
194 | } | |
195 | ||
4bef3565 | 196 | const char *git_remote_url(git_remote *remote) |
9c82357b | 197 | { |
4bef3565 | 198 | assert(remote); |
9c82357b CMN |
199 | return remote->url; |
200 | } | |
201 | ||
4bef3565 | 202 | const git_refspec *git_remote_fetchspec(git_remote *remote) |
9c82357b | 203 | { |
4bef3565 | 204 | assert(remote); |
9c82357b CMN |
205 | return &remote->fetch; |
206 | } | |
207 | ||
4bef3565 | 208 | const git_refspec *git_remote_pushspec(git_remote *remote) |
9c82357b | 209 | { |
4bef3565 | 210 | assert(remote); |
9c82357b CMN |
211 | return &remote->push; |
212 | } | |
213 | ||
0ac2726f | 214 | int git_remote_connect(git_remote *remote, int direction) |
9ba49bb5 CMN |
215 | { |
216 | int error; | |
217 | git_transport *t; | |
218 | ||
4bef3565 VM |
219 | assert(remote); |
220 | ||
9ba49bb5 CMN |
221 | error = git_transport_new(&t, remote->url); |
222 | if (error < GIT_SUCCESS) | |
223 | return git__rethrow(error, "Failed to create transport"); | |
224 | ||
b5a8aa94 | 225 | error = t->connect(t, direction); |
9ba49bb5 CMN |
226 | if (error < GIT_SUCCESS) { |
227 | error = git__rethrow(error, "Failed to connect the transport"); | |
228 | goto cleanup; | |
229 | } | |
230 | ||
231 | remote->transport = t; | |
232 | ||
233 | cleanup: | |
234 | if (error < GIT_SUCCESS) | |
b5a8aa94 | 235 | t->free(t); |
9ba49bb5 CMN |
236 | |
237 | return error; | |
238 | } | |
239 | ||
d88d4311 | 240 | int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) |
9ba49bb5 | 241 | { |
d88d4311 VM |
242 | assert(remote); |
243 | ||
b2337143 | 244 | if (!remote->transport || !remote->transport->connected) |
d88d4311 VM |
245 | return git__throw(GIT_ERROR, "The remote is not connected"); |
246 | ||
247 | return remote->transport->ls(remote->transport, list_cb, payload); | |
9ba49bb5 CMN |
248 | } |
249 | ||
48a65a07 CMN |
250 | int git_remote_download(char **filename, git_remote *remote) |
251 | { | |
95057b85 CMN |
252 | int error; |
253 | ||
4bef3565 VM |
254 | assert(filename && remote); |
255 | ||
95057b85 CMN |
256 | if ((error = git_fetch_negotiate(remote)) < 0) |
257 | return git__rethrow(error, "Error negotiating"); | |
258 | ||
48a65a07 CMN |
259 | return git_fetch_download_pack(filename, remote); |
260 | } | |
261 | ||
4bef3565 | 262 | int git_remote_update_tips(git_remote *remote) |
441f57c2 CMN |
263 | { |
264 | int error = GIT_SUCCESS; | |
517bda19 | 265 | unsigned int i = 0; |
97769280 | 266 | git_buf refname = GIT_BUF_INIT; |
d88d4311 | 267 | git_vector *refs = &remote->refs; |
441f57c2 CMN |
268 | git_remote_head *head; |
269 | git_reference *ref; | |
270 | struct git_refspec *spec = &remote->fetch; | |
271 | ||
4bef3565 VM |
272 | assert(remote); |
273 | ||
d88d4311 | 274 | if (refs->length == 0) |
517bda19 CMN |
275 | return GIT_SUCCESS; |
276 | ||
277 | /* HEAD is only allowed to be the first in the list */ | |
d88d4311 | 278 | head = refs->contents[0]; |
517bda19 CMN |
279 | if (!strcmp(head->name, GIT_HEAD_FILE)) { |
280 | error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1); | |
281 | i = 1; | |
39157563 | 282 | git_reference_free(ref); |
517bda19 CMN |
283 | if (error < GIT_SUCCESS) |
284 | return git__rethrow(error, "Failed to update FETCH_HEAD"); | |
285 | } | |
286 | ||
d88d4311 VM |
287 | for (; i < refs->length; ++i) { |
288 | head = refs->contents[i]; | |
517bda19 | 289 | |
97769280 | 290 | error = git_refspec_transform_r(&refname, spec, head->name); |
441f57c2 | 291 | if (error < GIT_SUCCESS) |
97769280 | 292 | break; |
441f57c2 | 293 | |
97769280 | 294 | error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1); |
441f57c2 | 295 | if (error < GIT_SUCCESS) |
97769280 | 296 | break; |
39157563 CMN |
297 | |
298 | git_reference_free(ref); | |
441f57c2 CMN |
299 | } |
300 | ||
97769280 RB |
301 | git_buf_free(&refname); |
302 | ||
303 | return error; | |
441f57c2 CMN |
304 | } |
305 | ||
6ac3b707 CMN |
306 | int git_remote_connected(git_remote *remote) |
307 | { | |
4bef3565 | 308 | assert(remote); |
6ac3b707 CMN |
309 | return remote->transport == NULL ? 0 : remote->transport->connected; |
310 | } | |
311 | ||
4cf01e9a CMN |
312 | void git_remote_disconnect(git_remote *remote) |
313 | { | |
4bef3565 VM |
314 | assert(remote); |
315 | ||
4cf01e9a CMN |
316 | if (remote->transport != NULL) { |
317 | if (remote->transport->connected) | |
318 | remote->transport->close(remote->transport); | |
319 | ||
320 | remote->transport->free(remote->transport); | |
a3147114 | 321 | remote->transport = NULL; |
4cf01e9a CMN |
322 | } |
323 | } | |
324 | ||
9c82357b CMN |
325 | void git_remote_free(git_remote *remote) |
326 | { | |
2aae2188 CMN |
327 | if (remote == NULL) |
328 | return; | |
329 | ||
3286c408 VM |
330 | git__free(remote->fetch.src); |
331 | git__free(remote->fetch.dst); | |
332 | git__free(remote->push.src); | |
333 | git__free(remote->push.dst); | |
334 | git__free(remote->url); | |
335 | git__free(remote->name); | |
d88d4311 | 336 | git_vector_free(&remote->refs); |
4cf01e9a | 337 | git_remote_disconnect(remote); |
3286c408 | 338 | git__free(remote); |
9c82357b | 339 | } |