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.
10 #include "repository.h"
15 #include "git2/types.h"
16 #include "git2/merge.h"
17 #include "git2/revert.h"
18 #include "git2/commit.h"
19 #include "git2/sys/commit.h"
21 #define GIT_REVERT_FILE_MODE 0666
23 static int write_revert_head(
25 const char *commit_oidstr
)
27 git_filebuf file
= GIT_FILEBUF_INIT
;
28 git_str file_path
= GIT_STR_INIT
;
31 if ((error
= git_str_joinpath(&file_path
, repo
->gitdir
, GIT_REVERT_HEAD_FILE
)) >= 0 &&
32 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_CREATE_LEADING_DIRS
, GIT_REVERT_FILE_MODE
)) >= 0 &&
33 (error
= git_filebuf_printf(&file
, "%s\n", commit_oidstr
)) >= 0)
34 error
= git_filebuf_commit(&file
);
37 git_filebuf_cleanup(&file
);
39 git_str_dispose(&file_path
);
44 static int write_merge_msg(
46 const char *commit_oidstr
,
47 const char *commit_msgline
)
49 git_filebuf file
= GIT_FILEBUF_INIT
;
50 git_str file_path
= GIT_STR_INIT
;
53 if ((error
= git_str_joinpath(&file_path
, repo
->gitdir
, GIT_MERGE_MSG_FILE
)) < 0 ||
54 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_CREATE_LEADING_DIRS
, GIT_REVERT_FILE_MODE
)) < 0 ||
55 (error
= git_filebuf_printf(&file
, "Revert \"%s\"\n\nThis reverts commit %s.\n",
56 commit_msgline
, commit_oidstr
)) < 0)
59 error
= git_filebuf_commit(&file
);
63 git_filebuf_cleanup(&file
);
65 git_str_dispose(&file_path
);
70 static int revert_normalize_opts(
72 git_revert_options
*opts
,
73 const git_revert_options
*given
,
74 const char *their_label
)
77 unsigned int default_checkout_strategy
= GIT_CHECKOUT_SAFE
|
78 GIT_CHECKOUT_ALLOW_CONFLICTS
;
83 memcpy(opts
, given
, sizeof(git_revert_options
));
85 git_revert_options default_opts
= GIT_REVERT_OPTIONS_INIT
;
86 memcpy(opts
, &default_opts
, sizeof(git_revert_options
));
89 if (!opts
->checkout_opts
.checkout_strategy
)
90 opts
->checkout_opts
.checkout_strategy
= default_checkout_strategy
;
92 if (!opts
->checkout_opts
.our_label
)
93 opts
->checkout_opts
.our_label
= "HEAD";
95 if (!opts
->checkout_opts
.their_label
)
96 opts
->checkout_opts
.their_label
= their_label
;
101 static int revert_state_cleanup(git_repository
*repo
)
103 const char *state_files
[] = { GIT_REVERT_HEAD_FILE
, GIT_MERGE_MSG_FILE
};
105 return git_repository__cleanup_files(repo
, state_files
, ARRAY_SIZE(state_files
));
108 static int revert_seterr(git_commit
*commit
, const char *fmt
)
110 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
112 git_oid_fmt(commit_oidstr
, git_commit_id(commit
));
113 commit_oidstr
[GIT_OID_HEXSZ
] = '\0';
115 git_error_set(GIT_ERROR_REVERT
, fmt
, commit_oidstr
);
120 int git_revert_commit(
122 git_repository
*repo
,
123 git_commit
*revert_commit
,
124 git_commit
*our_commit
,
125 unsigned int mainline
,
126 const git_merge_options
*merge_opts
)
128 git_commit
*parent_commit
= NULL
;
129 git_tree
*parent_tree
= NULL
, *our_tree
= NULL
, *revert_tree
= NULL
;
130 int parent
= 0, error
= 0;
133 GIT_ASSERT_ARG(repo
);
134 GIT_ASSERT_ARG(revert_commit
);
135 GIT_ASSERT_ARG(our_commit
);
137 if (git_commit_parentcount(revert_commit
) > 1) {
139 return revert_seterr(revert_commit
,
140 "mainline branch is not specified but %s is a merge commit");
145 return revert_seterr(revert_commit
,
146 "mainline branch specified but %s is not a merge commit");
148 parent
= git_commit_parentcount(revert_commit
);
152 ((error
= git_commit_parent(&parent_commit
, revert_commit
, (parent
- 1))) < 0 ||
153 (error
= git_commit_tree(&parent_tree
, parent_commit
)) < 0))
156 if ((error
= git_commit_tree(&revert_tree
, revert_commit
)) < 0 ||
157 (error
= git_commit_tree(&our_tree
, our_commit
)) < 0)
160 error
= git_merge_trees(out
, repo
, revert_tree
, our_tree
, parent_tree
, merge_opts
);
163 git_tree_free(parent_tree
);
164 git_tree_free(our_tree
);
165 git_tree_free(revert_tree
);
166 git_commit_free(parent_commit
);
172 git_repository
*repo
,
174 const git_revert_options
*given_opts
)
176 git_revert_options opts
;
177 git_reference
*our_ref
= NULL
;
178 git_commit
*our_commit
= NULL
;
179 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
180 const char *commit_msg
;
181 git_str their_label
= GIT_STR_INIT
;
182 git_index
*index
= NULL
;
183 git_indexwriter indexwriter
= GIT_INDEXWRITER_INIT
;
186 GIT_ASSERT_ARG(repo
);
187 GIT_ASSERT_ARG(commit
);
189 GIT_ERROR_CHECK_VERSION(given_opts
, GIT_REVERT_OPTIONS_VERSION
, "git_revert_options");
191 if ((error
= git_repository__ensure_not_bare(repo
, "revert")) < 0)
194 git_oid_fmt(commit_oidstr
, git_commit_id(commit
));
195 commit_oidstr
[GIT_OID_HEXSZ
] = '\0';
197 if ((commit_msg
= git_commit_summary(commit
)) == NULL
) {
202 if ((error
= git_str_printf(&their_label
, "parent of %.7s... %s", commit_oidstr
, commit_msg
)) < 0 ||
203 (error
= revert_normalize_opts(repo
, &opts
, given_opts
, git_str_cstr(&their_label
))) < 0 ||
204 (error
= git_indexwriter_init_for_operation(&indexwriter
, repo
, &opts
.checkout_opts
.checkout_strategy
)) < 0 ||
205 (error
= write_revert_head(repo
, commit_oidstr
)) < 0 ||
206 (error
= write_merge_msg(repo
, commit_oidstr
, commit_msg
)) < 0 ||
207 (error
= git_repository_head(&our_ref
, repo
)) < 0 ||
208 (error
= git_reference_peel((git_object
**)&our_commit
, our_ref
, GIT_OBJECT_COMMIT
)) < 0 ||
209 (error
= git_revert_commit(&index
, repo
, commit
, our_commit
, opts
.mainline
, &opts
.merge_opts
)) < 0 ||
210 (error
= git_merge__check_result(repo
, index
)) < 0 ||
211 (error
= git_merge__append_conflicts_to_merge_msg(repo
, index
)) < 0 ||
212 (error
= git_checkout_index(repo
, index
, &opts
.checkout_opts
)) < 0 ||
213 (error
= git_indexwriter_commit(&indexwriter
)) < 0)
219 revert_state_cleanup(repo
);
222 git_indexwriter_cleanup(&indexwriter
);
223 git_index_free(index
);
224 git_commit_free(our_commit
);
225 git_reference_free(our_ref
);
226 git_str_dispose(&their_label
);
231 int git_revert_options_init(git_revert_options
*opts
, unsigned int version
)
233 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
234 opts
, version
, git_revert_options
, GIT_REVERT_OPTIONS_INIT
);
238 #ifndef GIT_DEPRECATE_HARD
239 int git_revert_init_options(git_revert_options
*opts
, unsigned int version
)
241 return git_revert_options_init(opts
, version
);