]> git.proxmox.com Git - libgit2.git/blob - src/reset.c
git_checkout_opts -> git_checkout_options
[libgit2.git] / src / reset.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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"
9 #include "commit.h"
10 #include "tag.h"
11 #include "merge.h"
12 #include "diff.h"
13 #include "git2/reset.h"
14 #include "git2/checkout.h"
15 #include "git2/merge.h"
16 #include "git2/refs.h"
17
18 #define ERROR_MSG "Cannot perform reset"
19
20 int git_reset_default(
21 git_repository *repo,
22 git_object *target,
23 git_strarray* pathspecs)
24 {
25 git_object *commit = NULL;
26 git_tree *tree = NULL;
27 git_diff *diff = NULL;
28 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
29 size_t i, max_i;
30 git_index_entry entry;
31 int error;
32 git_index *index = NULL;
33
34 assert(pathspecs != NULL && pathspecs->count > 0);
35
36 memset(&entry, 0, sizeof(git_index_entry));
37
38 if ((error = git_repository_index(&index, repo)) < 0)
39 goto cleanup;
40
41 if (target) {
42 if (git_object_owner(target) != repo) {
43 giterr_set(GITERR_OBJECT,
44 "%s_default - The given target does not belong to this repository.", ERROR_MSG);
45 return -1;
46 }
47
48 if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
49 (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
50 goto cleanup;
51 }
52
53 opts.pathspec = *pathspecs;
54 opts.flags = GIT_DIFF_REVERSE;
55
56 if ((error = git_diff_tree_to_index(
57 &diff, repo, tree, index, &opts)) < 0)
58 goto cleanup;
59
60 for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
61 const git_diff_delta *delta = git_diff_get_delta(diff, i);
62
63 if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0)
64 goto cleanup;
65
66 assert(delta->status == GIT_DELTA_ADDED ||
67 delta->status == GIT_DELTA_MODIFIED ||
68 delta->status == GIT_DELTA_DELETED);
69
70 if (delta->status == GIT_DELTA_DELETED) {
71 if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
72 goto cleanup;
73 } else {
74 entry.mode = delta->new_file.mode;
75 git_oid_cpy(&entry.id, &delta->new_file.id);
76 entry.path = (char *)delta->new_file.path;
77
78 if ((error = git_index_add(index, &entry)) < 0)
79 goto cleanup;
80 }
81 }
82
83 error = git_index_write(index);
84
85 cleanup:
86 git_object_free(commit);
87 git_tree_free(tree);
88 git_index_free(index);
89 git_diff_free(diff);
90
91 return error;
92 }
93
94 int git_reset(
95 git_repository *repo,
96 git_object *target,
97 git_reset_t reset_type,
98 git_signature *signature,
99 const char *log_message)
100 {
101 git_object *commit = NULL;
102 git_index *index = NULL;
103 git_tree *tree = NULL;
104 int error = 0;
105 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
106 git_buf log_message_buf = GIT_BUF_INIT;
107
108 assert(repo && target);
109
110 if (git_object_owner(target) != repo) {
111 giterr_set(GITERR_OBJECT,
112 "%s - The given target does not belong to this repository.", ERROR_MSG);
113 return -1;
114 }
115
116 if (reset_type != GIT_RESET_SOFT &&
117 (error = git_repository__ensure_not_bare(repo,
118 reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0)
119 return error;
120
121 if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
122 (error = git_repository_index(&index, repo)) < 0 ||
123 (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
124 goto cleanup;
125
126 if (reset_type == GIT_RESET_SOFT &&
127 (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
128 git_index_has_conflicts(index)))
129 {
130 giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG);
131 error = GIT_EUNMERGED;
132 goto cleanup;
133 }
134
135 if (log_message)
136 git_buf_sets(&log_message_buf, log_message);
137 else
138 git_buf_sets(&log_message_buf, "reset: moving");
139
140 /* move HEAD to the new target */
141 if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE,
142 git_object_id(commit), signature, git_buf_cstr(&log_message_buf))) < 0)
143 goto cleanup;
144
145 if (reset_type == GIT_RESET_HARD) {
146 /* overwrite working directory with HEAD */
147 opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_SKIP_UNMERGED;
148
149 if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
150 goto cleanup;
151 }
152
153 if (reset_type > GIT_RESET_SOFT) {
154 /* reset index to the target content */
155
156 if ((error = git_index_read_tree(index, tree)) < 0 ||
157 (error = git_index_write(index)) < 0)
158 goto cleanup;
159
160 if ((error = git_repository_state_cleanup(repo)) < 0) {
161 giterr_set(GITERR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
162 goto cleanup;
163 }
164 }
165
166 cleanup:
167 git_object_free(commit);
168 git_index_free(index);
169 git_tree_free(tree);
170
171 return error;
172 }