File Coverage

deps/libgit2/src/fetchhead.c
Criterion Covered Total %
statement 0 169 0.0
branch 0 112 0.0
condition n/a
subroutine n/a
pod n/a
total 0 281 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 "buffer.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_buf buf = GIT_BUF_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_buf_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         assert(out && oid);
    0          
77              
78 0           *out = NULL;
79              
80 0           fetchhead_ref = git__malloc(sizeof(git_fetchhead_ref));
81 0 0         GIT_ERROR_CHECK_ALLOC(fetchhead_ref);
82              
83 0           memset(fetchhead_ref, 0x0, sizeof(git_fetchhead_ref));
84              
85 0           git_oid_cpy(&fetchhead_ref->oid, oid);
86 0           fetchhead_ref->is_merge = is_merge;
87              
88 0 0         if (ref_name) {
89 0           fetchhead_ref->ref_name = git__strdup(ref_name);
90 0 0         GIT_ERROR_CHECK_ALLOC(fetchhead_ref->ref_name);
91             }
92              
93 0 0         if (remote_url) {
94 0           fetchhead_ref->remote_url = sanitized_remote_url(remote_url);
95 0 0         GIT_ERROR_CHECK_ALLOC(fetchhead_ref->remote_url);
96             }
97              
98 0           *out = fetchhead_ref;
99              
100 0           return 0;
101             }
102              
103 0           static int fetchhead_ref_write(
104             git_filebuf *file,
105             git_fetchhead_ref *fetchhead_ref)
106             {
107             char oid[GIT_OID_HEXSZ + 1];
108             const char *type, *name;
109 0           int head = 0;
110              
111 0 0         assert(file && fetchhead_ref);
    0          
112              
113 0           git_oid_fmt(oid, &fetchhead_ref->oid);
114 0           oid[GIT_OID_HEXSZ] = '\0';
115              
116 0 0         if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) {
117 0           type = "branch ";
118 0           name = fetchhead_ref->ref_name + strlen(GIT_REFS_HEADS_DIR);
119 0 0         } else if(git__prefixcmp(fetchhead_ref->ref_name,
120             GIT_REFS_TAGS_DIR) == 0) {
121 0           type = "tag ";
122 0           name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
123 0 0         } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
124 0           head = 1;
125             } else {
126 0           type = "";
127 0           name = fetchhead_ref->ref_name;
128             }
129              
130 0 0         if (head)
131 0           return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
132              
133 0 0         return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
134             oid,
135 0           (fetchhead_ref->is_merge) ? "" : "not-for-merge",
136             type,
137             name,
138             fetchhead_ref->remote_url);
139             }
140              
141 0           int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
142             {
143 0           git_filebuf file = GIT_FILEBUF_INIT;
144 0           git_buf path = GIT_BUF_INIT;
145             unsigned int i;
146             git_fetchhead_ref *fetchhead_ref;
147              
148 0 0         assert(repo && fetchhead_refs);
    0          
149              
150 0 0         if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
151 0           return -1;
152              
153 0 0         if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) {
154 0           git_buf_dispose(&path);
155 0           return -1;
156             }
157              
158 0           git_buf_dispose(&path);
159              
160 0           git_vector_sort(fetchhead_refs);
161              
162 0 0         git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
163 0           fetchhead_ref_write(&file, fetchhead_ref);
164              
165 0           return git_filebuf_commit(&file);
166             }
167              
168 0           static int fetchhead_ref_parse(
169             git_oid *oid,
170             unsigned int *is_merge,
171             git_buf *ref_name,
172             const char **remote_url,
173             char *line,
174             size_t line_num)
175             {
176 0           char *oid_str, *is_merge_str, *desc, *name = NULL;
177 0           const char *type = NULL;
178 0           int error = 0;
179              
180 0           *remote_url = NULL;
181              
182 0 0         if (!*line) {
183 0           git_error_set(GIT_ERROR_FETCHHEAD,
184             "empty line in FETCH_HEAD line %"PRIuZ, line_num);
185 0           return -1;
186             }
187              
188             /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
189 0 0         if ((oid_str = git__strsep(&line, "\t")) == NULL) {
190 0           oid_str = line;
191 0           line += strlen(line);
192              
193 0           *is_merge = 1;
194             }
195              
196 0 0         if (strlen(oid_str) != GIT_OID_HEXSZ) {
197 0           git_error_set(GIT_ERROR_FETCHHEAD,
198             "invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
199 0           return -1;
200             }
201              
202 0 0         if (git_oid_fromstr(oid, oid_str) < 0) {
203 0           const git_error *oid_err = git_error_last();
204 0 0         const char *err_msg = oid_err ? oid_err->message : "invalid object ID";
205              
206 0           git_error_set(GIT_ERROR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ,
207             err_msg, line_num);
208 0           return -1;
209             }
210              
211             /* Parse new data from newer git clients */
212 0 0         if (*line) {
213 0 0         if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
214 0           git_error_set(GIT_ERROR_FETCHHEAD,
215             "invalid description data in FETCH_HEAD line %"PRIuZ, line_num);
216 0           return -1;
217             }
218              
219 0 0         if (*is_merge_str == '\0')
220 0           *is_merge = 1;
221 0 0         else if (strcmp(is_merge_str, "not-for-merge") == 0)
222 0           *is_merge = 0;
223             else {
224 0           git_error_set(GIT_ERROR_FETCHHEAD,
225             "invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num);
226 0           return -1;
227             }
228              
229 0 0         if ((desc = line) == NULL) {
230 0           git_error_set(GIT_ERROR_FETCHHEAD,
231             "invalid description in FETCH_HEAD line %"PRIuZ, line_num);
232 0           return -1;
233             }
234              
235 0 0         if (git__prefixcmp(desc, "branch '") == 0) {
236 0           type = GIT_REFS_HEADS_DIR;
237 0           name = desc + 8;
238 0 0         } else if (git__prefixcmp(desc, "tag '") == 0) {
239 0           type = GIT_REFS_TAGS_DIR;
240 0           name = desc + 5;
241 0 0         } else if (git__prefixcmp(desc, "'") == 0)
242 0           name = desc + 1;
243              
244 0 0         if (name) {
245 0           if ((desc = strstr(name, "' ")) == NULL ||
246 0           git__prefixcmp(desc, "' of ") != 0) {
247 0           git_error_set(GIT_ERROR_FETCHHEAD,
248             "invalid description in FETCH_HEAD line %"PRIuZ, line_num);
249 0           return -1;
250             }
251              
252 0           *desc = '\0';
253 0           desc += 5;
254             }
255              
256 0           *remote_url = desc;
257             }
258              
259 0           git_buf_clear(ref_name);
260              
261 0 0         if (type)
262 0           git_buf_join(ref_name, '/', type, name);
263 0 0         else if(name)
264 0           git_buf_puts(ref_name, name);
265              
266 0           return error;
267             }
268              
269 0           int git_repository_fetchhead_foreach(git_repository *repo,
270             git_repository_fetchhead_foreach_cb cb,
271             void *payload)
272             {
273 0           git_buf path = GIT_BUF_INIT, file = GIT_BUF_INIT, name = GIT_BUF_INIT;
274             const char *ref_name;
275             git_oid oid;
276             const char *remote_url;
277 0           unsigned int is_merge = 0;
278             char *buffer, *line;
279 0           size_t line_num = 0;
280 0           int error = 0;
281              
282 0 0         assert(repo && cb);
    0          
283              
284 0 0         if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
285 0           return -1;
286              
287 0 0         if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0)
288 0           goto done;
289              
290 0           buffer = file.ptr;
291              
292 0 0         while ((line = git__strsep(&buffer, "\n")) != NULL) {
293 0           ++line_num;
294              
295 0 0         if ((error = fetchhead_ref_parse(
296             &oid, &is_merge, &name, &remote_url, line, line_num)) < 0)
297 0           goto done;
298              
299 0 0         if (git_buf_len(&name) > 0)
300 0           ref_name = git_buf_cstr(&name);
301             else
302 0           ref_name = NULL;
303              
304 0           error = cb(ref_name, remote_url, &oid, is_merge, payload);
305 0 0         if (error) {
306 0           git_error_set_after_callback(error);
307 0           goto done;
308             }
309             }
310              
311 0 0         if (*buffer) {
312 0           git_error_set(GIT_ERROR_FETCHHEAD, "no EOL at line %"PRIuZ, line_num+1);
313 0           error = -1;
314 0           goto done;
315             }
316              
317             done:
318 0           git_buf_dispose(&file);
319 0           git_buf_dispose(&path);
320 0           git_buf_dispose(&name);
321              
322 0           return error;
323             }
324              
325 0           void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref)
326             {
327 0 0         if (fetchhead_ref == NULL)
328 0           return;
329              
330 0           git__free(fetchhead_ref->remote_url);
331 0           git__free(fetchhead_ref->ref_name);
332 0           git__free(fetchhead_ref);
333             }
334