Hash :
bfe7d7de
Author :
Date :
2012-11-26T17:24:02
Reorder operations in git reset This corrects the order of operations in git reset so that the checkout to reset the working directory content is done before the HEAD is moved. This allows us to use the HEAD and the index content to know what files can / should safely be reset. Unfortunately, there are still some cases where the behavior of this revision differs from core git. Notable, a file which has been added to the index but is not present in the HEAD is considered to be tracked by core git (and thus removable by a reset command) whereas since this loads the target state into the index prior to resetting, it will consider such a file to be untracked and won't touch it. That is a larger fix that I'll defer to a future commit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
/*
* 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 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(
&target,
repo,
git_reference_symbolic_target(head),
git_object_id(commit), 0)) < 0)
goto cleanup;
} else {
if ((error = git_reference_set_target(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_t reset_type)
{
git_object *commit = NULL;
git_index *index = NULL;
git_tree *tree = NULL;
int error = 0;
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
assert(repo && target);
if (git_object_owner(target) != repo) {
giterr_set(GITERR_OBJECT,
"%s - The given target does not belong to this repository.", ERROR_MSG);
return -1;
}
if (reset_type != GIT_RESET_SOFT &&
(error = git_repository__ensure_not_bare(repo,
reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0)
return error;
if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
(error = git_repository_index(&index, repo)) < 0 ||
(error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
goto cleanup;
if (reset_type == GIT_RESET_SOFT &&
(git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
git_index_has_conflicts(index)))
{
giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG);
error = GIT_EUNMERGED;
goto cleanup;
}
/* move HEAD to the new target */
if ((error = update_head(repo, commit)) < 0)
goto cleanup;
if (reset_type == GIT_RESET_HARD) {
/* overwrite working directory with HEAD */
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
goto cleanup;
}
if (reset_type > GIT_RESET_SOFT) {
/* reset index to the target content */
if ((error = git_repository_index(&index, repo)) < 0 ||
(error = git_index_read_tree(index, tree)) < 0 ||
(error = git_index_write(index)) < 0)
goto cleanup;
if ((error = git_repository_merge_cleanup(repo)) < 0) {
giterr_set(GITERR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
goto cleanup;
}
}
cleanup:
git_object_free(commit);
git_index_free(index);
git_tree_free(tree);
return error;
}