]>
Commit | Line | Data |
---|---|---|
ecb6ca0e | 1 | /* |
bb742ede | 2 | * Copyright (C) 2009-2011 the libgit2 contributors |
ecb6ca0e | 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. | |
ecb6ca0e CMN |
6 | */ |
7 | ||
ecb6ca0e | 8 | #include "git2/net.h" |
ecb6ca0e CMN |
9 | #include "git2/common.h" |
10 | #include "git2/types.h" | |
11 | #include "git2/errors.h" | |
22f65b9e CMN |
12 | #include "git2/net.h" |
13 | #include "git2/revwalk.h" | |
ecb6ca0e CMN |
14 | |
15 | #include "vector.h" | |
16 | #include "transport.h" | |
bdd18829 | 17 | #include "pkt.h" |
ecb6ca0e | 18 | #include "common.h" |
1b4f8140 | 19 | #include "netops.h" |
da290220 CMN |
20 | #include "filebuf.h" |
21 | #include "repository.h" | |
ecb6ca0e CMN |
22 | |
23 | typedef struct { | |
4e913309 | 24 | git_transport parent; |
ecb6ca0e CMN |
25 | int socket; |
26 | git_vector refs; | |
27 | git_remote_head **heads; | |
0437d991 | 28 | git_transport_caps caps; |
4e913309 CMN |
29 | } transport_git; |
30 | ||
fd679021 CMN |
31 | /* |
32 | * Create a git procol request. | |
33 | * | |
34 | * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 | |
35 | */ | |
36 | static int gen_proto(char **out, int *outlen, const char *cmd, const char *url) | |
37 | { | |
38 | char *delim, *repo, *ptr; | |
39 | char default_command[] = "git-upload-pack"; | |
40 | char host[] = "host="; | |
41 | int len; | |
42 | ||
43 | delim = strchr(url, '/'); | |
44 | if (delim == NULL) | |
45 | return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL"); | |
46 | ||
47 | repo = delim; | |
48 | ||
49 | delim = strchr(url, ':'); | |
50 | if (delim == NULL) | |
51 | delim = strchr(url, '/'); | |
52 | ||
53 | if (cmd == NULL) | |
54 | cmd = default_command; | |
55 | ||
932669b8 | 56 | len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 2; |
fd679021 CMN |
57 | |
58 | *out = git__malloc(len); | |
59 | if (*out == NULL) | |
60 | return GIT_ENOMEM; | |
61 | ||
62 | *outlen = len - 1; | |
63 | ptr = *out; | |
64 | memset(ptr, 0x0, len); | |
65 | /* We expect the return value to be > len - 1 so don't bother checking it */ | |
66 | snprintf(ptr, len -1, "%04x%s %s%c%s%s", len - 1, cmd, repo, 0, host, url); | |
67 | ||
68 | return GIT_SUCCESS; | |
69 | } | |
70 | ||
71 | static int send_request(int s, const char *cmd, const char *url) | |
72 | { | |
73 | int error, len; | |
74 | char *msg = NULL; | |
75 | ||
76 | error = gen_proto(&msg, &len, cmd, url); | |
77 | if (error < GIT_SUCCESS) | |
78 | goto cleanup; | |
79 | ||
80 | error = gitno_send(s, msg, len, 0); | |
81 | ||
82 | cleanup: | |
83 | free(msg); | |
84 | return error; | |
85 | } | |
ecb6ca0e CMN |
86 | |
87 | /* The URL should already have been stripped of the protocol */ | |
88 | static int extract_host_and_port(char **host, char **port, const char *url) | |
89 | { | |
90 | char *colon, *slash, *delim; | |
91 | int error = GIT_SUCCESS; | |
92 | ||
93 | colon = strchr(url, ':'); | |
94 | slash = strchr(url, '/'); | |
95 | ||
96 | if (slash == NULL) | |
97 | return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /"); | |
98 | ||
99 | if (colon == NULL) { | |
100 | *port = git__strdup(GIT_DEFAULT_PORT); | |
101 | } else { | |
102 | *port = git__strndup(colon + 1, slash - colon - 1); | |
103 | } | |
104 | if (*port == NULL) | |
105 | return GIT_ENOMEM;; | |
106 | ||
107 | ||
108 | delim = colon == NULL ? slash : colon; | |
109 | *host = git__strndup(url, delim - url); | |
110 | if (*host == NULL) { | |
111 | free(*port); | |
112 | error = GIT_ENOMEM; | |
113 | } | |
114 | ||
115 | return error; | |
116 | } | |
117 | ||
118 | /* | |
119 | * Parse the URL and connect to a server, storing the socket in | |
120 | * out. For convenience this also takes care of asking for the remote | |
121 | * refs | |
122 | */ | |
4e913309 | 123 | static int do_connect(transport_git *t, const char *url) |
ecb6ca0e CMN |
124 | { |
125 | int s = -1; | |
c4d0fa85 | 126 | char *host, *port; |
ecb6ca0e | 127 | const char prefix[] = "git://"; |
c4d0fa85 | 128 | int error, connected = 0; |
ecb6ca0e CMN |
129 | |
130 | if (!git__prefixcmp(url, prefix)) | |
932669b8 | 131 | url += strlen(prefix); |
ecb6ca0e CMN |
132 | |
133 | error = extract_host_and_port(&host, &port, url); | |
c75a890b KS |
134 | if (error < GIT_SUCCESS) |
135 | return error; | |
1b4f8140 CMN |
136 | s = gitno_connect(host, port); |
137 | connected = 1; | |
fd679021 | 138 | error = send_request(s, NULL, url); |
4e913309 | 139 | t->socket = s; |
ecb6ca0e | 140 | |
ecb6ca0e CMN |
141 | free(host); |
142 | free(port); | |
143 | ||
144 | if (error < GIT_SUCCESS && s > 0) | |
145 | close(s); | |
146 | if (!connected) | |
147 | error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses"); | |
148 | ||
149 | return error; | |
150 | } | |
151 | ||
152 | /* | |
153 | * Read from the socket and store the references in the vector | |
154 | */ | |
4e913309 | 155 | static int store_refs(transport_git *t) |
ecb6ca0e | 156 | { |
c7c787ce | 157 | gitno_buffer buf; |
4e913309 CMN |
158 | int s = t->socket; |
159 | git_vector *refs = &t->refs; | |
ecb6ca0e | 160 | int error = GIT_SUCCESS; |
7632e249 | 161 | char buffer[1024]; |
ecb6ca0e | 162 | const char *line_end, *ptr; |
ecb6ca0e CMN |
163 | git_pkt *pkt; |
164 | ||
c7c787ce | 165 | gitno_buffer_setup(&buf, buffer, sizeof(buffer), s); |
7632e249 | 166 | |
ecb6ca0e | 167 | while (1) { |
c7c787ce CMN |
168 | error = gitno_recv(&buf); |
169 | if (error < GIT_SUCCESS) | |
170 | return git__rethrow(GIT_EOSERR, "Failed to receive data"); | |
171 | if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */ | |
7632e249 | 172 | return GIT_SUCCESS; |
ecb6ca0e | 173 | |
c7c787ce | 174 | ptr = buf.data; |
ecb6ca0e | 175 | while (1) { |
c7c787ce | 176 | if (buf.offset == 0) |
7632e249 | 177 | break; |
c7c787ce | 178 | error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset); |
ecb6ca0e | 179 | /* |
7632e249 CMN |
180 | * If the error is GIT_ESHORTBUFFER, it means the buffer |
181 | * isn't long enough to satisfy the request. Break out and | |
182 | * wait for more input. | |
183 | * On any other error, fail. | |
ecb6ca0e | 184 | */ |
7632e249 | 185 | if (error == GIT_ESHORTBUFFER) { |
ecb6ca0e | 186 | break; |
7632e249 CMN |
187 | } |
188 | if (error < GIT_SUCCESS) { | |
189 | return error; | |
190 | } | |
ecb6ca0e | 191 | |
c7c787ce CMN |
192 | /* Get rid of the part we've used already */ |
193 | gitno_consume(&buf, line_end); | |
194 | ||
ecb6ca0e CMN |
195 | error = git_vector_insert(refs, pkt); |
196 | if (error < GIT_SUCCESS) | |
197 | return error; | |
198 | ||
7632e249 | 199 | if (pkt->type == GIT_PKT_FLUSH) |
ecb6ca0e CMN |
200 | return GIT_SUCCESS; |
201 | ||
ecb6ca0e | 202 | } |
ecb6ca0e CMN |
203 | } |
204 | ||
205 | return error; | |
206 | } | |
207 | ||
0437d991 CMN |
208 | static int detect_caps(transport_git *t) |
209 | { | |
210 | git_vector *refs = &t->refs; | |
211 | git_pkt_ref *pkt; | |
212 | git_transport_caps *caps = &t->caps; | |
213 | const char *ptr; | |
214 | ||
215 | pkt = git_vector_get(refs, 0); | |
216 | /* No refs or capabilites, odd but not a problem */ | |
217 | if (pkt == NULL || pkt->capabilities == NULL) | |
218 | return GIT_SUCCESS; | |
219 | ||
220 | ptr = pkt->capabilities; | |
221 | while (ptr != NULL && *ptr != '\0') { | |
222 | if (*ptr == ' ') | |
223 | ptr++; | |
224 | ||
225 | if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { | |
226 | caps->common = caps->ofs_delta = 1; | |
932669b8 | 227 | ptr += strlen(GIT_CAP_OFS_DELTA); |
0437d991 CMN |
228 | continue; |
229 | } | |
230 | ||
231 | /* We don't know this capability, so skip it */ | |
232 | ptr = strchr(ptr, ' '); | |
233 | } | |
234 | ||
235 | return GIT_SUCCESS; | |
236 | } | |
237 | ||
ecb6ca0e CMN |
238 | /* |
239 | * Since this is a network connection, we need to parse and store the | |
240 | * pkt-lines at this stage and keep them there. | |
241 | */ | |
0ac2726f | 242 | static int git_connect(git_transport *transport, int direction) |
ecb6ca0e | 243 | { |
4e913309 | 244 | transport_git *t = (transport_git *) transport; |
ecb6ca0e CMN |
245 | int error = GIT_SUCCESS; |
246 | ||
0ac2726f | 247 | if (direction == GIT_DIR_PUSH) |
ecb6ca0e CMN |
248 | return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol"); |
249 | ||
0ac2726f | 250 | t->parent.direction = direction; |
4e913309 | 251 | error = git_vector_init(&t->refs, 16, NULL); |
ecb6ca0e CMN |
252 | if (error < GIT_SUCCESS) |
253 | goto cleanup; | |
254 | ||
255 | /* Connect and ask for the refs */ | |
4e913309 | 256 | error = do_connect(t, transport->url); |
ecb6ca0e CMN |
257 | if (error < GIT_SUCCESS) |
258 | return error; | |
259 | ||
5da5321d | 260 | t->parent.connected = 1; |
4e913309 | 261 | error = store_refs(t); |
0437d991 CMN |
262 | if (error < GIT_SUCCESS) |
263 | return error; | |
264 | ||
265 | error = detect_caps(t); | |
ecb6ca0e CMN |
266 | |
267 | cleanup: | |
268 | if (error < GIT_SUCCESS) { | |
4e913309 | 269 | git_vector_free(&t->refs); |
ecb6ca0e CMN |
270 | } |
271 | ||
272 | return error; | |
273 | } | |
274 | ||
275 | static int git_ls(git_transport *transport, git_headarray *array) | |
276 | { | |
4e913309 CMN |
277 | transport_git *t = (transport_git *) transport; |
278 | git_vector *refs = &t->refs; | |
ecb6ca0e CMN |
279 | int len = 0; |
280 | unsigned int i; | |
281 | ||
282 | array->heads = git__calloc(refs->length, sizeof(git_remote_head *)); | |
283 | if (array->heads == NULL) | |
284 | return GIT_ENOMEM; | |
285 | ||
286 | for (i = 0; i < refs->length; ++i) { | |
287 | git_pkt *p = git_vector_get(refs, i); | |
288 | if (p->type != GIT_PKT_REF) | |
289 | continue; | |
290 | ||
291 | ++len; | |
292 | array->heads[i] = &(((git_pkt_ref *) p)->head); | |
293 | } | |
294 | array->len = len; | |
4e913309 | 295 | t->heads = array->heads; |
ecb6ca0e CMN |
296 | |
297 | return GIT_SUCCESS; | |
298 | } | |
299 | ||
0e20ba60 CMN |
300 | static int git_send_wants(git_transport *transport, git_headarray *array) |
301 | { | |
302 | transport_git *t = (transport_git *) transport; | |
303 | ||
0437d991 | 304 | return git_pkt_send_wants(array, &t->caps, t->socket); |
0e20ba60 CMN |
305 | } |
306 | ||
7e1a94db | 307 | static int git_send_have(git_transport *transport, git_oid *oid) |
b4c90630 CMN |
308 | { |
309 | transport_git *t = (transport_git *) transport; | |
310 | ||
7e1a94db CMN |
311 | return git_pkt_send_have(oid, t->socket); |
312 | } | |
313 | ||
427ca3d3 | 314 | static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *GIT_UNUSED(list)) |
22f65b9e CMN |
315 | { |
316 | transport_git *t = (transport_git *) transport; | |
317 | git_revwalk *walk; | |
318 | git_reference *ref; | |
319 | git_strarray refs; | |
320 | git_oid oid; | |
321 | int error; | |
322 | unsigned int i; | |
427ca3d3 CMN |
323 | char buff[128]; |
324 | gitno_buffer buf; | |
325 | GIT_UNUSED_ARG(list); | |
326 | ||
327 | gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket); | |
22f65b9e CMN |
328 | |
329 | error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); | |
330 | if (error < GIT_ERROR) | |
331 | return git__rethrow(error, "Failed to list all references"); | |
332 | ||
333 | error = git_revwalk_new(&walk, repo); | |
334 | if (error < GIT_ERROR) { | |
335 | error = git__rethrow(error, "Failed to list all references"); | |
336 | goto cleanup; | |
337 | } | |
338 | git_revwalk_sorting(walk, GIT_SORT_TIME); | |
339 | ||
340 | for (i = 0; i < refs.count; ++i) { | |
427ca3d3 CMN |
341 | /* No tags */ |
342 | if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) | |
343 | continue; | |
344 | ||
22f65b9e CMN |
345 | error = git_reference_lookup(&ref, repo, refs.strings[i]); |
346 | if (error < GIT_ERROR) { | |
347 | error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]); | |
348 | goto cleanup; | |
349 | } | |
350 | ||
427ca3d3 CMN |
351 | if (git_reference_type(ref) == GIT_REF_SYMBOLIC) |
352 | continue; | |
22f65b9e CMN |
353 | error = git_revwalk_push(walk, git_reference_oid(ref)); |
354 | if (error < GIT_ERROR) { | |
355 | error = git__rethrow(error, "Failed to push %s", refs.strings[i]); | |
356 | goto cleanup; | |
357 | } | |
358 | } | |
359 | git_strarray_free(&refs); | |
360 | ||
361 | /* | |
362 | * We don't support any kind of ACK extensions, so the negotiation | |
363 | * boils down to sending what we have and listening for an ACK | |
364 | * every once in a while. | |
365 | */ | |
366 | i = 0; | |
367 | while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) { | |
368 | error = git_pkt_send_have(&oid, t->socket); | |
369 | i++; | |
427ca3d3 CMN |
370 | if (i % 20 == 0) { |
371 | const char *ptr = buf.data, *line_end; | |
372 | git_pkt *pkt; | |
373 | git_pkt_send_flush(t->socket); | |
374 | while (1) { | |
74bd343a CMN |
375 | /* Wait for max. 1 second */ |
376 | error = gitno_select_in(&buf, 1, 0); | |
7adba5f4 CMN |
377 | if (error < GIT_SUCCESS) { |
378 | error = git__throw(GIT_EOSERR, "Error in select"); | |
379 | } else if (error == 0) { | |
380 | /* | |
381 | * Some servers don't respond immediately, so if this | |
382 | * happens, we keep sending information until it | |
383 | * answers. | |
384 | */ | |
385 | break; | |
386 | } | |
387 | ||
427ca3d3 CMN |
388 | error = gitno_recv(&buf); |
389 | if (error < GIT_SUCCESS) { | |
87d9869f VM |
390 | error = git__rethrow(error, "Error receiving data"); |
391 | goto cleanup; | |
427ca3d3 CMN |
392 | } |
393 | error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset); | |
394 | if (error == GIT_ESHORTBUFFER) | |
395 | continue; | |
396 | if (error < GIT_SUCCESS) { | |
397 | error = git__rethrow(error, "Failed to get answer"); | |
398 | goto cleanup; | |
399 | } | |
400 | ||
401 | gitno_consume(&buf, line_end); | |
402 | ||
403 | if (pkt->type == GIT_PKT_ACK) { | |
404 | error = GIT_SUCCESS; | |
405 | goto done; | |
406 | } else if (pkt->type == GIT_PKT_NAK) { | |
407 | break; | |
408 | } else { | |
409 | error = git__throw(GIT_ERROR, "Got unexpected pkt type"); | |
410 | goto cleanup; | |
411 | } | |
412 | } | |
413 | } | |
22f65b9e CMN |
414 | } |
415 | if (error == GIT_EREVWALKOVER) | |
416 | error = GIT_SUCCESS; | |
417 | ||
427ca3d3 | 418 | done: |
22f65b9e CMN |
419 | git_pkt_send_flush(t->socket); |
420 | git_pkt_send_done(t->socket); | |
421 | ||
422 | cleanup: | |
423 | git_revwalk_free(walk); | |
424 | return error; | |
425 | } | |
426 | ||
da290220 CMN |
427 | static int git_send_flush(git_transport *transport) |
428 | { | |
429 | transport_git *t = (transport_git *) transport; | |
430 | ||
431 | return git_pkt_send_flush(t->socket); | |
432 | } | |
433 | ||
7e1a94db CMN |
434 | static int git_send_done(git_transport *transport) |
435 | { | |
436 | transport_git *t = (transport_git *) transport; | |
437 | ||
438 | return git_pkt_send_done(t->socket); | |
b4c90630 CMN |
439 | } |
440 | ||
9cf0f287 | 441 | static int store_pack(char **out, gitno_buffer *buf, git_repository *repo) |
da290220 CMN |
442 | { |
443 | git_filebuf file; | |
444 | int error; | |
9cf0f287 | 445 | char path[GIT_PATH_MAX], suff[] = "/objects/pack/pack-received\0"; |
da290220 CMN |
446 | off_t off = 0; |
447 | ||
2f512ff8 | 448 | strcpy(path, repo->path_repository); |
da290220 | 449 | off += strlen(repo->path_repository); |
2f512ff8 | 450 | strcat(path, suff); |
932669b8 | 451 | //memcpy(path + off, suff, GIT_PATH_MAX - off - strlen(suff) - 1); |
da290220 | 452 | |
932669b8 | 453 | if (memcmp(buf->data, "PACK", strlen("PACK"))) { |
7284c105 CMN |
454 | return git__throw(GIT_ERROR, "The pack doesn't start with the signature"); |
455 | } | |
456 | ||
da290220 CMN |
457 | error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY); |
458 | if (error < GIT_SUCCESS) | |
459 | goto cleanup; | |
460 | ||
461 | while (1) { | |
427ca3d3 | 462 | /* Part of the packfile has been received, don't loose it */ |
da290220 CMN |
463 | error = git_filebuf_write(&file, buf->data, buf->offset); |
464 | if (error < GIT_SUCCESS) | |
465 | goto cleanup; | |
466 | ||
467 | gitno_consume_n(buf, buf->offset); | |
427ca3d3 CMN |
468 | error = gitno_recv(buf); |
469 | if (error < GIT_SUCCESS) | |
470 | goto cleanup; | |
471 | if (error == 0) /* Orderly shutdown */ | |
472 | break; | |
da290220 CMN |
473 | } |
474 | ||
9cf0f287 | 475 | *out = git__strdup(file.path_lock); |
48a65a07 | 476 | if (*out == NULL) { |
9cf0f287 | 477 | error = GIT_ENOMEM; |
48a65a07 CMN |
478 | goto cleanup; |
479 | } | |
9cf0f287 | 480 | |
48a65a07 CMN |
481 | /* A bit dodgy, but we need to keep the pack at the temporary path */ |
482 | error = git_filebuf_commit_at(&file, file.path_lock); | |
da290220 CMN |
483 | cleanup: |
484 | if (error < GIT_SUCCESS) | |
485 | git_filebuf_cleanup(&file); | |
48a65a07 | 486 | |
da290220 CMN |
487 | return error; |
488 | } | |
489 | ||
9cf0f287 | 490 | static int git_download_pack(char **out, git_transport *transport, git_repository *repo) |
da290220 CMN |
491 | { |
492 | transport_git *t = (transport_git *) transport; | |
7284c105 | 493 | int s = t->socket, error = GIT_SUCCESS; |
da290220 CMN |
494 | gitno_buffer buf; |
495 | char buffer[1024]; | |
496 | git_pkt *pkt; | |
497 | const char *line_end, *ptr; | |
498 | ||
499 | gitno_buffer_setup(&buf, buffer, sizeof(buffer), s); | |
500 | /* | |
7284c105 | 501 | * For now, we ignore everything and wait for the pack |
da290220 CMN |
502 | */ |
503 | while (1) { | |
504 | error = gitno_recv(&buf); | |
505 | if (error < GIT_SUCCESS) | |
506 | return git__rethrow(GIT_EOSERR, "Failed to receive data"); | |
48a65a07 | 507 | if (error == 0) /* Orderly shutdown */ |
da290220 CMN |
508 | return GIT_SUCCESS; |
509 | ||
510 | ptr = buf.data; | |
511 | /* Whilst we're searching for the pack */ | |
7284c105 | 512 | while (1) { |
da290220 CMN |
513 | if (buf.offset == 0) |
514 | break; | |
515 | error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset); | |
516 | if (error == GIT_ESHORTBUFFER) | |
517 | break; | |
518 | if (error < GIT_SUCCESS) | |
519 | return error; | |
520 | ||
427ca3d3 | 521 | if (pkt->type == GIT_PKT_PACK) |
7284c105 | 522 | return store_pack(out, &buf, repo); |
427ca3d3 | 523 | |
da290220 CMN |
524 | /* For now we don't care about anything */ |
525 | free(pkt); | |
7284c105 | 526 | gitno_consume(&buf, line_end); |
da290220 | 527 | } |
da290220 | 528 | } |
da290220 CMN |
529 | } |
530 | ||
531 | ||
ecb6ca0e CMN |
532 | static int git_close(git_transport *transport) |
533 | { | |
4e913309 CMN |
534 | transport_git *t = (transport_git*) transport; |
535 | int s = t->socket; | |
ecb6ca0e CMN |
536 | int error; |
537 | ||
87d9869f | 538 | /* Can't do anything if there's an error, so don't bother checking */ |
4e913309 | 539 | git_pkt_send_flush(s); |
ecb6ca0e CMN |
540 | error = close(s); |
541 | if (error < 0) | |
542 | error = git__throw(GIT_EOSERR, "Failed to close socket"); | |
543 | ||
544 | return error; | |
545 | } | |
546 | ||
547 | static void git_free(git_transport *transport) | |
548 | { | |
4e913309 CMN |
549 | transport_git *t = (transport_git *) transport; |
550 | git_vector *refs = &t->refs; | |
ecb6ca0e CMN |
551 | unsigned int i; |
552 | ||
553 | for (i = 0; i < refs->length; ++i) { | |
554 | git_pkt *p = git_vector_get(refs, i); | |
be9fe679 | 555 | git_pkt_free(p); |
ecb6ca0e CMN |
556 | } |
557 | ||
558 | git_vector_free(refs); | |
4e913309 CMN |
559 | free(t->heads); |
560 | free(t->parent.url); | |
561 | free(t); | |
ecb6ca0e CMN |
562 | } |
563 | ||
4e913309 | 564 | int git_transport_git(git_transport **out) |
ecb6ca0e | 565 | { |
4e913309 CMN |
566 | transport_git *t; |
567 | ||
568 | t = git__malloc(sizeof(transport_git)); | |
569 | if (t == NULL) | |
570 | return GIT_ENOMEM; | |
571 | ||
5da5321d CMN |
572 | memset(t, 0x0, sizeof(transport_git)); |
573 | ||
4e913309 CMN |
574 | t->parent.connect = git_connect; |
575 | t->parent.ls = git_ls; | |
0e20ba60 | 576 | t->parent.send_wants = git_send_wants; |
7e1a94db | 577 | t->parent.send_have = git_send_have; |
22f65b9e | 578 | t->parent.negotiate_fetch = git_negotiate_fetch; |
da290220 | 579 | t->parent.send_flush = git_send_flush; |
7e1a94db | 580 | t->parent.send_done = git_send_done; |
da290220 | 581 | t->parent.download_pack = git_download_pack; |
4e913309 CMN |
582 | t->parent.close = git_close; |
583 | t->parent.free = git_free; | |
584 | ||
585 | *out = (git_transport *) t; | |
ecb6ca0e CMN |
586 | |
587 | return GIT_SUCCESS; | |
588 | } |