]> git.proxmox.com Git - libgit2.git/blame - src/branch.c
Refresh patches
[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
eae0bfdc
PP
8#include "branch.h"
9
731df570 10#include "commit.h"
731df570 11#include "tag.h"
fb910281 12#include "config.h"
13#include "refspec.h"
bf031581 14#include "refs.h"
4330ab26 15#include "remote.h"
62d38a1d 16#include "annotated_commit.h"
e3acd37b 17#include "worktree.h"
731df570 18
326ca710 19#include "git2/branch.h"
20
731df570 21static int retrieve_branch_reference(
22 git_reference **branch_reference_out,
23 git_repository *repo,
24 const char *branch_name,
22a2d3d5 25 bool is_remote)
731df570 26{
3cf11eef
RB
27 git_reference *branch = NULL;
28 int error = 0;
731df570 29 char *prefix;
30 git_buf ref_name = GIT_BUF_INIT;
31
731df570 32 prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
33
3cf11eef
RB
34 if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0)
35 /* OOM */;
36 else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
ac3d33df
JK
37 git_error_set(
38 GIT_ERROR_REFERENCE, "cannot locate %s branch '%s'",
3cf11eef 39 is_remote ? "remote-tracking" : "local", branch_name);
731df570 40
3cf11eef 41 *branch_reference_out = branch; /* will be NULL on error */
731df570 42
ac3d33df 43 git_buf_dispose(&ref_name);
731df570 44 return error;
45}
46
bf031581 47static int not_a_local_branch(const char *reference_name)
1c947daa 48{
ac3d33df
JK
49 git_error_set(
50 GIT_ERROR_INVALID,
909d5494 51 "reference '%s' is not a local branch.", reference_name);
1c947daa
VM
52 return -1;
53}
54
62d38a1d 55static int create_branch(
d00d5464
ET
56 git_reference **ref_out,
57 git_repository *repository,
58 const char *branch_name,
59 const git_commit *commit,
62d38a1d 60 const char *from,
6bfb990d 61 int force)
731df570 62{
f9793884 63 int is_unmovable_head = 0;
731df570 64 git_reference *branch = NULL;
59bb1126 65 git_buf canonical_branch_name = GIT_BUF_INIT,
6bfb990d 66 log_message = GIT_BUF_INIT;
59bb1126 67 int error = -1;
f9793884 68 int bare = git_repository_is_bare(repository);
731df570 69
c25aa7cd
PP
70 GIT_ASSERT_ARG(branch_name);
71 GIT_ASSERT_ARG(commit);
72 GIT_ASSERT_ARG(ref_out);
73 GIT_ASSERT_ARG(git_commit_owner(commit) == repository);
a07b1698 74
eae0bfdc 75 if (!git__strcmp(branch_name, "HEAD")) {
ac3d33df 76 git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name");
eae0bfdc
PP
77 error = -1;
78 goto cleanup;
79 }
80
f9793884 81 if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
a07b1698
CMN
82 error = git_branch_is_head(branch);
83 git_reference_free(branch);
84 branch = NULL;
85
86 if (error < 0)
59b1dbcd 87 goto cleanup;
a07b1698 88
f9793884 89 is_unmovable_head = error;
59b1dbcd 90 }
731df570 91
f9793884 92 if (is_unmovable_head && force) {
ac3d33df 93 git_error_set(GIT_ERROR_REFERENCE, "cannot force update branch '%s' as it is "
a07b1698
CMN
94 "the current HEAD of the repository.", branch_name);
95 error = -1;
59b1dbcd
L
96 goto cleanup;
97 }
05e644dd 98
b31ebfbc
BS
99 if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
100 goto cleanup;
731df570 101
62d38a1d 102 if (git_buf_printf(&log_message, "branch: Created from %s", from) < 0)
59bb1126
BS
103 goto cleanup;
104
105 error = git_reference_create(&branch, repository,
659cf202 106 git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force,
6bfb990d 107 git_buf_cstr(&log_message));
59bb1126
BS
108
109 if (!error)
b31ebfbc 110 *ref_out = branch;
731df570 111
b31ebfbc 112cleanup:
ac3d33df
JK
113 git_buf_dispose(&canonical_branch_name);
114 git_buf_dispose(&log_message);
731df570 115 return error;
116}
117
62d38a1d
CMN
118int git_branch_create(
119 git_reference **ref_out,
120 git_repository *repository,
121 const char *branch_name,
122 const git_commit *commit,
123 int force)
124{
125 return create_branch(ref_out, repository, branch_name, commit, git_oid_tostr_s(git_commit_id(commit)), force);
126}
127
128int git_branch_create_from_annotated(
129 git_reference **ref_out,
130 git_repository *repository,
131 const char *branch_name,
132 const git_annotated_commit *commit,
133 int force)
134{
d5592378
ET
135 return create_branch(ref_out,
136 repository, branch_name, commit->commit, commit->description, force);
62d38a1d
CMN
137}
138
22a2d3d5 139static int branch_is_checked_out(git_repository *worktree, void *payload)
e3acd37b 140{
38fc5ab0 141 git_reference *branch = (git_reference *) payload;
be343b88 142 git_reference *head = NULL;
22a2d3d5 143 int error;
e3acd37b 144
22a2d3d5
UG
145 if (git_repository_is_bare(worktree))
146 return 0;
e3acd37b 147
22a2d3d5
UG
148 if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) {
149 if (error == GIT_ENOTFOUND)
150 error = 0;
151 goto out;
152 }
be343b88 153
22a2d3d5
UG
154 if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC)
155 goto out;
156
157 error = !git__strcmp(head->target.symbolic, branch->name);
158
159out:
38fc5ab0 160 git_reference_free(head);
22a2d3d5 161 return error;
38fc5ab0 162}
e3acd37b 163
38fc5ab0
PS
164int git_branch_is_checked_out(const git_reference *branch)
165{
c25aa7cd
PP
166 GIT_ASSERT_ARG(branch);
167
22a2d3d5
UG
168 if (!git_reference_is_branch(branch))
169 return 0;
170 return git_repository_foreach_worktree(git_reference_owner(branch),
171 branch_is_checked_out, (void *)branch) == 1;
e3acd37b
PS
172}
173
1c947daa 174int git_branch_delete(git_reference *branch)
731df570 175{
4ba23be1 176 int is_head;
aba70781 177 git_buf config_section = GIT_BUF_INIT;
178 int error = -1;
731df570 179
c25aa7cd 180 GIT_ASSERT_ARG(branch);
731df570 181
96869a4e 182 if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
ac3d33df 183 git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a valid branch.",
96869a4e
RB
184 git_reference_name(branch));
185 return GIT_ENOTFOUND;
1c947daa 186 }
731df570 187
4ba23be1 188 if ((is_head = git_branch_is_head(branch)) < 0)
189 return is_head;
731df570 190
4ba23be1 191 if (is_head) {
ac3d33df 192 git_error_set(GIT_ERROR_REFERENCE, "cannot delete branch '%s' as it is "
96869a4e 193 "the current HEAD of the repository.", git_reference_name(branch));
4ba23be1 194 return -1;
731df570 195 }
196
143e539f 197 if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) {
ac3d33df 198 git_error_set(GIT_ERROR_REFERENCE, "Cannot delete branch '%s' as it is "
143e539f
PS
199 "the current HEAD of a linked repository.", git_reference_name(branch));
200 return -1;
201 }
202
96869a4e
RB
203 if (git_buf_join(&config_section, '.', "branch",
204 git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
aba70781 205 goto on_error;
206
207 if (git_config_rename_section(
96869a4e
RB
208 git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
209 goto on_error;
0b98a8a4 210
01d0c02d 211 error = git_reference_delete(branch);
aba70781 212
213on_error:
ac3d33df 214 git_buf_dispose(&config_section);
aba70781 215 return error;
731df570 216}
217
8ec889a4 218typedef struct {
9bd89d96 219 git_reference_iterator *iter;
8ec889a4
CMN
220 unsigned int flags;
221} branch_iter;
222
a667ca82 223int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
8ec889a4
CMN
224{
225 branch_iter *iter = (branch_iter *) _iter;
56960b83 226 git_reference *ref;
8ec889a4
CMN
227 int error;
228
229 while ((error = git_reference_next(&ref, iter->iter)) == 0) {
230 if ((iter->flags & GIT_BRANCH_LOCAL) &&
231 !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
232 *out = ref;
233 *out_type = GIT_BRANCH_LOCAL;
234
235 return 0;
236 } else if ((iter->flags & GIT_BRANCH_REMOTE) &&
237 !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
238 *out = ref;
239 *out_type = GIT_BRANCH_REMOTE;
240
241 return 0;
242 } else {
243 git_reference_free(ref);
244 }
245 }
a8fd805e 246
8ec889a4
CMN
247 return error;
248}
a8fd805e 249
8ec889a4
CMN
250int git_branch_iterator_new(
251 git_branch_iterator **out,
252 git_repository *repo,
a667ca82 253 git_branch_t list_flags)
8ec889a4
CMN
254{
255 branch_iter *iter;
9bd89d96 256
8ec889a4 257 iter = git__calloc(1, sizeof(branch_iter));
ac3d33df 258 GIT_ERROR_CHECK_ALLOC(iter);
56960b83 259
8ec889a4 260 iter->flags = list_flags;
09c2f91c 261
8ec889a4
CMN
262 if (git_reference_iterator_new(&iter->iter, repo) < 0) {
263 git__free(iter);
264 return -1;
9bd89d96
CMN
265 }
266
8ec889a4 267 *out = (git_branch_iterator *) iter;
9bd89d96 268
8ec889a4
CMN
269 return 0;
270}
271
272void git_branch_iterator_free(git_branch_iterator *_iter)
273{
274 branch_iter *iter = (branch_iter *) _iter;
275
9eb45fc5
BR
276 if (iter == NULL)
277 return;
278
8ec889a4
CMN
279 git_reference_iterator_free(iter->iter);
280 git__free(iter);
a8fd805e 281}
282
bf9e8cc8 283int git_branch_move(
d00d5464 284 git_reference **out,
bf9e8cc8 285 git_reference *branch,
286 const char *new_branch_name,
6bfb990d 287 int force)
bf9e8cc8 288{
383f164a 289 git_buf new_reference_name = GIT_BUF_INIT,
59bb1126
BS
290 old_config_section = GIT_BUF_INIT,
291 new_config_section = GIT_BUF_INIT,
6bfb990d 292 log_message = GIT_BUF_INIT;
bf9e8cc8 293 int error;
6a8bcfa4 294
c25aa7cd
PP
295 GIT_ASSERT_ARG(branch);
296 GIT_ASSERT_ARG(new_branch_name);
bf9e8cc8 297
298 if (!git_reference_is_branch(branch))
bf031581 299 return not_a_local_branch(git_reference_name(branch));
4615f0f7 300
59bb1126 301 if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
d00d5464 302 goto done;
383f164a 303
7eb76734 304 if ((error = git_buf_printf(&log_message, "branch: renamed %s to %s",
6bfb990d 305 git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0)
59bb1126 306 goto done;
59bb1126 307
96869a4e 308 /* first update ref then config so failure won't trash config */
4e6e2ff2 309
96869a4e 310 error = git_reference_rename(
ccf6ce5c 311 out, branch, git_buf_cstr(&new_reference_name), force,
6bfb990d 312 git_buf_cstr(&log_message));
96869a4e 313 if (error < 0)
d00d5464 314 goto done;
3e199f42 315
96869a4e
RB
316 git_buf_join(&old_config_section, '.', "branch",
317 git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
318 git_buf_join(&new_config_section, '.', "branch", new_branch_name);
319
320 error = git_config_rename_section(
321 git_reference_owner(branch),
322 git_buf_cstr(&old_config_section),
323 git_buf_cstr(&new_config_section));
4615f0f7 324
d00d5464 325done:
ac3d33df
JK
326 git_buf_dispose(&new_reference_name);
327 git_buf_dispose(&old_config_section);
328 git_buf_dispose(&new_config_section);
329 git_buf_dispose(&log_message);
6a8bcfa4 330
6a625435 331 return error;
4615f0f7 332}
eed378b6 333
334int git_branch_lookup(
d00d5464
ET
335 git_reference **ref_out,
336 git_repository *repo,
337 const char *branch_name,
338 git_branch_t branch_type)
eed378b6 339{
22a2d3d5 340 int error = -1;
c25aa7cd
PP
341
342 GIT_ASSERT_ARG(ref_out);
343 GIT_ASSERT_ARG(repo);
344 GIT_ASSERT_ARG(branch_name);
eed378b6 345
22a2d3d5
UG
346 switch (branch_type) {
347 case GIT_BRANCH_LOCAL:
348 case GIT_BRANCH_REMOTE:
349 error = retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
350 break;
351 case GIT_BRANCH_ALL:
352 error = retrieve_branch_reference(ref_out, repo, branch_name, false);
353 if (error == GIT_ENOTFOUND)
354 error = retrieve_branch_reference(ref_out, repo, branch_name, true);
355 break;
356 default:
c25aa7cd 357 GIT_ASSERT(false);
22a2d3d5
UG
358 }
359 return error;
eed378b6 360}
fb910281 361
3b4ba278
JG
362int git_branch_name(
363 const char **out,
364 const git_reference *ref)
c253056d
SB
365{
366 const char *branch_name;
367
c25aa7cd
PP
368 GIT_ASSERT_ARG(out);
369 GIT_ASSERT_ARG(ref);
c253056d
SB
370
371 branch_name = ref->name;
372
373 if (git_reference_is_branch(ref)) {
374 branch_name += strlen(GIT_REFS_HEADS_DIR);
375 } else if (git_reference_is_remote(ref)) {
376 branch_name += strlen(GIT_REFS_REMOTES_DIR);
377 } else {
ac3d33df 378 git_error_set(GIT_ERROR_INVALID,
909d5494 379 "reference '%s' is neither a local nor a remote branch.", ref->name);
c253056d
SB
380 return -1;
381 }
382 *out = branch_name;
383 return 0;
384}
385
a258d8e3 386static int retrieve_upstream_configuration(
9a97f49e 387 git_buf *out,
29c4cb09 388 const git_config *config,
bf031581 389 const char *canonical_branch_name,
390 const char *format)
fb910281 391{
fb910281 392 git_buf buf = GIT_BUF_INIT;
393 int error;
394
fb910281 395 if (git_buf_printf(&buf, format,
bf031581 396 canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
fb910281 397 return -1;
398
9a97f49e 399 error = git_config_get_string_buf(out, config, git_buf_cstr(&buf));
ac3d33df 400 git_buf_dispose(&buf);
fb910281 401 return error;
402}
403
b25d87c9
CMN
404int git_branch_upstream_name(
405 git_buf *out,
bf031581 406 git_repository *repo,
b25d87c9 407 const char *refname)
fb910281 408{
9a97f49e
CMN
409 git_buf remote_name = GIT_BUF_INIT;
410 git_buf merge_name = GIT_BUF_INIT;
fb910281 411 git_buf buf = GIT_BUF_INIT;
412 int error = -1;
413 git_remote *remote = NULL;
414 const git_refspec *refspec;
ac99d86b 415 git_config *config;
fb910281 416
c25aa7cd
PP
417 GIT_ASSERT_ARG(out);
418 GIT_ASSERT_ARG(refname);
b25d87c9 419
c25aa7cd
PP
420 if ((error = git_buf_sanitize(out)) < 0)
421 return error;
fb910281 422
b25d87c9
CMN
423 if (!git_reference__is_branch(refname))
424 return not_a_local_branch(refname);
fb910281 425
ac99d86b 426 if ((error = git_repository_config_snapshot(&config, repo)) < 0)
29c4cb09
CMN
427 return error;
428
a258d8e3 429 if ((error = retrieve_upstream_configuration(
29c4cb09 430 &remote_name, config, refname, "branch.%s.remote")) < 0)
bf031581 431 goto cleanup;
fb910281 432
a258d8e3 433 if ((error = retrieve_upstream_configuration(
29c4cb09 434 &merge_name, config, refname, "branch.%s.merge")) < 0)
bf031581 435 goto cleanup;
37849a8e 436
9a97f49e 437 if (git_buf_len(&remote_name) == 0 || git_buf_len(&merge_name) == 0) {
ac3d33df 438 git_error_set(GIT_ERROR_REFERENCE,
b25d87c9 439 "branch '%s' does not have an upstream", refname);
28cbd2e2 440 error = GIT_ENOTFOUND;
441 goto cleanup;
442 }
fb910281 443
9a97f49e
CMN
444 if (strcmp(".", git_buf_cstr(&remote_name)) != 0) {
445 if ((error = git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name))) < 0)
fb910281 446 goto cleanup;
447
9a97f49e 448 refspec = git_remote__matching_refspec(remote, git_buf_cstr(&merge_name));
4330ab26
CMN
449 if (!refspec) {
450 error = GIT_ENOTFOUND;
451 goto cleanup;
fb910281 452 }
453
9a97f49e 454 if (git_refspec_transform(&buf, refspec, git_buf_cstr(&merge_name)) < 0)
fb910281 455 goto cleanup;
456 } else
9a97f49e 457 if (git_buf_set(&buf, git_buf_cstr(&merge_name), git_buf_len(&merge_name)) < 0)
fb910281 458 goto cleanup;
459
b25d87c9 460 error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
fb910281 461
462cleanup:
29c4cb09 463 git_config_free(config);
fb910281 464 git_remote_free(remote);
ac3d33df
JK
465 git_buf_dispose(&remote_name);
466 git_buf_dispose(&merge_name);
467 git_buf_dispose(&buf);
fb910281 468 return error;
469}
0c78f685 470
c25aa7cd 471static int git_branch_upstream_with_format(git_buf *buf, git_repository *repo, const char *refname, const char *format, const char *format_name)
82374d98
CMN
472{
473 int error;
82374d98
CMN
474 git_config *cfg;
475
476 if (!git_reference__is_branch(refname))
477 return not_a_local_branch(refname);
478
9a97f49e 479 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
82374d98
CMN
480 return error;
481
c25aa7cd
PP
482 if ((error = git_buf_sanitize(buf)) < 0 ||
483 (error = retrieve_upstream_configuration(buf, cfg, refname, format)) < 0)
9a97f49e
CMN
484 return error;
485
486 if (git_buf_len(buf) == 0) {
c25aa7cd 487 git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream %s", refname, format_name);
5915d700 488 error = GIT_ENOTFOUND;
9a97f49e 489 git_buf_clear(buf);
5915d700
CMN
490 }
491
82374d98
CMN
492 return error;
493}
494
c25aa7cd
PP
495int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
496{
497 return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.remote", "remote");
498}
499
500int git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname)
501{
502 return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.merge", "merge");
503}
504
b25d87c9 505int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
2e3e8c88
JM
506{
507 git_strarray remote_list = {0};
97016f29 508 size_t i;
2e3e8c88
JM
509 git_remote *remote;
510 const git_refspec *fetchspec;
511 int error = 0;
512 char *remote_name = NULL;
513
c25aa7cd
PP
514 GIT_ASSERT_ARG(buf);
515 GIT_ASSERT_ARG(repo);
516 GIT_ASSERT_ARG(refname);
b25d87c9 517
c25aa7cd
PP
518 if ((error = git_buf_sanitize(buf)) < 0)
519 return error;
2e3e8c88
JM
520
521 /* Verify that this is a remote branch */
b25d87c9 522 if (!git_reference__is_remote(refname)) {
ac3d33df 523 git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a remote branch.",
b25d87c9 524 refname);
2e3e8c88
JM
525 error = GIT_ERROR;
526 goto cleanup;
527 }
528
529 /* Get the remotes */
530 if ((error = git_remote_list(&remote_list, repo)) < 0)
531 goto cleanup;
532
533 /* Find matching remotes */
534 for (i = 0; i < remote_list.count; i++) {
209425ce 535 if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0)
d59942c2 536 continue;
2e3e8c88 537
b25d87c9 538 fetchspec = git_remote__matching_dst_refspec(remote, refname);
4330ab26 539 if (fetchspec) {
2e3e8c88
JM
540 /* If we have not already set out yet, then set
541 * it to the matching remote name. Otherwise
542 * multiple remotes match this reference, and it
543 * is ambiguous. */
544 if (!remote_name) {
545 remote_name = remote_list.strings[i];
546 } else {
547 git_remote_free(remote);
3e199f42 548
ac3d33df 549 git_error_set(GIT_ERROR_REFERENCE,
909d5494 550 "reference '%s' is ambiguous", refname);
2e3e8c88
JM
551 error = GIT_EAMBIGUOUS;
552 goto cleanup;
553 }
554 }
555
556 git_remote_free(remote);
557 }
558
559 if (remote_name) {
97016f29
CMN
560 git_buf_clear(buf);
561 error = git_buf_puts(buf, remote_name);
2e3e8c88 562 } else {
ac3d33df 563 git_error_set(GIT_ERROR_REFERENCE,
909d5494 564 "could not determine remote for '%s'", refname);
2e3e8c88 565 error = GIT_ENOTFOUND;
2e3e8c88
JM
566 }
567
568cleanup:
b25d87c9 569 if (error < 0)
ac3d33df 570 git_buf_dispose(buf);
b25d87c9 571
22a2d3d5 572 git_strarray_dispose(&remote_list);
2e3e8c88
JM
573 return error;
574}
575
a258d8e3 576int git_branch_upstream(
3b4ba278
JG
577 git_reference **tracking_out,
578 const git_reference *branch)
bf031581 579{
580 int error;
581 git_buf tracking_name = GIT_BUF_INIT;
582
b25d87c9 583 if ((error = git_branch_upstream_name(&tracking_name,
bf031581 584 git_reference_owner(branch), git_reference_name(branch))) < 0)
585 return error;
586
587 error = git_reference_lookup(
588 tracking_out,
589 git_reference_owner(branch),
590 git_buf_cstr(&tracking_name));
591
ac3d33df 592 git_buf_dispose(&tracking_name);
bf031581 593 return error;
594}
595
d59942c2
CMN
596static int unset_upstream(git_config *config, const char *shortname)
597{
598 git_buf buf = GIT_BUF_INIT;
599
600 if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
601 return -1;
602
603 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
604 goto on_error;
605
606 git_buf_clear(&buf);
607 if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
608 goto on_error;
609
610 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
611 goto on_error;
612
ac3d33df 613 git_buf_dispose(&buf);
d59942c2
CMN
614 return 0;
615
616on_error:
ac3d33df 617 git_buf_dispose(&buf);
d59942c2
CMN
618 return -1;
619}
620
22a2d3d5 621int git_branch_set_upstream(git_reference *branch, const char *branch_name)
d59942c2 622{
22a2d3d5 623 git_buf key = GIT_BUF_INIT, remote_name = GIT_BUF_INIT, merge_refspec = GIT_BUF_INIT;
d59942c2
CMN
624 git_reference *upstream;
625 git_repository *repo;
626 git_remote *remote = NULL;
627 git_config *config;
22a2d3d5 628 const char *refname, *shortname;
5014fe95 629 int local, error;
d59942c2
CMN
630 const git_refspec *fetchspec;
631
22a2d3d5
UG
632 refname = git_reference_name(branch);
633 if (!git_reference__is_branch(refname))
634 return not_a_local_branch(refname);
d59942c2
CMN
635
636 if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
637 return -1;
638
22a2d3d5 639 shortname = refname + strlen(GIT_REFS_HEADS_DIR);
d59942c2 640
22a2d3d5
UG
641 /* We're unsetting, delegate and bail-out */
642 if (branch_name == NULL)
d59942c2
CMN
643 return unset_upstream(config, shortname);
644
645 repo = git_reference_owner(branch);
646
22a2d3d5
UG
647 /* First we need to resolve name to a branch */
648 if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_LOCAL) == 0)
d59942c2 649 local = 1;
22a2d3d5 650 else if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_REMOTE) == 0)
d59942c2 651 local = 0;
3e199f42 652 else {
ac3d33df 653 git_error_set(GIT_ERROR_REFERENCE,
909d5494 654 "cannot set upstream for branch '%s'", shortname);
d59942c2 655 return GIT_ENOTFOUND;
3e199f42 656 }
d59942c2
CMN
657
658 /*
22a2d3d5
UG
659 * If it's a local-tracking branch, its remote is "." (as "the local
660 * repository"), and the branch name is simply the refname.
661 * Otherwise we need to figure out what the remote-tracking branch's
662 * name on the remote is and use that.
d59942c2
CMN
663 */
664 if (local)
22a2d3d5 665 error = git_buf_puts(&remote_name, ".");
d59942c2 666 else
22a2d3d5 667 error = git_branch_remote_name(&remote_name, repo, git_reference_name(upstream));
5014fe95
CMN
668
669 if (error < 0)
670 goto on_error;
d59942c2 671
22a2d3d5 672 /* Update the upsteam branch config with the new name */
d59942c2
CMN
673 if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
674 goto on_error;
675
22a2d3d5 676 if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&remote_name)) < 0)
d59942c2
CMN
677 goto on_error;
678
679 if (local) {
22a2d3d5
UG
680 /* A local branch uses the upstream refname directly */
681 if (git_buf_puts(&merge_refspec, git_reference_name(upstream)) < 0)
d59942c2
CMN
682 goto on_error;
683 } else {
22a2d3d5
UG
684 /* We transform the upstream branch name according to the remote's refspecs */
685 if (git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name)) < 0)
d59942c2
CMN
686 goto on_error;
687
4330ab26 688 fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
22a2d3d5 689 if (!fetchspec || git_refspec_rtransform(&merge_refspec, fetchspec, git_reference_name(upstream)) < 0)
d59942c2
CMN
690 goto on_error;
691
692 git_remote_free(remote);
693 remote = NULL;
694 }
695
22a2d3d5 696 /* Update the merge branch config with the refspec */
d59942c2
CMN
697 git_buf_clear(&key);
698 if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
699 goto on_error;
700
22a2d3d5 701 if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&merge_refspec)) < 0)
d59942c2
CMN
702 goto on_error;
703
704 git_reference_free(upstream);
ac3d33df 705 git_buf_dispose(&key);
22a2d3d5
UG
706 git_buf_dispose(&remote_name);
707 git_buf_dispose(&merge_refspec);
d59942c2
CMN
708
709 return 0;
710
711on_error:
712 git_reference_free(upstream);
ac3d33df 713 git_buf_dispose(&key);
22a2d3d5
UG
714 git_buf_dispose(&remote_name);
715 git_buf_dispose(&merge_refspec);
d59942c2
CMN
716 git_remote_free(remote);
717
718 return -1;
719}
720
0c78f685 721int git_branch_is_head(
853b1407 722 const git_reference *branch)
0c78f685 723{
724 git_reference *head;
725 bool is_same = false;
0532e7bb 726 int error;
0c78f685 727
c25aa7cd 728 GIT_ASSERT_ARG(branch);
0c78f685 729
730 if (!git_reference_is_branch(branch))
731 return false;
732
0532e7bb 733 error = git_repository_head(&head, git_reference_owner(branch));
734
605da51a 735 if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
0532e7bb 736 return false;
737
738 if (error < 0)
0c78f685 739 return -1;
740
741 is_same = strcmp(
742 git_reference_name(branch),
743 git_reference_name(head)) == 0;
744
745 git_reference_free(head);
746
747 return is_same;
748}
c25aa7cd
PP
749
750int git_branch_name_is_valid(int *valid, const char *name)
751{
752 git_buf ref_name = GIT_BUF_INIT;
753 int error = 0;
754
755 GIT_ASSERT(valid);
756
757 *valid = 0;
758
759 /*
760 * Discourage branch name starting with dash,
761 * https://github.com/git/git/commit/6348624010888b
762 * and discourage HEAD as branch name,
763 * https://github.com/git/git/commit/a625b092cc5994
764 */
765 if (!name || name[0] == '-' || !git__strcmp(name, "HEAD"))
766 goto done;
767
768 if ((error = git_buf_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 ||
769 (error = git_buf_puts(&ref_name, name)) < 0)
770 goto done;
771
772 error = git_reference_name_is_valid(valid, ref_name.ptr);
773
774done:
775 git_buf_dispose(&ref_name);
776 return error;
777}