]>
Commit | Line | Data |
---|---|---|
edebceff | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
edebceff | 3 | * |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
8 | #include "common.h" | |
eae0bfdc | 9 | |
edebceff | 10 | #include "commit.h" |
11 | #include "tag.h" | |
632d8b23 | 12 | #include "merge.h" |
a0c34c94 | 13 | #include "diff.h" |
62d38a1d | 14 | #include "annotated_commit.h" |
edebceff | 15 | #include "git2/reset.h" |
ee8bb8ba | 16 | #include "git2/checkout.h" |
632d8b23 | 17 | #include "git2/merge.h" |
d00d5464 | 18 | #include "git2/refs.h" |
edebceff | 19 | |
20 | #define ERROR_MSG "Cannot perform reset" | |
21 | ||
a0c34c94 | 22 | int git_reset_default( |
23 | git_repository *repo, | |
eae0bfdc PP |
24 | const git_object *target, |
25 | const git_strarray* pathspecs) | |
a0c34c94 | 26 | { |
27 | git_object *commit = NULL; | |
28 | git_tree *tree = NULL; | |
3ff1d123 | 29 | git_diff *diff = NULL; |
a0c34c94 | 30 | git_diff_options opts = GIT_DIFF_OPTIONS_INIT; |
10672e3e | 31 | size_t i, max_i; |
a0c34c94 | 32 | git_index_entry entry; |
33 | int error; | |
34 | git_index *index = NULL; | |
35 | ||
1fed6b07 | 36 | assert(pathspecs != NULL && pathspecs->count > 0); |
a0c34c94 | 37 | |
38 | memset(&entry, 0, sizeof(git_index_entry)); | |
39 | ||
40 | if ((error = git_repository_index(&index, repo)) < 0) | |
41 | goto cleanup; | |
42 | ||
43 | if (target) { | |
44 | if (git_object_owner(target) != repo) { | |
ac3d33df | 45 | git_error_set(GIT_ERROR_OBJECT, |
a0c34c94 | 46 | "%s_default - The given target does not belong to this repository.", ERROR_MSG); |
47 | return -1; | |
48 | } | |
49 | ||
ac3d33df | 50 | if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 || |
a0c34c94 | 51 | (error = git_commit_tree(&tree, (git_commit *)commit)) < 0) |
52 | goto cleanup; | |
53 | } | |
54 | ||
55 | opts.pathspec = *pathspecs; | |
56 | opts.flags = GIT_DIFF_REVERSE; | |
57 | ||
58 | if ((error = git_diff_tree_to_index( | |
59 | &diff, repo, tree, index, &opts)) < 0) | |
60 | goto cleanup; | |
61 | ||
10672e3e RB |
62 | for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) { |
63 | const git_diff_delta *delta = git_diff_get_delta(diff, i); | |
64 | ||
a0c34c94 | 65 | assert(delta->status == GIT_DELTA_ADDED || |
66 | delta->status == GIT_DELTA_MODIFIED || | |
7c948014 | 67 | delta->status == GIT_DELTA_CONFLICTED || |
a0c34c94 | 68 | delta->status == GIT_DELTA_DELETED); |
69 | ||
bd101a7e RB |
70 | error = git_index_conflict_remove(index, delta->old_file.path); |
71 | if (error < 0) { | |
72 | if (delta->status == GIT_DELTA_ADDED && error == GIT_ENOTFOUND) | |
ac3d33df | 73 | git_error_clear(); |
bd101a7e RB |
74 | else |
75 | goto cleanup; | |
76 | } | |
77 | ||
a0c34c94 | 78 | if (delta->status == GIT_DELTA_DELETED) { |
79 | if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0) | |
80 | goto cleanup; | |
81 | } else { | |
82 | entry.mode = delta->new_file.mode; | |
9950bb4e | 83 | git_oid_cpy(&entry.id, &delta->new_file.id); |
a0c34c94 | 84 | entry.path = (char *)delta->new_file.path; |
85 | ||
86 | if ((error = git_index_add(index, &entry)) < 0) | |
87 | goto cleanup; | |
88 | } | |
89 | } | |
90 | ||
91 | error = git_index_write(index); | |
92 | ||
93 | cleanup: | |
94 | git_object_free(commit); | |
95 | git_tree_free(tree); | |
96 | git_index_free(index); | |
3ff1d123 | 97 | git_diff_free(diff); |
a0c34c94 | 98 | |
99 | return error; | |
100 | } | |
101 | ||
62d38a1d | 102 | static int reset( |
edebceff | 103 | git_repository *repo, |
eae0bfdc | 104 | const git_object *target, |
62d38a1d | 105 | const char *to, |
586be3b8 | 106 | git_reset_t reset_type, |
649834fd | 107 | const git_checkout_options *checkout_opts) |
edebceff | 108 | { |
edebceff | 109 | git_object *commit = NULL; |
110 | git_index *index = NULL; | |
111 | git_tree *tree = NULL; | |
bfe7d7de | 112 | int error = 0; |
6affd71f | 113 | git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; |
23a17803 | 114 | git_buf log_message = GIT_BUF_INIT; |
edebceff | 115 | |
116 | assert(repo && target); | |
edebceff | 117 | |
b8add6c4 SS |
118 | if (checkout_opts) |
119 | opts = *checkout_opts; | |
120 | ||
9a0d5904 | 121 | if (git_object_owner(target) != repo) { |
ac3d33df | 122 | git_error_set(GIT_ERROR_OBJECT, |
9a0d5904 | 123 | "%s - The given target does not belong to this repository.", ERROR_MSG); |
124 | return -1; | |
125 | } | |
edebceff | 126 | |
bfe7d7de RB |
127 | if (reset_type != GIT_RESET_SOFT && |
128 | (error = git_repository__ensure_not_bare(repo, | |
9a0d5904 | 129 | reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0) |
bfe7d7de | 130 | return error; |
edebceff | 131 | |
ac3d33df | 132 | if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 || |
bfe7d7de RB |
133 | (error = git_repository_index(&index, repo)) < 0 || |
134 | (error = git_commit_tree(&tree, (git_commit *)commit)) < 0) | |
632d8b23 | 135 | goto cleanup; |
632d8b23 | 136 | |
bfe7d7de | 137 | if (reset_type == GIT_RESET_SOFT && |
9a0d5904 | 138 | (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE || |
bfe7d7de RB |
139 | git_index_has_conflicts(index))) |
140 | { | |
ac3d33df | 141 | git_error_set(GIT_ERROR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG); |
bfe7d7de | 142 | error = GIT_EUNMERGED; |
edebceff | 143 | goto cleanup; |
144 | } | |
145 | ||
62d38a1d | 146 | if ((error = git_buf_printf(&log_message, "reset: moving to %s", to)) < 0) |
23a17803 | 147 | return error; |
586be3b8 | 148 | |
bfe7d7de | 149 | if (reset_type == GIT_RESET_HARD) { |
465c3b38 | 150 | /* overwrite working directory with the new tree */ |
2461e0d2 | 151 | opts.checkout_strategy = GIT_CHECKOUT_FORCE; |
edebceff | 152 | |
bfe7d7de RB |
153 | if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0) |
154 | goto cleanup; | |
632d8b23 ET |
155 | } |
156 | ||
465c3b38 CMN |
157 | /* move HEAD to the new target */ |
158 | if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE, | |
159 | git_object_id(commit), NULL, git_buf_cstr(&log_message))) < 0) | |
160 | goto cleanup; | |
161 | ||
bfe7d7de RB |
162 | if (reset_type > GIT_RESET_SOFT) { |
163 | /* reset index to the target content */ | |
ee8bb8ba | 164 | |
d01fe380 | 165 | if ((error = git_index_read_tree(index, tree)) < 0 || |
bfe7d7de RB |
166 | (error = git_index_write(index)) < 0) |
167 | goto cleanup; | |
c214fa1c | 168 | |
bab0b9f2 | 169 | if ((error = git_repository_state_cleanup(repo)) < 0) { |
ac3d33df | 170 | git_error_set(GIT_ERROR_INDEX, "%s - failed to clean up merge data", ERROR_MSG); |
bfe7d7de RB |
171 | goto cleanup; |
172 | } | |
173 | } | |
edebceff | 174 | |
175 | cleanup: | |
d8057a5b | 176 | git_object_free(commit); |
edebceff | 177 | git_index_free(index); |
178 | git_tree_free(tree); | |
ac3d33df | 179 | git_buf_dispose(&log_message); |
edebceff | 180 | |
181 | return error; | |
182 | } | |
62d38a1d CMN |
183 | |
184 | int git_reset( | |
185 | git_repository *repo, | |
eae0bfdc | 186 | const git_object *target, |
62d38a1d | 187 | git_reset_t reset_type, |
649834fd | 188 | const git_checkout_options *checkout_opts) |
62d38a1d CMN |
189 | { |
190 | return reset(repo, target, git_oid_tostr_s(git_object_id(target)), reset_type, checkout_opts); | |
191 | } | |
192 | ||
193 | int git_reset_from_annotated( | |
194 | git_repository *repo, | |
eae0bfdc | 195 | const git_annotated_commit *commit, |
62d38a1d | 196 | git_reset_t reset_type, |
649834fd | 197 | const git_checkout_options *checkout_opts) |
62d38a1d | 198 | { |
d5592378 | 199 | return reset(repo, (git_object *) commit->commit, commit->description, reset_type, checkout_opts); |
62d38a1d | 200 | } |