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"
16 #include "git2/types.h"
17 #include "git2/merge.h"
18 #include "git2/cherrypick.h"
19 #include "git2/commit.h"
20 #include "git2/sys/commit.h"
22 #define GIT_CHERRYPICK_FILE_MODE 0666
24 static int write_cherrypick_head(
26 const char *commit_oidstr
)
28 git_filebuf file
= GIT_FILEBUF_INIT
;
29 git_buf file_path
= GIT_BUF_INIT
;
32 if ((error
= git_buf_joinpath(&file_path
, repo
->gitdir
, GIT_CHERRYPICK_HEAD_FILE
)) >= 0 &&
33 (error
= git_filebuf_open(&file
, file_path
.ptr
, GIT_FILEBUF_CREATE_LEADING_DIRS
, GIT_CHERRYPICK_FILE_MODE
)) >= 0 &&
34 (error
= git_filebuf_printf(&file
, "%s\n", commit_oidstr
)) >= 0)
35 error
= git_filebuf_commit(&file
);
38 git_filebuf_cleanup(&file
);
40 git_buf_dispose(&file_path
);
45 static int write_merge_msg(
47 const char *commit_msg
)
49 git_filebuf file
= GIT_FILEBUF_INIT
;
50 git_buf file_path
= GIT_BUF_INIT
;
53 if ((error
= git_buf_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_CHERRYPICK_FILE_MODE
)) < 0 ||
55 (error
= git_filebuf_printf(&file
, "%s", commit_msg
)) < 0)
58 error
= git_filebuf_commit(&file
);
62 git_filebuf_cleanup(&file
);
64 git_buf_dispose(&file_path
);
69 static int cherrypick_normalize_opts(
71 git_cherrypick_options
*opts
,
72 const git_cherrypick_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_cherrypick_options
));
84 git_cherrypick_options default_opts
= GIT_CHERRYPICK_OPTIONS_INIT
;
85 memcpy(opts
, &default_opts
, sizeof(git_cherrypick_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 cherrypick_state_cleanup(git_repository
*repo
)
102 const char *state_files
[] = { GIT_CHERRYPICK_HEAD_FILE
, GIT_MERGE_MSG_FILE
};
104 return git_repository__cleanup_files(repo
, state_files
, ARRAY_SIZE(state_files
));
107 static int cherrypick_seterr(git_commit
*commit
, const char *fmt
)
109 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
111 git_error_set(GIT_ERROR_CHERRYPICK
, fmt
,
112 git_oid_tostr(commit_oidstr
, GIT_OID_HEXSZ
+ 1, git_commit_id(commit
)));
117 int git_cherrypick_commit(
119 git_repository
*repo
,
120 git_commit
*cherrypick_commit
,
121 git_commit
*our_commit
,
122 unsigned int mainline
,
123 const git_merge_options
*merge_opts
)
125 git_commit
*parent_commit
= NULL
;
126 git_tree
*parent_tree
= NULL
, *our_tree
= NULL
, *cherrypick_tree
= NULL
;
127 int parent
= 0, error
= 0;
130 GIT_ASSERT_ARG(repo
);
131 GIT_ASSERT_ARG(cherrypick_commit
);
132 GIT_ASSERT_ARG(our_commit
);
134 if (git_commit_parentcount(cherrypick_commit
) > 1) {
136 return cherrypick_seterr(cherrypick_commit
,
137 "mainline branch is not specified but %s is a merge commit");
142 return cherrypick_seterr(cherrypick_commit
,
143 "mainline branch specified but %s is not a merge commit");
145 parent
= git_commit_parentcount(cherrypick_commit
);
149 ((error
= git_commit_parent(&parent_commit
, cherrypick_commit
, (parent
- 1))) < 0 ||
150 (error
= git_commit_tree(&parent_tree
, parent_commit
)) < 0))
153 if ((error
= git_commit_tree(&cherrypick_tree
, cherrypick_commit
)) < 0 ||
154 (error
= git_commit_tree(&our_tree
, our_commit
)) < 0)
157 error
= git_merge_trees(out
, repo
, parent_tree
, our_tree
, cherrypick_tree
, merge_opts
);
160 git_tree_free(parent_tree
);
161 git_tree_free(our_tree
);
162 git_tree_free(cherrypick_tree
);
163 git_commit_free(parent_commit
);
169 git_repository
*repo
,
171 const git_cherrypick_options
*given_opts
)
173 git_cherrypick_options opts
;
174 git_reference
*our_ref
= NULL
;
175 git_commit
*our_commit
= NULL
;
176 char commit_oidstr
[GIT_OID_HEXSZ
+ 1];
177 const char *commit_msg
, *commit_summary
;
178 git_buf their_label
= GIT_BUF_INIT
;
179 git_index
*index
= NULL
;
180 git_indexwriter indexwriter
= GIT_INDEXWRITER_INIT
;
183 GIT_ASSERT_ARG(repo
);
184 GIT_ASSERT_ARG(commit
);
186 GIT_ERROR_CHECK_VERSION(given_opts
, GIT_CHERRYPICK_OPTIONS_VERSION
, "git_cherrypick_options");
188 if ((error
= git_repository__ensure_not_bare(repo
, "cherry-pick")) < 0)
191 if ((commit_msg
= git_commit_message(commit
)) == NULL
||
192 (commit_summary
= git_commit_summary(commit
)) == NULL
) {
197 git_oid_nfmt(commit_oidstr
, sizeof(commit_oidstr
), git_commit_id(commit
));
199 if ((error
= write_merge_msg(repo
, commit_msg
)) < 0 ||
200 (error
= git_buf_printf(&their_label
, "%.7s... %s", commit_oidstr
, commit_summary
)) < 0 ||
201 (error
= cherrypick_normalize_opts(repo
, &opts
, given_opts
, git_buf_cstr(&their_label
))) < 0 ||
202 (error
= git_indexwriter_init_for_operation(&indexwriter
, repo
, &opts
.checkout_opts
.checkout_strategy
)) < 0 ||
203 (error
= write_cherrypick_head(repo
, commit_oidstr
)) < 0 ||
204 (error
= git_repository_head(&our_ref
, repo
)) < 0 ||
205 (error
= git_reference_peel((git_object
**)&our_commit
, our_ref
, GIT_OBJECT_COMMIT
)) < 0 ||
206 (error
= git_cherrypick_commit(&index
, repo
, commit
, our_commit
, opts
.mainline
, &opts
.merge_opts
)) < 0 ||
207 (error
= git_merge__check_result(repo
, index
)) < 0 ||
208 (error
= git_merge__append_conflicts_to_merge_msg(repo
, index
)) < 0 ||
209 (error
= git_checkout_index(repo
, index
, &opts
.checkout_opts
)) < 0 ||
210 (error
= git_indexwriter_commit(&indexwriter
)) < 0)
216 cherrypick_state_cleanup(repo
);
219 git_indexwriter_cleanup(&indexwriter
);
220 git_index_free(index
);
221 git_commit_free(our_commit
);
222 git_reference_free(our_ref
);
223 git_buf_dispose(&their_label
);
228 int git_cherrypick_options_init(
229 git_cherrypick_options
*opts
, unsigned int version
)
231 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
232 opts
, version
, git_cherrypick_options
, GIT_CHERRYPICK_OPTIONS_INIT
);
236 #ifndef GIT_DEPRECATE_HARD
237 int git_cherrypick_init_options(
238 git_cherrypick_options
*opts
, unsigned int version
)
240 return git_cherrypick_options_init(opts
, version
);