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.
9 #include "repository.h"
14 #include "git2/types.h"
15 #include "git2/merge.h"
16 #include "git2/revert.h"
17 #include "git2/commit.h"
18 #include "git2/sys/commit.h"
20 #define GIT_REVERT_FILE_MODE 0666
22 static int write_revert_head(
24 const char *commit_oidstr
)
26 git_filebuf file
= GIT_FILEBUF_INIT
;
27 git_buf file_path
= GIT_BUF_INIT
;
30 if ((error
= git_buf_joinpath(&file_path
, repo
->path_repository
, GIT_REVERT_HEAD_FILE
)) >= 0 &&
31 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_FORCE
, GIT_REVERT_FILE_MODE
)) >= 0 &&
32 (error
= git_filebuf_printf(&file
, "%s\n", commit_oidstr
)) >= 0)
33 error
= git_filebuf_commit(&file
);
36 git_filebuf_cleanup(&file
);
38 git_buf_free(&file_path
);
43 static int write_merge_msg(
45 const char *commit_oidstr
,
46 const char *commit_msgline
)
48 git_filebuf file
= GIT_FILEBUF_INIT
;
49 git_buf file_path
= GIT_BUF_INIT
;
52 if ((error
= git_buf_joinpath(&file_path
, repo
->path_repository
, GIT_MERGE_MSG_FILE
)) < 0 ||
53 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_FORCE
, GIT_REVERT_FILE_MODE
)) < 0 ||
54 (error
= git_filebuf_printf(&file
, "Revert \"%s\"\n\nThis reverts commit %s.\n",
55 commit_msgline
, commit_oidstr
)) < 0)
58 error
= git_filebuf_commit(&file
);
62 git_filebuf_cleanup(&file
);
64 git_buf_free(&file_path
);
69 static int revert_normalize_opts(
71 git_revert_options
*opts
,
72 const git_revert_options
*given
,
73 const char *their_label
)
76 unsigned int default_checkout_strategy
= GIT_CHECKOUT_SAFE
|
77 GIT_CHECKOUT_ALLOW_CONFLICTS
;
82 memcpy(opts
, given
, sizeof(git_revert_options
));
84 git_revert_options default_opts
= GIT_REVERT_OPTIONS_INIT
;
85 memcpy(opts
, &default_opts
, sizeof(git_revert_options
));
88 if (!opts
->checkout_opts
.checkout_strategy
)
89 opts
->checkout_opts
.checkout_strategy
= default_checkout_strategy
;
91 if (!opts
->checkout_opts
.our_label
)
92 opts
->checkout_opts
.our_label
= "HEAD";
94 if (!opts
->checkout_opts
.their_label
)
95 opts
->checkout_opts
.their_label
= their_label
;
100 static int revert_state_cleanup(git_repository
*repo
)
102 const char *state_files
[] = { GIT_REVERT_HEAD_FILE
, GIT_MERGE_MSG_FILE
};
104 return git_repository__cleanup_files(repo
, state_files
, ARRAY_SIZE(state_files
));
107 static int revert_seterr(git_commit
*commit
, const char *fmt
)
109 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
111 git_oid_fmt(commit_oidstr
, git_commit_id(commit
));
112 commit_oidstr
[GIT_OID_HEXSZ
] = '\0';
114 giterr_set(GITERR_REVERT
, fmt
, commit_oidstr
);
119 int git_revert_commit(
121 git_repository
*repo
,
122 git_commit
*revert_commit
,
123 git_commit
*our_commit
,
124 unsigned int mainline
,
125 const git_merge_options
*merge_opts
)
127 git_commit
*parent_commit
= NULL
;
128 git_tree
*parent_tree
= NULL
, *our_tree
= NULL
, *revert_tree
= NULL
;
129 int parent
= 0, error
= 0;
131 assert(out
&& repo
&& revert_commit
&& our_commit
);
133 if (git_commit_parentcount(revert_commit
) > 1) {
135 return revert_seterr(revert_commit
,
136 "Mainline branch is not specified but %s is a merge commit");
141 return revert_seterr(revert_commit
,
142 "Mainline branch specified but %s is not a merge commit");
144 parent
= git_commit_parentcount(revert_commit
);
148 ((error
= git_commit_parent(&parent_commit
, revert_commit
, (parent
- 1))) < 0 ||
149 (error
= git_commit_tree(&parent_tree
, parent_commit
)) < 0))
152 if ((error
= git_commit_tree(&revert_tree
, revert_commit
)) < 0 ||
153 (error
= git_commit_tree(&our_tree
, our_commit
)) < 0)
156 error
= git_merge_trees(out
, repo
, revert_tree
, our_tree
, parent_tree
, merge_opts
);
159 git_tree_free(parent_tree
);
160 git_tree_free(our_tree
);
161 git_tree_free(revert_tree
);
162 git_commit_free(parent_commit
);
168 git_repository
*repo
,
170 const git_revert_options
*given_opts
)
172 git_revert_options opts
;
173 git_reference
*our_ref
= NULL
;
174 git_commit
*our_commit
= NULL
;
175 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
176 const char *commit_msg
;
177 git_buf their_label
= GIT_BUF_INIT
;
178 git_index
*index
= NULL
;
179 git_indexwriter indexwriter
= GIT_INDEXWRITER_INIT
;
182 assert(repo
&& commit
);
184 GITERR_CHECK_VERSION(given_opts
, GIT_REVERT_OPTIONS_VERSION
, "git_revert_options");
186 if ((error
= git_repository__ensure_not_bare(repo
, "revert")) < 0)
189 git_oid_fmt(commit_oidstr
, git_commit_id(commit
));
190 commit_oidstr
[GIT_OID_HEXSZ
] = '\0';
192 if ((commit_msg
= git_commit_summary(commit
)) == NULL
) {
197 if ((error
= git_buf_printf(&their_label
, "parent of %.7s... %s", commit_oidstr
, commit_msg
)) < 0 ||
198 (error
= revert_normalize_opts(repo
, &opts
, given_opts
, git_buf_cstr(&their_label
))) < 0 ||
199 (error
= git_indexwriter_init_for_operation(&indexwriter
, repo
, &opts
.checkout_opts
.checkout_strategy
)) < 0 ||
200 (error
= write_revert_head(repo
, commit_oidstr
)) < 0 ||
201 (error
= write_merge_msg(repo
, commit_oidstr
, commit_msg
)) < 0 ||
202 (error
= git_repository_head(&our_ref
, repo
)) < 0 ||
203 (error
= git_reference_peel((git_object
**)&our_commit
, our_ref
, GIT_OBJ_COMMIT
)) < 0 ||
204 (error
= git_revert_commit(&index
, repo
, commit
, our_commit
, opts
.mainline
, &opts
.merge_opts
)) < 0 ||
205 (error
= git_merge__check_result(repo
, index
)) < 0 ||
206 (error
= git_merge__append_conflicts_to_merge_msg(repo
, index
)) < 0 ||
207 (error
= git_checkout_index(repo
, index
, &opts
.checkout_opts
)) < 0 ||
208 (error
= git_indexwriter_commit(&indexwriter
)) < 0)
214 revert_state_cleanup(repo
);
217 git_indexwriter_cleanup(&indexwriter
);
218 git_index_free(index
);
219 git_commit_free(our_commit
);
220 git_reference_free(our_ref
);
221 git_buf_free(&their_label
);
226 int git_revert_init_options(git_revert_options
*opts
, unsigned int version
)
228 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
229 opts
, version
, git_revert_options
, GIT_REVERT_OPTIONS_INIT
);