| /* |
| * Copyright (C) 2009-2012 the libgit2 contributors |
| * |
| * This file is part of libgit2, distributed under the GNU GPL v2 with |
| * a Linking Exception. For full terms see the included COPYING file. |
| */ |
| |
| #include "common.h" |
| #include "commit.h" |
| #include "branch.h" |
| #include "tag.h" |
| |
| static int retrieve_branch_reference( |
| git_reference **branch_reference_out, |
| git_repository *repo, |
| const char *branch_name, |
| int is_remote) |
| { |
| git_reference *branch; |
| int error = -1; |
| char *prefix; |
| git_buf ref_name = GIT_BUF_INIT; |
| |
| *branch_reference_out = NULL; |
| |
| prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR; |
| |
| if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0) |
| goto cleanup; |
| |
| if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) { |
| giterr_set(GITERR_REFERENCE, |
| "Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name); |
| goto cleanup; |
| } |
| |
| *branch_reference_out = branch; |
| |
| cleanup: |
| git_buf_free(&ref_name); |
| return error; |
| } |
| |
| static int create_error_invalid(const char *msg) |
| { |
| giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg); |
| return -1; |
| } |
| |
| int git_branch_create( |
| git_oid *oid_out, |
| git_repository *repo, |
| const char *branch_name, |
| const git_object *target, |
| int force) |
| { |
| git_otype target_type = GIT_OBJ_BAD; |
| git_object *commit = NULL; |
| git_reference *branch = NULL; |
| git_buf canonical_branch_name = GIT_BUF_INIT; |
| int error = -1; |
| |
| assert(repo && branch_name && target && oid_out); |
| |
| if (git_object_owner(target) != repo) |
| return create_error_invalid("The given target does not belong to this repository"); |
| |
| target_type = git_object_type(target); |
| |
| switch (target_type) |
| { |
| case GIT_OBJ_TAG: |
| if (git_tag_peel(&commit, (git_tag *)target) < 0) |
| goto cleanup; |
| |
| if (git_object_type(commit) != GIT_OBJ_COMMIT) { |
| create_error_invalid("The given target does not resolve to a commit"); |
| goto cleanup; |
| } |
| break; |
| |
| case GIT_OBJ_COMMIT: |
| commit = (git_object *)target; |
| break; |
| |
| default: |
| return create_error_invalid("Only git_tag and git_commit objects are valid targets."); |
| } |
| |
| if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0) |
| goto cleanup; |
| |
| if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0) |
| goto cleanup; |
| |
| git_oid_cpy(oid_out, git_reference_oid(branch)); |
| error = 0; |
| |
| cleanup: |
| if (target_type == GIT_OBJ_TAG) |
| git_object_free(commit); |
| |
| git_reference_free(branch); |
| git_buf_free(&canonical_branch_name); |
| return error; |
| } |
| |
| int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_t branch_type) |
| { |
| git_reference *branch = NULL; |
| git_reference *head = NULL; |
| int error; |
| |
| assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE)); |
| |
| if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0) |
| return error; |
| |
| if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) { |
| giterr_set(GITERR_REFERENCE, "Cannot locate HEAD."); |
| goto on_error; |
| } |
| |
| if ((git_reference_type(head) == GIT_REF_SYMBOLIC) |
| && (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) { |
| giterr_set(GITERR_REFERENCE, |
| "Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name); |
| goto on_error; |
| } |
| |
| if (git_reference_delete(branch) < 0) |
| goto on_error; |
| |
| git_reference_free(head); |
| return 0; |
| |
| on_error: |
| git_reference_free(head); |
| git_reference_free(branch); |
| return -1; |
| } |
| |
| typedef struct { |
| int (*branch_cb)( |
| const char *branch_name, |
| git_branch_t branch_type, |
| void *payload); |
| void *callback_payload; |
| unsigned int branch_type; |
| } branch_foreach_filter; |
| |
| static int branch_foreach_cb(const char *branch_name, void *payload) |
| { |
| branch_foreach_filter *filter = (branch_foreach_filter *)payload; |
| |
| if (filter->branch_type & GIT_BRANCH_LOCAL && |
| git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0) |
| return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload); |
| |
| if (filter->branch_type & GIT_BRANCH_REMOTE && |
| git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0) |
| return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload); |
| |
| return 0; |
| } |
| |
| int git_branch_foreach( |
| git_repository *repo, |
| unsigned int list_flags, |
| int (*branch_cb)( |
| const char *branch_name, |
| git_branch_t branch_type, |
| void *payload), |
| void *payload |
| ) |
| { |
| branch_foreach_filter filter; |
| |
| filter.branch_cb = branch_cb; |
| filter.branch_type = list_flags; |
| filter.callback_payload = payload; |
| |
| return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter); |
| } |
| |
| int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force) |
| { |
| git_reference *reference = NULL; |
| git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT; |
| int error = 0; |
| |
| if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0) |
| goto cleanup; |
| |
| /* We need to be able to return GIT_ENOTFOUND */ |
| if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0) |
| goto cleanup; |
| |
| if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0) |
| goto cleanup; |
| |
| error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force); |
| |
| cleanup: |
| git_reference_free(reference); |
| git_buf_free(&old_reference_name); |
| git_buf_free(&new_reference_name); |
| |
| return error; |
| } |