]> git.proxmox.com Git - libgit2.git/blob - src/branch.c
branch: remove config section upon deletion
[libgit2.git] / src / branch.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
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
8 #include "common.h"
9 #include "commit.h"
10 #include "tag.h"
11 #include "config.h"
12 #include "refspec.h"
13
14 #include "git2/branch.h"
15
16 static int retrieve_branch_reference(
17 git_reference **branch_reference_out,
18 git_repository *repo,
19 const char *branch_name,
20 int is_remote)
21 {
22 git_reference *branch;
23 int error = -1;
24 char *prefix;
25 git_buf ref_name = GIT_BUF_INIT;
26
27 *branch_reference_out = NULL;
28
29 prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
30
31 if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
32 goto cleanup;
33
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);
37 goto cleanup;
38 }
39
40 *branch_reference_out = branch;
41
42 cleanup:
43 git_buf_free(&ref_name);
44 return error;
45 }
46
47 static int create_error_invalid(const char *msg)
48 {
49 giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
50 return -1;
51 }
52
53 static int not_a_local_branch(git_reference *ref)
54 {
55 giterr_set(GITERR_INVALID, "Reference '%s' is not a local branch.", git_reference_name(ref));
56 return -1;
57 }
58
59 int git_branch_create(
60 git_reference **ref_out,
61 git_repository *repository,
62 const char *branch_name,
63 const git_object *target,
64 int force)
65 {
66 git_object *commit = NULL;
67 git_reference *branch = NULL;
68 git_buf canonical_branch_name = GIT_BUF_INIT;
69 int error = -1;
70
71 assert(branch_name && target && ref_out);
72 assert(git_object_owner(target) == repository);
73
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");
76
77 if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
78 goto cleanup;
79
80 error = git_reference_create_oid(&branch, repository,
81 git_buf_cstr(&canonical_branch_name), git_object_id(commit), force);
82
83 if (!error)
84 *ref_out = branch;
85
86 cleanup:
87 git_object_free(commit);
88 git_buf_free(&canonical_branch_name);
89 return error;
90 }
91
92 static int delete_config_entries_cb(
93 const char *var_name,
94 const char *value,
95 void *payload)
96 {
97 git_config *config;
98
99 GIT_UNUSED(value);
100
101 config = (git_config *)payload;
102
103 return git_config_delete(config, var_name);
104 }
105
106 static int delete_branch_config_entries(
107 git_repository *repo,
108 const char *branch_name)
109 {
110 git_config *config;
111 git_buf pattern = GIT_BUF_INIT;
112 int error = -1;
113
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))
118 goto cleanup;
119
120 if (git_repository_config__weakptr(&config, repo) < 0)
121 goto cleanup;
122
123 if ((error = git_config_foreach_match(
124 config,
125 git_buf_cstr(&pattern),
126 delete_config_entries_cb, config)) < 0)
127 goto cleanup;
128
129 error = 0;
130
131 cleanup:
132 git_buf_free(&pattern);
133 return error;
134 }
135
136 int git_branch_delete(git_reference *branch)
137 {
138 int is_head;
139
140 assert(branch);
141
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));
145 return -1;
146 }
147
148 if ((is_head = git_branch_is_head(branch)) < 0)
149 return is_head;
150
151 if (is_head) {
152 giterr_set(GITERR_REFERENCE,
153 "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch));
154 return -1;
155 }
156
157 if (delete_branch_config_entries(
158 git_reference_owner(branch),
159 git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
160 goto on_error;
161
162 return git_reference_delete(branch);
163 }
164
165 typedef struct {
166 int (*branch_cb)(
167 const char *branch_name,
168 git_branch_t branch_type,
169 void *payload);
170 void *callback_payload;
171 unsigned int branch_type;
172 } branch_foreach_filter;
173
174 static int branch_foreach_cb(const char *branch_name, void *payload)
175 {
176 branch_foreach_filter *filter = (branch_foreach_filter *)payload;
177
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);
181
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);
185
186 return 0;
187 }
188
189 int git_branch_foreach(
190 git_repository *repo,
191 unsigned int list_flags,
192 int (*branch_cb)(
193 const char *branch_name,
194 git_branch_t branch_type,
195 void *payload),
196 void *payload
197 )
198 {
199 branch_foreach_filter filter;
200
201 filter.branch_cb = branch_cb;
202 filter.branch_type = list_flags;
203 filter.callback_payload = payload;
204
205 return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter);
206 }
207
208 int git_branch_move(
209 git_reference *branch,
210 const char *new_branch_name,
211 int force)
212 {
213 git_buf new_reference_name = GIT_BUF_INIT;
214 int error;
215
216 assert(branch && new_branch_name);
217
218 if (!git_reference_is_branch(branch))
219 return not_a_local_branch(branch);
220
221 if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
222 goto cleanup;
223
224 error = git_reference_rename(branch, git_buf_cstr(&new_reference_name), force);
225
226 cleanup:
227 git_buf_free(&new_reference_name);
228
229 return error;
230 }
231
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)
237 {
238 assert(ref_out && repo && branch_name);
239
240 return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
241 }
242
243 static int retrieve_tracking_configuration(
244 const char **out, git_reference *branch, const char *format)
245 {
246 git_config *config;
247 git_buf buf = GIT_BUF_INIT;
248 int error;
249
250 if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
251 return -1;
252
253 if (git_buf_printf(&buf, format,
254 git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
255 return -1;
256
257 error = git_config_get_string(out, config, git_buf_cstr(&buf));
258 git_buf_free(&buf);
259 return error;
260 }
261
262 int git_branch_tracking(
263 git_reference **tracking_out,
264 git_reference *branch)
265 {
266 const char *remote_name, *merge_name;
267 git_buf buf = GIT_BUF_INIT;
268 int error = -1;
269 git_remote *remote = NULL;
270 const git_refspec *refspec;
271
272 assert(tracking_out && branch);
273
274 if (!git_reference_is_branch(branch))
275 return not_a_local_branch(branch);
276
277 if ((error = retrieve_tracking_configuration(&remote_name, branch, "branch.%s.remote")) < 0)
278 goto cleanup;
279
280 if ((error = retrieve_tracking_configuration(&merge_name, branch, "branch.%s.merge")) < 0)
281 goto cleanup;
282
283 if (strcmp(".", remote_name) != 0) {
284 if ((error = git_remote_load(&remote, git_reference_owner(branch), remote_name)) < 0)
285 goto cleanup;
286
287 refspec = git_remote_fetchspec(remote);
288 if (refspec == NULL
289 || refspec->src == NULL
290 || refspec->dst == NULL) {
291 error = GIT_ENOTFOUND;
292 goto cleanup;
293 }
294
295 if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
296 goto cleanup;
297 } else
298 if (git_buf_sets(&buf, merge_name) < 0)
299 goto cleanup;
300
301 error = git_reference_lookup(
302 tracking_out,
303 git_reference_owner(branch),
304 git_buf_cstr(&buf));
305
306 cleanup:
307 git_remote_free(remote);
308 git_buf_free(&buf);
309 return error;
310 }
311
312 int git_branch_is_head(
313 git_reference *branch)
314 {
315 git_reference *head;
316 bool is_same = false;
317 int error;
318
319 assert(branch);
320
321 if (!git_reference_is_branch(branch))
322 return false;
323
324 error = git_repository_head(&head, git_reference_owner(branch));
325
326 if (error == GIT_EORPHANEDHEAD)
327 return false;
328
329 if (error < 0)
330 return -1;
331
332 is_same = strcmp(
333 git_reference_name(branch),
334 git_reference_name(head)) == 0;
335
336 git_reference_free(head);
337
338 return is_same;
339 }