]>
Commit | Line | Data |
---|---|---|
867a36f3 ET |
1 | /* |
2 | * Copyright (C) the libgit2 contributors. All rights reserved. | |
3 | * | |
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. | |
6 | */ | |
7 | ||
8 | #include "common.h" | |
eae0bfdc | 9 | |
867a36f3 ET |
10 | #include "buffer.h" |
11 | #include "repository.h" | |
12 | #include "posix.h" | |
13 | #include "filebuf.h" | |
14 | #include "merge.h" | |
15 | #include "array.h" | |
5ae9d296 | 16 | #include "config.h" |
18b00406 | 17 | #include "annotated_commit.h" |
be8404a7 | 18 | #include "index.h" |
867a36f3 ET |
19 | |
20 | #include <git2/types.h> | |
18b00406 | 21 | #include <git2/annotated_commit.h> |
867a36f3 ET |
22 | #include <git2/rebase.h> |
23 | #include <git2/commit.h> | |
24 | #include <git2/reset.h> | |
25 | #include <git2/revwalk.h> | |
5ae9d296 | 26 | #include <git2/notes.h> |
867a36f3 | 27 | |
979645a7 ET |
28 | #define REBASE_APPLY_DIR "rebase-apply" |
29 | #define REBASE_MERGE_DIR "rebase-merge" | |
867a36f3 | 30 | |
979645a7 ET |
31 | #define HEAD_NAME_FILE "head-name" |
32 | #define ORIG_HEAD_FILE "orig-head" | |
33 | #define HEAD_FILE "head" | |
34 | #define ONTO_FILE "onto" | |
35 | #define ONTO_NAME_FILE "onto_name" | |
36 | #define QUIET_FILE "quiet" | |
867a36f3 | 37 | |
979645a7 ET |
38 | #define MSGNUM_FILE "msgnum" |
39 | #define END_FILE "end" | |
40 | #define CMT_FILE_FMT "cmt.%" PRIuZ | |
41 | #define CURRENT_FILE "current" | |
42 | #define REWRITTEN_FILE "rewritten" | |
867a36f3 | 43 | |
979645a7 | 44 | #define ORIG_DETACHED_HEAD "detached HEAD" |
867a36f3 | 45 | |
979645a7 | 46 | #define NOTES_DEFAULT_REF NULL |
5ae9d296 | 47 | |
979645a7 ET |
48 | #define REBASE_DIR_MODE 0777 |
49 | #define REBASE_FILE_MODE 0666 | |
867a36f3 ET |
50 | |
51 | typedef enum { | |
22a2d3d5 UG |
52 | GIT_REBASE_NONE = 0, |
53 | GIT_REBASE_APPLY = 1, | |
54 | GIT_REBASE_MERGE = 2, | |
55 | GIT_REBASE_INTERACTIVE = 3, | |
56 | } git_rebase_t; | |
867a36f3 | 57 | |
b6b636a7 ET |
58 | struct git_rebase { |
59 | git_repository *repo; | |
60 | ||
f3a199dd | 61 | git_rebase_options options; |
f3a199dd | 62 | |
22a2d3d5 | 63 | git_rebase_t type; |
4fe84d62 ET |
64 | char *state_path; |
65 | ||
b6b636a7 | 66 | int head_detached : 1, |
ee667307 | 67 | inmemory : 1, |
f152f8ac ET |
68 | quiet : 1, |
69 | started : 1; | |
4fe84d62 | 70 | |
ee667307 ET |
71 | git_array_t(git_rebase_operation) operations; |
72 | size_t current; | |
73 | ||
74 | /* Used by in-memory rebase */ | |
f28bae0c | 75 | git_index *index; |
ee667307 | 76 | git_commit *last_commit; |
ee667307 ET |
77 | |
78 | /* Used by regular (not in-memory) merge-style rebase */ | |
4fe84d62 | 79 | git_oid orig_head_id; |
ee667307 | 80 | char *orig_head_name; |
950a7091 | 81 | |
517644cc | 82 | git_oid onto_id; |
f152f8ac | 83 | char *onto_name; |
b6b636a7 | 84 | }; |
4fe84d62 ET |
85 | |
86 | #define GIT_REBASE_STATE_INIT {0} | |
87 | ||
867a36f3 | 88 | static int rebase_state_type( |
22a2d3d5 | 89 | git_rebase_t *type_out, |
867a36f3 ET |
90 | char **path_out, |
91 | git_repository *repo) | |
92 | { | |
93 | git_buf path = GIT_BUF_INIT; | |
22a2d3d5 | 94 | git_rebase_t type = GIT_REBASE_NONE; |
867a36f3 | 95 | |
84f56cb0 | 96 | if (git_buf_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0) |
867a36f3 ET |
97 | return -1; |
98 | ||
99 | if (git_path_isdir(git_buf_cstr(&path))) { | |
22a2d3d5 | 100 | type = GIT_REBASE_APPLY; |
867a36f3 ET |
101 | goto done; |
102 | } | |
103 | ||
104 | git_buf_clear(&path); | |
84f56cb0 | 105 | if (git_buf_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0) |
867a36f3 ET |
106 | return -1; |
107 | ||
108 | if (git_path_isdir(git_buf_cstr(&path))) { | |
22a2d3d5 | 109 | type = GIT_REBASE_MERGE; |
867a36f3 ET |
110 | goto done; |
111 | } | |
112 | ||
113 | done: | |
114 | *type_out = type; | |
115 | ||
22a2d3d5 | 116 | if (type != GIT_REBASE_NONE && path_out) |
867a36f3 ET |
117 | *path_out = git_buf_detach(&path); |
118 | ||
ac3d33df | 119 | git_buf_dispose(&path); |
867a36f3 ET |
120 | |
121 | return 0; | |
122 | } | |
123 | ||
796b03bd ET |
124 | GIT_INLINE(int) rebase_readfile( |
125 | git_buf *out, | |
126 | git_buf *state_path, | |
127 | const char *filename) | |
950a7091 | 128 | { |
f152f8ac ET |
129 | size_t state_path_len = state_path->size; |
130 | int error; | |
950a7091 | 131 | |
f152f8ac ET |
132 | git_buf_clear(out); |
133 | ||
134 | if ((error = git_buf_joinpath(state_path, state_path->ptr, filename)) < 0 || | |
135 | (error = git_futils_readbuffer(out, state_path->ptr)) < 0) | |
950a7091 ET |
136 | goto done; |
137 | ||
f152f8ac | 138 | git_buf_rtrim(out); |
950a7091 | 139 | |
f152f8ac ET |
140 | done: |
141 | git_buf_truncate(state_path, state_path_len); | |
142 | return error; | |
143 | } | |
443d5674 | 144 | |
f152f8ac ET |
145 | GIT_INLINE(int) rebase_readint( |
146 | size_t *out, git_buf *asc_out, git_buf *state_path, const char *filename) | |
147 | { | |
148 | int32_t num; | |
796b03bd | 149 | const char *eol; |
f152f8ac | 150 | int error = 0; |
443d5674 | 151 | |
f152f8ac ET |
152 | if ((error = rebase_readfile(asc_out, state_path, filename)) < 0) |
153 | return error; | |
443d5674 | 154 | |
6c7cee42 | 155 | if (git__strntol32(&num, asc_out->ptr, asc_out->size, &eol, 10) < 0 || num < 0 || *eol) { |
ac3d33df | 156 | git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid numeric value", filename); |
f152f8ac ET |
157 | return -1; |
158 | } | |
443d5674 | 159 | |
f152f8ac | 160 | *out = (size_t) num; |
443d5674 | 161 | |
f152f8ac ET |
162 | return 0; |
163 | } | |
443d5674 | 164 | |
f152f8ac ET |
165 | GIT_INLINE(int) rebase_readoid( |
166 | git_oid *out, git_buf *str_out, git_buf *state_path, const char *filename) | |
167 | { | |
168 | int error; | |
443d5674 | 169 | |
f152f8ac ET |
170 | if ((error = rebase_readfile(str_out, state_path, filename)) < 0) |
171 | return error; | |
443d5674 | 172 | |
f152f8ac | 173 | if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) { |
ac3d33df | 174 | git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid object ID", filename); |
f152f8ac ET |
175 | return -1; |
176 | } | |
950a7091 | 177 | |
f152f8ac ET |
178 | return 0; |
179 | } | |
950a7091 | 180 | |
92e87dd7 ET |
181 | static git_rebase_operation *rebase_operation_alloc( |
182 | git_rebase *rebase, | |
183 | git_rebase_operation_t type, | |
184 | git_oid *id, | |
185 | const char *exec) | |
186 | { | |
187 | git_rebase_operation *operation; | |
188 | ||
c25aa7cd PP |
189 | GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !id, NULL); |
190 | GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !!exec, NULL); | |
92e87dd7 ET |
191 | |
192 | if ((operation = git_array_alloc(rebase->operations)) == NULL) | |
193 | return NULL; | |
194 | ||
195 | operation->type = type; | |
196 | git_oid_cpy((git_oid *)&operation->id, id); | |
197 | operation->exec = exec; | |
198 | ||
199 | return operation; | |
200 | } | |
201 | ||
f152f8ac ET |
202 | static int rebase_open_merge(git_rebase *rebase) |
203 | { | |
204 | git_buf state_path = GIT_BUF_INIT, buf = GIT_BUF_INIT, cmt = GIT_BUF_INIT; | |
92e87dd7 | 205 | git_oid id; |
f152f8ac ET |
206 | git_rebase_operation *operation; |
207 | size_t i, msgnum = 0, end; | |
208 | int error; | |
950a7091 | 209 | |
f152f8ac ET |
210 | if ((error = git_buf_puts(&state_path, rebase->state_path)) < 0) |
211 | goto done; | |
950a7091 | 212 | |
f152f8ac ET |
213 | /* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */ |
214 | if ((error = rebase_readint(&msgnum, &buf, &state_path, MSGNUM_FILE)) < 0 && | |
215 | error != GIT_ENOTFOUND) | |
216 | goto done; | |
443d5674 | 217 | |
f152f8ac ET |
218 | if (msgnum) { |
219 | rebase->started = 1; | |
220 | rebase->current = msgnum - 1; | |
221 | } | |
950a7091 | 222 | |
f152f8ac ET |
223 | /* Read 'end' */ |
224 | if ((error = rebase_readint(&end, &buf, &state_path, END_FILE)) < 0) | |
950a7091 ET |
225 | goto done; |
226 | ||
f152f8ac | 227 | /* Read 'current' if it exists */ |
92e87dd7 | 228 | if ((error = rebase_readoid(&id, &buf, &state_path, CURRENT_FILE)) < 0 && |
f152f8ac ET |
229 | error != GIT_ENOTFOUND) |
230 | goto done; | |
231 | ||
232 | /* Read cmt.* */ | |
233 | git_array_init_to_size(rebase->operations, end); | |
ac3d33df | 234 | GIT_ERROR_CHECK_ARRAY(rebase->operations); |
950a7091 | 235 | |
f152f8ac | 236 | for (i = 0; i < end; i++) { |
f152f8ac ET |
237 | git_buf_clear(&cmt); |
238 | ||
239 | if ((error = git_buf_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 || | |
92e87dd7 | 240 | (error = rebase_readoid(&id, &buf, &state_path, cmt.ptr)) < 0) |
443d5674 | 241 | goto done; |
92e87dd7 ET |
242 | |
243 | operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL); | |
ac3d33df | 244 | GIT_ERROR_CHECK_ALLOC(operation); |
443d5674 | 245 | } |
950a7091 | 246 | |
f152f8ac ET |
247 | /* Read 'onto_name' */ |
248 | if ((error = rebase_readfile(&buf, &state_path, ONTO_NAME_FILE)) < 0) | |
249 | goto done; | |
250 | ||
251 | rebase->onto_name = git_buf_detach(&buf); | |
252 | ||
950a7091 | 253 | done: |
ac3d33df JK |
254 | git_buf_dispose(&cmt); |
255 | git_buf_dispose(&state_path); | |
256 | git_buf_dispose(&buf); | |
950a7091 ET |
257 | |
258 | return error; | |
259 | } | |
260 | ||
2afb6fa4 | 261 | static int rebase_alloc(git_rebase **out, const git_rebase_options *rebase_opts) |
f3a199dd ET |
262 | { |
263 | git_rebase *rebase = git__calloc(1, sizeof(git_rebase)); | |
ac3d33df | 264 | GIT_ERROR_CHECK_ALLOC(rebase); |
f3a199dd | 265 | |
2afb6fa4 | 266 | *out = NULL; |
f3a199dd ET |
267 | |
268 | if (rebase_opts) | |
269 | memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options)); | |
270 | else | |
22a2d3d5 | 271 | git_rebase_options_init(&rebase->options, GIT_REBASE_OPTIONS_VERSION); |
f3a199dd | 272 | |
f3a199dd | 273 | if (rebase_opts && rebase_opts->rewrite_notes_ref) { |
2afb6fa4 | 274 | rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref); |
ac3d33df | 275 | GIT_ERROR_CHECK_ALLOC(rebase->options.rewrite_notes_ref); |
f3a199dd ET |
276 | } |
277 | ||
2afb6fa4 PS |
278 | *out = rebase; |
279 | ||
280 | return 0; | |
f3a199dd ET |
281 | } |
282 | ||
283 | static int rebase_check_versions(const git_rebase_options *given_opts) | |
284 | { | |
ac3d33df | 285 | GIT_ERROR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options"); |
f3a199dd ET |
286 | |
287 | if (given_opts) | |
ac3d33df | 288 | GIT_ERROR_CHECK_VERSION(&given_opts->checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options"); |
f3a199dd ET |
289 | |
290 | return 0; | |
291 | } | |
292 | ||
293 | int git_rebase_open( | |
294 | git_rebase **out, | |
295 | git_repository *repo, | |
296 | const git_rebase_options *given_opts) | |
4fe84d62 | 297 | { |
b6b636a7 | 298 | git_rebase *rebase; |
4fe84d62 | 299 | git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT, |
517644cc | 300 | orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT; |
22a2d3d5 UG |
301 | size_t state_path_len; |
302 | int error; | |
4fe84d62 | 303 | |
c25aa7cd | 304 | GIT_ASSERT_ARG(repo); |
b6b636a7 | 305 | |
f3a199dd ET |
306 | if ((error = rebase_check_versions(given_opts)) < 0) |
307 | return error; | |
308 | ||
2afb6fa4 PS |
309 | if (rebase_alloc(&rebase, given_opts) < 0) |
310 | return -1; | |
4fe84d62 | 311 | |
b6b636a7 ET |
312 | rebase->repo = repo; |
313 | ||
314 | if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0) | |
4fe84d62 ET |
315 | goto done; |
316 | ||
22a2d3d5 | 317 | if (rebase->type == GIT_REBASE_NONE) { |
ac3d33df | 318 | git_error_set(GIT_ERROR_REBASE, "there is no rebase in progress"); |
6f73e026 JG |
319 | error = GIT_ENOTFOUND; |
320 | goto done; | |
4fe84d62 ET |
321 | } |
322 | ||
b6b636a7 | 323 | if ((error = git_buf_puts(&path, rebase->state_path)) < 0) |
4fe84d62 ET |
324 | goto done; |
325 | ||
326 | state_path_len = git_buf_len(&path); | |
327 | ||
328 | if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 || | |
329 | (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0) | |
330 | goto done; | |
331 | ||
332 | git_buf_rtrim(&orig_head_name); | |
333 | ||
334 | if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0) | |
b6b636a7 | 335 | rebase->head_detached = 1; |
4fe84d62 ET |
336 | |
337 | git_buf_truncate(&path, state_path_len); | |
338 | ||
339 | if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) | |
340 | goto done; | |
341 | ||
342 | if (!git_path_isfile(path.ptr)) { | |
343 | /* Previous versions of git.git used 'head' here; support that. */ | |
344 | git_buf_truncate(&path, state_path_len); | |
345 | ||
346 | if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0) | |
347 | goto done; | |
348 | } | |
349 | ||
350 | if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0) | |
351 | goto done; | |
352 | ||
353 | git_buf_rtrim(&orig_head_id); | |
354 | ||
b6b636a7 | 355 | if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0) |
4fe84d62 ET |
356 | goto done; |
357 | ||
517644cc ET |
358 | git_buf_truncate(&path, state_path_len); |
359 | ||
360 | if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 || | |
361 | (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0) | |
362 | goto done; | |
363 | ||
364 | git_buf_rtrim(&onto_id); | |
365 | ||
b6b636a7 | 366 | if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0) |
517644cc ET |
367 | goto done; |
368 | ||
b6b636a7 ET |
369 | if (!rebase->head_detached) |
370 | rebase->orig_head_name = git_buf_detach(&orig_head_name); | |
4fe84d62 | 371 | |
b6b636a7 | 372 | switch (rebase->type) { |
22a2d3d5 | 373 | case GIT_REBASE_INTERACTIVE: |
ac3d33df | 374 | git_error_set(GIT_ERROR_REBASE, "interactive rebase is not supported"); |
950a7091 ET |
375 | error = -1; |
376 | break; | |
22a2d3d5 | 377 | case GIT_REBASE_MERGE: |
b6b636a7 | 378 | error = rebase_open_merge(rebase); |
950a7091 | 379 | break; |
22a2d3d5 | 380 | case GIT_REBASE_APPLY: |
ac3d33df | 381 | git_error_set(GIT_ERROR_REBASE, "patch application rebase is not supported"); |
950a7091 ET |
382 | error = -1; |
383 | break; | |
384 | default: | |
385 | abort(); | |
386 | } | |
387 | ||
4fe84d62 | 388 | done: |
b6b636a7 ET |
389 | if (error == 0) |
390 | *out = rebase; | |
391 | else | |
392 | git_rebase_free(rebase); | |
393 | ||
ac3d33df JK |
394 | git_buf_dispose(&path); |
395 | git_buf_dispose(&orig_head_name); | |
396 | git_buf_dispose(&orig_head_id); | |
397 | git_buf_dispose(&onto_id); | |
4fe84d62 ET |
398 | return error; |
399 | } | |
400 | ||
b6b636a7 | 401 | static int rebase_cleanup(git_rebase *rebase) |
4fe84d62 | 402 | { |
ee667307 ET |
403 | if (!rebase || rebase->inmemory) |
404 | return 0; | |
405 | ||
b6b636a7 ET |
406 | return git_path_isdir(rebase->state_path) ? |
407 | git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : | |
4fe84d62 ET |
408 | 0; |
409 | } | |
410 | ||
b6b636a7 | 411 | static int rebase_setupfile(git_rebase *rebase, const char *filename, int flags, const char *fmt, ...) |
867a36f3 ET |
412 | { |
413 | git_buf path = GIT_BUF_INIT, | |
414 | contents = GIT_BUF_INIT; | |
415 | va_list ap; | |
416 | int error; | |
417 | ||
418 | va_start(ap, fmt); | |
419 | git_buf_vprintf(&contents, fmt, ap); | |
420 | va_end(ap); | |
421 | ||
b6b636a7 | 422 | if ((error = git_buf_joinpath(&path, rebase->state_path, filename)) == 0) |
a35a9890 | 423 | error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE); |
867a36f3 | 424 | |
ac3d33df JK |
425 | git_buf_dispose(&path); |
426 | git_buf_dispose(&contents); | |
867a36f3 ET |
427 | |
428 | return error; | |
429 | } | |
430 | ||
18b00406 | 431 | static const char *rebase_onto_name(const git_annotated_commit *onto) |
867a36f3 ET |
432 | { |
433 | if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0) | |
434 | return onto->ref_name + 11; | |
435 | else if (onto->ref_name) | |
436 | return onto->ref_name; | |
437 | else | |
18b00406 | 438 | return onto->id_str; |
867a36f3 ET |
439 | } |
440 | ||
b6b636a7 | 441 | static int rebase_setupfiles_merge(git_rebase *rebase) |
867a36f3 | 442 | { |
867a36f3 | 443 | git_buf commit_filename = GIT_BUF_INIT; |
867a36f3 | 444 | char id_str[GIT_OID_HEXSZ]; |
b6b636a7 ET |
445 | git_rebase_operation *operation; |
446 | size_t i; | |
447 | int error = 0; | |
867a36f3 | 448 | |
5a747e0c ET |
449 | if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || |
450 | (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0) | |
867a36f3 ET |
451 | goto done; |
452 | ||
b6b636a7 ET |
453 | for (i = 0; i < git_array_size(rebase->operations); i++) { |
454 | operation = git_array_get(rebase->operations, i); | |
867a36f3 ET |
455 | |
456 | git_buf_clear(&commit_filename); | |
b6b636a7 ET |
457 | git_buf_printf(&commit_filename, CMT_FILE_FMT, i+1); |
458 | ||
459 | git_oid_fmt(id_str, &operation->id); | |
867a36f3 | 460 | |
5a747e0c | 461 | if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0, |
867a36f3 ET |
462 | "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0) |
463 | goto done; | |
464 | } | |
465 | ||
867a36f3 | 466 | done: |
ac3d33df | 467 | git_buf_dispose(&commit_filename); |
867a36f3 ET |
468 | return error; |
469 | } | |
470 | ||
b6b636a7 | 471 | static int rebase_setupfiles(git_rebase *rebase) |
867a36f3 | 472 | { |
b6b636a7 | 473 | char onto[GIT_OID_HEXSZ], orig_head[GIT_OID_HEXSZ]; |
badc7283 | 474 | const char *orig_head_name; |
867a36f3 | 475 | |
b6b636a7 ET |
476 | git_oid_fmt(onto, &rebase->onto_id); |
477 | git_oid_fmt(orig_head, &rebase->orig_head_id); | |
867a36f3 | 478 | |
b6b636a7 | 479 | if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) { |
ac3d33df | 480 | git_error_set(GIT_ERROR_OS, "failed to create rebase directory '%s'", rebase->state_path); |
b6b636a7 | 481 | return -1; |
867a36f3 ET |
482 | } |
483 | ||
badc7283 ET |
484 | orig_head_name = rebase->head_detached ? ORIG_DETACHED_HEAD : |
485 | rebase->orig_head_name; | |
486 | ||
b6b636a7 | 487 | if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 || |
5a747e0c ET |
488 | rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 || |
489 | rebase_setupfile(rebase, ONTO_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || | |
490 | rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || | |
491 | rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0) | |
b6b636a7 | 492 | return -1; |
867a36f3 | 493 | |
b6b636a7 | 494 | return rebase_setupfiles_merge(rebase); |
867a36f3 ET |
495 | } |
496 | ||
22a2d3d5 | 497 | int git_rebase_options_init(git_rebase_options *opts, unsigned int version) |
867a36f3 ET |
498 | { |
499 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( | |
500 | opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT); | |
501 | return 0; | |
502 | } | |
503 | ||
22a2d3d5 UG |
504 | #ifndef GIT_DEPRECATE_HARD |
505 | int git_rebase_init_options(git_rebase_options *opts, unsigned int version) | |
506 | { | |
507 | return git_rebase_options_init(opts, version); | |
508 | } | |
509 | #endif | |
510 | ||
867a36f3 ET |
511 | static int rebase_ensure_not_in_progress(git_repository *repo) |
512 | { | |
513 | int error; | |
22a2d3d5 | 514 | git_rebase_t type; |
867a36f3 ET |
515 | |
516 | if ((error = rebase_state_type(&type, NULL, repo)) < 0) | |
517 | return error; | |
518 | ||
22a2d3d5 | 519 | if (type != GIT_REBASE_NONE) { |
ac3d33df | 520 | git_error_set(GIT_ERROR_REBASE, "there is an existing rebase in progress"); |
867a36f3 ET |
521 | return -1; |
522 | } | |
523 | ||
524 | return 0; | |
525 | } | |
526 | ||
eaf0d688 ET |
527 | static int rebase_ensure_not_dirty( |
528 | git_repository *repo, | |
529 | bool check_index, | |
530 | bool check_workdir, | |
531 | int fail_with) | |
867a36f3 ET |
532 | { |
533 | git_tree *head = NULL; | |
534 | git_index *index = NULL; | |
535 | git_diff *diff = NULL; | |
02980bdc | 536 | int error = 0; |
867a36f3 | 537 | |
eaf0d688 ET |
538 | if (check_index) { |
539 | if ((error = git_repository_head_tree(&head, repo)) < 0 || | |
540 | (error = git_repository_index(&index, repo)) < 0 || | |
541 | (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0) | |
542 | goto done; | |
867a36f3 | 543 | |
eaf0d688 | 544 | if (git_diff_num_deltas(diff) > 0) { |
ac3d33df | 545 | git_error_set(GIT_ERROR_REBASE, "uncommitted changes exist in index"); |
eaf0d688 ET |
546 | error = fail_with; |
547 | goto done; | |
548 | } | |
867a36f3 | 549 | |
eaf0d688 ET |
550 | git_diff_free(diff); |
551 | diff = NULL; | |
552 | } | |
867a36f3 | 553 | |
eaf0d688 | 554 | if (check_workdir) { |
2270ca9f DT |
555 | git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; |
556 | diff_opts.ignore_submodules = GIT_SUBMODULE_IGNORE_UNTRACKED; | |
557 | if ((error = git_diff_index_to_workdir(&diff, repo, index, &diff_opts)) < 0) | |
eaf0d688 | 558 | goto done; |
867a36f3 | 559 | |
eaf0d688 | 560 | if (git_diff_num_deltas(diff) > 0) { |
ac3d33df | 561 | git_error_set(GIT_ERROR_REBASE, "unstaged changes exist in workdir"); |
eaf0d688 ET |
562 | error = fail_with; |
563 | goto done; | |
564 | } | |
867a36f3 ET |
565 | } |
566 | ||
567 | done: | |
568 | git_diff_free(diff); | |
569 | git_index_free(index); | |
570 | git_tree_free(head); | |
571 | ||
572 | return error; | |
573 | } | |
574 | ||
b6b636a7 ET |
575 | static int rebase_init_operations( |
576 | git_rebase *rebase, | |
577 | git_repository *repo, | |
18b00406 ET |
578 | const git_annotated_commit *branch, |
579 | const git_annotated_commit *upstream, | |
580 | const git_annotated_commit *onto) | |
b6b636a7 ET |
581 | { |
582 | git_revwalk *revwalk = NULL; | |
583 | git_commit *commit; | |
584 | git_oid id; | |
585 | bool merge; | |
586 | git_rebase_operation *operation; | |
587 | int error; | |
588 | ||
589 | if (!upstream) | |
590 | upstream = onto; | |
591 | ||
592 | if ((error = git_revwalk_new(&revwalk, rebase->repo)) < 0 || | |
18b00406 ET |
593 | (error = git_revwalk_push(revwalk, git_annotated_commit_id(branch))) < 0 || |
594 | (error = git_revwalk_hide(revwalk, git_annotated_commit_id(upstream))) < 0) | |
b6b636a7 ET |
595 | goto done; |
596 | ||
3cc5ec94 | 597 | git_revwalk_sorting(revwalk, GIT_SORT_REVERSE); |
b6b636a7 ET |
598 | |
599 | while ((error = git_revwalk_next(&id, revwalk)) == 0) { | |
600 | if ((error = git_commit_lookup(&commit, repo, &id)) < 0) | |
601 | goto done; | |
602 | ||
603 | merge = (git_commit_parentcount(commit) > 1); | |
604 | git_commit_free(commit); | |
605 | ||
606 | if (merge) | |
607 | continue; | |
608 | ||
92e87dd7 | 609 | operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL); |
ac3d33df | 610 | GIT_ERROR_CHECK_ALLOC(operation); |
b6b636a7 ET |
611 | } |
612 | ||
613 | error = 0; | |
614 | ||
615 | done: | |
616 | git_revwalk_free(revwalk); | |
617 | return error; | |
618 | } | |
619 | ||
620 | static int rebase_init_merge( | |
621 | git_rebase *rebase, | |
622 | git_repository *repo, | |
18b00406 ET |
623 | const git_annotated_commit *branch, |
624 | const git_annotated_commit *upstream, | |
625 | const git_annotated_commit *onto) | |
b6b636a7 | 626 | { |
49b8293c | 627 | git_reference *head_ref = NULL; |
ee667307 ET |
628 | git_commit *onto_commit = NULL; |
629 | git_buf reflog = GIT_BUF_INIT; | |
b6b636a7 ET |
630 | git_buf state_path = GIT_BUF_INIT; |
631 | int error; | |
632 | ||
ee667307 ET |
633 | GIT_UNUSED(upstream); |
634 | ||
84f56cb0 | 635 | if ((error = git_buf_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0) |
49b8293c ET |
636 | goto done; |
637 | ||
b6b636a7 | 638 | rebase->state_path = git_buf_detach(&state_path); |
ac3d33df | 639 | GIT_ERROR_CHECK_ALLOC(rebase->state_path); |
ee667307 | 640 | |
4db1fc7e | 641 | if (branch->ref_name && strcmp(branch->ref_name, "HEAD")) { |
badc7283 | 642 | rebase->orig_head_name = git__strdup(branch->ref_name); |
ac3d33df | 643 | GIT_ERROR_CHECK_ALLOC(rebase->orig_head_name); |
badc7283 ET |
644 | } else { |
645 | rebase->head_detached = 1; | |
646 | } | |
ee667307 ET |
647 | |
648 | rebase->onto_name = git__strdup(rebase_onto_name(onto)); | |
ac3d33df | 649 | GIT_ERROR_CHECK_ALLOC(rebase->onto_name); |
ee667307 | 650 | |
f3a199dd | 651 | rebase->quiet = rebase->options.quiet; |
b6b636a7 | 652 | |
18b00406 ET |
653 | git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); |
654 | git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); | |
b6b636a7 | 655 | |
ee667307 ET |
656 | if ((error = rebase_setupfiles(rebase)) < 0 || |
657 | (error = git_buf_printf(&reflog, | |
658 | "rebase: checkout %s", rebase_onto_name(onto))) < 0 || | |
659 | (error = git_commit_lookup( | |
660 | &onto_commit, repo, git_annotated_commit_id(onto))) < 0 || | |
661 | (error = git_checkout_tree(repo, | |
662 | (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || | |
663 | (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, | |
664 | git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) | |
665 | goto done; | |
b6b636a7 | 666 | |
49b8293c ET |
667 | done: |
668 | git_reference_free(head_ref); | |
ee667307 | 669 | git_commit_free(onto_commit); |
ac3d33df JK |
670 | git_buf_dispose(&reflog); |
671 | git_buf_dispose(&state_path); | |
49b8293c | 672 | |
b6b636a7 ET |
673 | return error; |
674 | } | |
675 | ||
ee667307 ET |
676 | static int rebase_init_inmemory( |
677 | git_rebase *rebase, | |
678 | git_repository *repo, | |
679 | const git_annotated_commit *branch, | |
680 | const git_annotated_commit *upstream, | |
681 | const git_annotated_commit *onto) | |
682 | { | |
683 | GIT_UNUSED(branch); | |
684 | GIT_UNUSED(upstream); | |
685 | ||
686 | return git_commit_lookup( | |
687 | &rebase->last_commit, repo, git_annotated_commit_id(onto)); | |
688 | } | |
689 | ||
b6b636a7 ET |
690 | int git_rebase_init( |
691 | git_rebase **out, | |
867a36f3 | 692 | git_repository *repo, |
18b00406 ET |
693 | const git_annotated_commit *branch, |
694 | const git_annotated_commit *upstream, | |
695 | const git_annotated_commit *onto, | |
f3a199dd | 696 | const git_rebase_options *given_opts) |
867a36f3 | 697 | { |
b6b636a7 | 698 | git_rebase *rebase = NULL; |
ee667307 | 699 | git_annotated_commit *head_branch = NULL; |
659cf202 | 700 | git_reference *head_ref = NULL; |
ee667307 | 701 | bool inmemory = (given_opts && given_opts->inmemory); |
867a36f3 ET |
702 | int error; |
703 | ||
c25aa7cd PP |
704 | GIT_ASSERT_ARG(repo); |
705 | GIT_ASSERT_ARG(upstream || onto); | |
867a36f3 | 706 | |
b6b636a7 ET |
707 | *out = NULL; |
708 | ||
b6b636a7 ET |
709 | if (!onto) |
710 | onto = upstream; | |
711 | ||
ee667307 ET |
712 | if ((error = rebase_check_versions(given_opts)) < 0) |
713 | goto done; | |
714 | ||
715 | if (!inmemory) { | |
716 | if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || | |
717 | (error = rebase_ensure_not_in_progress(repo)) < 0 || | |
718 | (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0) | |
719 | goto done; | |
720 | } | |
721 | ||
722 | if (!branch) { | |
723 | if ((error = git_repository_head(&head_ref, repo)) < 0 || | |
724 | (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) | |
725 | goto done; | |
726 | ||
727 | branch = head_branch; | |
728 | } | |
867a36f3 | 729 | |
2afb6fa4 PS |
730 | if (rebase_alloc(&rebase, given_opts) < 0) |
731 | return -1; | |
867a36f3 | 732 | |
ee667307 ET |
733 | rebase->repo = repo; |
734 | rebase->inmemory = inmemory; | |
22a2d3d5 | 735 | rebase->type = GIT_REBASE_MERGE; |
ee667307 ET |
736 | |
737 | if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0) | |
867a36f3 ET |
738 | goto done; |
739 | ||
ee667307 ET |
740 | if (inmemory) |
741 | error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); | |
742 | else | |
f928c69a | 743 | error = rebase_init_merge(rebase, repo, branch ,upstream, onto); |
ee667307 ET |
744 | |
745 | if (error == 0) | |
746 | *out = rebase; | |
867a36f3 ET |
747 | |
748 | done: | |
659cf202 | 749 | git_reference_free(head_ref); |
ee667307 ET |
750 | git_annotated_commit_free(head_branch); |
751 | ||
b6b636a7 ET |
752 | if (error < 0) { |
753 | rebase_cleanup(rebase); | |
754 | git_rebase_free(rebase); | |
755 | } | |
756 | ||
867a36f3 ET |
757 | return error; |
758 | } | |
4fe84d62 | 759 | |
f3a199dd | 760 | static void normalize_checkout_options_for_apply( |
443d5674 | 761 | git_checkout_options *checkout_opts, |
f3a199dd ET |
762 | git_rebase *rebase, |
763 | git_commit *current_commit) | |
443d5674 | 764 | { |
94c988f6 | 765 | memcpy(checkout_opts, &rebase->options.checkout_options, sizeof(git_checkout_options)); |
443d5674 ET |
766 | |
767 | if (!checkout_opts->ancestor_label) | |
768 | checkout_opts->ancestor_label = "ancestor"; | |
769 | ||
22a2d3d5 | 770 | if (rebase->type == GIT_REBASE_MERGE) { |
443d5674 | 771 | if (!checkout_opts->our_label) |
f152f8ac | 772 | checkout_opts->our_label = rebase->onto_name; |
443d5674 | 773 | |
796b03bd ET |
774 | if (!checkout_opts->their_label) |
775 | checkout_opts->their_label = git_commit_summary(current_commit); | |
443d5674 ET |
776 | } else { |
777 | abort(); | |
778 | } | |
443d5674 ET |
779 | } |
780 | ||
f152f8ac ET |
781 | GIT_INLINE(int) rebase_movenext(git_rebase *rebase) |
782 | { | |
783 | size_t next = rebase->started ? rebase->current + 1 : 0; | |
784 | ||
785 | if (next == git_array_size(rebase->operations)) | |
786 | return GIT_ITEROVER; | |
787 | ||
788 | rebase->started = 1; | |
789 | rebase->current = next; | |
790 | ||
791 | return 0; | |
792 | } | |
793 | ||
950a7091 | 794 | static int rebase_next_merge( |
f152f8ac | 795 | git_rebase_operation **out, |
f3a199dd | 796 | git_rebase *rebase) |
950a7091 | 797 | { |
f152f8ac | 798 | git_buf path = GIT_BUF_INIT; |
f152f8ac | 799 | git_commit *current_commit = NULL, *parent_commit = NULL; |
950a7091 ET |
800 | git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; |
801 | git_index *index = NULL; | |
be8404a7 | 802 | git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; |
f152f8ac | 803 | git_rebase_operation *operation; |
f3a199dd | 804 | git_checkout_options checkout_opts; |
f152f8ac | 805 | char current_idstr[GIT_OID_HEXSZ]; |
950a7091 ET |
806 | unsigned int parent_count; |
807 | int error; | |
808 | ||
f152f8ac | 809 | *out = NULL; |
950a7091 | 810 | |
f152f8ac | 811 | operation = git_array_get(rebase->operations, rebase->current); |
443d5674 | 812 | |
f152f8ac ET |
813 | if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || |
814 | (error = git_commit_tree(¤t_tree, current_commit)) < 0 || | |
b6b636a7 | 815 | (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0) |
950a7091 ET |
816 | goto done; |
817 | ||
f152f8ac | 818 | if ((parent_count = git_commit_parentcount(current_commit)) > 1) { |
ac3d33df | 819 | git_error_set(GIT_ERROR_REBASE, "cannot rebase a merge commit"); |
950a7091 ET |
820 | error = -1; |
821 | goto done; | |
822 | } else if (parent_count) { | |
f152f8ac | 823 | if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || |
950a7091 ET |
824 | (error = git_commit_tree(&parent_tree, parent_commit)) < 0) |
825 | goto done; | |
826 | } | |
827 | ||
f152f8ac ET |
828 | git_oid_fmt(current_idstr, &operation->id); |
829 | ||
f3a199dd | 830 | normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit); |
796b03bd | 831 | |
41fae48d | 832 | if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || |
5a747e0c ET |
833 | (error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 || |
834 | (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || | |
a202e0d4 | 835 | (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || |
b6b636a7 | 836 | (error = git_merge__check_result(rebase->repo, index)) < 0 || |
41fae48d ET |
837 | (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || |
838 | (error = git_indexwriter_commit(&indexwriter)) < 0) | |
950a7091 ET |
839 | goto done; |
840 | ||
f152f8ac | 841 | *out = operation; |
18b439b9 | 842 | |
950a7091 | 843 | done: |
be8404a7 | 844 | git_indexwriter_cleanup(&indexwriter); |
950a7091 ET |
845 | git_index_free(index); |
846 | git_tree_free(current_tree); | |
847 | git_tree_free(head_tree); | |
848 | git_tree_free(parent_tree); | |
950a7091 | 849 | git_commit_free(parent_commit); |
f152f8ac | 850 | git_commit_free(current_commit); |
ac3d33df | 851 | git_buf_dispose(&path); |
950a7091 ET |
852 | |
853 | return error; | |
854 | } | |
855 | ||
ee667307 ET |
856 | static int rebase_next_inmemory( |
857 | git_rebase_operation **out, | |
858 | git_rebase *rebase) | |
859 | { | |
860 | git_commit *current_commit = NULL, *parent_commit = NULL; | |
861 | git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; | |
862 | git_rebase_operation *operation; | |
863 | git_index *index = NULL; | |
9a363d1b | 864 | unsigned int parent_count; |
ee667307 ET |
865 | int error; |
866 | ||
867 | *out = NULL; | |
868 | ||
869 | operation = git_array_get(rebase->operations, rebase->current); | |
870 | ||
871 | if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || | |
9a363d1b ET |
872 | (error = git_commit_tree(¤t_tree, current_commit)) < 0) |
873 | goto done; | |
874 | ||
875 | if ((parent_count = git_commit_parentcount(current_commit)) > 1) { | |
ac3d33df | 876 | git_error_set(GIT_ERROR_REBASE, "cannot rebase a merge commit"); |
9a363d1b ET |
877 | error = -1; |
878 | goto done; | |
879 | } else if (parent_count) { | |
880 | if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || | |
881 | (error = git_commit_tree(&parent_tree, parent_commit)) < 0) | |
882 | goto done; | |
883 | } | |
884 | ||
885 | if ((error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || | |
a202e0d4 | 886 | (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) |
ee667307 ET |
887 | goto done; |
888 | ||
f28bae0c ET |
889 | if (!rebase->index) { |
890 | rebase->index = index; | |
891 | index = NULL; | |
892 | } else { | |
893 | if ((error = git_index_read_index(rebase->index, index)) < 0) | |
894 | goto done; | |
895 | } | |
ee667307 ET |
896 | |
897 | *out = operation; | |
898 | ||
899 | done: | |
900 | git_commit_free(current_commit); | |
901 | git_commit_free(parent_commit); | |
902 | git_tree_free(current_tree); | |
903 | git_tree_free(head_tree); | |
904 | git_tree_free(parent_tree); | |
905 | git_index_free(index); | |
906 | ||
907 | return error; | |
908 | } | |
909 | ||
443d5674 | 910 | int git_rebase_next( |
f152f8ac | 911 | git_rebase_operation **out, |
f3a199dd | 912 | git_rebase *rebase) |
950a7091 | 913 | { |
950a7091 ET |
914 | int error; |
915 | ||
c25aa7cd PP |
916 | GIT_ASSERT_ARG(out); |
917 | GIT_ASSERT_ARG(rebase); | |
18b439b9 | 918 | |
ee667307 ET |
919 | if ((error = rebase_movenext(rebase)) < 0) |
920 | return error; | |
921 | ||
922 | if (rebase->inmemory) | |
923 | error = rebase_next_inmemory(out, rebase); | |
22a2d3d5 | 924 | else if (rebase->type == GIT_REBASE_MERGE) |
f3a199dd | 925 | error = rebase_next_merge(out, rebase); |
ee667307 | 926 | else |
950a7091 | 927 | abort(); |
950a7091 | 928 | |
a35a9890 ET |
929 | return error; |
930 | } | |
931 | ||
f28bae0c ET |
932 | int git_rebase_inmemory_index( |
933 | git_index **out, | |
934 | git_rebase *rebase) | |
935 | { | |
c25aa7cd PP |
936 | GIT_ASSERT_ARG(out); |
937 | GIT_ASSERT_ARG(rebase); | |
938 | GIT_ASSERT_ARG(rebase->index); | |
f28bae0c ET |
939 | |
940 | GIT_REFCOUNT_INC(rebase->index); | |
941 | *out = rebase->index; | |
942 | ||
943 | return 0; | |
944 | } | |
945 | ||
c25aa7cd PP |
946 | #ifndef GIT_DEPRECATE_HARD |
947 | static int create_signed( | |
948 | git_oid *out, | |
949 | git_rebase *rebase, | |
950 | const git_signature *author, | |
951 | const git_signature *committer, | |
952 | const char *message_encoding, | |
953 | const char *message, | |
954 | git_tree *tree, | |
955 | size_t parent_count, | |
956 | const git_commit **parents) | |
957 | { | |
958 | git_buf commit_content = GIT_BUF_INIT, | |
959 | commit_signature = GIT_BUF_INIT, | |
960 | signature_field = GIT_BUF_INIT; | |
961 | int error; | |
962 | ||
963 | git_error_clear(); | |
964 | ||
965 | if ((error = git_commit_create_buffer(&commit_content, | |
966 | rebase->repo, author, committer, message_encoding, | |
967 | message, tree, parent_count, parents)) < 0) | |
968 | goto done; | |
969 | ||
970 | error = rebase->options.signing_cb(&commit_signature, | |
971 | &signature_field, commit_content.ptr, | |
972 | rebase->options.payload); | |
973 | ||
974 | if (error) { | |
975 | if (error != GIT_PASSTHROUGH) | |
976 | git_error_set_after_callback_function(error, "signing_cb"); | |
977 | ||
978 | goto done; | |
979 | } | |
980 | ||
981 | error = git_commit_create_with_signature(out, rebase->repo, | |
982 | commit_content.ptr, | |
983 | commit_signature.size > 0 ? commit_signature.ptr : NULL, | |
984 | signature_field.size > 0 ? signature_field.ptr : NULL); | |
985 | ||
986 | done: | |
987 | git_buf_dispose(&commit_signature); | |
988 | git_buf_dispose(&signature_field); | |
989 | git_buf_dispose(&commit_content); | |
990 | return error; | |
991 | } | |
992 | #endif | |
993 | ||
ee667307 ET |
994 | static int rebase_commit__create( |
995 | git_commit **out, | |
b6b636a7 | 996 | git_rebase *rebase, |
ee667307 ET |
997 | git_index *index, |
998 | git_commit *parent_commit, | |
a35a9890 ET |
999 | const git_signature *author, |
1000 | const git_signature *committer, | |
1001 | const char *message_encoding, | |
1002 | const char *message) | |
1003 | { | |
f152f8ac | 1004 | git_rebase_operation *operation; |
ee667307 ET |
1005 | git_commit *current_commit = NULL, *commit = NULL; |
1006 | git_tree *parent_tree = NULL, *tree = NULL; | |
1007 | git_oid tree_id, commit_id; | |
a35a9890 ET |
1008 | int error; |
1009 | ||
f152f8ac | 1010 | operation = git_array_get(rebase->operations, rebase->current); |
a35a9890 ET |
1011 | |
1012 | if (git_index_has_conflicts(index)) { | |
ac3d33df | 1013 | git_error_set(GIT_ERROR_REBASE, "conflicts have not been resolved"); |
eaf0d688 | 1014 | error = GIT_EUNMERGED; |
a35a9890 ET |
1015 | goto done; |
1016 | } | |
1017 | ||
ee667307 ET |
1018 | if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || |
1019 | (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || | |
1020 | (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 || | |
1021 | (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) | |
93a7004c ET |
1022 | goto done; |
1023 | ||
ee667307 | 1024 | if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) { |
ac3d33df | 1025 | git_error_set(GIT_ERROR_REBASE, "this patch has already been applied"); |
93a7004c ET |
1026 | error = GIT_EAPPLIED; |
1027 | goto done; | |
1028 | } | |
1029 | ||
a35a9890 | 1030 | if (!author) |
f152f8ac | 1031 | author = git_commit_author(current_commit); |
a35a9890 ET |
1032 | |
1033 | if (!message) { | |
f152f8ac ET |
1034 | message_encoding = git_commit_message_encoding(current_commit); |
1035 | message = git_commit_message(current_commit); | |
a35a9890 ET |
1036 | } |
1037 | ||
c25aa7cd PP |
1038 | git_error_clear(); |
1039 | error = GIT_PASSTHROUGH; | |
22a2d3d5 | 1040 | |
c25aa7cd PP |
1041 | if (rebase->options.commit_create_cb) { |
1042 | error = rebase->options.commit_create_cb(&commit_id, | |
1043 | author, committer, message_encoding, message, | |
1044 | tree, 1, (const git_commit **)&parent_commit, | |
1045 | rebase->options.payload); | |
22a2d3d5 | 1046 | |
c25aa7cd PP |
1047 | git_error_set_after_callback_function(error, |
1048 | "commit_create_cb"); | |
22a2d3d5 | 1049 | } |
c25aa7cd PP |
1050 | #ifndef GIT_DEPRECATE_HARD |
1051 | else if (rebase->options.signing_cb) { | |
1052 | error = create_signed(&commit_id, rebase, author, | |
1053 | committer, message_encoding, message, tree, | |
1054 | 1, (const git_commit **)&parent_commit); | |
22a2d3d5 | 1055 | } |
c25aa7cd | 1056 | #endif |
22a2d3d5 | 1057 | |
c25aa7cd PP |
1058 | if (error == GIT_PASSTHROUGH) |
1059 | error = git_commit_create(&commit_id, rebase->repo, NULL, | |
1060 | author, committer, message_encoding, message, | |
1061 | tree, 1, (const git_commit **)&parent_commit); | |
1062 | ||
1063 | if (error) | |
22a2d3d5 UG |
1064 | goto done; |
1065 | ||
1066 | if ((error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0) | |
ee667307 ET |
1067 | goto done; |
1068 | ||
1069 | *out = commit; | |
1070 | ||
1071 | done: | |
1072 | if (error < 0) | |
1073 | git_commit_free(commit); | |
1074 | ||
1075 | git_commit_free(current_commit); | |
1076 | git_tree_free(parent_tree); | |
1077 | git_tree_free(tree); | |
1078 | ||
1079 | return error; | |
1080 | } | |
1081 | ||
1082 | static int rebase_commit_merge( | |
1083 | git_oid *commit_id, | |
1084 | git_rebase *rebase, | |
1085 | const git_signature *author, | |
1086 | const git_signature *committer, | |
1087 | const char *message_encoding, | |
1088 | const char *message) | |
1089 | { | |
1090 | git_rebase_operation *operation; | |
1091 | git_reference *head = NULL; | |
1092 | git_commit *head_commit = NULL, *commit = NULL; | |
1093 | git_index *index = NULL; | |
1094 | char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; | |
1095 | int error; | |
1096 | ||
1097 | operation = git_array_get(rebase->operations, rebase->current); | |
c25aa7cd | 1098 | GIT_ASSERT(operation); |
ee667307 ET |
1099 | |
1100 | if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || | |
1101 | (error = git_repository_head(&head, rebase->repo)) < 0 || | |
ac3d33df | 1102 | (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT)) < 0 || |
ee667307 ET |
1103 | (error = git_repository_index(&index, rebase->repo)) < 0 || |
1104 | (error = rebase_commit__create(&commit, rebase, index, head_commit, | |
1105 | author, committer, message_encoding, message)) < 0 || | |
a612a25f | 1106 | (error = git_reference__update_for_commit( |
ee667307 | 1107 | rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0) |
a35a9890 ET |
1108 | goto done; |
1109 | ||
ee667307 ET |
1110 | git_oid_fmt(old_idstr, &operation->id); |
1111 | git_oid_fmt(new_idstr, git_commit_id(commit)); | |
1112 | ||
1113 | if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, | |
1114 | "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr)) < 0) | |
1115 | goto done; | |
a35a9890 | 1116 | |
ee667307 | 1117 | git_oid_cpy(commit_id, git_commit_id(commit)); |
a35a9890 ET |
1118 | |
1119 | done: | |
a35a9890 | 1120 | git_index_free(index); |
ee667307 ET |
1121 | git_reference_free(head); |
1122 | git_commit_free(head_commit); | |
1123 | git_commit_free(commit); | |
1124 | return error; | |
1125 | } | |
1126 | ||
1127 | static int rebase_commit_inmemory( | |
1128 | git_oid *commit_id, | |
1129 | git_rebase *rebase, | |
1130 | const git_signature *author, | |
1131 | const git_signature *committer, | |
1132 | const char *message_encoding, | |
1133 | const char *message) | |
1134 | { | |
ee667307 ET |
1135 | git_commit *commit = NULL; |
1136 | int error = 0; | |
1137 | ||
c25aa7cd PP |
1138 | GIT_ASSERT_ARG(rebase->index); |
1139 | GIT_ASSERT_ARG(rebase->last_commit); | |
1140 | GIT_ASSERT_ARG(rebase->current < rebase->operations.size); | |
ee667307 | 1141 | |
f28bae0c | 1142 | if ((error = rebase_commit__create(&commit, rebase, rebase->index, |
ee667307 ET |
1143 | rebase->last_commit, author, committer, message_encoding, message)) < 0) |
1144 | goto done; | |
1145 | ||
ee667307 ET |
1146 | git_commit_free(rebase->last_commit); |
1147 | rebase->last_commit = commit; | |
1148 | ||
1149 | git_oid_cpy(commit_id, git_commit_id(commit)); | |
1150 | ||
1151 | done: | |
1152 | if (error < 0) | |
1153 | git_commit_free(commit); | |
a35a9890 ET |
1154 | |
1155 | return error; | |
1156 | } | |
1157 | ||
1158 | int git_rebase_commit( | |
1159 | git_oid *id, | |
b6b636a7 | 1160 | git_rebase *rebase, |
a35a9890 ET |
1161 | const git_signature *author, |
1162 | const git_signature *committer, | |
1163 | const char *message_encoding, | |
1164 | const char *message) | |
1165 | { | |
a35a9890 ET |
1166 | int error; |
1167 | ||
c25aa7cd PP |
1168 | GIT_ASSERT_ARG(rebase); |
1169 | GIT_ASSERT_ARG(committer); | |
a35a9890 | 1170 | |
ee667307 ET |
1171 | if (rebase->inmemory) |
1172 | error = rebase_commit_inmemory( | |
1173 | id, rebase, author, committer, message_encoding, message); | |
22a2d3d5 | 1174 | else if (rebase->type == GIT_REBASE_MERGE) |
a35a9890 | 1175 | error = rebase_commit_merge( |
b6b636a7 | 1176 | id, rebase, author, committer, message_encoding, message); |
ee667307 | 1177 | else |
a35a9890 | 1178 | abort(); |
a35a9890 | 1179 | |
950a7091 ET |
1180 | return error; |
1181 | } | |
1182 | ||
f3a199dd | 1183 | int git_rebase_abort(git_rebase *rebase) |
4fe84d62 | 1184 | { |
4fe84d62 ET |
1185 | git_reference *orig_head_ref = NULL; |
1186 | git_commit *orig_head_commit = NULL; | |
1187 | int error; | |
1188 | ||
c25aa7cd | 1189 | GIT_ASSERT_ARG(rebase); |
4fe84d62 | 1190 | |
ee667307 ET |
1191 | if (rebase->inmemory) |
1192 | return 0; | |
1193 | ||
b6b636a7 ET |
1194 | error = rebase->head_detached ? |
1195 | git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, | |
659cf202 | 1196 | &rebase->orig_head_id, 1, "rebase: aborting") : |
4fe84d62 | 1197 | git_reference_symbolic_create( |
b6b636a7 | 1198 | &orig_head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, |
659cf202 | 1199 | "rebase: aborting"); |
4fe84d62 ET |
1200 | |
1201 | if (error < 0) | |
1202 | goto done; | |
1203 | ||
1204 | if ((error = git_commit_lookup( | |
b6b636a7 ET |
1205 | &orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 || |
1206 | (error = git_reset(rebase->repo, (git_object *)orig_head_commit, | |
94c988f6 | 1207 | GIT_RESET_HARD, &rebase->options.checkout_options)) < 0) |
4fe84d62 ET |
1208 | goto done; |
1209 | ||
b6b636a7 | 1210 | error = rebase_cleanup(rebase); |
4fe84d62 ET |
1211 | |
1212 | done: | |
1213 | git_commit_free(orig_head_commit); | |
1214 | git_reference_free(orig_head_ref); | |
4fe84d62 ET |
1215 | |
1216 | return error; | |
1217 | } | |
517644cc | 1218 | |
f3a199dd ET |
1219 | static int notes_ref_lookup(git_buf *out, git_rebase *rebase) |
1220 | { | |
1221 | git_config *config = NULL; | |
1222 | int do_rewrite, error; | |
1223 | ||
1224 | if (rebase->options.rewrite_notes_ref) { | |
1225 | git_buf_attach_notowned(out, | |
1226 | rebase->options.rewrite_notes_ref, | |
1227 | strlen(rebase->options.rewrite_notes_ref)); | |
1228 | return 0; | |
1229 | } | |
1230 | ||
1231 | if ((error = git_repository_config(&config, rebase->repo)) < 0 || | |
1232 | (error = git_config_get_bool(&do_rewrite, config, "notes.rewrite.rebase")) < 0) { | |
1233 | ||
1234 | if (error != GIT_ENOTFOUND) | |
1235 | goto done; | |
1236 | ||
ac3d33df | 1237 | git_error_clear(); |
f3a199dd ET |
1238 | do_rewrite = 1; |
1239 | } | |
1240 | ||
1241 | error = do_rewrite ? | |
1242 | git_config_get_string_buf(out, config, "notes.rewriteref") : | |
1243 | GIT_ENOTFOUND; | |
1244 | ||
1245 | done: | |
1246 | git_config_free(config); | |
1247 | return error; | |
1248 | } | |
1249 | ||
5ae9d296 | 1250 | static int rebase_copy_note( |
b6b636a7 | 1251 | git_rebase *rebase, |
f3a199dd | 1252 | const char *notes_ref, |
5ae9d296 ET |
1253 | git_oid *from, |
1254 | git_oid *to, | |
f3a199dd | 1255 | const git_signature *committer) |
517644cc | 1256 | { |
5ae9d296 ET |
1257 | git_note *note = NULL; |
1258 | git_oid note_id; | |
979645a7 | 1259 | git_signature *who = NULL; |
5ae9d296 ET |
1260 | int error; |
1261 | ||
f3a199dd | 1262 | if ((error = git_note_read(¬e, rebase->repo, notes_ref, from)) < 0) { |
5ae9d296 | 1263 | if (error == GIT_ENOTFOUND) { |
ac3d33df | 1264 | git_error_clear(); |
5ae9d296 ET |
1265 | error = 0; |
1266 | } | |
1267 | ||
1268 | goto done; | |
1269 | } | |
1270 | ||
979645a7 ET |
1271 | if (!committer) { |
1272 | if((error = git_signature_default(&who, rebase->repo)) < 0) { | |
1273 | if (error != GIT_ENOTFOUND || | |
1274 | (error = git_signature_now(&who, "unknown", "unknown")) < 0) | |
1275 | goto done; | |
1276 | ||
ac3d33df | 1277 | git_error_clear(); |
979645a7 ET |
1278 | } |
1279 | ||
1280 | committer = who; | |
1281 | } | |
1282 | ||
f3a199dd | 1283 | error = git_note_create(¬e_id, rebase->repo, notes_ref, |
21083a71 | 1284 | git_note_author(note), committer, to, git_note_message(note), 0); |
5ae9d296 ET |
1285 | |
1286 | done: | |
1287 | git_note_free(note); | |
979645a7 | 1288 | git_signature_free(who); |
5ae9d296 ET |
1289 | |
1290 | return error; | |
1291 | } | |
1292 | ||
1293 | static int rebase_copy_notes( | |
b6b636a7 | 1294 | git_rebase *rebase, |
f3a199dd | 1295 | const git_signature *committer) |
5ae9d296 | 1296 | { |
f3a199dd | 1297 | git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT, notes_ref = GIT_BUF_INIT; |
5ae9d296 ET |
1298 | char *pair_list, *fromstr, *tostr, *end; |
1299 | git_oid from, to; | |
1300 | unsigned int linenum = 1; | |
1301 | int error = 0; | |
1302 | ||
f3a199dd ET |
1303 | if ((error = notes_ref_lookup(¬es_ref, rebase)) < 0) { |
1304 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 1305 | git_error_clear(); |
f3a199dd ET |
1306 | error = 0; |
1307 | } | |
1308 | ||
5ae9d296 | 1309 | goto done; |
f3a199dd | 1310 | } |
5ae9d296 | 1311 | |
b6b636a7 | 1312 | if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 || |
5ae9d296 ET |
1313 | (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0) |
1314 | goto done; | |
1315 | ||
1316 | pair_list = rewritten.ptr; | |
1317 | ||
1318 | while (*pair_list) { | |
1319 | fromstr = pair_list; | |
1320 | ||
1321 | if ((end = strchr(fromstr, '\n')) == NULL) | |
1322 | goto on_error; | |
1323 | ||
1324 | pair_list = end+1; | |
1325 | *end = '\0'; | |
1326 | ||
1327 | if ((end = strchr(fromstr, ' ')) == NULL) | |
1328 | goto on_error; | |
1329 | ||
1330 | tostr = end+1; | |
1331 | *end = '\0'; | |
1332 | ||
1333 | if (strlen(fromstr) != GIT_OID_HEXSZ || | |
1334 | strlen(tostr) != GIT_OID_HEXSZ || | |
1335 | git_oid_fromstr(&from, fromstr) < 0 || | |
1336 | git_oid_fromstr(&to, tostr) < 0) | |
1337 | goto on_error; | |
1338 | ||
f3a199dd | 1339 | if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0) |
5ae9d296 ET |
1340 | goto done; |
1341 | ||
1342 | linenum++; | |
1343 | } | |
1344 | ||
1345 | goto done; | |
1346 | ||
1347 | on_error: | |
ac3d33df | 1348 | git_error_set(GIT_ERROR_REBASE, "invalid rewritten file at line %d", linenum); |
5ae9d296 ET |
1349 | error = -1; |
1350 | ||
1351 | done: | |
ac3d33df JK |
1352 | git_buf_dispose(&rewritten); |
1353 | git_buf_dispose(&path); | |
1354 | git_buf_dispose(¬es_ref); | |
5ae9d296 ET |
1355 | |
1356 | return error; | |
1357 | } | |
1358 | ||
1f84caf0 | 1359 | static int return_to_orig_head(git_rebase *rebase) |
5ae9d296 | 1360 | { |
517644cc ET |
1361 | git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL; |
1362 | git_commit *terminal_commit = NULL; | |
1363 | git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT; | |
1364 | char onto[GIT_OID_HEXSZ]; | |
1f84caf0 | 1365 | int error = 0; |
ee667307 | 1366 | |
b6b636a7 | 1367 | git_oid_fmt(onto, &rebase->onto_id); |
517644cc | 1368 | |
1f84caf0 ET |
1369 | if ((error = git_buf_printf(&branch_msg, |
1370 | "rebase finished: %s onto %.*s", | |
1371 | rebase->orig_head_name, GIT_OID_HEXSZ, onto)) == 0 && | |
1372 | (error = git_buf_printf(&head_msg, | |
1373 | "rebase finished: returning to %s", | |
1374 | rebase->orig_head_name)) == 0 && | |
1375 | (error = git_repository_head(&terminal_ref, rebase->repo)) == 0 && | |
517644cc | 1376 | (error = git_reference_peel((git_object **)&terminal_commit, |
ac3d33df | 1377 | terminal_ref, GIT_OBJECT_COMMIT)) == 0 && |
5ae9d296 | 1378 | (error = git_reference_create_matching(&branch_ref, |
1f84caf0 ET |
1379 | rebase->repo, rebase->orig_head_name, |
1380 | git_commit_id(terminal_commit), 1, | |
1381 | &rebase->orig_head_id, branch_msg.ptr)) == 0) | |
1382 | error = git_reference_symbolic_create(&head_ref, | |
b6b636a7 | 1383 | rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, |
1f84caf0 | 1384 | head_msg.ptr); |
517644cc | 1385 | |
ac3d33df JK |
1386 | git_buf_dispose(&head_msg); |
1387 | git_buf_dispose(&branch_msg); | |
517644cc ET |
1388 | git_commit_free(terminal_commit); |
1389 | git_reference_free(head_ref); | |
1390 | git_reference_free(branch_ref); | |
1391 | git_reference_free(terminal_ref); | |
517644cc ET |
1392 | |
1393 | return error; | |
1394 | } | |
1395 | ||
1f84caf0 ET |
1396 | int git_rebase_finish( |
1397 | git_rebase *rebase, | |
1398 | const git_signature *signature) | |
1399 | { | |
1400 | int error = 0; | |
1401 | ||
c25aa7cd | 1402 | GIT_ASSERT_ARG(rebase); |
1f84caf0 ET |
1403 | |
1404 | if (rebase->inmemory) | |
1405 | return 0; | |
1406 | ||
1407 | if (!rebase->head_detached) | |
1408 | error = return_to_orig_head(rebase); | |
1409 | ||
1410 | if (error == 0 && (error = rebase_copy_notes(rebase, signature)) == 0) | |
1411 | error = rebase_cleanup(rebase); | |
1412 | ||
1413 | return error; | |
1414 | } | |
1415 | ||
22a2d3d5 | 1416 | const char *git_rebase_orig_head_name(git_rebase *rebase) { |
c25aa7cd | 1417 | GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); |
22a2d3d5 UG |
1418 | return rebase->orig_head_name; |
1419 | } | |
1420 | ||
1421 | const git_oid *git_rebase_orig_head_id(git_rebase *rebase) { | |
c25aa7cd | 1422 | GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); |
22a2d3d5 UG |
1423 | return &rebase->orig_head_id; |
1424 | } | |
1425 | ||
1426 | const char *git_rebase_onto_name(git_rebase *rebase) { | |
c25aa7cd | 1427 | GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); |
22a2d3d5 UG |
1428 | return rebase->onto_name; |
1429 | } | |
1430 | ||
1431 | const git_oid *git_rebase_onto_id(git_rebase *rebase) { | |
1432 | return &rebase->onto_id; | |
1433 | } | |
1434 | ||
ed2c06a6 ET |
1435 | size_t git_rebase_operation_entrycount(git_rebase *rebase) |
1436 | { | |
c25aa7cd | 1437 | GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0); |
ed2c06a6 ET |
1438 | |
1439 | return git_array_size(rebase->operations); | |
1440 | } | |
1441 | ||
1442 | size_t git_rebase_operation_current(git_rebase *rebase) | |
1443 | { | |
c25aa7cd | 1444 | GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0); |
ed2c06a6 | 1445 | |
30640aa9 | 1446 | return rebase->started ? rebase->current : GIT_REBASE_NO_OPERATION; |
ed2c06a6 ET |
1447 | } |
1448 | ||
1449 | git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx) | |
1450 | { | |
c25aa7cd | 1451 | GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL); |
ed2c06a6 ET |
1452 | |
1453 | return git_array_get(rebase->operations, idx); | |
1454 | } | |
1455 | ||
b6b636a7 ET |
1456 | void git_rebase_free(git_rebase *rebase) |
1457 | { | |
1458 | if (rebase == NULL) | |
1459 | return; | |
1460 | ||
f28bae0c | 1461 | git_index_free(rebase->index); |
ee667307 | 1462 | git_commit_free(rebase->last_commit); |
f152f8ac | 1463 | git__free(rebase->onto_name); |
b6b636a7 ET |
1464 | git__free(rebase->orig_head_name); |
1465 | git__free(rebase->state_path); | |
f152f8ac | 1466 | git_array_clear(rebase->operations); |
f3a199dd | 1467 | git__free((char *)rebase->options.rewrite_notes_ref); |
f152f8ac | 1468 | git__free(rebase); |
b6b636a7 | 1469 | } |