2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
13 #include "git2/reset.h"
14 #include "git2/checkout.h"
15 #include "git2/merge.h"
16 #include "git2/refs.h"
18 #define ERROR_MSG "Cannot perform reset"
20 int git_reset_default(
23 git_strarray
* pathspecs
)
25 git_object
*commit
= NULL
;
26 git_tree
*tree
= NULL
;
27 git_diff
*diff
= NULL
;
28 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
30 git_index_entry entry
;
32 git_index
*index
= NULL
;
34 assert(pathspecs
!= NULL
&& pathspecs
->count
> 0);
36 memset(&entry
, 0, sizeof(git_index_entry
));
38 if ((error
= git_repository_index(&index
, repo
)) < 0)
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
);
48 if ((error
= git_object_peel(&commit
, target
, GIT_OBJ_COMMIT
)) < 0 ||
49 (error
= git_commit_tree(&tree
, (git_commit
*)commit
)) < 0)
53 opts
.pathspec
= *pathspecs
;
54 opts
.flags
= GIT_DIFF_REVERSE
;
56 if ((error
= git_diff_tree_to_index(
57 &diff
, repo
, tree
, index
, &opts
)) < 0)
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
);
63 if ((error
= git_index_conflict_remove(index
, delta
->old_file
.path
)) < 0)
66 assert(delta
->status
== GIT_DELTA_ADDED
||
67 delta
->status
== GIT_DELTA_MODIFIED
||
68 delta
->status
== GIT_DELTA_DELETED
);
70 if (delta
->status
== GIT_DELTA_DELETED
) {
71 if ((error
= git_index_remove(index
, delta
->old_file
.path
, 0)) < 0)
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
;
78 if ((error
= git_index_add(index
, &entry
)) < 0)
83 error
= git_index_write(index
);
86 git_object_free(commit
);
88 git_index_free(index
);
97 git_reset_t reset_type
,
98 git_signature
*signature
,
99 const char *log_message
)
101 git_object
*commit
= NULL
;
102 git_index
*index
= NULL
;
103 git_tree
*tree
= NULL
;
105 git_checkout_options opts
= GIT_CHECKOUT_OPTIONS_INIT
;
106 git_buf log_message_buf
= GIT_BUF_INIT
;
108 assert(repo
&& target
);
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
);
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)
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)
126 if (reset_type
== GIT_RESET_SOFT
&&
127 (git_repository_state(repo
) == GIT_REPOSITORY_STATE_MERGE
||
128 git_index_has_conflicts(index
)))
130 giterr_set(GITERR_OBJECT
, "%s (soft) in the middle of a merge.", ERROR_MSG
);
131 error
= GIT_EUNMERGED
;
136 git_buf_sets(&log_message_buf
, log_message
);
138 git_buf_sets(&log_message_buf
, "reset: moving");
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)
145 if (reset_type
== GIT_RESET_HARD
) {
146 /* overwrite working directory with HEAD */
147 opts
.checkout_strategy
= GIT_CHECKOUT_FORCE
| GIT_CHECKOUT_SKIP_UNMERGED
;
149 if ((error
= git_checkout_tree(repo
, (git_object
*)tree
, &opts
)) < 0)
153 if (reset_type
> GIT_RESET_SOFT
) {
154 /* reset index to the target content */
156 if ((error
= git_index_read_tree(index
, tree
)) < 0 ||
157 (error
= git_index_write(index
)) < 0)
160 if ((error
= git_repository_state_cleanup(repo
)) < 0) {
161 giterr_set(GITERR_INDEX
, "%s - failed to clean up merge data", ERROR_MSG
);
167 git_object_free(commit
);
168 git_index_free(index
);