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