]> git.proxmox.com Git - libgit2.git/blame - src/fetchhead.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / fetchhead.c
CommitLineData
b0f6e45d 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
b0f6e45d
ET
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
eae0bfdc
PP
8#include "fetchhead.h"
9
b0f6e45d
ET
10#include "git2/types.h"
11#include "git2/oid.h"
12
e579e0f7 13#include "str.h"
22a2d3d5 14#include "futils.h"
b0f6e45d
ET
15#include "filebuf.h"
16#include "refs.h"
22a2d3d5 17#include "net.h"
b0f6e45d
ET
18#include "repository.h"
19
b0f6e45d
ET
20int git_fetchhead_ref_cmp(const void *a, const void *b)
21{
22 const git_fetchhead_ref *one = (const git_fetchhead_ref *)a;
23 const git_fetchhead_ref *two = (const git_fetchhead_ref *)b;
24
25 if (one->is_merge && !two->is_merge)
26 return -1;
27 if (two->is_merge && !one->is_merge)
28 return 1;
29
7fcec834
ET
30 if (one->ref_name && two->ref_name)
31 return strcmp(one->ref_name, two->ref_name);
32 else if (one->ref_name)
33 return -1;
34 else if (two->ref_name)
35 return 1;
36
37 return 0;
b0f6e45d
ET
38}
39
22a2d3d5
UG
40static char *sanitized_remote_url(const char *remote_url)
41{
42 git_net_url url = GIT_NET_URL_INIT;
43 char *sanitized = NULL;
44 int error;
45
46 if (git_net_url_parse(&url, remote_url) == 0) {
e579e0f7 47 git_str buf = GIT_STR_INIT;
22a2d3d5
UG
48
49 git__free(url.username);
50 git__free(url.password);
51 url.username = url.password = NULL;
52
53 if ((error = git_net_url_fmt(&buf, &url)) < 0)
54 goto fallback;
55
e579e0f7 56 sanitized = git_str_detach(&buf);
22a2d3d5
UG
57 }
58
59fallback:
60 if (!sanitized)
61 sanitized = git__strdup(remote_url);
62
63 git_net_url_dispose(&url);
64 return sanitized;
65}
66
b0f6e45d 67int git_fetchhead_ref_create(
7fcec834 68 git_fetchhead_ref **out,
b0f6e45d 69 git_oid *oid,
7fcec834 70 unsigned int is_merge,
b0f6e45d
ET
71 const char *ref_name,
72 const char *remote_url)
73{
7fcec834
ET
74 git_fetchhead_ref *fetchhead_ref;
75
c25aa7cd
PP
76 GIT_ASSERT_ARG(out);
77 GIT_ASSERT_ARG(oid);
b0f6e45d 78
7fcec834 79 *out = NULL;
b0f6e45d
ET
80
81 fetchhead_ref = git__malloc(sizeof(git_fetchhead_ref));
ac3d33df 82 GIT_ERROR_CHECK_ALLOC(fetchhead_ref);
b0f6e45d
ET
83
84 memset(fetchhead_ref, 0x0, sizeof(git_fetchhead_ref));
85
86 git_oid_cpy(&fetchhead_ref->oid, oid);
87 fetchhead_ref->is_merge = is_merge;
b0f6e45d 88
22a2d3d5 89 if (ref_name) {
7fcec834 90 fetchhead_ref->ref_name = git__strdup(ref_name);
22a2d3d5
UG
91 GIT_ERROR_CHECK_ALLOC(fetchhead_ref->ref_name);
92 }
7fcec834 93
22a2d3d5
UG
94 if (remote_url) {
95 fetchhead_ref->remote_url = sanitized_remote_url(remote_url);
96 GIT_ERROR_CHECK_ALLOC(fetchhead_ref->remote_url);
97 }
7fcec834
ET
98
99 *out = fetchhead_ref;
b0f6e45d
ET
100
101 return 0;
102}
103
104static int fetchhead_ref_write(
105 git_filebuf *file,
106 git_fetchhead_ref *fetchhead_ref)
107{
108 char oid[GIT_OID_HEXSZ + 1];
109 const char *type, *name;
968c7d07 110 int head = 0;
b0f6e45d 111
c25aa7cd
PP
112 GIT_ASSERT_ARG(file);
113 GIT_ASSERT_ARG(fetchhead_ref);
b0f6e45d
ET
114
115 git_oid_fmt(oid, &fetchhead_ref->oid);
116 oid[GIT_OID_HEXSZ] = '\0';
117
118 if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) {
119 type = "branch ";
120 name = fetchhead_ref->ref_name + strlen(GIT_REFS_HEADS_DIR);
121 } else if(git__prefixcmp(fetchhead_ref->ref_name,
122 GIT_REFS_TAGS_DIR) == 0) {
123 type = "tag ";
124 name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
968c7d07
CMN
125 } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
126 head = 1;
b0f6e45d
ET
127 } else {
128 type = "";
129 name = fetchhead_ref->ref_name;
130 }
131
968c7d07
CMN
132 if (head)
133 return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
134
b0f6e45d
ET
135 return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
136 oid,
137 (fetchhead_ref->is_merge) ? "" : "not-for-merge",
138 type,
139 name,
140 fetchhead_ref->remote_url);
141}
142
143int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
144{
145 git_filebuf file = GIT_FILEBUF_INIT;
e579e0f7 146 git_str path = GIT_STR_INIT;
b0f6e45d
ET
147 unsigned int i;
148 git_fetchhead_ref *fetchhead_ref;
149
c25aa7cd
PP
150 GIT_ASSERT_ARG(repo);
151 GIT_ASSERT_ARG(fetchhead_refs);
b0f6e45d 152
e579e0f7 153 if (git_str_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
b0f6e45d
ET
154 return -1;
155
eae0bfdc 156 if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) {
e579e0f7 157 git_str_dispose(&path);
b0f6e45d
ET
158 return -1;
159 }
160
e579e0f7 161 git_str_dispose(&path);
b0f6e45d
ET
162
163 git_vector_sort(fetchhead_refs);
164
165 git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
166 fetchhead_ref_write(&file, fetchhead_ref);
167
1d3a8aeb 168 return git_filebuf_commit(&file);
b0f6e45d
ET
169}
170
7fcec834
ET
171static int fetchhead_ref_parse(
172 git_oid *oid,
173 unsigned int *is_merge,
e579e0f7 174 git_str *ref_name,
7fcec834
ET
175 const char **remote_url,
176 char *line,
177 size_t line_num)
178{
179 char *oid_str, *is_merge_str, *desc, *name = NULL;
180 const char *type = NULL;
181 int error = 0;
182
183 *remote_url = NULL;
184
185 if (!*line) {
ac3d33df 186 git_error_set(GIT_ERROR_FETCHHEAD,
909d5494 187 "empty line in FETCH_HEAD line %"PRIuZ, line_num);
7fcec834
ET
188 return -1;
189 }
190
191 /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
192 if ((oid_str = git__strsep(&line, "\t")) == NULL) {
193 oid_str = line;
194 line += strlen(line);
195
196 *is_merge = 1;
197 }
198
199 if (strlen(oid_str) != GIT_OID_HEXSZ) {
ac3d33df 200 git_error_set(GIT_ERROR_FETCHHEAD,
909d5494 201 "invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
7fcec834
ET
202 return -1;
203 }
204
205 if (git_oid_fromstr(oid, oid_str) < 0) {
ac3d33df 206 const git_error *oid_err = git_error_last();
909d5494 207 const char *err_msg = oid_err ? oid_err->message : "invalid object ID";
7fcec834 208
ac3d33df 209 git_error_set(GIT_ERROR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ,
7fcec834
ET
210 err_msg, line_num);
211 return -1;
212 }
213
214 /* Parse new data from newer git clients */
215 if (*line) {
216 if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
ac3d33df 217 git_error_set(GIT_ERROR_FETCHHEAD,
909d5494 218 "invalid description data in FETCH_HEAD line %"PRIuZ, line_num);
7fcec834
ET
219 return -1;
220 }
221
222 if (*is_merge_str == '\0')
223 *is_merge = 1;
224 else if (strcmp(is_merge_str, "not-for-merge") == 0)
225 *is_merge = 0;
226 else {
ac3d33df 227 git_error_set(GIT_ERROR_FETCHHEAD,
909d5494 228 "invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num);
7fcec834
ET
229 return -1;
230 }
231
232 if ((desc = line) == NULL) {
ac3d33df 233 git_error_set(GIT_ERROR_FETCHHEAD,
909d5494 234 "invalid description in FETCH_HEAD line %"PRIuZ, line_num);
7fcec834
ET
235 return -1;
236 }
237
238 if (git__prefixcmp(desc, "branch '") == 0) {
239 type = GIT_REFS_HEADS_DIR;
240 name = desc + 8;
241 } else if (git__prefixcmp(desc, "tag '") == 0) {
242 type = GIT_REFS_TAGS_DIR;
243 name = desc + 5;
244 } else if (git__prefixcmp(desc, "'") == 0)
245 name = desc + 1;
246
247 if (name) {
bdc82e1c 248 if ((desc = strstr(name, "' ")) == NULL ||
7fcec834 249 git__prefixcmp(desc, "' of ") != 0) {
ac3d33df 250 git_error_set(GIT_ERROR_FETCHHEAD,
909d5494 251 "invalid description in FETCH_HEAD line %"PRIuZ, line_num);
7fcec834
ET
252 return -1;
253 }
254
255 *desc = '\0';
256 desc += 5;
257 }
258
259 *remote_url = desc;
260 }
261
e579e0f7 262 git_str_clear(ref_name);
7fcec834
ET
263
264 if (type)
e579e0f7 265 git_str_join(ref_name, '/', type, name);
7fcec834 266 else if(name)
e579e0f7 267 git_str_puts(ref_name, name);
7fcec834
ET
268
269 return error;
270}
271
272int git_repository_fetchhead_foreach(git_repository *repo,
273 git_repository_fetchhead_foreach_cb cb,
274 void *payload)
275{
e579e0f7 276 git_str path = GIT_STR_INIT, file = GIT_STR_INIT, name = GIT_STR_INIT;
7fcec834
ET
277 const char *ref_name;
278 git_oid oid;
279 const char *remote_url;
7382551f 280 unsigned int is_merge = 0;
7fcec834
ET
281 char *buffer, *line;
282 size_t line_num = 0;
283 int error = 0;
284
c25aa7cd
PP
285 GIT_ASSERT_ARG(repo);
286 GIT_ASSERT_ARG(cb);
7fcec834 287
e579e0f7 288 if (git_str_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
7fcec834
ET
289 return -1;
290
e579e0f7 291 if ((error = git_futils_readbuffer(&file, git_str_cstr(&path))) < 0)
7fcec834
ET
292 goto done;
293
294 buffer = file.ptr;
295
296 while ((line = git__strsep(&buffer, "\n")) != NULL) {
297 ++line_num;
298
c7b3e1b3
RB
299 if ((error = fetchhead_ref_parse(
300 &oid, &is_merge, &name, &remote_url, line, line_num)) < 0)
7fcec834
ET
301 goto done;
302
e579e0f7
MB
303 if (git_str_len(&name) > 0)
304 ref_name = git_str_cstr(&name);
7fcec834
ET
305 else
306 ref_name = NULL;
307
c7b3e1b3
RB
308 error = cb(ref_name, remote_url, &oid, is_merge, payload);
309 if (error) {
ac3d33df 310 git_error_set_after_callback(error);
7fcec834 311 goto done;
c7b3e1b3 312 }
7fcec834
ET
313 }
314
315 if (*buffer) {
ac3d33df 316 git_error_set(GIT_ERROR_FETCHHEAD, "no EOL at line %"PRIuZ, line_num+1);
7fcec834
ET
317 error = -1;
318 goto done;
319 }
320
321done:
e579e0f7
MB
322 git_str_dispose(&file);
323 git_str_dispose(&path);
324 git_str_dispose(&name);
7fcec834
ET
325
326 return error;
327}
328
b0f6e45d
ET
329void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref)
330{
331 if (fetchhead_ref == NULL)
332 return;
333
334 git__free(fetchhead_ref->remote_url);
335 git__free(fetchhead_ref->ref_name);
336 git__free(fetchhead_ref);
337}
338