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