]> git.proxmox.com Git - libgit2.git/blobdiff - src/branch.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / branch.c
index 61ed03e2e36940c5c2a8772c1ceeae091de6a395..715f6cf99c70efa94c87f4cd670afe6fb87fc93a 100644 (file)
@@ -22,7 +22,7 @@ static int retrieve_branch_reference(
        git_reference **branch_reference_out,
        git_repository *repo,
        const char *branch_name,
-       int is_remote)
+       bool is_remote)
 {
        git_reference *branch = NULL;
        int error = 0;
@@ -134,29 +134,37 @@ int git_branch_create_from_annotated(
                repository, branch_name, commit->commit, commit->description, force);
 }
 
-static int branch_equals(git_repository *repo, const char *path, void *payload)
+static int branch_is_checked_out(git_repository *worktree, void *payload)
 {
        git_reference *branch = (git_reference *) payload;
        git_reference *head = NULL;
-       int equal = 0;
+       int error;
 
-       if (git_reference__read_head(&head, repo, path) < 0 ||
-               git_reference_type(head) != GIT_REFERENCE_SYMBOLIC)
-               goto done;
+       if (git_repository_is_bare(worktree))
+               return 0;
 
-       equal = !git__strcmp(head->target.symbolic, branch->name);
+       if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) {
+               if (error == GIT_ENOTFOUND)
+                       error = 0;
+               goto out;
+       }
 
-done:
+       if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC)
+               goto out;
+
+       error = !git__strcmp(head->target.symbolic, branch->name);
+
+out:
        git_reference_free(head);
-       return equal;
+       return error;
 }
 
 int git_branch_is_checked_out(const git_reference *branch)
 {
-       assert(branch && git_reference_is_branch(branch));
-
-       return git_repository_foreach_head(git_reference_owner(branch),
-               branch_equals, (void *) branch) == 1;
+       if (!git_reference_is_branch(branch))
+               return 0;
+       return git_repository_foreach_worktree(git_reference_owner(branch),
+                                              branch_is_checked_out, (void *)branch) == 1;
 }
 
 int git_branch_delete(git_reference *branch)
@@ -324,9 +332,23 @@ int git_branch_lookup(
        const char *branch_name,
        git_branch_t branch_type)
 {
+       int error = -1;
        assert(ref_out && repo && branch_name);
 
-       return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
+       switch (branch_type) {
+       case GIT_BRANCH_LOCAL:
+       case GIT_BRANCH_REMOTE:
+               error = retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
+               break;
+       case GIT_BRANCH_ALL:
+               error = retrieve_branch_reference(ref_out, repo, branch_name, false);
+               if (error == GIT_ENOTFOUND)
+                       error = retrieve_branch_reference(ref_out, repo, branch_name, true);
+               break;
+       default:
+               assert(false);
+       }
+       return error;
 }
 
 int git_branch_name(
@@ -524,7 +546,7 @@ cleanup:
        if (error < 0)
                git_buf_dispose(buf);
 
-       git_strarray_free(&remote_list);
+       git_strarray_dispose(&remote_list);
        return error;
 }
 
@@ -573,35 +595,36 @@ on_error:
        return -1;
 }
 
-int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
+int git_branch_set_upstream(git_reference *branch, const char *branch_name)
 {
-       git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
+       git_buf key = GIT_BUF_INIT, remote_name = GIT_BUF_INIT, merge_refspec = GIT_BUF_INIT;
        git_reference *upstream;
        git_repository *repo;
        git_remote *remote = NULL;
        git_config *config;
-       const char *name, *shortname;
+       const char *refname, *shortname;
        int local, error;
        const git_refspec *fetchspec;
 
-       name = git_reference_name(branch);
-       if (!git_reference__is_branch(name))
-               return not_a_local_branch(name);
+       refname = git_reference_name(branch);
+       if (!git_reference__is_branch(refname))
+               return not_a_local_branch(refname);
 
        if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
                return -1;
 
-       shortname = name + strlen(GIT_REFS_HEADS_DIR);
+       shortname = refname + strlen(GIT_REFS_HEADS_DIR);
 
-       if (upstream_name == NULL)
+       /* We're unsetting, delegate and bail-out */
+       if (branch_name == NULL)
                return unset_upstream(config, shortname);
 
        repo = git_reference_owner(branch);
 
-       /* First we need to figure out whether it's a branch or remote-tracking */
-       if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
+       /* First we need to resolve name to a branch */
+       if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_LOCAL) == 0)
                local = 1;
-       else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
+       else if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_REMOTE) == 0)
                local = 0;
        else {
                git_error_set(GIT_ERROR_REFERENCE,
@@ -610,60 +633,63 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
        }
 
        /*
-        * If it's local, the remote is "." and the branch name is
-        * simply the refname. Otherwise we need to figure out what
-        * the remote-tracking branch's name on the remote is and use
-        * that.
+        * If it's a local-tracking branch, its remote is "." (as "the local
+        * repository"), and the branch name is simply the refname.
+        * Otherwise we need to figure out what the remote-tracking branch's
+        * name on the remote is and use that.
         */
        if (local)
-               error = git_buf_puts(&value, ".");
+               error = git_buf_puts(&remote_name, ".");
        else
-               error = git_branch_remote_name(&value, repo, git_reference_name(upstream));
+               error = git_branch_remote_name(&remote_name, repo, git_reference_name(upstream));
 
        if (error < 0)
                goto on_error;
 
+       /* Update the upsteam branch config with the new name */
        if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
                goto on_error;
 
-       if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
+       if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&remote_name)) < 0)
                goto on_error;
 
        if (local) {
-               git_buf_clear(&value);
-               if (git_buf_puts(&value, git_reference_name(upstream)) < 0)
+               /* A local branch uses the upstream refname directly */
+               if (git_buf_puts(&merge_refspec, git_reference_name(upstream)) < 0)
                        goto on_error;
        } else {
-               /* Get the remoe-tracking branch's refname in its repo */
-               if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0)
+               /* We transform the upstream branch name according to the remote's refspecs */
+               if (git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name)) < 0)
                        goto on_error;
 
                fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
-               git_buf_clear(&value);
-               if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0)
+               if (!fetchspec || git_refspec_rtransform(&merge_refspec, fetchspec, git_reference_name(upstream)) < 0)
                        goto on_error;
 
                git_remote_free(remote);
                remote = NULL;
        }
 
+       /* Update the merge branch config with the refspec */
        git_buf_clear(&key);
        if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
                goto on_error;
 
-       if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
+       if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&merge_refspec)) < 0)
                goto on_error;
 
        git_reference_free(upstream);
        git_buf_dispose(&key);
-       git_buf_dispose(&value);
+       git_buf_dispose(&remote_name);
+       git_buf_dispose(&merge_refspec);
 
        return 0;
 
 on_error:
        git_reference_free(upstream);
        git_buf_dispose(&key);
-       git_buf_dispose(&value);
+       git_buf_dispose(&remote_name);
+       git_buf_dispose(&merge_refspec);
        git_remote_free(remote);
 
        return -1;