]> git.proxmox.com Git - libgit2.git/blame - src/branch.c
branch: don't accept a reflog message override
[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{
3cf11eef
RB
24 git_reference *branch = NULL;
25 int error = 0;
731df570 26 char *prefix;
27 git_buf ref_name = GIT_BUF_INIT;
28
731df570 29 prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
30
3cf11eef
RB
31 if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0)
32 /* OOM */;
33 else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
34 giterr_set(
35 GITERR_REFERENCE, "Cannot locate %s branch '%s'",
36 is_remote ? "remote-tracking" : "local", branch_name);
731df570 37
3cf11eef 38 *branch_reference_out = branch; /* will be NULL on error */
731df570 39
731df570 40 git_buf_free(&ref_name);
41 return error;
42}
43
bf031581 44static int not_a_local_branch(const char *reference_name)
1c947daa 45{
bf031581 46 giterr_set(
47 GITERR_INVALID,
48 "Reference '%s' is not a local branch.", reference_name);
1c947daa
VM
49 return -1;
50}
51
731df570 52int git_branch_create(
d00d5464
ET
53 git_reference **ref_out,
54 git_repository *repository,
55 const char *branch_name,
56 const git_commit *commit,
6bfb990d 57 int force)
731df570 58{
59b1dbcd 59 int is_head = 0;
731df570 60 git_reference *branch = NULL;
59bb1126 61 git_buf canonical_branch_name = GIT_BUF_INIT,
6bfb990d 62 log_message = GIT_BUF_INIT;
59bb1126 63 int error = -1;
731df570 64
cfbe4be3
VM
65 assert(branch_name && commit && ref_out);
66 assert(git_object_owner((const git_object *)commit) == repository);
a07b1698
CMN
67
68 if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
69 error = git_branch_is_head(branch);
70 git_reference_free(branch);
71 branch = NULL;
72
73 if (error < 0)
59b1dbcd 74 goto cleanup;
a07b1698
CMN
75
76 is_head = error;
59b1dbcd 77 }
731df570 78
59b1dbcd
L
79 if (is_head && force) {
80 giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
a07b1698
CMN
81 "the current HEAD of the repository.", branch_name);
82 error = -1;
59b1dbcd
L
83 goto cleanup;
84 }
85
b31ebfbc
BS
86 if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
87 goto cleanup;
731df570 88
6bfb990d 89 if (git_buf_printf(&log_message, "Branch: created from %s", git_oid_tostr_s(git_commit_id(commit))) < 0)
59bb1126
BS
90 goto cleanup;
91
92 error = git_reference_create(&branch, repository,
659cf202 93 git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force,
6bfb990d 94 git_buf_cstr(&log_message));
59bb1126
BS
95
96 if (!error)
b31ebfbc 97 *ref_out = branch;
731df570 98
b31ebfbc 99cleanup:
731df570 100 git_buf_free(&canonical_branch_name);
6bfb990d 101 git_buf_free(&log_message);
731df570 102 return error;
103}
104
1c947daa 105int git_branch_delete(git_reference *branch)
731df570 106{
4ba23be1 107 int is_head;
aba70781 108 git_buf config_section = GIT_BUF_INIT;
109 int error = -1;
731df570 110
1c947daa 111 assert(branch);
731df570 112
96869a4e
RB
113 if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
114 giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.",
115 git_reference_name(branch));
116 return GIT_ENOTFOUND;
1c947daa 117 }
731df570 118
4ba23be1 119 if ((is_head = git_branch_is_head(branch)) < 0)
120 return is_head;
731df570 121
4ba23be1 122 if (is_head) {
96869a4e
RB
123 giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
124 "the current HEAD of the repository.", git_reference_name(branch));
4ba23be1 125 return -1;
731df570 126 }
127
96869a4e
RB
128 if (git_buf_join(&config_section, '.', "branch",
129 git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
aba70781 130 goto on_error;
131
132 if (git_config_rename_section(
96869a4e
RB
133 git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
134 goto on_error;
0b98a8a4 135
aba70781 136 if (git_reference_delete(branch) < 0)
137 goto on_error;
138
31bc6c04 139 if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) {
79863917
POL
140 if (error == GIT_ENOTFOUND) {
141 giterr_clear();
142 error = 0;
143 }
48110f67 144 goto on_error;
79863917 145 }
48110f67 146
aba70781 147 error = 0;
148
149on_error:
150 git_buf_free(&config_section);
151 return error;
731df570 152}
153
8ec889a4 154typedef struct {
9bd89d96 155 git_reference_iterator *iter;
8ec889a4
CMN
156 unsigned int flags;
157} branch_iter;
158
a667ca82 159int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
8ec889a4
CMN
160{
161 branch_iter *iter = (branch_iter *) _iter;
56960b83 162 git_reference *ref;
8ec889a4
CMN
163 int error;
164
165 while ((error = git_reference_next(&ref, iter->iter)) == 0) {
166 if ((iter->flags & GIT_BRANCH_LOCAL) &&
167 !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
168 *out = ref;
169 *out_type = GIT_BRANCH_LOCAL;
170
171 return 0;
172 } else if ((iter->flags & GIT_BRANCH_REMOTE) &&
173 !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
174 *out = ref;
175 *out_type = GIT_BRANCH_REMOTE;
176
177 return 0;
178 } else {
179 git_reference_free(ref);
180 }
181 }
a8fd805e 182
8ec889a4
CMN
183 return error;
184}
a8fd805e 185
8ec889a4
CMN
186int git_branch_iterator_new(
187 git_branch_iterator **out,
188 git_repository *repo,
a667ca82 189 git_branch_t list_flags)
8ec889a4
CMN
190{
191 branch_iter *iter;
9bd89d96 192
8ec889a4
CMN
193 iter = git__calloc(1, sizeof(branch_iter));
194 GITERR_CHECK_ALLOC(iter);
56960b83 195
8ec889a4 196 iter->flags = list_flags;
09c2f91c 197
8ec889a4
CMN
198 if (git_reference_iterator_new(&iter->iter, repo) < 0) {
199 git__free(iter);
200 return -1;
9bd89d96
CMN
201 }
202
8ec889a4 203 *out = (git_branch_iterator *) iter;
9bd89d96 204
8ec889a4
CMN
205 return 0;
206}
207
208void git_branch_iterator_free(git_branch_iterator *_iter)
209{
210 branch_iter *iter = (branch_iter *) _iter;
211
9eb45fc5
BR
212 if (iter == NULL)
213 return;
214
8ec889a4
CMN
215 git_reference_iterator_free(iter->iter);
216 git__free(iter);
a8fd805e 217}
218
bf9e8cc8 219int git_branch_move(
d00d5464 220 git_reference **out,
bf9e8cc8 221 git_reference *branch,
222 const char *new_branch_name,
6bfb990d 223 int force)
bf9e8cc8 224{
383f164a 225 git_buf new_reference_name = GIT_BUF_INIT,
59bb1126
BS
226 old_config_section = GIT_BUF_INIT,
227 new_config_section = GIT_BUF_INIT,
6bfb990d 228 log_message = GIT_BUF_INIT;
bf9e8cc8 229 int error;
6a8bcfa4 230
bf9e8cc8 231 assert(branch && new_branch_name);
232
233 if (!git_reference_is_branch(branch))
bf031581 234 return not_a_local_branch(git_reference_name(branch));
4615f0f7 235
59bb1126 236 if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
d00d5464 237 goto done;
383f164a 238
6bfb990d
CMN
239 if ((error = git_buf_printf(&log_message, "Branch: renamed %s to %s",
240 git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0)
59bb1126 241 goto done;
59bb1126 242
96869a4e 243 /* first update ref then config so failure won't trash config */
4e6e2ff2 244
96869a4e 245 error = git_reference_rename(
ccf6ce5c 246 out, branch, git_buf_cstr(&new_reference_name), force,
6bfb990d 247 git_buf_cstr(&log_message));
96869a4e 248 if (error < 0)
d00d5464 249 goto done;
3e199f42 250
96869a4e
RB
251 git_buf_join(&old_config_section, '.', "branch",
252 git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
253 git_buf_join(&new_config_section, '.', "branch", new_branch_name);
254
255 error = git_config_rename_section(
256 git_reference_owner(branch),
257 git_buf_cstr(&old_config_section),
258 git_buf_cstr(&new_config_section));
4615f0f7 259
d00d5464 260done:
6a8bcfa4 261 git_buf_free(&new_reference_name);
aba70781 262 git_buf_free(&old_config_section);
263 git_buf_free(&new_config_section);
6bfb990d 264 git_buf_free(&log_message);
6a8bcfa4 265
6a625435 266 return error;
4615f0f7 267}
eed378b6 268
269int git_branch_lookup(
d00d5464
ET
270 git_reference **ref_out,
271 git_repository *repo,
272 const char *branch_name,
273 git_branch_t branch_type)
eed378b6 274{
275 assert(ref_out && repo && branch_name);
276
277 return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
278}
fb910281 279
3b4ba278
JG
280int git_branch_name(
281 const char **out,
282 const git_reference *ref)
c253056d
SB
283{
284 const char *branch_name;
285
286 assert(out && ref);
287
288 branch_name = ref->name;
289
290 if (git_reference_is_branch(ref)) {
291 branch_name += strlen(GIT_REFS_HEADS_DIR);
292 } else if (git_reference_is_remote(ref)) {
293 branch_name += strlen(GIT_REFS_REMOTES_DIR);
294 } else {
295 giterr_set(GITERR_INVALID,
296 "Reference '%s' is neither a local nor a remote branch.", ref->name);
297 return -1;
298 }
299 *out = branch_name;
300 return 0;
301}
302
a258d8e3 303static int retrieve_upstream_configuration(
bf031581 304 const char **out,
29c4cb09 305 const git_config *config,
bf031581 306 const char *canonical_branch_name,
307 const char *format)
fb910281 308{
fb910281 309 git_buf buf = GIT_BUF_INIT;
310 int error;
311
fb910281 312 if (git_buf_printf(&buf, format,
bf031581 313 canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
fb910281 314 return -1;
315
316 error = git_config_get_string(out, config, git_buf_cstr(&buf));
317 git_buf_free(&buf);
318 return error;
319}
320
b25d87c9
CMN
321int git_branch_upstream_name(
322 git_buf *out,
bf031581 323 git_repository *repo,
b25d87c9 324 const char *refname)
fb910281 325{
326 const char *remote_name, *merge_name;
327 git_buf buf = GIT_BUF_INIT;
328 int error = -1;
329 git_remote *remote = NULL;
330 const git_refspec *refspec;
ac99d86b 331 git_config *config;
fb910281 332
b25d87c9
CMN
333 assert(out && refname);
334
335 git_buf_sanitize(out);
fb910281 336
b25d87c9
CMN
337 if (!git_reference__is_branch(refname))
338 return not_a_local_branch(refname);
fb910281 339
ac99d86b 340 if ((error = git_repository_config_snapshot(&config, repo)) < 0)
29c4cb09
CMN
341 return error;
342
a258d8e3 343 if ((error = retrieve_upstream_configuration(
29c4cb09 344 &remote_name, config, refname, "branch.%s.remote")) < 0)
bf031581 345 goto cleanup;
fb910281 346
a258d8e3 347 if ((error = retrieve_upstream_configuration(
29c4cb09 348 &merge_name, config, refname, "branch.%s.merge")) < 0)
bf031581 349 goto cleanup;
37849a8e 350
28cbd2e2 351 if (!*remote_name || !*merge_name) {
3e199f42 352 giterr_set(GITERR_REFERENCE,
b25d87c9 353 "branch '%s' does not have an upstream", refname);
28cbd2e2 354 error = GIT_ENOTFOUND;
355 goto cleanup;
356 }
fb910281 357
358 if (strcmp(".", remote_name) != 0) {
209425ce 359 if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0)
fb910281 360 goto cleanup;
361
4330ab26
CMN
362 refspec = git_remote__matching_refspec(remote, merge_name);
363 if (!refspec) {
364 error = GIT_ENOTFOUND;
365 goto cleanup;
fb910281 366 }
367
bf522e08 368 if (git_refspec_transform(&buf, refspec, merge_name) < 0)
fb910281 369 goto cleanup;
370 } else
371 if (git_buf_sets(&buf, merge_name) < 0)
372 goto cleanup;
373
b25d87c9 374 error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
fb910281 375
376cleanup:
29c4cb09 377 git_config_free(config);
fb910281 378 git_remote_free(remote);
379 git_buf_free(&buf);
380 return error;
381}
0c78f685 382
82374d98
CMN
383int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
384{
385 int error;
386 const char *str;
387 git_config *cfg;
388
389 if (!git_reference__is_branch(refname))
390 return not_a_local_branch(refname);
391
392 git_buf_sanitize(buf);
393 if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
394 return error;
395
396 if ((error = retrieve_upstream_configuration(&str, cfg, refname, "branch.%s.remote")) < 0)
397 goto cleanup;
398
5915d700
CMN
399 if (!*str) {
400 giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname);
401 error = GIT_ENOTFOUND;
402 goto cleanup;
403 }
404
82374d98
CMN
405 error = git_buf_puts(buf, str);
406
407cleanup:
408 git_config_free(cfg);
409 return error;
410}
411
b25d87c9 412int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
2e3e8c88
JM
413{
414 git_strarray remote_list = {0};
97016f29 415 size_t i;
2e3e8c88
JM
416 git_remote *remote;
417 const git_refspec *fetchspec;
418 int error = 0;
419 char *remote_name = NULL;
420
b25d87c9
CMN
421 assert(buf && repo && refname);
422
423 git_buf_sanitize(buf);
2e3e8c88
JM
424
425 /* Verify that this is a remote branch */
b25d87c9 426 if (!git_reference__is_remote(refname)) {
c1b5e8c4 427 giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
b25d87c9 428 refname);
2e3e8c88
JM
429 error = GIT_ERROR;
430 goto cleanup;
431 }
432
433 /* Get the remotes */
434 if ((error = git_remote_list(&remote_list, repo)) < 0)
435 goto cleanup;
436
437 /* Find matching remotes */
438 for (i = 0; i < remote_list.count; i++) {
209425ce 439 if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0)
d59942c2 440 continue;
2e3e8c88 441
b25d87c9 442 fetchspec = git_remote__matching_dst_refspec(remote, refname);
4330ab26 443 if (fetchspec) {
2e3e8c88
JM
444 /* If we have not already set out yet, then set
445 * it to the matching remote name. Otherwise
446 * multiple remotes match this reference, and it
447 * is ambiguous. */
448 if (!remote_name) {
449 remote_name = remote_list.strings[i];
450 } else {
451 git_remote_free(remote);
3e199f42
RB
452
453 giterr_set(GITERR_REFERENCE,
b25d87c9 454 "Reference '%s' is ambiguous", refname);
2e3e8c88
JM
455 error = GIT_EAMBIGUOUS;
456 goto cleanup;
457 }
458 }
459
460 git_remote_free(remote);
461 }
462
463 if (remote_name) {
97016f29
CMN
464 git_buf_clear(buf);
465 error = git_buf_puts(buf, remote_name);
2e3e8c88 466 } else {
3e199f42 467 giterr_set(GITERR_REFERENCE,
b25d87c9 468 "Could not determine remote for '%s'", refname);
2e3e8c88 469 error = GIT_ENOTFOUND;
2e3e8c88
JM
470 }
471
472cleanup:
b25d87c9
CMN
473 if (error < 0)
474 git_buf_free(buf);
475
2e3e8c88
JM
476 git_strarray_free(&remote_list);
477 return error;
478}
479
a258d8e3 480int git_branch_upstream(
3b4ba278
JG
481 git_reference **tracking_out,
482 const git_reference *branch)
bf031581 483{
484 int error;
485 git_buf tracking_name = GIT_BUF_INIT;
486
b25d87c9 487 if ((error = git_branch_upstream_name(&tracking_name,
bf031581 488 git_reference_owner(branch), git_reference_name(branch))) < 0)
489 return error;
490
491 error = git_reference_lookup(
492 tracking_out,
493 git_reference_owner(branch),
494 git_buf_cstr(&tracking_name));
495
496 git_buf_free(&tracking_name);
497 return error;
498}
499
d59942c2
CMN
500static int unset_upstream(git_config *config, const char *shortname)
501{
502 git_buf buf = GIT_BUF_INIT;
503
504 if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
505 return -1;
506
507 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
508 goto on_error;
509
510 git_buf_clear(&buf);
511 if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
512 goto on_error;
513
514 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
515 goto on_error;
516
517 git_buf_free(&buf);
518 return 0;
519
520on_error:
521 git_buf_free(&buf);
522 return -1;
523}
524
525int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
526{
527 git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
528 git_reference *upstream;
529 git_repository *repo;
530 git_remote *remote = NULL;
531 git_config *config;
532 const char *name, *shortname;
533 int local;
534 const git_refspec *fetchspec;
535
536 name = git_reference_name(branch);
537 if (!git_reference__is_branch(name))
538 return not_a_local_branch(name);
539
540 if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
541 return -1;
542
543 shortname = name + strlen(GIT_REFS_HEADS_DIR);
544
545 if (upstream_name == NULL)
546 return unset_upstream(config, shortname);
547
548 repo = git_reference_owner(branch);
549
550 /* First we need to figure out whether it's a branch or remote-tracking */
551 if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
552 local = 1;
553 else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
554 local = 0;
3e199f42
RB
555 else {
556 giterr_set(GITERR_REFERENCE,
557 "Cannot set upstream for branch '%s'", shortname);
d59942c2 558 return GIT_ENOTFOUND;
3e199f42 559 }
d59942c2
CMN
560
561 /*
562 * If it's local, the remote is "." and the branch name is
563 * simply the refname. Otherwise we need to figure out what
564 * the remote-tracking branch's name on the remote is and use
565 * that.
566 */
567 if (local)
568 git_buf_puts(&value, ".");
569 else
b25d87c9 570 git_branch_remote_name(&value, repo, git_reference_name(upstream));
d59942c2
CMN
571
572 if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
573 goto on_error;
574
575 if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
576 goto on_error;
577
578 if (local) {
3d42e9a3
NV
579 git_buf_clear(&value);
580 if (git_buf_puts(&value, git_reference_name(upstream)) < 0)
d59942c2
CMN
581 goto on_error;
582 } else {
583 /* Get the remoe-tracking branch's refname in its repo */
209425ce 584 if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0)
d59942c2
CMN
585 goto on_error;
586
4330ab26 587 fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
d59942c2 588 git_buf_clear(&value);
bf522e08 589 if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0)
d59942c2
CMN
590 goto on_error;
591
592 git_remote_free(remote);
593 remote = NULL;
594 }
595
596 git_buf_clear(&key);
597 if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
598 goto on_error;
599
600 if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
601 goto on_error;
602
603 git_reference_free(upstream);
604 git_buf_free(&key);
605 git_buf_free(&value);
606
607 return 0;
608
609on_error:
610 git_reference_free(upstream);
611 git_buf_free(&key);
612 git_buf_free(&value);
613 git_remote_free(remote);
614
615 return -1;
616}
617
0c78f685 618int git_branch_is_head(
853b1407 619 const git_reference *branch)
0c78f685 620{
621 git_reference *head;
622 bool is_same = false;
0532e7bb 623 int error;
0c78f685 624
625 assert(branch);
626
627 if (!git_reference_is_branch(branch))
628 return false;
629
0532e7bb 630 error = git_repository_head(&head, git_reference_owner(branch));
631
605da51a 632 if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
0532e7bb 633 return false;
634
635 if (error < 0)
0c78f685 636 return -1;
637
638 is_same = strcmp(
639 git_reference_name(branch),
640 git_reference_name(head)) == 0;
641
642 git_reference_free(head);
643
644 return is_same;
645}