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.
10 #include "git2/types.h"
18 #include "repository.h"
20 int git_fetchhead_ref_cmp(const void *a
, const void *b
)
22 const git_fetchhead_ref
*one
= (const git_fetchhead_ref
*)a
;
23 const git_fetchhead_ref
*two
= (const git_fetchhead_ref
*)b
;
25 if (one
->is_merge
&& !two
->is_merge
)
27 if (two
->is_merge
&& !one
->is_merge
)
30 if (one
->ref_name
&& two
->ref_name
)
31 return strcmp(one
->ref_name
, two
->ref_name
);
32 else if (one
->ref_name
)
34 else if (two
->ref_name
)
40 static char *sanitized_remote_url(const char *remote_url
)
42 git_net_url url
= GIT_NET_URL_INIT
;
43 char *sanitized
= NULL
;
46 if (git_net_url_parse(&url
, remote_url
) == 0) {
47 git_str buf
= GIT_STR_INIT
;
49 git__free(url
.username
);
50 git__free(url
.password
);
51 url
.username
= url
.password
= NULL
;
53 if ((error
= git_net_url_fmt(&buf
, &url
)) < 0)
56 sanitized
= git_str_detach(&buf
);
61 sanitized
= git__strdup(remote_url
);
63 git_net_url_dispose(&url
);
67 int git_fetchhead_ref_create(
68 git_fetchhead_ref
**out
,
70 unsigned int is_merge
,
72 const char *remote_url
)
74 git_fetchhead_ref
*fetchhead_ref
;
81 fetchhead_ref
= git__malloc(sizeof(git_fetchhead_ref
));
82 GIT_ERROR_CHECK_ALLOC(fetchhead_ref
);
84 memset(fetchhead_ref
, 0x0, sizeof(git_fetchhead_ref
));
86 git_oid_cpy(&fetchhead_ref
->oid
, oid
);
87 fetchhead_ref
->is_merge
= is_merge
;
90 fetchhead_ref
->ref_name
= git__strdup(ref_name
);
91 GIT_ERROR_CHECK_ALLOC(fetchhead_ref
->ref_name
);
95 fetchhead_ref
->remote_url
= sanitized_remote_url(remote_url
);
96 GIT_ERROR_CHECK_ALLOC(fetchhead_ref
->remote_url
);
104 static int fetchhead_ref_write(
106 git_fetchhead_ref
*fetchhead_ref
)
108 char oid
[GIT_OID_HEXSZ
+ 1];
109 const char *type
, *name
;
112 GIT_ASSERT_ARG(file
);
113 GIT_ASSERT_ARG(fetchhead_ref
);
115 git_oid_fmt(oid
, &fetchhead_ref
->oid
);
116 oid
[GIT_OID_HEXSZ
] = '\0';
118 if (git__prefixcmp(fetchhead_ref
->ref_name
, GIT_REFS_HEADS_DIR
) == 0) {
120 name
= fetchhead_ref
->ref_name
+ strlen(GIT_REFS_HEADS_DIR
);
121 } else if(git__prefixcmp(fetchhead_ref
->ref_name
,
122 GIT_REFS_TAGS_DIR
) == 0) {
124 name
= fetchhead_ref
->ref_name
+ strlen(GIT_REFS_TAGS_DIR
);
125 } else if (!git__strcmp(fetchhead_ref
->ref_name
, GIT_HEAD_FILE
)) {
129 name
= fetchhead_ref
->ref_name
;
133 return git_filebuf_printf(file
, "%s\t\t%s\n", oid
, fetchhead_ref
->remote_url
);
135 return git_filebuf_printf(file
, "%s\t%s\t%s'%s' of %s\n",
137 (fetchhead_ref
->is_merge
) ? "" : "not-for-merge",
140 fetchhead_ref
->remote_url
);
143 int git_fetchhead_write(git_repository
*repo
, git_vector
*fetchhead_refs
)
145 git_filebuf file
= GIT_FILEBUF_INIT
;
146 git_str path
= GIT_STR_INIT
;
148 git_fetchhead_ref
*fetchhead_ref
;
150 GIT_ASSERT_ARG(repo
);
151 GIT_ASSERT_ARG(fetchhead_refs
);
153 if (git_str_joinpath(&path
, repo
->gitdir
, GIT_FETCH_HEAD_FILE
) < 0)
156 if (git_filebuf_open(&file
, path
.ptr
, GIT_FILEBUF_APPEND
, GIT_REFS_FILE_MODE
) < 0) {
157 git_str_dispose(&path
);
161 git_str_dispose(&path
);
163 git_vector_sort(fetchhead_refs
);
165 git_vector_foreach(fetchhead_refs
, i
, fetchhead_ref
)
166 fetchhead_ref_write(&file
, fetchhead_ref
);
168 return git_filebuf_commit(&file
);
171 static int fetchhead_ref_parse(
173 unsigned int *is_merge
,
175 const char **remote_url
,
179 char *oid_str
, *is_merge_str
, *desc
, *name
= NULL
;
180 const char *type
= NULL
;
186 git_error_set(GIT_ERROR_FETCHHEAD
,
187 "empty line in FETCH_HEAD line %"PRIuZ
, line_num
);
191 /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
192 if ((oid_str
= git__strsep(&line
, "\t")) == NULL
) {
194 line
+= strlen(line
);
199 if (strlen(oid_str
) != GIT_OID_HEXSZ
) {
200 git_error_set(GIT_ERROR_FETCHHEAD
,
201 "invalid object ID in FETCH_HEAD line %"PRIuZ
, line_num
);
205 if (git_oid_fromstr(oid
, oid_str
) < 0) {
206 const git_error
*oid_err
= git_error_last();
207 const char *err_msg
= oid_err
? oid_err
->message
: "invalid object ID";
209 git_error_set(GIT_ERROR_FETCHHEAD
, "%s in FETCH_HEAD line %"PRIuZ
,
214 /* Parse new data from newer git clients */
216 if ((is_merge_str
= git__strsep(&line
, "\t")) == NULL
) {
217 git_error_set(GIT_ERROR_FETCHHEAD
,
218 "invalid description data in FETCH_HEAD line %"PRIuZ
, line_num
);
222 if (*is_merge_str
== '\0')
224 else if (strcmp(is_merge_str
, "not-for-merge") == 0)
227 git_error_set(GIT_ERROR_FETCHHEAD
,
228 "invalid for-merge entry in FETCH_HEAD line %"PRIuZ
, line_num
);
232 if ((desc
= line
) == NULL
) {
233 git_error_set(GIT_ERROR_FETCHHEAD
,
234 "invalid description in FETCH_HEAD line %"PRIuZ
, line_num
);
238 if (git__prefixcmp(desc
, "branch '") == 0) {
239 type
= GIT_REFS_HEADS_DIR
;
241 } else if (git__prefixcmp(desc
, "tag '") == 0) {
242 type
= GIT_REFS_TAGS_DIR
;
244 } else if (git__prefixcmp(desc
, "'") == 0)
248 if ((desc
= strstr(name
, "' ")) == NULL
||
249 git__prefixcmp(desc
, "' of ") != 0) {
250 git_error_set(GIT_ERROR_FETCHHEAD
,
251 "invalid description in FETCH_HEAD line %"PRIuZ
, line_num
);
262 git_str_clear(ref_name
);
265 git_str_join(ref_name
, '/', type
, name
);
267 git_str_puts(ref_name
, name
);
272 int git_repository_fetchhead_foreach(git_repository
*repo
,
273 git_repository_fetchhead_foreach_cb cb
,
276 git_str path
= GIT_STR_INIT
, file
= GIT_STR_INIT
, name
= GIT_STR_INIT
;
277 const char *ref_name
;
279 const char *remote_url
;
280 unsigned int is_merge
= 0;
285 GIT_ASSERT_ARG(repo
);
288 if (git_str_joinpath(&path
, repo
->gitdir
, GIT_FETCH_HEAD_FILE
) < 0)
291 if ((error
= git_futils_readbuffer(&file
, git_str_cstr(&path
))) < 0)
296 while ((line
= git__strsep(&buffer
, "\n")) != NULL
) {
299 if ((error
= fetchhead_ref_parse(
300 &oid
, &is_merge
, &name
, &remote_url
, line
, line_num
)) < 0)
303 if (git_str_len(&name
) > 0)
304 ref_name
= git_str_cstr(&name
);
308 error
= cb(ref_name
, remote_url
, &oid
, is_merge
, payload
);
310 git_error_set_after_callback(error
);
316 git_error_set(GIT_ERROR_FETCHHEAD
, "no EOL at line %"PRIuZ
, line_num
+1);
322 git_str_dispose(&file
);
323 git_str_dispose(&path
);
324 git_str_dispose(&name
);
329 void git_fetchhead_ref_free(git_fetchhead_ref
*fetchhead_ref
)
331 if (fetchhead_ref
== NULL
)
334 git__free(fetchhead_ref
->remote_url
);
335 git__free(fetchhead_ref
->ref_name
);
336 git__free(fetchhead_ref
);