2 * Copyright (C) 2009-2012 the libgit2 contributors
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.
14 #include "git2/branch.h"
16 static int retrieve_branch_reference(
17 git_reference
**branch_reference_out
,
19 const char *branch_name
,
22 git_reference
*branch
;
25 git_buf ref_name
= GIT_BUF_INIT
;
27 *branch_reference_out
= NULL
;
29 prefix
= is_remote
? GIT_REFS_REMOTES_DIR
: GIT_REFS_HEADS_DIR
;
31 if (git_buf_joinpath(&ref_name
, prefix
, branch_name
) < 0)
34 if ((error
= git_reference_lookup(&branch
, repo
, ref_name
.ptr
)) < 0) {
35 giterr_set(GITERR_REFERENCE
,
36 "Cannot locate %s branch '%s'.", is_remote
? "remote-tracking" : "local", branch_name
);
40 *branch_reference_out
= branch
;
43 git_buf_free(&ref_name
);
47 static int create_error_invalid(const char *msg
)
49 giterr_set(GITERR_INVALID
, "Cannot create branch - %s", msg
);
53 static int not_a_local_branch(git_reference
*ref
)
55 giterr_set(GITERR_INVALID
, "Reference '%s' is not a local branch.", git_reference_name(ref
));
59 int git_branch_create(
60 git_reference
**ref_out
,
61 git_repository
*repository
,
62 const char *branch_name
,
63 const git_object
*target
,
66 git_object
*commit
= NULL
;
67 git_reference
*branch
= NULL
;
68 git_buf canonical_branch_name
= GIT_BUF_INIT
;
71 assert(branch_name
&& target
&& ref_out
);
72 assert(git_object_owner(target
) == repository
);
74 if (git_object_peel(&commit
, (git_object
*)target
, GIT_OBJ_COMMIT
) < 0)
75 return create_error_invalid("The given target does not resolve to a commit");
77 if (git_buf_joinpath(&canonical_branch_name
, GIT_REFS_HEADS_DIR
, branch_name
) < 0)
80 error
= git_reference_create_oid(&branch
, repository
,
81 git_buf_cstr(&canonical_branch_name
), git_object_id(commit
), force
);
87 git_object_free(commit
);
88 git_buf_free(&canonical_branch_name
);
92 static int delete_config_entries_cb(
101 config
= (git_config
*)payload
;
103 return git_config_delete(config
, var_name
);
106 static int delete_branch_config_entries(
107 git_repository
*repo
,
108 const char *branch_name
)
111 git_buf pattern
= GIT_BUF_INIT
;
114 git_buf_sets(&pattern
, "branch\\.");
115 git_buf_puts_escape_regex(&pattern
, branch_name
);
116 git_buf_puts(&pattern
, "\\..+");
117 if (git_buf_oom(&pattern
))
120 if (git_repository_config__weakptr(&config
, repo
) < 0)
123 if ((error
= git_config_foreach_match(
125 git_buf_cstr(&pattern
),
126 delete_config_entries_cb
, config
)) < 0)
132 git_buf_free(&pattern
);
136 int git_branch_delete(git_reference
*branch
)
142 if (!git_reference_is_branch(branch
) &&
143 !git_reference_is_remote(branch
)) {
144 giterr_set(GITERR_INVALID
, "Reference '%s' is not a valid branch.", git_reference_name(branch
));
148 if ((is_head
= git_branch_is_head(branch
)) < 0)
152 giterr_set(GITERR_REFERENCE
,
153 "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch
));
157 if (delete_branch_config_entries(
158 git_reference_owner(branch
),
159 git_reference_name(branch
) + strlen(GIT_REFS_HEADS_DIR
)) < 0)
162 return git_reference_delete(branch
);
167 const char *branch_name
,
168 git_branch_t branch_type
,
170 void *callback_payload
;
171 unsigned int branch_type
;
172 } branch_foreach_filter
;
174 static int branch_foreach_cb(const char *branch_name
, void *payload
)
176 branch_foreach_filter
*filter
= (branch_foreach_filter
*)payload
;
178 if (filter
->branch_type
& GIT_BRANCH_LOCAL
&&
179 git__prefixcmp(branch_name
, GIT_REFS_HEADS_DIR
) == 0)
180 return filter
->branch_cb(branch_name
+ strlen(GIT_REFS_HEADS_DIR
), GIT_BRANCH_LOCAL
, filter
->callback_payload
);
182 if (filter
->branch_type
& GIT_BRANCH_REMOTE
&&
183 git__prefixcmp(branch_name
, GIT_REFS_REMOTES_DIR
) == 0)
184 return filter
->branch_cb(branch_name
+ strlen(GIT_REFS_REMOTES_DIR
), GIT_BRANCH_REMOTE
, filter
->callback_payload
);
189 int git_branch_foreach(
190 git_repository
*repo
,
191 unsigned int list_flags
,
193 const char *branch_name
,
194 git_branch_t branch_type
,
199 branch_foreach_filter filter
;
201 filter
.branch_cb
= branch_cb
;
202 filter
.branch_type
= list_flags
;
203 filter
.callback_payload
= payload
;
205 return git_reference_foreach(repo
, GIT_REF_LISTALL
, &branch_foreach_cb
, (void *)&filter
);
209 git_reference
*branch
,
210 const char *new_branch_name
,
213 git_buf new_reference_name
= GIT_BUF_INIT
;
216 assert(branch
&& new_branch_name
);
218 if (!git_reference_is_branch(branch
))
219 return not_a_local_branch(branch
);
221 if ((error
= git_buf_joinpath(&new_reference_name
, GIT_REFS_HEADS_DIR
, new_branch_name
)) < 0)
224 error
= git_reference_rename(branch
, git_buf_cstr(&new_reference_name
), force
);
227 git_buf_free(&new_reference_name
);
232 int git_branch_lookup(
233 git_reference
**ref_out
,
234 git_repository
*repo
,
235 const char *branch_name
,
236 git_branch_t branch_type
)
238 assert(ref_out
&& repo
&& branch_name
);
240 return retrieve_branch_reference(ref_out
, repo
, branch_name
, branch_type
== GIT_BRANCH_REMOTE
);
243 static int retrieve_tracking_configuration(
244 const char **out
, git_reference
*branch
, const char *format
)
247 git_buf buf
= GIT_BUF_INIT
;
250 if (git_repository_config__weakptr(&config
, git_reference_owner(branch
)) < 0)
253 if (git_buf_printf(&buf
, format
,
254 git_reference_name(branch
) + strlen(GIT_REFS_HEADS_DIR
)) < 0)
257 error
= git_config_get_string(out
, config
, git_buf_cstr(&buf
));
262 int git_branch_tracking(
263 git_reference
**tracking_out
,
264 git_reference
*branch
)
266 const char *remote_name
, *merge_name
;
267 git_buf buf
= GIT_BUF_INIT
;
269 git_remote
*remote
= NULL
;
270 const git_refspec
*refspec
;
272 assert(tracking_out
&& branch
);
274 if (!git_reference_is_branch(branch
))
275 return not_a_local_branch(branch
);
277 if ((error
= retrieve_tracking_configuration(&remote_name
, branch
, "branch.%s.remote")) < 0)
280 if ((error
= retrieve_tracking_configuration(&merge_name
, branch
, "branch.%s.merge")) < 0)
283 if (strcmp(".", remote_name
) != 0) {
284 if ((error
= git_remote_load(&remote
, git_reference_owner(branch
), remote_name
)) < 0)
287 refspec
= git_remote_fetchspec(remote
);
289 || refspec
->src
== NULL
290 || refspec
->dst
== NULL
) {
291 error
= GIT_ENOTFOUND
;
295 if (git_refspec_transform_r(&buf
, refspec
, merge_name
) < 0)
298 if (git_buf_sets(&buf
, merge_name
) < 0)
301 error
= git_reference_lookup(
303 git_reference_owner(branch
),
307 git_remote_free(remote
);
312 int git_branch_is_head(
313 git_reference
*branch
)
316 bool is_same
= false;
321 if (!git_reference_is_branch(branch
))
324 error
= git_repository_head(&head
, git_reference_owner(branch
));
326 if (error
== GIT_EORPHANEDHEAD
)
333 git_reference_name(branch
),
334 git_reference_name(head
)) == 0;
336 git_reference_free(head
);