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