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