/* * 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 "tag.h" #include "merge.h" #include "git2/reset.h" #include "git2/checkout.h" #include "git2/merge.h" #define ERROR_MSG "Cannot perform reset" static int reset_error_invalid(const char *msg) { giterr_set(GITERR_INVALID, "%s - %s", ERROR_MSG, msg); return -1; } static int update_head(git_repository *repo, git_object *commit) { int error; git_reference *head = NULL, *target = NULL; error = git_repository_head(&head, repo); if (error < 0 && error != GIT_EORPHANEDHEAD) return error; if (error == GIT_EORPHANEDHEAD) { giterr_clear(); /* * TODO: This is a bit weak as this doesn't support chained * symbolic references. yet. */ if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0) goto cleanup; if ((error = git_reference_create_oid( &target, repo, git_reference_target(head), git_object_id(commit), 0)) < 0) goto cleanup; } else { if ((error = git_reference_set_oid(head, git_object_id(commit))) < 0) goto cleanup; } error = 0; cleanup: git_reference_free(head); git_reference_free(target); return error; } int git_reset( git_repository *repo, git_object *target, git_reset_type reset_type) { git_object *commit = NULL; git_index *index = NULL; git_tree *tree = NULL; int error = -1; git_checkout_opts opts; assert(repo && target); assert(reset_type == GIT_RESET_SOFT || reset_type == GIT_RESET_MIXED || reset_type == GIT_RESET_HARD); if (git_object_owner(target) != repo) return reset_error_invalid("The given target does not belong to this repository."); if (reset_type != GIT_RESET_SOFT && git_repository__ensure_not_bare( repo, reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard") < 0) return GIT_EBAREREPO; if (git_object_peel(&commit, target, GIT_OBJ_COMMIT) < 0) { reset_error_invalid("The given target does not resolve to a commit"); goto cleanup; } if (reset_type == GIT_RESET_SOFT && (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE)) { giterr_set(GITERR_OBJECT, "%s (soft) while in the middle of a merge.", ERROR_MSG); error = GIT_EUNMERGED; goto cleanup; } //TODO: Check for unmerged entries if (update_head(repo, commit) < 0) goto cleanup; if (reset_type == GIT_RESET_SOFT) { error = 0; goto cleanup; } if (git_commit_tree(&tree, (git_commit *)commit) < 0) { giterr_set(GITERR_OBJECT, "%s - Failed to retrieve the commit tree.", ERROR_MSG); goto cleanup; } if (git_repository_index(&index, repo) < 0) { giterr_set(GITERR_OBJECT, "%s - Failed to retrieve the index.", ERROR_MSG); goto cleanup; } if (git_index_read_tree(index, tree) < 0) { giterr_set(GITERR_INDEX, "%s - Failed to update the index.", ERROR_MSG); goto cleanup; } if (git_index_write(index) < 0) { giterr_set(GITERR_INDEX, "%s - Failed to write the index.", ERROR_MSG); goto cleanup; } if ((error = git_merge__cleanup(repo)) < 0) { giterr_set(GITERR_INDEX, "%s - Failed to clean up merge data.", ERROR_MSG); goto cleanup; } if (reset_type == GIT_RESET_MIXED) { error = 0; goto cleanup; } memset(&opts, 0, sizeof(opts)); opts.checkout_strategy = GIT_CHECKOUT_FORCE; if (git_checkout_index(repo, NULL, &opts) < 0) { giterr_set(GITERR_INDEX, "%s - Failed to checkout the index.", ERROR_MSG); goto cleanup; } error = 0; cleanup: git_object_free(commit); git_index_free(index); git_tree_free(tree); return error; }