File Coverage

deps/libgit2/src/libgit2/fetchhead.c
Criterion Covered Total %
statement 0 173 0.0
branch 0 112 0.0
condition n/a
subroutine n/a
pod n/a
total 0 285 0.0


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