]> git.proxmox.com Git - libgit2.git/blob - src/transport_local.c
Merge pull request #392 from sschuberth/development
[libgit2.git] / src / transport_local.c
1 /*
2 * Copyright (C) 2009-2011 the libgit2 contributors
3 *
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.
6 */
7 #include "common.h"
8 #include "git2/types.h"
9 #include "git2/transport.h"
10 #include "git2/net.h"
11 #include "git2/repository.h"
12 #include "git2/object.h"
13 #include "git2/tag.h"
14 #include "refs.h"
15 #include "transport.h"
16 #include "posix.h"
17
18 typedef struct {
19 git_transport parent;
20 git_repository *repo;
21 git_vector *refs;
22 git_headarray wants_list;
23 } transport_local;
24
25 /*
26 * Try to open the url as a git directory. The direction doesn't
27 * matter in this case because we're calulating the heads ourselves.
28 */
29 static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
30 {
31 git_repository *repo;
32 int error;
33 transport_local *t = (transport_local *) transport;
34 const char *path;
35 const char file_prefix[] = "file://";
36 GIT_UNUSED_ARG(direction);
37
38 /* The repo layer doesn't want the prefix */
39 if (!git__prefixcmp(transport->url, file_prefix))
40 path = transport->url + strlen(file_prefix);
41 else
42 path = transport->url;
43
44 error = git_repository_open(&repo, path);
45 if (error < GIT_SUCCESS)
46 return git__rethrow(error, "Failed to open remote");
47
48 t->repo = repo;
49 t->parent.connected = 1;
50
51 return GIT_SUCCESS;
52 }
53
54 static int add_ref(const char *name, git_repository *repo, git_vector *vec)
55 {
56 const char peeled[] = "^{}";
57 git_remote_head *head;
58 git_reference *ref;
59 git_object *obj = NULL;
60 int error = GIT_SUCCESS, peel_len, ret;
61
62 head = git__malloc(sizeof(git_remote_head));
63 if (head == NULL)
64 return GIT_ENOMEM;
65
66 head->name = git__strdup(name);
67 if (head->name == NULL) {
68 error = GIT_ENOMEM;
69 goto out;
70 }
71
72 error = git_reference_lookup(&ref, repo, name);
73 if (error < GIT_SUCCESS)
74 goto out;
75
76 error = git_reference_resolve(&ref, ref);
77 if (error < GIT_SUCCESS)
78 goto out;
79
80 git_oid_cpy(&head->oid, git_reference_oid(ref));
81
82 error = git_vector_insert(vec, head);
83 if (error < GIT_SUCCESS)
84 goto out;
85
86 /* If it's not a tag, we don't need to try to peel it */
87 if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
88 goto out;
89
90 error = git_object_lookup(&obj, repo, &head->oid, GIT_OBJ_ANY);
91 if (error < GIT_SUCCESS) {
92 git__rethrow(error, "Failed to lookup object");
93 }
94
95 /* If it's not an annotated tag, just get out */
96 if (git_object_type(obj) != GIT_OBJ_TAG)
97 goto out;
98
99 /* And if it's a tag, peel it, and add it to the list */
100 head = git__malloc(sizeof(git_remote_head));
101 peel_len = strlen(name) + strlen(peeled);
102 head->name = git__malloc(peel_len + 1);
103 ret = p_snprintf(head->name, peel_len + 1, "%s%s", name, peeled);
104 if (ret >= peel_len + 1) {
105 error = git__throw(GIT_ERROR, "The string is magically to long");
106 }
107
108 git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj));
109
110 error = git_vector_insert(vec, head);
111 if (error < GIT_SUCCESS)
112 goto out;
113
114 out:
115 git_object_close(obj);
116 if (error < GIT_SUCCESS) {
117 free(head->name);
118 free(head);
119 }
120 return error;
121 }
122
123 static int local_ls(git_transport *transport, git_headarray *array)
124 {
125 int error;
126 unsigned int i;
127 git_repository *repo;
128 git_vector *vec;
129 git_strarray refs;
130 transport_local *t = (transport_local *) transport;
131
132 assert(transport && transport->connected);
133
134 repo = t->repo;
135
136 error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
137 if (error < GIT_SUCCESS)
138 return git__rethrow(error, "Failed to list remote heads");
139
140 vec = git__malloc(sizeof(git_vector));
141 if (vec == NULL) {
142 error = GIT_ENOMEM;
143 goto out;
144 }
145
146 error = git_vector_init(vec, refs.count, NULL);
147 if (error < GIT_SUCCESS)
148 return error;
149
150 /* Sort the references first */
151 git__tsort((void **)refs.strings, refs.count, &git__strcmp_cb);
152
153 /* Add HEAD */
154 error = add_ref(GIT_HEAD_FILE, repo, vec);
155 if (error < GIT_SUCCESS)
156 goto out;
157
158 for (i = 0; i < refs.count; ++i) {
159 error = add_ref(refs.strings[i], repo, vec);
160 if (error < GIT_SUCCESS)
161 goto out;
162 }
163
164 array->len = vec->length;
165 array->heads = (git_remote_head **)vec->contents;
166
167 t->refs = vec;
168
169 out:
170
171 git_strarray_free(&refs);
172
173 return error;
174 }
175
176 static int local_send_wants(git_transport *transport, git_headarray *array)
177 {
178 transport_local *t = (transport_local *) transport;
179 git_headarray *wants = &t->wants_list;
180
181 /*
182 * We need to store the list of wanted references so we can figure
183 * out what to transmit later.
184 */
185 wants->len = array->len;
186 wants->heads = array->heads;
187
188 /* We're local anyway, so we don't need this */
189 return GIT_SUCCESS;
190 }
191
192 static int local_close(git_transport *GIT_UNUSED(transport))
193 {
194 /* Nothing to do */
195 GIT_UNUSED_ARG(transport);
196 return GIT_SUCCESS;
197 }
198
199 static void local_free(git_transport *transport)
200 {
201 unsigned int i;
202 transport_local *t = (transport_local *) transport;
203 git_vector *vec = t->refs;
204
205 assert(transport);
206
207 for (i = 0; i < vec->length; ++i) {
208 git_remote_head *h = git_vector_get(vec, i);
209 free(h->name);
210 free(h);
211 }
212 git_vector_free(vec);
213 free(vec);
214 git_repository_free(t->repo);
215 free(t->parent.url);
216 free(t);
217 }
218
219 /**************
220 * Public API *
221 **************/
222
223 int git_transport_local(git_transport **out)
224 {
225 transport_local *t;
226
227 t = git__malloc(sizeof(transport_local));
228 if (t == NULL)
229 return GIT_ENOMEM;
230
231 t->parent.connect = local_connect;
232 t->parent.ls = local_ls;
233 t->parent.send_wants = local_send_wants;
234 t->parent.close = local_close;
235 t->parent.free = local_free;
236
237 *out = (git_transport *) t;
238
239 return GIT_SUCCESS;
240 }