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"
13 #include "git2/types.h"
14 #include "git2/merge.h"
15 #include "git2/revert.h"
16 #include "git2/commit.h"
17 #include "git2/sys/commit.h"
19 #define GIT_REVERT_FILE_MODE 0666
21 static int write_revert_head(
23 const char *commit_oidstr
)
25 git_filebuf file
= GIT_FILEBUF_INIT
;
26 git_buf file_path
= GIT_BUF_INIT
;
29 if ((error
= git_buf_joinpath(&file_path
, repo
->path_repository
, GIT_REVERT_HEAD_FILE
)) >= 0 &&
30 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_FORCE
, GIT_REVERT_FILE_MODE
)) >= 0 &&
31 (error
= git_filebuf_printf(&file
, "%s\n", commit_oidstr
)) >= 0)
32 error
= git_filebuf_commit(&file
);
35 git_filebuf_cleanup(&file
);
37 git_buf_free(&file_path
);
42 static int write_merge_msg(
44 const char *commit_oidstr
,
45 const char *commit_msgline
)
47 git_filebuf file
= GIT_FILEBUF_INIT
;
48 git_buf file_path
= GIT_BUF_INIT
;
51 if ((error
= git_buf_joinpath(&file_path
, repo
->path_repository
, GIT_MERGE_MSG_FILE
)) < 0 ||
52 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_FORCE
, GIT_REVERT_FILE_MODE
)) < 0 ||
53 (error
= git_filebuf_printf(&file
, "Revert \"%s\"\n\nThis reverts commit %s.\n",
54 commit_msgline
, commit_oidstr
)) < 0)
57 error
= git_filebuf_commit(&file
);
61 git_filebuf_cleanup(&file
);
63 git_buf_free(&file_path
);
68 static int revert_normalize_opts(
70 git_revert_options
*opts
,
71 const git_revert_options
*given
,
72 const char *their_label
)
75 unsigned int default_checkout_strategy
= GIT_CHECKOUT_SAFE_CREATE
|
76 GIT_CHECKOUT_ALLOW_CONFLICTS
;
81 memcpy(opts
, given
, sizeof(git_revert_options
));
83 git_revert_options default_opts
= GIT_REVERT_OPTIONS_INIT
;
84 memcpy(opts
, &default_opts
, sizeof(git_revert_options
));
87 if (!opts
->checkout_opts
.checkout_strategy
)
88 opts
->checkout_opts
.checkout_strategy
= default_checkout_strategy
;
90 if (!opts
->checkout_opts
.our_label
)
91 opts
->checkout_opts
.our_label
= "HEAD";
93 if (!opts
->checkout_opts
.their_label
)
94 opts
->checkout_opts
.their_label
= their_label
;
99 static int revert_state_cleanup(git_repository
*repo
)
101 const char *state_files
[] = { GIT_REVERT_HEAD_FILE
, GIT_MERGE_MSG_FILE
};
103 return git_repository__cleanup_files(repo
, state_files
, ARRAY_SIZE(state_files
));
106 static int revert_seterr(git_commit
*commit
, const char *fmt
)
108 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
110 git_oid_fmt(commit_oidstr
, git_commit_id(commit
));
111 commit_oidstr
[GIT_OID_HEXSZ
] = '\0';
113 giterr_set(GITERR_REVERT
, fmt
, commit_oidstr
);
118 int git_revert_commit(
120 git_repository
*repo
,
121 git_commit
*revert_commit
,
122 git_commit
*our_commit
,
123 unsigned int mainline
,
124 const git_merge_options
*merge_opts
)
126 git_commit
*parent_commit
= NULL
;
127 git_tree
*parent_tree
= NULL
, *our_tree
= NULL
, *revert_tree
= NULL
;
128 int parent
= 0, error
= 0;
130 assert(out
&& repo
&& revert_commit
&& our_commit
);
132 if (git_commit_parentcount(revert_commit
) > 1) {
134 return revert_seterr(revert_commit
,
135 "Mainline branch is not specified but %s is a merge commit");
140 return revert_seterr(revert_commit
,
141 "Mainline branch specified but %s is not a merge commit");
143 parent
= git_commit_parentcount(revert_commit
);
147 ((error
= git_commit_parent(&parent_commit
, revert_commit
, (parent
- 1))) < 0 ||
148 (error
= git_commit_tree(&parent_tree
, parent_commit
)) < 0))
151 if ((error
= git_commit_tree(&revert_tree
, revert_commit
)) < 0 ||
152 (error
= git_commit_tree(&our_tree
, our_commit
)) < 0)
155 error
= git_merge_trees(out
, repo
, revert_tree
, our_tree
, parent_tree
, merge_opts
);
158 git_tree_free(parent_tree
);
159 git_tree_free(our_tree
);
160 git_tree_free(revert_tree
);
161 git_commit_free(parent_commit
);
167 git_repository
*repo
,
169 const git_revert_options
*given_opts
)
171 git_revert_options opts
;
172 git_reference
*our_ref
= NULL
;
173 git_commit
*our_commit
= NULL
;
174 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
175 const char *commit_msg
;
176 git_buf their_label
= GIT_BUF_INIT
;
177 git_index
*index_new
= NULL
;
180 assert(repo
&& commit
);
182 GITERR_CHECK_VERSION(given_opts
, GIT_REVERT_OPTIONS_VERSION
, "git_revert_options");
184 if ((error
= git_repository__ensure_not_bare(repo
, "revert")) < 0)
187 git_oid_fmt(commit_oidstr
, git_commit_id(commit
));
188 commit_oidstr
[GIT_OID_HEXSZ
] = '\0';
190 if ((commit_msg
= git_commit_summary(commit
)) == NULL
) {
195 if ((error
= git_buf_printf(&their_label
, "parent of %.7s... %s", commit_oidstr
, commit_msg
)) < 0 ||
196 (error
= revert_normalize_opts(repo
, &opts
, given_opts
, git_buf_cstr(&their_label
))) < 0 ||
197 (error
= write_revert_head(repo
, commit_oidstr
)) < 0 ||
198 (error
= write_merge_msg(repo
, commit_oidstr
, commit_msg
)) < 0 ||
199 (error
= git_repository_head(&our_ref
, repo
)) < 0 ||
200 (error
= git_reference_peel((git_object
**)&our_commit
, our_ref
, GIT_OBJ_COMMIT
)) < 0 ||
201 (error
= git_revert_commit(&index_new
, repo
, commit
, our_commit
, opts
.mainline
, &opts
.merge_opts
)) < 0 ||
202 (error
= git_merge__check_result(repo
, index_new
)) < 0 ||
203 (error
= git_merge__append_conflicts_to_merge_msg(repo
, index_new
)) < 0 ||
204 (error
= git_checkout_index(repo
, index_new
, &opts
.checkout_opts
)) < 0)
210 revert_state_cleanup(repo
);
213 git_index_free(index_new
);
214 git_commit_free(our_commit
);
215 git_reference_free(our_ref
);
216 git_buf_free(&their_label
);
221 int git_revert_init_options(git_revert_options
*opts
, unsigned int version
)
223 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
224 opts
, version
, git_revert_options
, GIT_REVERT_OPTIONS_INIT
);