]> git.proxmox.com Git - libgit2.git/blame - src/branch.c
Add range checking around cache opts
[libgit2.git] / src / branch.c
CommitLineData
731df570 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
731df570 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"
731df570 10#include "tag.h"
fb910281 11#include "config.h"
12#include "refspec.h"
bf031581 13#include "refs.h"
4330ab26 14#include "remote.h"
731df570 15
326ca710 16#include "git2/branch.h"
17
731df570 18static int retrieve_branch_reference(
19 git_reference **branch_reference_out,
20 git_repository *repo,
21 const char *branch_name,
22 int is_remote)
23{
24 git_reference *branch;
25 int error = -1;
26 char *prefix;
27 git_buf ref_name = GIT_BUF_INIT;
28
29 *branch_reference_out = NULL;
30
31 prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
32
33 if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
34 goto cleanup;
35
36 if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
37 giterr_set(GITERR_REFERENCE,
38 "Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
39 goto cleanup;
40 }
41
42 *branch_reference_out = branch;
43
44cleanup:
45 git_buf_free(&ref_name);
46 return error;
47}
48
bf031581 49static int not_a_local_branch(const char *reference_name)
1c947daa 50{
bf031581 51 giterr_set(
52 GITERR_INVALID,
53 "Reference '%s' is not a local branch.", reference_name);
1c947daa
VM
54 return -1;
55}
56
731df570 57int git_branch_create(
d00d5464
ET
58 git_reference **ref_out,
59 git_repository *repository,
60 const char *branch_name,
61 const git_commit *commit,
62 int force)
731df570 63{
731df570 64 git_reference *branch = NULL;
65 git_buf canonical_branch_name = GIT_BUF_INIT;
66 int error = -1;
67
cfbe4be3
VM
68 assert(branch_name && commit && ref_out);
69 assert(git_object_owner((const git_object *)commit) == repository);
731df570 70
71 if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
72 goto cleanup;
73
2508cc66 74 error = git_reference_create(&branch, repository,
cfbe4be3 75 git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force);
731df570 76
62993b61 77 if (!error)
78 *ref_out = branch;
731df570 79
80cleanup:
731df570 81 git_buf_free(&canonical_branch_name);
82 return error;
83}
84
1c947daa 85int git_branch_delete(git_reference *branch)
731df570 86{
4ba23be1 87 int is_head;
aba70781 88 git_buf config_section = GIT_BUF_INIT;
89 int error = -1;
731df570 90
1c947daa 91 assert(branch);
731df570 92
1c947daa
VM
93 if (!git_reference_is_branch(branch) &&
94 !git_reference_is_remote(branch)) {
95 giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch));
96 return -1;
97 }
731df570 98
4ba23be1 99 if ((is_head = git_branch_is_head(branch)) < 0)
100 return is_head;
731df570 101
4ba23be1 102 if (is_head) {
103 giterr_set(GITERR_REFERENCE,
104 "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch));
105 return -1;
731df570 106 }
107
aba70781 108 if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
109 goto on_error;
110
111 if (git_config_rename_section(
0b98a8a4 112 git_reference_owner(branch),
aba70781 113 git_buf_cstr(&config_section),
383f164a 114 NULL) < 0)
0b98a8a4 115 goto on_error;
116
aba70781 117 if (git_reference_delete(branch) < 0)
118 goto on_error;
119
120 error = 0;
121
122on_error:
123 git_buf_free(&config_section);
124 return error;
731df570 125}
126
a8fd805e 127typedef struct {
d00d5464 128 git_branch_foreach_cb branch_cb;
a8fd805e 129 void *callback_payload;
130 unsigned int branch_type;
131} branch_foreach_filter;
132
133static int branch_foreach_cb(const char *branch_name, void *payload)
134{
135 branch_foreach_filter *filter = (branch_foreach_filter *)payload;
136
137 if (filter->branch_type & GIT_BRANCH_LOCAL &&
138 git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
139 return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload);
140
141 if (filter->branch_type & GIT_BRANCH_REMOTE &&
142 git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0)
143 return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload);
144
145 return 0;
146}
147
148int git_branch_foreach(
d00d5464
ET
149 git_repository *repo,
150 unsigned int list_flags,
151 git_branch_foreach_cb branch_cb,
152 void *payload)
a8fd805e 153{
154 branch_foreach_filter filter;
155
156 filter.branch_cb = branch_cb;
157 filter.branch_type = list_flags;
158 filter.callback_payload = payload;
159
160 return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter);
161}
162
bf9e8cc8 163int git_branch_move(
d00d5464 164 git_reference **out,
bf9e8cc8 165 git_reference *branch,
166 const char *new_branch_name,
167 int force)
168{
383f164a 169 git_buf new_reference_name = GIT_BUF_INIT,
aba70781 170 old_config_section = GIT_BUF_INIT,
171 new_config_section = GIT_BUF_INIT;
bf9e8cc8 172 int error;
6a8bcfa4 173
bf9e8cc8 174 assert(branch && new_branch_name);
175
176 if (!git_reference_is_branch(branch))
bf031581 177 return not_a_local_branch(git_reference_name(branch));
4615f0f7 178
d00d5464 179 if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0 ||
10c06114 180 (error = git_buf_printf(&old_config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0 ||
d00d5464
ET
181 (error = git_buf_printf(&new_config_section, "branch.%s", new_branch_name)) < 0)
182 goto done;
383f164a 183
d00d5464 184 if ((error = git_config_rename_section(git_reference_owner(branch),
aba70781 185 git_buf_cstr(&old_config_section),
186 git_buf_cstr(&new_config_section))) < 0)
d00d5464
ET
187 goto done;
188
189 if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0)
190 goto done;
4615f0f7 191
d00d5464 192done:
6a8bcfa4 193 git_buf_free(&new_reference_name);
aba70781 194 git_buf_free(&old_config_section);
195 git_buf_free(&new_config_section);
6a8bcfa4 196
6a625435 197 return error;
4615f0f7 198}
eed378b6 199
200int git_branch_lookup(
d00d5464
ET
201 git_reference **ref_out,
202 git_repository *repo,
203 const char *branch_name,
204 git_branch_t branch_type)
eed378b6 205{
206 assert(ref_out && repo && branch_name);
207
208 return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
209}
fb910281 210
c253056d
SB
211int git_branch_name(const char **out, git_reference *ref)
212{
213 const char *branch_name;
214
215 assert(out && ref);
216
217 branch_name = ref->name;
218
219 if (git_reference_is_branch(ref)) {
220 branch_name += strlen(GIT_REFS_HEADS_DIR);
221 } else if (git_reference_is_remote(ref)) {
222 branch_name += strlen(GIT_REFS_REMOTES_DIR);
223 } else {
224 giterr_set(GITERR_INVALID,
225 "Reference '%s' is neither a local nor a remote branch.", ref->name);
226 return -1;
227 }
228 *out = branch_name;
229 return 0;
230}
231
a258d8e3 232static int retrieve_upstream_configuration(
bf031581 233 const char **out,
234 git_repository *repo,
235 const char *canonical_branch_name,
236 const char *format)
fb910281 237{
238 git_config *config;
239 git_buf buf = GIT_BUF_INIT;
240 int error;
241
bf031581 242 if (git_repository_config__weakptr(&config, repo) < 0)
fb910281 243 return -1;
244
245 if (git_buf_printf(&buf, format,
bf031581 246 canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
fb910281 247 return -1;
248
249 error = git_config_get_string(out, config, git_buf_cstr(&buf));
250 git_buf_free(&buf);
251 return error;
252}
253
a258d8e3 254int git_branch_upstream__name(
bf031581 255 git_buf *tracking_name,
256 git_repository *repo,
257 const char *canonical_branch_name)
fb910281 258{
259 const char *remote_name, *merge_name;
260 git_buf buf = GIT_BUF_INIT;
261 int error = -1;
262 git_remote *remote = NULL;
263 const git_refspec *refspec;
264
bf031581 265 assert(tracking_name && canonical_branch_name);
fb910281 266
bf031581 267 if (!git_reference__is_branch(canonical_branch_name))
268 return not_a_local_branch(canonical_branch_name);
fb910281 269
a258d8e3 270 if ((error = retrieve_upstream_configuration(
bf031581 271 &remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0)
272 goto cleanup;
fb910281 273
a258d8e3 274 if ((error = retrieve_upstream_configuration(
bf031581 275 &merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0)
276 goto cleanup;
37849a8e 277
28cbd2e2 278 if (!*remote_name || !*merge_name) {
279 error = GIT_ENOTFOUND;
280 goto cleanup;
281 }
fb910281 282
283 if (strcmp(".", remote_name) != 0) {
bf031581 284 if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
fb910281 285 goto cleanup;
286
4330ab26
CMN
287 refspec = git_remote__matching_refspec(remote, merge_name);
288 if (!refspec) {
289 error = GIT_ENOTFOUND;
290 goto cleanup;
fb910281 291 }
292
293 if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
294 goto cleanup;
295 } else
296 if (git_buf_sets(&buf, merge_name) < 0)
297 goto cleanup;
298
bf031581 299 error = git_buf_set(tracking_name, git_buf_cstr(&buf), git_buf_len(&buf));
fb910281 300
301cleanup:
302 git_remote_free(remote);
303 git_buf_free(&buf);
304 return error;
305}
0c78f685 306
97016f29 307static int remote_name(git_buf *buf, git_repository *repo, const char *canonical_branch_name)
2e3e8c88
JM
308{
309 git_strarray remote_list = {0};
97016f29 310 size_t i;
2e3e8c88
JM
311 git_remote *remote;
312 const git_refspec *fetchspec;
313 int error = 0;
314 char *remote_name = NULL;
315
97016f29 316 assert(buf && repo && canonical_branch_name);
2e3e8c88
JM
317
318 /* Verify that this is a remote branch */
c1b5e8c4 319 if (!git_reference__is_remote(canonical_branch_name)) {
320 giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
321 canonical_branch_name);
2e3e8c88
JM
322 error = GIT_ERROR;
323 goto cleanup;
324 }
325
326 /* Get the remotes */
327 if ((error = git_remote_list(&remote_list, repo)) < 0)
328 goto cleanup;
329
330 /* Find matching remotes */
331 for (i = 0; i < remote_list.count; i++) {
332 if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
d59942c2 333 continue;
2e3e8c88 334
4330ab26
CMN
335 fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name);
336 if (fetchspec) {
2e3e8c88
JM
337 /* If we have not already set out yet, then set
338 * it to the matching remote name. Otherwise
339 * multiple remotes match this reference, and it
340 * is ambiguous. */
341 if (!remote_name) {
342 remote_name = remote_list.strings[i];
343 } else {
344 git_remote_free(remote);
345 error = GIT_EAMBIGUOUS;
346 goto cleanup;
347 }
348 }
349
350 git_remote_free(remote);
351 }
352
353 if (remote_name) {
97016f29
CMN
354 git_buf_clear(buf);
355 error = git_buf_puts(buf, remote_name);
2e3e8c88
JM
356 } else {
357 error = GIT_ENOTFOUND;
2e3e8c88
JM
358 }
359
360cleanup:
361 git_strarray_free(&remote_list);
362 return error;
363}
364
97016f29
CMN
365int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo, const char *refname)
366{
367 int ret;
368 git_buf buf = GIT_BUF_INIT;
369
370 if ((ret = remote_name(&buf, repo, refname)) < 0)
371 return ret;
372
373 if (buffer)
374 git_buf_copy_cstr(buffer, buffer_len, &buf);
375
38fd8121 376 ret = (int)git_buf_len(&buf) + 1;
97016f29
CMN
377 git_buf_free(&buf);
378
379 return ret;
380}
381
a258d8e3 382int git_branch_upstream_name(
bf031581 383 char *tracking_branch_name_out,
384 size_t buffer_size,
385 git_repository *repo,
386 const char *canonical_branch_name)
387{
388 git_buf buf = GIT_BUF_INIT;
389 int error;
390
391 assert(canonical_branch_name);
392
393 if (tracking_branch_name_out && buffer_size)
394 *tracking_branch_name_out = '\0';
395
a258d8e3 396 if ((error = git_branch_upstream__name(
bf031581 397 &buf, repo, canonical_branch_name)) < 0)
398 goto cleanup;
399
400 if (tracking_branch_name_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */
401 giterr_set(
402 GITERR_INVALID,
403 "Buffer too short to hold the tracked reference name.");
404 error = -1;
405 goto cleanup;
406 }
407
408 if (tracking_branch_name_out)
409 git_buf_copy_cstr(tracking_branch_name_out, buffer_size, &buf);
410
3ad05221 411 error = (int)buf.size + 1;
bf031581 412
413cleanup:
414 git_buf_free(&buf);
415 return (int)error;
416}
417
a258d8e3 418int git_branch_upstream(
bf031581 419 git_reference **tracking_out,
420 git_reference *branch)
421{
422 int error;
423 git_buf tracking_name = GIT_BUF_INIT;
424
a258d8e3 425 if ((error = git_branch_upstream__name(&tracking_name,
bf031581 426 git_reference_owner(branch), git_reference_name(branch))) < 0)
427 return error;
428
429 error = git_reference_lookup(
430 tracking_out,
431 git_reference_owner(branch),
432 git_buf_cstr(&tracking_name));
433
434 git_buf_free(&tracking_name);
435 return error;
436}
437
d59942c2
CMN
438static int unset_upstream(git_config *config, const char *shortname)
439{
440 git_buf buf = GIT_BUF_INIT;
441
442 if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
443 return -1;
444
445 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
446 goto on_error;
447
448 git_buf_clear(&buf);
449 if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
450 goto on_error;
451
452 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
453 goto on_error;
454
455 git_buf_free(&buf);
456 return 0;
457
458on_error:
459 git_buf_free(&buf);
460 return -1;
461}
462
463int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
464{
465 git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
466 git_reference *upstream;
467 git_repository *repo;
468 git_remote *remote = NULL;
469 git_config *config;
470 const char *name, *shortname;
471 int local;
472 const git_refspec *fetchspec;
473
474 name = git_reference_name(branch);
475 if (!git_reference__is_branch(name))
476 return not_a_local_branch(name);
477
478 if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
479 return -1;
480
481 shortname = name + strlen(GIT_REFS_HEADS_DIR);
482
483 if (upstream_name == NULL)
484 return unset_upstream(config, shortname);
485
486 repo = git_reference_owner(branch);
487
488 /* First we need to figure out whether it's a branch or remote-tracking */
489 if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
490 local = 1;
491 else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
492 local = 0;
493 else
494 return GIT_ENOTFOUND;
495
496 /*
497 * If it's local, the remote is "." and the branch name is
498 * simply the refname. Otherwise we need to figure out what
499 * the remote-tracking branch's name on the remote is and use
500 * that.
501 */
502 if (local)
503 git_buf_puts(&value, ".");
504 else
505 remote_name(&value, repo, git_reference_name(upstream));
506
507 if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
508 goto on_error;
509
510 if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
511 goto on_error;
512
513 if (local) {
514 if (git_buf_puts(&value, git_reference_name(branch)) < 0)
515 goto on_error;
516 } else {
517 /* Get the remoe-tracking branch's refname in its repo */
518 if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
519 goto on_error;
520
4330ab26 521 fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
d59942c2 522 git_buf_clear(&value);
4330ab26 523 if (!fetchspec || git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
d59942c2
CMN
524 goto on_error;
525
526 git_remote_free(remote);
527 remote = NULL;
528 }
529
530 git_buf_clear(&key);
531 if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
532 goto on_error;
533
534 if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
535 goto on_error;
536
537 git_reference_free(upstream);
538 git_buf_free(&key);
539 git_buf_free(&value);
540
541 return 0;
542
543on_error:
544 git_reference_free(upstream);
545 git_buf_free(&key);
546 git_buf_free(&value);
547 git_remote_free(remote);
548
549 return -1;
550}
551
0c78f685 552int git_branch_is_head(
553 git_reference *branch)
554{
555 git_reference *head;
556 bool is_same = false;
0532e7bb 557 int error;
0c78f685 558
559 assert(branch);
560
561 if (!git_reference_is_branch(branch))
562 return false;
563
0532e7bb 564 error = git_repository_head(&head, git_reference_owner(branch));
565
b1a3a70e 566 if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
0532e7bb 567 return false;
568
569 if (error < 0)
0c78f685 570 return -1;
571
572 is_same = strcmp(
573 git_reference_name(branch),
574 git_reference_name(head)) == 0;
575
576 git_reference_free(head);
577
578 return is_same;
579}