File Coverage

deps/libgit2/src/libgit2/reset.c
Criterion Covered Total %
statement 67 91 73.6
branch 34 76 44.7
condition n/a
subroutine n/a
pod n/a
total 101 167 60.4


line stmt bran cond sub pod time code
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              
10             #include "commit.h"
11             #include "tag.h"
12             #include "merge.h"
13             #include "diff.h"
14             #include "annotated_commit.h"
15             #include "git2/reset.h"
16             #include "git2/checkout.h"
17             #include "git2/merge.h"
18             #include "git2/refs.h"
19              
20             #define ERROR_MSG "Cannot perform reset"
21              
22 1           int git_reset_default(
23             git_repository *repo,
24             const git_object *target,
25             const git_strarray *pathspecs)
26             {
27 1           git_object *commit = NULL;
28 1           git_tree *tree = NULL;
29 1           git_diff *diff = NULL;
30 1           git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
31             size_t i, max_i;
32             git_index_entry entry;
33             int error;
34 1           git_index *index = NULL;
35              
36 1 50         GIT_ASSERT_ARG(pathspecs && pathspecs->count > 0);
    50          
37              
38 1           memset(&entry, 0, sizeof(git_index_entry));
39              
40 1 50         if ((error = git_repository_index(&index, repo)) < 0)
41 0           goto cleanup;
42              
43 1 50         if (target) {
44 1 50         if (git_object_owner(target) != repo) {
45 0           git_error_set(GIT_ERROR_OBJECT,
46             "%s_default - The given target does not belong to this repository.", ERROR_MSG);
47 0           return -1;
48             }
49              
50 1 50         if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 ||
    50          
51 1           (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
52             goto cleanup;
53             }
54              
55 1           opts.pathspec = *pathspecs;
56 1           opts.flags = GIT_DIFF_REVERSE;
57              
58 1 50         if ((error = git_diff_tree_to_index(
59             &diff, repo, tree, index, &opts)) < 0)
60 0           goto cleanup;
61              
62 2 100         for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
63 1           const git_diff_delta *delta = git_diff_get_delta(diff, i);
64              
65 1 50         GIT_ASSERT(delta->status == GIT_DELTA_ADDED ||
    50          
    0          
    0          
66             delta->status == GIT_DELTA_MODIFIED ||
67             delta->status == GIT_DELTA_CONFLICTED ||
68             delta->status == GIT_DELTA_DELETED);
69              
70 1           error = git_index_conflict_remove(index, delta->old_file.path);
71 1 50         if (error < 0) {
72 0 0         if (delta->status == GIT_DELTA_ADDED && error == GIT_ENOTFOUND)
    0          
73 0           git_error_clear();
74             else
75             goto cleanup;
76             }
77              
78 1 50         if (delta->status == GIT_DELTA_DELETED) {
79 0 0         if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
80 0           goto cleanup;
81             } else {
82 1           entry.mode = delta->new_file.mode;
83 1           git_oid_cpy(&entry.id, &delta->new_file.id);
84 1           entry.path = (char *)delta->new_file.path;
85              
86 1 50         if ((error = git_index_add(index, &entry)) < 0)
87 0           goto cleanup;
88             }
89             }
90              
91 1           error = git_index_write(index);
92              
93             cleanup:
94 1           git_object_free(commit);
95 1           git_tree_free(tree);
96 1           git_index_free(index);
97 1           git_diff_free(diff);
98              
99 1           return error;
100             }
101              
102 2           static int reset(
103             git_repository *repo,
104             const git_object *target,
105             const char *to,
106             git_reset_t reset_type,
107             const git_checkout_options *checkout_opts)
108             {
109 2           git_object *commit = NULL;
110 2           git_index *index = NULL;
111 2           git_tree *tree = NULL;
112 2           int error = 0;
113 2           git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
114 2           git_str log_message = GIT_STR_INIT;
115              
116 2 50         GIT_ASSERT_ARG(repo);
117 2 50         GIT_ASSERT_ARG(target);
118              
119 2 100         if (checkout_opts)
120 1           opts = *checkout_opts;
121              
122 2 50         if (git_object_owner(target) != repo) {
123 0           git_error_set(GIT_ERROR_OBJECT,
124             "%s - The given target does not belong to this repository.", ERROR_MSG);
125 0           return -1;
126             }
127              
128 4 50         if (reset_type != GIT_RESET_SOFT &&
    50          
129 2 50         (error = git_repository__ensure_not_bare(repo,
130             reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0)
131 0           return error;
132              
133 2 50         if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 ||
    50          
134 2 50         (error = git_repository_index(&index, repo)) < 0 ||
135 2           (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
136             goto cleanup;
137              
138 2           if (reset_type == GIT_RESET_SOFT &&
139 0 0         (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
140 0           git_index_has_conflicts(index)))
141             {
142 0           git_error_set(GIT_ERROR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG);
143 0           error = GIT_EUNMERGED;
144 0           goto cleanup;
145             }
146              
147 2 50         if ((error = git_str_printf(&log_message, "reset: moving to %s", to)) < 0)
148 0           return error;
149              
150 2 50         if (reset_type == GIT_RESET_HARD) {
151             /* overwrite working directory with the new tree */
152 2           opts.checkout_strategy = GIT_CHECKOUT_FORCE;
153              
154 2 50         if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
155 0           goto cleanup;
156             }
157              
158             /* move HEAD to the new target */
159 2 50         if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE,
160             git_object_id(commit), NULL, git_str_cstr(&log_message))) < 0)
161 0           goto cleanup;
162              
163 2 50         if (reset_type > GIT_RESET_SOFT) {
164             /* reset index to the target content */
165              
166 2 50         if ((error = git_index_read_tree(index, tree)) < 0 ||
    50          
167 2           (error = git_index_write(index)) < 0)
168             goto cleanup;
169              
170 2 50         if ((error = git_repository_state_cleanup(repo)) < 0) {
171 0           git_error_set(GIT_ERROR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
172 0           goto cleanup;
173             }
174             }
175              
176             cleanup:
177 2           git_object_free(commit);
178 2           git_index_free(index);
179 2           git_tree_free(tree);
180 2           git_str_dispose(&log_message);
181              
182 2           return error;
183             }
184              
185 2           int git_reset(
186             git_repository *repo,
187             const git_object *target,
188             git_reset_t reset_type,
189             const git_checkout_options *checkout_opts)
190             {
191             char to[GIT_OID_HEXSZ + 1];
192              
193 2           git_oid_tostr(to, GIT_OID_HEXSZ + 1, git_object_id(target));
194 2           return reset(repo, target, to, reset_type, checkout_opts);
195             }
196              
197 0           int git_reset_from_annotated(
198             git_repository *repo,
199             const git_annotated_commit *commit,
200             git_reset_t reset_type,
201             const git_checkout_options *checkout_opts)
202             {
203 0           return reset(repo, (git_object *) commit->commit, commit->description, reset_type, checkout_opts);
204             }