2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
8 #include "git2/types.h"
11 #include "fetchhead.h"
17 #include "repository.h"
19 int git_fetchhead_ref_cmp(const void *a
, const void *b
)
21 const git_fetchhead_ref
*one
= (const git_fetchhead_ref
*)a
;
22 const git_fetchhead_ref
*two
= (const git_fetchhead_ref
*)b
;
24 if (one
->is_merge
&& !two
->is_merge
)
26 if (two
->is_merge
&& !one
->is_merge
)
29 if (one
->ref_name
&& two
->ref_name
)
30 return strcmp(one
->ref_name
, two
->ref_name
);
31 else if (one
->ref_name
)
33 else if (two
->ref_name
)
39 int git_fetchhead_ref_create(
40 git_fetchhead_ref
**out
,
42 unsigned int is_merge
,
44 const char *remote_url
)
46 git_fetchhead_ref
*fetchhead_ref
;
52 fetchhead_ref
= git__malloc(sizeof(git_fetchhead_ref
));
53 GITERR_CHECK_ALLOC(fetchhead_ref
);
55 memset(fetchhead_ref
, 0x0, sizeof(git_fetchhead_ref
));
57 git_oid_cpy(&fetchhead_ref
->oid
, oid
);
58 fetchhead_ref
->is_merge
= is_merge
;
61 fetchhead_ref
->ref_name
= git__strdup(ref_name
);
64 fetchhead_ref
->remote_url
= git__strdup(remote_url
);
71 static int fetchhead_ref_write(
73 git_fetchhead_ref
*fetchhead_ref
)
75 char oid
[GIT_OID_HEXSZ
+ 1];
76 const char *type
, *name
;
79 assert(file
&& fetchhead_ref
);
81 git_oid_fmt(oid
, &fetchhead_ref
->oid
);
82 oid
[GIT_OID_HEXSZ
] = '\0';
84 if (git__prefixcmp(fetchhead_ref
->ref_name
, GIT_REFS_HEADS_DIR
) == 0) {
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) {
90 name
= fetchhead_ref
->ref_name
+ strlen(GIT_REFS_TAGS_DIR
);
91 } else if (!git__strcmp(fetchhead_ref
->ref_name
, GIT_HEAD_FILE
)) {
95 name
= fetchhead_ref
->ref_name
;
99 return git_filebuf_printf(file
, "%s\t\t%s\n", oid
, fetchhead_ref
->remote_url
);
101 return git_filebuf_printf(file
, "%s\t%s\t%s'%s' of %s\n",
103 (fetchhead_ref
->is_merge
) ? "" : "not-for-merge",
106 fetchhead_ref
->remote_url
);
109 int git_fetchhead_write(git_repository
*repo
, git_vector
*fetchhead_refs
)
111 git_filebuf file
= GIT_FILEBUF_INIT
;
112 git_buf path
= GIT_BUF_INIT
;
114 git_fetchhead_ref
*fetchhead_ref
;
116 assert(repo
&& fetchhead_refs
);
118 if (git_buf_joinpath(&path
, repo
->path_repository
, GIT_FETCH_HEAD_FILE
) < 0)
121 if (git_filebuf_open(&file
, path
.ptr
, GIT_FILEBUF_FORCE
, GIT_REFS_FILE_MODE
) < 0) {
128 git_vector_sort(fetchhead_refs
);
130 git_vector_foreach(fetchhead_refs
, i
, fetchhead_ref
)
131 fetchhead_ref_write(&file
, fetchhead_ref
);
133 return git_filebuf_commit(&file
);
136 static int fetchhead_ref_parse(
138 unsigned int *is_merge
,
140 const char **remote_url
,
144 char *oid_str
, *is_merge_str
, *desc
, *name
= NULL
;
145 const char *type
= NULL
;
151 giterr_set(GITERR_FETCHHEAD
,
152 "Empty line in FETCH_HEAD line %d", line_num
);
156 /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
157 if ((oid_str
= git__strsep(&line
, "\t")) == NULL
) {
159 line
+= strlen(line
);
164 if (strlen(oid_str
) != GIT_OID_HEXSZ
) {
165 giterr_set(GITERR_FETCHHEAD
,
166 "Invalid object ID in FETCH_HEAD line %d", line_num
);
170 if (git_oid_fromstr(oid
, oid_str
) < 0) {
171 const git_error
*oid_err
= giterr_last();
172 const char *err_msg
= oid_err
? oid_err
->message
: "Invalid object ID";
174 giterr_set(GITERR_FETCHHEAD
, "%s in FETCH_HEAD line %d",
179 /* Parse new data from newer git clients */
181 if ((is_merge_str
= git__strsep(&line
, "\t")) == NULL
) {
182 giterr_set(GITERR_FETCHHEAD
,
183 "Invalid description data in FETCH_HEAD line %d", line_num
);
187 if (*is_merge_str
== '\0')
189 else if (strcmp(is_merge_str
, "not-for-merge") == 0)
192 giterr_set(GITERR_FETCHHEAD
,
193 "Invalid for-merge entry in FETCH_HEAD line %d", line_num
);
197 if ((desc
= line
) == NULL
) {
198 giterr_set(GITERR_FETCHHEAD
,
199 "Invalid description in FETCH_HEAD line %d", line_num
);
203 if (git__prefixcmp(desc
, "branch '") == 0) {
204 type
= GIT_REFS_HEADS_DIR
;
206 } else if (git__prefixcmp(desc
, "tag '") == 0) {
207 type
= GIT_REFS_TAGS_DIR
;
209 } else if (git__prefixcmp(desc
, "'") == 0)
213 if ((desc
= strchr(name
, '\'')) == NULL
||
214 git__prefixcmp(desc
, "' of ") != 0) {
215 giterr_set(GITERR_FETCHHEAD
,
216 "Invalid description in FETCH_HEAD line %d", line_num
);
227 git_buf_clear(ref_name
);
230 git_buf_join(ref_name
, '/', type
, name
);
232 git_buf_puts(ref_name
, name
);
237 int git_repository_fetchhead_foreach(git_repository
*repo
,
238 git_repository_fetchhead_foreach_cb cb
,
241 git_buf path
= GIT_BUF_INIT
, file
= GIT_BUF_INIT
, name
= GIT_BUF_INIT
;
242 const char *ref_name
;
244 const char *remote_url
;
245 unsigned int is_merge
= 0;
252 if (git_buf_joinpath(&path
, repo
->path_repository
, GIT_FETCH_HEAD_FILE
) < 0)
255 if ((error
= git_futils_readbuffer(&file
, git_buf_cstr(&path
))) < 0)
260 while ((line
= git__strsep(&buffer
, "\n")) != NULL
) {
263 if ((error
= fetchhead_ref_parse(&oid
, &is_merge
, &name
, &remote_url
,
264 line
, line_num
)) < 0)
267 if (git_buf_len(&name
) > 0)
268 ref_name
= git_buf_cstr(&name
);
272 if (cb(ref_name
, remote_url
, &oid
, is_merge
, payload
) != 0) {
273 error
= giterr_user_cancel();
279 giterr_set(GITERR_FETCHHEAD
, "No EOL at line %d", line_num
+1);
292 void git_fetchhead_ref_free(git_fetchhead_ref
*fetchhead_ref
)
294 if (fetchhead_ref
== NULL
)
297 git__free(fetchhead_ref
->remote_url
);
298 git__free(fetchhead_ref
->ref_name
);
299 git__free(fetchhead_ref
);