Blame src/branch.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "commit.h"
Packit Service 20376f
#include "tag.h"
Packit Service 20376f
#include "config.h"
Packit Service 20376f
#include "refspec.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "remote.h"
Packit Service 20376f
#include "annotated_commit.h"
Packit Service 20376f
#include "worktree.h"
Packit Service 20376f
Packit Service 20376f
#include "git2/branch.h"
Packit Service 20376f
Packit Service 20376f
static int retrieve_branch_reference(
Packit Service 20376f
	git_reference **branch_reference_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *branch_name,
Packit Service 20376f
	int is_remote)
Packit Service 20376f
{
Packit Service 20376f
	git_reference *branch = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	char *prefix;
Packit Service 20376f
	git_buf ref_name = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0)
Packit Service 20376f
		/* OOM */;
Packit Service 20376f
	else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
Packit Service 20376f
		giterr_set(
Packit Service 20376f
			GITERR_REFERENCE, "cannot locate %s branch '%s'",
Packit Service 20376f
			is_remote ? "remote-tracking" : "local", branch_name);
Packit Service 20376f
Packit Service 20376f
	*branch_reference_out = branch; /* will be NULL on error */
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&ref_name);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int not_a_local_branch(const char *reference_name)
Packit Service 20376f
{
Packit Service 20376f
	giterr_set(
Packit Service 20376f
		GITERR_INVALID,
Packit Service 20376f
		"reference '%s' is not a local branch.", reference_name);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int create_branch(
Packit Service 20376f
	git_reference **ref_out,
Packit Service 20376f
	git_repository *repository,
Packit Service 20376f
	const char *branch_name,
Packit Service 20376f
	const git_commit *commit,
Packit Service 20376f
	const char *from,
Packit Service 20376f
	int force)
Packit Service 20376f
{
Packit Service 20376f
	int is_unmovable_head = 0;
Packit Service 20376f
	git_reference *branch = NULL;
Packit Service 20376f
	git_buf canonical_branch_name = GIT_BUF_INIT,
Packit Service 20376f
			  log_message = GIT_BUF_INIT;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
	int bare = git_repository_is_bare(repository);
Packit Service 20376f
Packit Service 20376f
	assert(branch_name && commit && ref_out);
Packit Service 20376f
	assert(git_object_owner((const git_object *)commit) == repository);
Packit Service 20376f
Packit Service 20376f
	if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
Packit Service 20376f
		error = git_branch_is_head(branch);
Packit Service 20376f
		git_reference_free(branch);
Packit Service 20376f
		branch = NULL;
Packit Service 20376f
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		is_unmovable_head = error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (is_unmovable_head && force) {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE, "cannot force update branch '%s' as it is "
Packit Service 20376f
			"the current HEAD of the repository.", branch_name);
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_printf(&log_message, "branch: Created from %s", from) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_reference_create(&branch, repository,
Packit Service 20376f
		git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force,
Packit Service 20376f
		git_buf_cstr(&log_message));
Packit Service 20376f
Packit Service 20376f
	if (!error)
Packit Service 20376f
		*ref_out = branch;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_buf_free(&canonical_branch_name);
Packit Service 20376f
	git_buf_free(&log_message);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_create(
Packit Service 20376f
	git_reference **ref_out,
Packit Service 20376f
	git_repository *repository,
Packit Service 20376f
	const char *branch_name,
Packit Service 20376f
	const git_commit *commit,
Packit Service 20376f
	int force)
Packit Service 20376f
{
Packit Service 20376f
	return create_branch(ref_out, repository, branch_name, commit, git_oid_tostr_s(git_commit_id(commit)), force);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_create_from_annotated(
Packit Service 20376f
	git_reference **ref_out,
Packit Service 20376f
	git_repository *repository,
Packit Service 20376f
	const char *branch_name,
Packit Service 20376f
	const git_annotated_commit *commit,
Packit Service 20376f
	int force)
Packit Service 20376f
{
Packit Service 20376f
	return create_branch(ref_out,
Packit Service 20376f
		repository, branch_name, commit->commit, commit->description, force);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int branch_equals(git_repository *repo, const char *path, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	git_reference *branch = (git_reference *) payload;
Packit Service 20376f
	git_reference *head = NULL;
Packit Service 20376f
	int equal = 0;
Packit Service 20376f
Packit Service 20376f
	if (git_reference__read_head(&head, repo, path) < 0 ||
Packit Service 20376f
		git_reference_type(head) != GIT_REF_SYMBOLIC)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	equal = !git__strcmp(head->target.symbolic, branch->name);
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_reference_free(head);
Packit Service 20376f
	return equal;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_is_checked_out(const git_reference *branch)
Packit Service 20376f
{
Packit Service 20376f
	assert(branch && git_reference_is_branch(branch));
Packit Service 20376f
Packit Service 20376f
	return git_repository_foreach_head(git_reference_owner(branch),
Packit Service 20376f
		branch_equals, (void *) branch) == 1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_delete(git_reference *branch)
Packit Service 20376f
{
Packit Service 20376f
	int is_head;
Packit Service 20376f
	git_buf config_section = GIT_BUF_INIT;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
Packit Service 20376f
	assert(branch);
Packit Service 20376f
Packit Service 20376f
	if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "reference '%s' is not a valid branch.",
Packit Service 20376f
			git_reference_name(branch));
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((is_head = git_branch_is_head(branch)) < 0)
Packit Service 20376f
		return is_head;
Packit Service 20376f
Packit Service 20376f
	if (is_head) {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE, "cannot delete branch '%s' as it is "
Packit Service 20376f
			"the current HEAD of the repository.", git_reference_name(branch));
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
Packit Service 20376f
			"the current HEAD of a linked repository.", git_reference_name(branch));
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_buf_join(&config_section, '.', "branch",
Packit Service 20376f
			git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_config_rename_section(
Packit Service 20376f
		git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	error = git_reference_delete(branch);
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_buf_free(&config_section);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_reference_iterator *iter;
Packit Service 20376f
	unsigned int flags;
Packit Service 20376f
} branch_iter;
Packit Service 20376f
Packit Service 20376f
int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
Packit Service 20376f
{
Packit Service 20376f
	branch_iter *iter = (branch_iter *) _iter;
Packit Service 20376f
	git_reference *ref;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	while ((error = git_reference_next(&ref, iter->iter)) == 0) {
Packit Service 20376f
		if ((iter->flags & GIT_BRANCH_LOCAL) &&
Packit Service 20376f
		    !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
Packit Service 20376f
			*out = ref;
Packit Service 20376f
			*out_type = GIT_BRANCH_LOCAL;
Packit Service 20376f
Packit Service 20376f
			return 0;
Packit Service 20376f
		} else  if ((iter->flags & GIT_BRANCH_REMOTE) &&
Packit Service 20376f
			    !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
Packit Service 20376f
			*out = ref;
Packit Service 20376f
			*out_type = GIT_BRANCH_REMOTE;
Packit Service 20376f
Packit Service 20376f
			return 0;
Packit Service 20376f
		} else {
Packit Service 20376f
			git_reference_free(ref);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_iterator_new(
Packit Service 20376f
	git_branch_iterator **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_branch_t list_flags)
Packit Service 20376f
{
Packit Service 20376f
	branch_iter *iter;
Packit Service 20376f
Packit Service 20376f
	iter = git__calloc(1, sizeof(branch_iter));
Packit Service 20376f
	GITERR_CHECK_ALLOC(iter);
Packit Service 20376f
Packit Service 20376f
	iter->flags = list_flags;
Packit Service 20376f
Packit Service 20376f
	if (git_reference_iterator_new(&iter->iter, repo) < 0) {
Packit Service 20376f
		git__free(iter);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = (git_branch_iterator *) iter;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_branch_iterator_free(git_branch_iterator *_iter)
Packit Service 20376f
{
Packit Service 20376f
	branch_iter *iter = (branch_iter *) _iter;
Packit Service 20376f
Packit Service 20376f
	if (iter == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	git_reference_iterator_free(iter->iter);
Packit Service 20376f
	git__free(iter);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_move(
Packit Service 20376f
	git_reference **out,
Packit Service 20376f
	git_reference *branch,
Packit Service 20376f
	const char *new_branch_name,
Packit Service 20376f
	int force)
Packit Service 20376f
{
Packit Service 20376f
	git_buf new_reference_name = GIT_BUF_INIT,
Packit Service 20376f
	        old_config_section = GIT_BUF_INIT,
Packit Service 20376f
	        new_config_section = GIT_BUF_INIT,
Packit Service 20376f
	        log_message = GIT_BUF_INIT;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(branch && new_branch_name);
Packit Service 20376f
Packit Service 20376f
	if (!git_reference_is_branch(branch))
Packit Service 20376f
		return not_a_local_branch(git_reference_name(branch));
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_buf_printf(&log_message, "branch: renamed %s to %s",
Packit Service 20376f
				    git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0)
Packit Service 20376f
			goto done;
Packit Service 20376f
Packit Service 20376f
	/* first update ref then config so failure won't trash config */
Packit Service 20376f
Packit Service 20376f
	error = git_reference_rename(
Packit Service 20376f
		out, branch, git_buf_cstr(&new_reference_name), force,
Packit Service 20376f
		git_buf_cstr(&log_message));
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	git_buf_join(&old_config_section, '.', "branch",
Packit Service 20376f
		git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
Packit Service 20376f
	git_buf_join(&new_config_section, '.', "branch", new_branch_name);
Packit Service 20376f
Packit Service 20376f
	error = git_config_rename_section(
Packit Service 20376f
		git_reference_owner(branch),
Packit Service 20376f
		git_buf_cstr(&old_config_section),
Packit Service 20376f
		git_buf_cstr(&new_config_section));
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&new_reference_name);
Packit Service 20376f
	git_buf_free(&old_config_section);
Packit Service 20376f
	git_buf_free(&new_config_section);
Packit Service 20376f
	git_buf_free(&log_message);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_lookup(
Packit Service 20376f
	git_reference **ref_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *branch_name,
Packit Service 20376f
	git_branch_t branch_type)
Packit Service 20376f
{
Packit Service 20376f
	assert(ref_out && repo && branch_name);
Packit Service 20376f
Packit Service 20376f
	return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_name(
Packit Service 20376f
	const char **out,
Packit Service 20376f
	const git_reference *ref)
Packit Service 20376f
{
Packit Service 20376f
	const char *branch_name;
Packit Service 20376f
Packit Service 20376f
	assert(out && ref);
Packit Service 20376f
Packit Service 20376f
	branch_name = ref->name;
Packit Service 20376f
Packit Service 20376f
	if (git_reference_is_branch(ref)) {
Packit Service 20376f
		branch_name += strlen(GIT_REFS_HEADS_DIR);
Packit Service 20376f
	} else if (git_reference_is_remote(ref)) {
Packit Service 20376f
		branch_name += strlen(GIT_REFS_REMOTES_DIR);
Packit Service 20376f
	} else {
Packit Service 20376f
		giterr_set(GITERR_INVALID,
Packit Service 20376f
				"reference '%s' is neither a local nor a remote branch.", ref->name);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
	*out = branch_name;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int retrieve_upstream_configuration(
Packit Service 20376f
	git_buf *out,
Packit Service 20376f
	const git_config *config,
Packit Service 20376f
	const char *canonical_branch_name,
Packit Service 20376f
	const char *format)
Packit Service 20376f
{
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_printf(&buf, format,
Packit Service 20376f
		canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
	error = git_config_get_string_buf(out, config, git_buf_cstr(&buf));
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_upstream_name(
Packit Service 20376f
	git_buf *out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *refname)
Packit Service 20376f
{
Packit Service 20376f
	git_buf remote_name = GIT_BUF_INIT;
Packit Service 20376f
	git_buf merge_name = GIT_BUF_INIT;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
	git_remote *remote = NULL;
Packit Service 20376f
	const git_refspec *refspec;
Packit Service 20376f
	git_config *config;
Packit Service 20376f
Packit Service 20376f
	assert(out && refname);
Packit Service 20376f
Packit Service 20376f
	git_buf_sanitize(out);
Packit Service 20376f
Packit Service 20376f
	if (!git_reference__is_branch(refname))
Packit Service 20376f
		return not_a_local_branch(refname);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_config_snapshot(&config, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = retrieve_upstream_configuration(
Packit Service 20376f
		&remote_name, config, refname, "branch.%s.remote")) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = retrieve_upstream_configuration(
Packit Service 20376f
		&merge_name, config, refname, "branch.%s.merge")) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_len(&remote_name) == 0 || git_buf_len(&merge_name) == 0) {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE,
Packit Service 20376f
			"branch '%s' does not have an upstream", refname);
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (strcmp(".", git_buf_cstr(&remote_name)) != 0) {
Packit Service 20376f
		if ((error = git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name))) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		refspec = git_remote__matching_refspec(remote, git_buf_cstr(&merge_name));
Packit Service 20376f
		if (!refspec) {
Packit Service 20376f
			error = GIT_ENOTFOUND;
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (git_refspec_transform(&buf, refspec, git_buf_cstr(&merge_name)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
	} else
Packit Service 20376f
		if (git_buf_set(&buf, git_buf_cstr(&merge_name), git_buf_len(&merge_name)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_config_free(config);
Packit Service 20376f
	git_remote_free(remote);
Packit Service 20376f
	git_buf_free(&remote_name);
Packit Service 20376f
	git_buf_free(&merge_name);
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_config *cfg;
Packit Service 20376f
Packit Service 20376f
	if (!git_reference__is_branch(refname))
Packit Service 20376f
		return not_a_local_branch(refname);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_buf_sanitize(buf);
Packit Service 20376f
Packit Service 20376f
	if ((error = retrieve_upstream_configuration(buf, cfg, refname, "branch.%s.remote")) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_len(buf) == 0) {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname);
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
		git_buf_clear(buf);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
Packit Service 20376f
{
Packit Service 20376f
	git_strarray remote_list = {0};
Packit Service 20376f
	size_t i;
Packit Service 20376f
	git_remote *remote;
Packit Service 20376f
	const git_refspec *fetchspec;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	char *remote_name = NULL;
Packit Service 20376f
Packit Service 20376f
	assert(buf && repo && refname);
Packit Service 20376f
Packit Service 20376f
	git_buf_sanitize(buf);
Packit Service 20376f
Packit Service 20376f
	/* Verify that this is a remote branch */
Packit Service 20376f
	if (!git_reference__is_remote(refname)) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "reference '%s' is not a remote branch.",
Packit Service 20376f
			refname);
Packit Service 20376f
		error = GIT_ERROR;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Get the remotes */
Packit Service 20376f
	if ((error = git_remote_list(&remote_list, repo)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/* Find matching remotes */
Packit Service 20376f
	for (i = 0; i < remote_list.count; i++) {
Packit Service 20376f
		if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0)
Packit Service 20376f
			continue;
Packit Service 20376f
Packit Service 20376f
		fetchspec = git_remote__matching_dst_refspec(remote, refname);
Packit Service 20376f
		if (fetchspec) {
Packit Service 20376f
			/* If we have not already set out yet, then set
Packit Service 20376f
			 * it to the matching remote name. Otherwise
Packit Service 20376f
			 * multiple remotes match this reference, and it
Packit Service 20376f
			 * is ambiguous. */
Packit Service 20376f
			if (!remote_name) {
Packit Service 20376f
				remote_name = remote_list.strings[i];
Packit Service 20376f
			} else {
Packit Service 20376f
				git_remote_free(remote);
Packit Service 20376f
Packit Service 20376f
				giterr_set(GITERR_REFERENCE,
Packit Service 20376f
					"reference '%s' is ambiguous", refname);
Packit Service 20376f
				error = GIT_EAMBIGUOUS;
Packit Service 20376f
				goto cleanup;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_remote_free(remote);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (remote_name) {
Packit Service 20376f
		git_buf_clear(buf);
Packit Service 20376f
		error = git_buf_puts(buf, remote_name);
Packit Service 20376f
	} else {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE,
Packit Service 20376f
			"could not determine remote for '%s'", refname);
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_buf_free(buf);
Packit Service 20376f
Packit Service 20376f
	git_strarray_free(&remote_list);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_upstream(
Packit Service 20376f
	git_reference **tracking_out,
Packit Service 20376f
	const git_reference *branch)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_buf tracking_name = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_branch_upstream_name(&tracking_name,
Packit Service 20376f
		git_reference_owner(branch), git_reference_name(branch))) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
	error = git_reference_lookup(
Packit Service 20376f
		tracking_out,
Packit Service 20376f
		git_reference_owner(branch),
Packit Service 20376f
		git_buf_cstr(&tracking_name));
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&tracking_name);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int unset_upstream(git_config *config, const char *shortname)
Packit Service 20376f
{
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(&buf;;
Packit Service 20376f
	if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
Packit Service 20376f
{
Packit Service 20376f
	git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
Packit Service 20376f
	git_reference *upstream;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	git_remote *remote = NULL;
Packit Service 20376f
	git_config *config;
Packit Service 20376f
	const char *name, *shortname;
Packit Service 20376f
	int local, error;
Packit Service 20376f
	const git_refspec *fetchspec;
Packit Service 20376f
Packit Service 20376f
	name = git_reference_name(branch);
Packit Service 20376f
	if (!git_reference__is_branch(name))
Packit Service 20376f
		return not_a_local_branch(name);
Packit Service 20376f
Packit Service 20376f
	if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	shortname = name + strlen(GIT_REFS_HEADS_DIR);
Packit Service 20376f
Packit Service 20376f
	if (upstream_name == NULL)
Packit Service 20376f
		return unset_upstream(config, shortname);
Packit Service 20376f
Packit Service 20376f
	repo = git_reference_owner(branch);
Packit Service 20376f
Packit Service 20376f
	/* First we need to figure out whether it's a branch or remote-tracking */
Packit Service 20376f
	if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
Packit Service 20376f
		local = 1;
Packit Service 20376f
	else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
Packit Service 20376f
		local = 0;
Packit Service 20376f
	else {
Packit Service 20376f
		giterr_set(GITERR_REFERENCE,
Packit Service 20376f
			"cannot set upstream for branch '%s'", shortname);
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * If it's local, the remote is "." and the branch name is
Packit Service 20376f
	 * simply the refname. Otherwise we need to figure out what
Packit Service 20376f
	 * the remote-tracking branch's name on the remote is and use
Packit Service 20376f
	 * that.
Packit Service 20376f
	 */
Packit Service 20376f
	if (local)
Packit Service 20376f
		error = git_buf_puts(&value, ".");
Packit Service 20376f
	else
Packit Service 20376f
		error = git_branch_remote_name(&value, repo, git_reference_name(upstream));
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (local) {
Packit Service 20376f
		git_buf_clear(&value);
Packit Service 20376f
		if (git_buf_puts(&value, git_reference_name(upstream)) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
	} else {
Packit Service 20376f
		/* Get the remoe-tracking branch's refname in its repo */
Packit Service 20376f
		if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
Packit Service 20376f
		git_buf_clear(&value);
Packit Service 20376f
		if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		git_remote_free(remote);
Packit Service 20376f
		remote = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(&key);
Packit Service 20376f
	if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	git_reference_free(upstream);
Packit Service 20376f
	git_buf_free(&key);
Packit Service 20376f
	git_buf_free(&value);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_reference_free(upstream);
Packit Service 20376f
	git_buf_free(&key);
Packit Service 20376f
	git_buf_free(&value);
Packit Service 20376f
	git_remote_free(remote);
Packit Service 20376f
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_branch_is_head(
Packit Service 20376f
		const git_reference *branch)
Packit Service 20376f
{
Packit Service 20376f
	git_reference *head;
Packit Service 20376f
	bool is_same = false;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(branch);
Packit Service 20376f
Packit Service 20376f
	if (!git_reference_is_branch(branch))
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	error = git_repository_head(&head, git_reference_owner(branch));
Packit Service 20376f
Packit Service 20376f
	if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
Packit Service 20376f
		return false;
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	is_same = strcmp(
Packit Service 20376f
		git_reference_name(branch),
Packit Service 20376f
		git_reference_name(head)) == 0;
Packit Service 20376f
Packit Service 20376f
	git_reference_free(head);
Packit Service 20376f
Packit Service 20376f
	return is_same;
Packit Service 20376f
}