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_buf buf
= GIT_BUF_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_buf_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
;
80 fetchhead_ref
= git__malloc(sizeof(git_fetchhead_ref
));
81 GIT_ERROR_CHECK_ALLOC(fetchhead_ref
);
83 memset(fetchhead_ref
, 0x0, sizeof(git_fetchhead_ref
));
85 git_oid_cpy(&fetchhead_ref
->oid
, oid
);
86 fetchhead_ref
->is_merge
= is_merge
;
89 fetchhead_ref
->ref_name
= git__strdup(ref_name
);
90 GIT_ERROR_CHECK_ALLOC(fetchhead_ref
->ref_name
);
94 fetchhead_ref
->remote_url
= sanitized_remote_url(remote_url
);
95 GIT_ERROR_CHECK_ALLOC(fetchhead_ref
->remote_url
);
103 static int fetchhead_ref_write(
105 git_fetchhead_ref
*fetchhead_ref
)
107 char oid
[GIT_OID_HEXSZ
+ 1];
108 const char *type
, *name
;
111 assert(file
&& fetchhead_ref
);
113 git_oid_fmt(oid
, &fetchhead_ref
->oid
);
114 oid
[GIT_OID_HEXSZ
] = '\0';
116 if (git__prefixcmp(fetchhead_ref
->ref_name
, GIT_REFS_HEADS_DIR
) == 0) {
118 name
= fetchhead_ref
->ref_name
+ strlen(GIT_REFS_HEADS_DIR
);
119 } else if(git__prefixcmp(fetchhead_ref
->ref_name
,
120 GIT_REFS_TAGS_DIR
) == 0) {
122 name
= fetchhead_ref
->ref_name
+ strlen(GIT_REFS_TAGS_DIR
);
123 } else if (!git__strcmp(fetchhead_ref
->ref_name
, GIT_HEAD_FILE
)) {
127 name
= fetchhead_ref
->ref_name
;
131 return git_filebuf_printf(file
, "%s\t\t%s\n", oid
, fetchhead_ref
->remote_url
);
133 return git_filebuf_printf(file
, "%s\t%s\t%s'%s' of %s\n",
135 (fetchhead_ref
->is_merge
) ? "" : "not-for-merge",
138 fetchhead_ref
->remote_url
);
141 int git_fetchhead_write(git_repository
*repo
, git_vector
*fetchhead_refs
)
143 git_filebuf file
= GIT_FILEBUF_INIT
;
144 git_buf path
= GIT_BUF_INIT
;
146 git_fetchhead_ref
*fetchhead_ref
;
148 assert(repo
&& fetchhead_refs
);
150 if (git_buf_joinpath(&path
, repo
->gitdir
, GIT_FETCH_HEAD_FILE
) < 0)
153 if (git_filebuf_open(&file
, path
.ptr
, GIT_FILEBUF_APPEND
, GIT_REFS_FILE_MODE
) < 0) {
154 git_buf_dispose(&path
);
158 git_buf_dispose(&path
);
160 git_vector_sort(fetchhead_refs
);
162 git_vector_foreach(fetchhead_refs
, i
, fetchhead_ref
)
163 fetchhead_ref_write(&file
, fetchhead_ref
);
165 return git_filebuf_commit(&file
);
168 static int fetchhead_ref_parse(
170 unsigned int *is_merge
,
172 const char **remote_url
,
176 char *oid_str
, *is_merge_str
, *desc
, *name
= NULL
;
177 const char *type
= NULL
;
183 git_error_set(GIT_ERROR_FETCHHEAD
,
184 "empty line in FETCH_HEAD line %"PRIuZ
, line_num
);
188 /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
189 if ((oid_str
= git__strsep(&line
, "\t")) == NULL
) {
191 line
+= strlen(line
);
196 if (strlen(oid_str
) != GIT_OID_HEXSZ
) {
197 git_error_set(GIT_ERROR_FETCHHEAD
,
198 "invalid object ID in FETCH_HEAD line %"PRIuZ
, line_num
);
202 if (git_oid_fromstr(oid
, oid_str
) < 0) {
203 const git_error
*oid_err
= git_error_last();
204 const char *err_msg
= oid_err
? oid_err
->message
: "invalid object ID";
206 git_error_set(GIT_ERROR_FETCHHEAD
, "%s in FETCH_HEAD line %"PRIuZ
,
211 /* Parse new data from newer git clients */
213 if ((is_merge_str
= git__strsep(&line
, "\t")) == NULL
) {
214 git_error_set(GIT_ERROR_FETCHHEAD
,
215 "invalid description data in FETCH_HEAD line %"PRIuZ
, line_num
);
219 if (*is_merge_str
== '\0')
221 else if (strcmp(is_merge_str
, "not-for-merge") == 0)
224 git_error_set(GIT_ERROR_FETCHHEAD
,
225 "invalid for-merge entry in FETCH_HEAD line %"PRIuZ
, line_num
);
229 if ((desc
= line
) == NULL
) {
230 git_error_set(GIT_ERROR_FETCHHEAD
,
231 "invalid description in FETCH_HEAD line %"PRIuZ
, line_num
);
235 if (git__prefixcmp(desc
, "branch '") == 0) {
236 type
= GIT_REFS_HEADS_DIR
;
238 } else if (git__prefixcmp(desc
, "tag '") == 0) {
239 type
= GIT_REFS_TAGS_DIR
;
241 } else if (git__prefixcmp(desc
, "'") == 0)
245 if ((desc
= strstr(name
, "' ")) == NULL
||
246 git__prefixcmp(desc
, "' of ") != 0) {
247 git_error_set(GIT_ERROR_FETCHHEAD
,
248 "invalid description in FETCH_HEAD line %"PRIuZ
, line_num
);
259 git_buf_clear(ref_name
);
262 git_buf_join(ref_name
, '/', type
, name
);
264 git_buf_puts(ref_name
, name
);
269 int git_repository_fetchhead_foreach(git_repository
*repo
,
270 git_repository_fetchhead_foreach_cb cb
,
273 git_buf path
= GIT_BUF_INIT
, file
= GIT_BUF_INIT
, name
= GIT_BUF_INIT
;
274 const char *ref_name
;
276 const char *remote_url
;
277 unsigned int is_merge
= 0;
284 if (git_buf_joinpath(&path
, repo
->gitdir
, GIT_FETCH_HEAD_FILE
) < 0)
287 if ((error
= git_futils_readbuffer(&file
, git_buf_cstr(&path
))) < 0)
292 while ((line
= git__strsep(&buffer
, "\n")) != NULL
) {
295 if ((error
= fetchhead_ref_parse(
296 &oid
, &is_merge
, &name
, &remote_url
, line
, line_num
)) < 0)
299 if (git_buf_len(&name
) > 0)
300 ref_name
= git_buf_cstr(&name
);
304 error
= cb(ref_name
, remote_url
, &oid
, is_merge
, payload
);
306 git_error_set_after_callback(error
);
312 git_error_set(GIT_ERROR_FETCHHEAD
, "no EOL at line %"PRIuZ
, line_num
+1);
318 git_buf_dispose(&file
);
319 git_buf_dispose(&path
);
320 git_buf_dispose(&name
);
325 void git_fetchhead_ref_free(git_fetchhead_ref
*fetchhead_ref
)
327 if (fetchhead_ref
== NULL
)
330 git__free(fetchhead_ref
->remote_url
);
331 git__free(fetchhead_ref
->ref_name
);
332 git__free(fetchhead_ref
);