]>
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" |
867a36f3 ET |
16 | |
17 | #include <git2/types.h> | |
18 | #include <git2/rebase.h> | |
19 | #include <git2/commit.h> | |
20 | #include <git2/reset.h> | |
21 | #include <git2/revwalk.h> | |
5ae9d296 | 22 | #include <git2/notes.h> |
867a36f3 ET |
23 | |
24 | #define REBASE_APPLY_DIR "rebase-apply" | |
25 | #define REBASE_MERGE_DIR "rebase-merge" | |
26 | ||
27 | #define HEAD_NAME_FILE "head-name" | |
28 | #define ORIG_HEAD_FILE "orig-head" | |
29 | #define HEAD_FILE "head" | |
30 | #define ONTO_FILE "onto" | |
31 | #define ONTO_NAME_FILE "onto_name" | |
32 | #define QUIET_FILE "quiet" | |
33 | ||
34 | #define MSGNUM_FILE "msgnum" | |
35 | #define END_FILE "end" | |
36 | #define CMT_FILE_FMT "cmt.%d" | |
950a7091 | 37 | #define CURRENT_FILE "current" |
a35a9890 | 38 | #define REWRITTEN_FILE "rewritten" |
867a36f3 ET |
39 | |
40 | #define ORIG_DETACHED_HEAD "detached HEAD" | |
41 | ||
5ae9d296 ET |
42 | #define NOTES_DEFAULT_REF NULL |
43 | ||
867a36f3 ET |
44 | #define REBASE_DIR_MODE 0777 |
45 | #define REBASE_FILE_MODE 0666 | |
46 | ||
47 | typedef enum { | |
48 | GIT_REBASE_TYPE_NONE = 0, | |
49 | GIT_REBASE_TYPE_APPLY = 1, | |
50 | GIT_REBASE_TYPE_MERGE = 2, | |
950a7091 | 51 | GIT_REBASE_TYPE_INTERACTIVE = 3, |
867a36f3 ET |
52 | } git_rebase_type_t; |
53 | ||
b6b636a7 ET |
54 | struct git_rebase { |
55 | git_repository *repo; | |
56 | ||
4fe84d62 ET |
57 | git_rebase_type_t type; |
58 | char *state_path; | |
59 | ||
b6b636a7 | 60 | int head_detached : 1, |
f152f8ac ET |
61 | quiet : 1, |
62 | started : 1; | |
4fe84d62 ET |
63 | |
64 | char *orig_head_name; | |
65 | git_oid orig_head_id; | |
950a7091 | 66 | |
517644cc | 67 | git_oid onto_id; |
f152f8ac | 68 | char *onto_name; |
517644cc | 69 | |
b6b636a7 | 70 | git_array_t(git_rebase_operation) operations; |
f152f8ac | 71 | size_t current; |
b6b636a7 | 72 | }; |
4fe84d62 ET |
73 | |
74 | #define GIT_REBASE_STATE_INIT {0} | |
75 | ||
867a36f3 ET |
76 | static int rebase_state_type( |
77 | git_rebase_type_t *type_out, | |
78 | char **path_out, | |
79 | git_repository *repo) | |
80 | { | |
81 | git_buf path = GIT_BUF_INIT; | |
82 | git_rebase_type_t type = GIT_REBASE_TYPE_NONE; | |
83 | ||
84 | if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0) | |
85 | return -1; | |
86 | ||
87 | if (git_path_isdir(git_buf_cstr(&path))) { | |
88 | type = GIT_REBASE_TYPE_APPLY; | |
89 | goto done; | |
90 | } | |
91 | ||
92 | git_buf_clear(&path); | |
93 | if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0) | |
94 | return -1; | |
95 | ||
96 | if (git_path_isdir(git_buf_cstr(&path))) { | |
97 | type = GIT_REBASE_TYPE_MERGE; | |
98 | goto done; | |
99 | } | |
100 | ||
101 | done: | |
102 | *type_out = type; | |
103 | ||
104 | if (type != GIT_REBASE_TYPE_NONE && path_out) | |
105 | *path_out = git_buf_detach(&path); | |
106 | ||
107 | git_buf_free(&path); | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
f152f8ac | 112 | GIT_INLINE(int) rebase_readfile(git_buf *out, git_buf *state_path, const char *filename) |
950a7091 | 113 | { |
f152f8ac ET |
114 | size_t state_path_len = state_path->size; |
115 | int error; | |
950a7091 | 116 | |
f152f8ac ET |
117 | git_buf_clear(out); |
118 | ||
119 | if ((error = git_buf_joinpath(state_path, state_path->ptr, filename)) < 0 || | |
120 | (error = git_futils_readbuffer(out, state_path->ptr)) < 0) | |
950a7091 ET |
121 | goto done; |
122 | ||
f152f8ac | 123 | git_buf_rtrim(out); |
950a7091 | 124 | |
f152f8ac ET |
125 | done: |
126 | git_buf_truncate(state_path, state_path_len); | |
127 | return error; | |
128 | } | |
443d5674 | 129 | |
f152f8ac ET |
130 | GIT_INLINE(int) rebase_readint( |
131 | size_t *out, git_buf *asc_out, git_buf *state_path, const char *filename) | |
132 | { | |
133 | int32_t num; | |
134 | char *eol; | |
135 | int error = 0; | |
443d5674 | 136 | |
f152f8ac ET |
137 | if ((error = rebase_readfile(asc_out, state_path, filename)) < 0) |
138 | return error; | |
443d5674 | 139 | |
f152f8ac ET |
140 | if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) { |
141 | giterr_set(GITERR_REBASE, "The file '%s' contains an invalid numeric value", filename); | |
142 | return -1; | |
143 | } | |
443d5674 | 144 | |
f152f8ac | 145 | *out = (size_t) num; |
443d5674 | 146 | |
f152f8ac ET |
147 | return 0; |
148 | } | |
443d5674 | 149 | |
f152f8ac ET |
150 | GIT_INLINE(int) rebase_readoid( |
151 | git_oid *out, git_buf *str_out, git_buf *state_path, const char *filename) | |
152 | { | |
153 | int error; | |
443d5674 | 154 | |
f152f8ac ET |
155 | if ((error = rebase_readfile(str_out, state_path, filename)) < 0) |
156 | return error; | |
443d5674 | 157 | |
f152f8ac ET |
158 | if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) { |
159 | giterr_set(GITERR_REBASE, "The file '%s' contains an invalid object ID", filename); | |
160 | return -1; | |
161 | } | |
950a7091 | 162 | |
f152f8ac ET |
163 | return 0; |
164 | } | |
950a7091 | 165 | |
f152f8ac ET |
166 | static int rebase_open_merge(git_rebase *rebase) |
167 | { | |
168 | git_buf state_path = GIT_BUF_INIT, buf = GIT_BUF_INIT, cmt = GIT_BUF_INIT; | |
169 | git_oid current_id = {0}; | |
170 | git_rebase_operation *operation; | |
171 | size_t i, msgnum = 0, end; | |
172 | int error; | |
950a7091 | 173 | |
f152f8ac ET |
174 | if ((error = git_buf_puts(&state_path, rebase->state_path)) < 0) |
175 | goto done; | |
950a7091 | 176 | |
f152f8ac ET |
177 | /* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */ |
178 | if ((error = rebase_readint(&msgnum, &buf, &state_path, MSGNUM_FILE)) < 0 && | |
179 | error != GIT_ENOTFOUND) | |
180 | goto done; | |
443d5674 | 181 | |
f152f8ac ET |
182 | if (msgnum) { |
183 | rebase->started = 1; | |
184 | rebase->current = msgnum - 1; | |
185 | } | |
950a7091 | 186 | |
f152f8ac ET |
187 | /* Read 'end' */ |
188 | if ((error = rebase_readint(&end, &buf, &state_path, END_FILE)) < 0) | |
950a7091 ET |
189 | goto done; |
190 | ||
f152f8ac ET |
191 | /* Read 'current' if it exists */ |
192 | if ((error = rebase_readoid(¤t_id, &buf, &state_path, CURRENT_FILE)) < 0 && | |
193 | error != GIT_ENOTFOUND) | |
194 | goto done; | |
195 | ||
196 | /* Read cmt.* */ | |
197 | git_array_init_to_size(rebase->operations, end); | |
198 | GITERR_CHECK_ARRAY(rebase->operations); | |
950a7091 | 199 | |
f152f8ac ET |
200 | for (i = 0; i < end; i++) { |
201 | operation = git_array_alloc(rebase->operations); | |
202 | GITERR_CHECK_ALLOC(operation); | |
443d5674 | 203 | |
f152f8ac ET |
204 | git_buf_clear(&cmt); |
205 | ||
206 | if ((error = git_buf_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 || | |
207 | (error = rebase_readoid(&operation->id, &buf, &state_path, cmt.ptr)) < 0) | |
443d5674 ET |
208 | goto done; |
209 | } | |
950a7091 | 210 | |
f152f8ac ET |
211 | /* Read 'onto_name' */ |
212 | if ((error = rebase_readfile(&buf, &state_path, ONTO_NAME_FILE)) < 0) | |
213 | goto done; | |
214 | ||
215 | rebase->onto_name = git_buf_detach(&buf); | |
216 | ||
950a7091 | 217 | done: |
f152f8ac ET |
218 | git_buf_free(&cmt); |
219 | git_buf_free(&state_path); | |
220 | git_buf_free(&buf); | |
950a7091 ET |
221 | |
222 | return error; | |
223 | } | |
224 | ||
b6b636a7 | 225 | int git_rebase_open(git_rebase **out, git_repository *repo) |
4fe84d62 | 226 | { |
b6b636a7 | 227 | git_rebase *rebase; |
4fe84d62 | 228 | git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT, |
517644cc | 229 | orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT; |
4fe84d62 ET |
230 | int state_path_len, error; |
231 | ||
b6b636a7 ET |
232 | assert(repo); |
233 | ||
234 | rebase = git__calloc(1, sizeof(git_rebase)); | |
235 | GITERR_CHECK_ALLOC(rebase); | |
4fe84d62 | 236 | |
b6b636a7 ET |
237 | rebase->repo = repo; |
238 | ||
239 | if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0) | |
4fe84d62 ET |
240 | goto done; |
241 | ||
b6b636a7 | 242 | if (rebase->type == GIT_REBASE_TYPE_NONE) { |
4fe84d62 ET |
243 | giterr_set(GITERR_REBASE, "There is no rebase in progress"); |
244 | return GIT_ENOTFOUND; | |
245 | } | |
246 | ||
b6b636a7 | 247 | if ((error = git_buf_puts(&path, rebase->state_path)) < 0) |
4fe84d62 ET |
248 | goto done; |
249 | ||
250 | state_path_len = git_buf_len(&path); | |
251 | ||
252 | if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 || | |
253 | (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0) | |
254 | goto done; | |
255 | ||
256 | git_buf_rtrim(&orig_head_name); | |
257 | ||
258 | if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0) | |
b6b636a7 | 259 | rebase->head_detached = 1; |
4fe84d62 ET |
260 | |
261 | git_buf_truncate(&path, state_path_len); | |
262 | ||
263 | if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) | |
264 | goto done; | |
265 | ||
266 | if (!git_path_isfile(path.ptr)) { | |
267 | /* Previous versions of git.git used 'head' here; support that. */ | |
268 | git_buf_truncate(&path, state_path_len); | |
269 | ||
270 | if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0) | |
271 | goto done; | |
272 | } | |
273 | ||
274 | if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0) | |
275 | goto done; | |
276 | ||
277 | git_buf_rtrim(&orig_head_id); | |
278 | ||
b6b636a7 | 279 | if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0) |
4fe84d62 ET |
280 | goto done; |
281 | ||
517644cc ET |
282 | git_buf_truncate(&path, state_path_len); |
283 | ||
284 | if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 || | |
285 | (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0) | |
286 | goto done; | |
287 | ||
288 | git_buf_rtrim(&onto_id); | |
289 | ||
b6b636a7 | 290 | if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0) |
517644cc ET |
291 | goto done; |
292 | ||
b6b636a7 ET |
293 | if (!rebase->head_detached) |
294 | rebase->orig_head_name = git_buf_detach(&orig_head_name); | |
4fe84d62 | 295 | |
b6b636a7 | 296 | switch (rebase->type) { |
950a7091 ET |
297 | case GIT_REBASE_TYPE_INTERACTIVE: |
298 | giterr_set(GITERR_REBASE, "Interactive rebase is not supported"); | |
299 | error = -1; | |
300 | break; | |
301 | case GIT_REBASE_TYPE_MERGE: | |
b6b636a7 | 302 | error = rebase_open_merge(rebase); |
950a7091 ET |
303 | break; |
304 | case GIT_REBASE_TYPE_APPLY: | |
305 | giterr_set(GITERR_REBASE, "Patch application rebase is not supported"); | |
306 | error = -1; | |
307 | break; | |
308 | default: | |
309 | abort(); | |
310 | } | |
311 | ||
4fe84d62 | 312 | done: |
b6b636a7 ET |
313 | if (error == 0) |
314 | *out = rebase; | |
315 | else | |
316 | git_rebase_free(rebase); | |
317 | ||
4fe84d62 ET |
318 | git_buf_free(&path); |
319 | git_buf_free(&orig_head_name); | |
320 | git_buf_free(&orig_head_id); | |
321 | git_buf_free(&onto_id); | |
322 | return error; | |
323 | } | |
324 | ||
b6b636a7 | 325 | static int rebase_cleanup(git_rebase *rebase) |
4fe84d62 | 326 | { |
b6b636a7 ET |
327 | return git_path_isdir(rebase->state_path) ? |
328 | git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : | |
4fe84d62 ET |
329 | 0; |
330 | } | |
331 | ||
b6b636a7 | 332 | static int rebase_setupfile(git_rebase *rebase, const char *filename, int flags, const char *fmt, ...) |
867a36f3 ET |
333 | { |
334 | git_buf path = GIT_BUF_INIT, | |
335 | contents = GIT_BUF_INIT; | |
336 | va_list ap; | |
337 | int error; | |
338 | ||
339 | va_start(ap, fmt); | |
340 | git_buf_vprintf(&contents, fmt, ap); | |
341 | va_end(ap); | |
342 | ||
b6b636a7 | 343 | if ((error = git_buf_joinpath(&path, rebase->state_path, filename)) == 0) |
a35a9890 | 344 | error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE); |
867a36f3 ET |
345 | |
346 | git_buf_free(&path); | |
347 | git_buf_free(&contents); | |
348 | ||
349 | return error; | |
350 | } | |
351 | ||
867a36f3 ET |
352 | static const char *rebase_onto_name(const git_merge_head *onto) |
353 | { | |
354 | if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0) | |
355 | return onto->ref_name + 11; | |
356 | else if (onto->ref_name) | |
357 | return onto->ref_name; | |
358 | else | |
359 | return onto->oid_str; | |
360 | } | |
361 | ||
b6b636a7 | 362 | static int rebase_setupfiles_merge(git_rebase *rebase) |
867a36f3 | 363 | { |
867a36f3 | 364 | git_buf commit_filename = GIT_BUF_INIT; |
867a36f3 | 365 | char id_str[GIT_OID_HEXSZ]; |
b6b636a7 ET |
366 | git_rebase_operation *operation; |
367 | size_t i; | |
368 | int error = 0; | |
867a36f3 | 369 | |
b6b636a7 | 370 | if ((error = rebase_setupfile(rebase, END_FILE, -1, "%d\n", git_array_size(rebase->operations))) < 0 || |
f152f8ac | 371 | (error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0) |
867a36f3 ET |
372 | goto done; |
373 | ||
b6b636a7 ET |
374 | for (i = 0; i < git_array_size(rebase->operations); i++) { |
375 | operation = git_array_get(rebase->operations, i); | |
867a36f3 ET |
376 | |
377 | git_buf_clear(&commit_filename); | |
b6b636a7 ET |
378 | git_buf_printf(&commit_filename, CMT_FILE_FMT, i+1); |
379 | ||
380 | git_oid_fmt(id_str, &operation->id); | |
867a36f3 | 381 | |
b6b636a7 | 382 | if ((error = rebase_setupfile(rebase, commit_filename.ptr, -1, |
867a36f3 ET |
383 | "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0) |
384 | goto done; | |
385 | } | |
386 | ||
867a36f3 | 387 | done: |
867a36f3 | 388 | git_buf_free(&commit_filename); |
867a36f3 ET |
389 | return error; |
390 | } | |
391 | ||
b6b636a7 | 392 | static int rebase_setupfiles(git_rebase *rebase) |
867a36f3 | 393 | { |
b6b636a7 | 394 | char onto[GIT_OID_HEXSZ], orig_head[GIT_OID_HEXSZ]; |
867a36f3 | 395 | |
b6b636a7 ET |
396 | git_oid_fmt(onto, &rebase->onto_id); |
397 | git_oid_fmt(orig_head, &rebase->orig_head_id); | |
867a36f3 | 398 | |
b6b636a7 ET |
399 | if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) { |
400 | giterr_set(GITERR_OS, "Failed to create rebase directory '%s'", rebase->state_path); | |
401 | return -1; | |
867a36f3 ET |
402 | } |
403 | ||
b6b636a7 ET |
404 | if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 || |
405 | rebase_setupfile(rebase, HEAD_NAME_FILE, -1, "%s\n", rebase->orig_head_name) < 0 || | |
406 | rebase_setupfile(rebase, ONTO_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || | |
407 | rebase_setupfile(rebase, ORIG_HEAD_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || | |
408 | rebase_setupfile(rebase, QUIET_FILE, -1, rebase->quiet ? "t\n" : "\n") < 0) | |
409 | return -1; | |
867a36f3 | 410 | |
b6b636a7 | 411 | return rebase_setupfiles_merge(rebase); |
867a36f3 ET |
412 | } |
413 | ||
414 | int git_rebase_init_options(git_rebase_options *opts, unsigned int version) | |
415 | { | |
416 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( | |
417 | opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT); | |
418 | return 0; | |
419 | } | |
420 | ||
5ae9d296 ET |
421 | static int rebase_normalize_opts( |
422 | git_repository *repo, | |
867a36f3 ET |
423 | git_rebase_options *opts, |
424 | const git_rebase_options *given_opts) | |
425 | { | |
5ae9d296 ET |
426 | git_rebase_options default_opts = GIT_REBASE_OPTIONS_INIT; |
427 | git_config *config; | |
428 | ||
867a36f3 | 429 | if (given_opts) |
5ae9d296 ET |
430 | memcpy(opts, given_opts, sizeof(git_rebase_options)); |
431 | else | |
432 | memcpy(opts, &default_opts, sizeof(git_rebase_options)); | |
433 | ||
434 | if (git_repository_config(&config, repo) < 0) | |
435 | return -1; | |
436 | ||
437 | if (given_opts && given_opts->rewrite_notes_ref) { | |
438 | opts->rewrite_notes_ref = git__strdup(given_opts->rewrite_notes_ref); | |
439 | GITERR_CHECK_ALLOC(opts->rewrite_notes_ref); | |
440 | } else if (git_config__get_bool_force(config, "notes.rewrite.rebase", 1)) { | |
441 | const char *rewrite_ref = git_config__get_string_force( | |
442 | config, "notes.rewriteref", NOTES_DEFAULT_REF); | |
443 | ||
444 | if (rewrite_ref) { | |
445 | opts->rewrite_notes_ref = git__strdup(rewrite_ref); | |
446 | GITERR_CHECK_ALLOC(opts->rewrite_notes_ref); | |
447 | } | |
448 | } | |
449 | ||
450 | git_config_free(config); | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
455 | static void rebase_opts_free(git_rebase_options *opts) | |
456 | { | |
457 | if (!opts) | |
458 | return; | |
459 | ||
460 | git__free((char *)opts->rewrite_notes_ref); | |
867a36f3 ET |
461 | } |
462 | ||
463 | static int rebase_ensure_not_in_progress(git_repository *repo) | |
464 | { | |
465 | int error; | |
466 | git_rebase_type_t type; | |
467 | ||
468 | if ((error = rebase_state_type(&type, NULL, repo)) < 0) | |
469 | return error; | |
470 | ||
471 | if (type != GIT_REBASE_TYPE_NONE) { | |
472 | giterr_set(GITERR_REBASE, "There is an existing rebase in progress"); | |
473 | return -1; | |
474 | } | |
475 | ||
476 | return 0; | |
477 | } | |
478 | ||
479 | static int rebase_ensure_not_dirty(git_repository *repo) | |
480 | { | |
481 | git_tree *head = NULL; | |
482 | git_index *index = NULL; | |
483 | git_diff *diff = NULL; | |
484 | int error; | |
485 | ||
486 | if ((error = git_repository_head_tree(&head, repo)) < 0 || | |
487 | (error = git_repository_index(&index, repo)) < 0 || | |
488 | (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0) | |
489 | goto done; | |
490 | ||
491 | if (git_diff_num_deltas(diff) > 0) { | |
492 | giterr_set(GITERR_REBASE, "Uncommitted changes exist in index"); | |
493 | error = -1; | |
494 | goto done; | |
495 | } | |
496 | ||
497 | git_diff_free(diff); | |
498 | diff = NULL; | |
499 | ||
500 | if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0) | |
501 | goto done; | |
502 | ||
503 | if (git_diff_num_deltas(diff) > 0) { | |
504 | giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir"); | |
505 | error = -1; | |
506 | } | |
507 | ||
508 | done: | |
509 | git_diff_free(diff); | |
510 | git_index_free(index); | |
511 | git_tree_free(head); | |
512 | ||
513 | return error; | |
514 | } | |
515 | ||
b6b636a7 ET |
516 | static int rebase_init_operations( |
517 | git_rebase *rebase, | |
518 | git_repository *repo, | |
519 | const git_merge_head *branch, | |
520 | const git_merge_head *upstream, | |
521 | const git_merge_head *onto) | |
522 | { | |
523 | git_revwalk *revwalk = NULL; | |
524 | git_commit *commit; | |
525 | git_oid id; | |
526 | bool merge; | |
527 | git_rebase_operation *operation; | |
528 | int error; | |
529 | ||
530 | if (!upstream) | |
531 | upstream = onto; | |
532 | ||
533 | if ((error = git_revwalk_new(&revwalk, rebase->repo)) < 0 || | |
534 | (error = git_revwalk_push(revwalk, &branch->oid)) < 0 || | |
535 | (error = git_revwalk_hide(revwalk, &upstream->oid)) < 0) | |
536 | goto done; | |
537 | ||
538 | git_revwalk_sorting(revwalk, GIT_SORT_REVERSE | GIT_SORT_TIME); | |
539 | ||
540 | while ((error = git_revwalk_next(&id, revwalk)) == 0) { | |
541 | if ((error = git_commit_lookup(&commit, repo, &id)) < 0) | |
542 | goto done; | |
543 | ||
544 | merge = (git_commit_parentcount(commit) > 1); | |
545 | git_commit_free(commit); | |
546 | ||
547 | if (merge) | |
548 | continue; | |
549 | ||
550 | operation = git_array_alloc(rebase->operations); | |
551 | operation->type = GIT_REBASE_OPERATION_PICK; | |
552 | git_oid_cpy(&operation->id, &id); | |
553 | } | |
554 | ||
555 | error = 0; | |
556 | ||
557 | done: | |
558 | git_revwalk_free(revwalk); | |
559 | return error; | |
560 | } | |
561 | ||
562 | static int rebase_init_merge( | |
563 | git_rebase *rebase, | |
564 | git_repository *repo, | |
565 | const git_merge_head *branch, | |
566 | const git_merge_head *upstream, | |
567 | const git_merge_head *onto) | |
568 | { | |
569 | if (rebase_init_operations(rebase, repo, branch, upstream, onto) < 0) | |
570 | return -1; | |
571 | ||
f152f8ac ET |
572 | rebase->onto_name = git__strdup(rebase_onto_name(onto)); |
573 | GITERR_CHECK_ALLOC(rebase->onto_name); | |
b6b636a7 ET |
574 | |
575 | return 0; | |
576 | } | |
577 | ||
578 | static int rebase_init( | |
579 | git_rebase *rebase, | |
580 | git_repository *repo, | |
581 | const git_merge_head *branch, | |
582 | const git_merge_head *upstream, | |
583 | const git_merge_head *onto, | |
584 | const git_rebase_options *opts) | |
585 | { | |
586 | git_buf state_path = GIT_BUF_INIT; | |
587 | int error; | |
588 | ||
589 | git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR); | |
590 | ||
591 | rebase->repo = repo; | |
592 | rebase->type = GIT_REBASE_TYPE_MERGE; | |
593 | rebase->state_path = git_buf_detach(&state_path); | |
594 | rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); | |
595 | rebase->quiet = opts->quiet; | |
596 | ||
597 | git_oid_cpy(&rebase->orig_head_id, &branch->oid); | |
598 | git_oid_cpy(&rebase->onto_id, &onto->oid); | |
599 | ||
600 | if (!rebase->orig_head_name || !rebase->state_path) | |
601 | return -1; | |
602 | ||
603 | error = rebase_init_merge(rebase, repo, branch, upstream, onto); | |
604 | ||
605 | git_buf_free(&state_path); | |
606 | ||
607 | return error; | |
608 | } | |
609 | ||
610 | int git_rebase_init( | |
611 | git_rebase **out, | |
867a36f3 ET |
612 | git_repository *repo, |
613 | const git_merge_head *branch, | |
614 | const git_merge_head *upstream, | |
615 | const git_merge_head *onto, | |
616 | const git_signature *signature, | |
617 | const git_rebase_options *given_opts) | |
618 | { | |
b6b636a7 | 619 | git_rebase *rebase = NULL; |
5ae9d296 | 620 | git_rebase_options opts; |
867a36f3 ET |
621 | git_reference *head_ref = NULL; |
622 | git_buf reflog = GIT_BUF_INIT; | |
623 | git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; | |
624 | int error; | |
625 | ||
626 | assert(repo && branch && (upstream || onto)); | |
627 | ||
b6b636a7 ET |
628 | *out = NULL; |
629 | ||
867a36f3 | 630 | GITERR_CHECK_VERSION(given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options"); |
867a36f3 | 631 | |
b6b636a7 ET |
632 | if (!onto) |
633 | onto = upstream; | |
634 | ||
635 | checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; | |
636 | ||
5ae9d296 ET |
637 | if ((error = rebase_normalize_opts(repo, &opts, given_opts)) < 0 || |
638 | (error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || | |
867a36f3 ET |
639 | (error = rebase_ensure_not_in_progress(repo)) < 0 || |
640 | (error = rebase_ensure_not_dirty(repo)) < 0) | |
b6b636a7 | 641 | return error; |
867a36f3 | 642 | |
b6b636a7 ET |
643 | rebase = git__calloc(1, sizeof(git_rebase)); |
644 | GITERR_CHECK_ALLOC(rebase); | |
867a36f3 | 645 | |
b6b636a7 ET |
646 | if ((error = rebase_init(rebase, repo, branch, upstream, onto, &opts)) < 0 || |
647 | (error = rebase_setupfiles(rebase)) < 0 || | |
648 | (error = git_buf_printf(&reflog, | |
867a36f3 ET |
649 | "rebase: checkout %s", rebase_onto_name(onto))) < 0 || |
650 | (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, | |
b6b636a7 ET |
651 | &onto->oid, 1, signature, reflog.ptr)) < 0 || |
652 | (error = git_checkout_head(repo, &checkout_opts)) < 0) | |
867a36f3 ET |
653 | goto done; |
654 | ||
b6b636a7 | 655 | *out = rebase; |
867a36f3 ET |
656 | |
657 | done: | |
b6b636a7 ET |
658 | if (error < 0) { |
659 | rebase_cleanup(rebase); | |
660 | git_rebase_free(rebase); | |
661 | } | |
662 | ||
867a36f3 ET |
663 | git_reference_free(head_ref); |
664 | git_buf_free(&reflog); | |
5ae9d296 | 665 | rebase_opts_free(&opts); |
b6b636a7 | 666 | |
867a36f3 ET |
667 | return error; |
668 | } | |
4fe84d62 | 669 | |
443d5674 | 670 | static int normalize_checkout_opts( |
b6b636a7 | 671 | git_rebase *rebase, |
443d5674 | 672 | git_checkout_options *checkout_opts, |
b6b636a7 | 673 | const git_checkout_options *given_checkout_opts) |
443d5674 | 674 | { |
f152f8ac | 675 | git_commit *current_commit = NULL; |
443d5674 ET |
676 | int error = 0; |
677 | ||
443d5674 ET |
678 | if (given_checkout_opts != NULL) |
679 | memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options)); | |
680 | else { | |
681 | git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; | |
682 | default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; | |
683 | ||
684 | memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options)); | |
685 | } | |
686 | ||
687 | if (!checkout_opts->ancestor_label) | |
688 | checkout_opts->ancestor_label = "ancestor"; | |
689 | ||
b6b636a7 | 690 | if (rebase->type == GIT_REBASE_TYPE_MERGE) { |
443d5674 | 691 | if (!checkout_opts->our_label) |
f152f8ac | 692 | checkout_opts->our_label = rebase->onto_name; |
443d5674 | 693 | |
f152f8ac ET |
694 | if (!checkout_opts->their_label) { |
695 | git_rebase_operation *operation = | |
696 | git_array_get(rebase->operations, rebase->current); | |
697 | ||
698 | if ((error = git_commit_lookup( | |
699 | ¤t_commit, rebase->repo, &operation->id)) < 0) | |
700 | goto done; | |
701 | ||
702 | checkout_opts->their_label = | |
703 | git__strdup(git_commit_summary(current_commit)); | |
704 | GITERR_CHECK_ALLOC(checkout_opts->their_label); | |
705 | } | |
443d5674 ET |
706 | } else { |
707 | abort(); | |
708 | } | |
709 | ||
f152f8ac ET |
710 | done: |
711 | git_commit_free(current_commit); | |
443d5674 ET |
712 | return error; |
713 | } | |
714 | ||
f152f8ac ET |
715 | GIT_INLINE(int) rebase_movenext(git_rebase *rebase) |
716 | { | |
717 | size_t next = rebase->started ? rebase->current + 1 : 0; | |
718 | ||
719 | if (next == git_array_size(rebase->operations)) | |
720 | return GIT_ITEROVER; | |
721 | ||
722 | rebase->started = 1; | |
723 | rebase->current = next; | |
724 | ||
725 | return 0; | |
726 | } | |
727 | ||
950a7091 | 728 | static int rebase_next_merge( |
f152f8ac | 729 | git_rebase_operation **out, |
b6b636a7 | 730 | git_rebase *rebase, |
443d5674 | 731 | git_checkout_options *given_checkout_opts) |
950a7091 | 732 | { |
f152f8ac | 733 | git_buf path = GIT_BUF_INIT; |
443d5674 | 734 | git_checkout_options checkout_opts = {0}; |
f152f8ac | 735 | git_commit *current_commit = NULL, *parent_commit = NULL; |
950a7091 ET |
736 | git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; |
737 | git_index *index = NULL; | |
f152f8ac ET |
738 | git_rebase_operation *operation; |
739 | char current_idstr[GIT_OID_HEXSZ]; | |
950a7091 ET |
740 | unsigned int parent_count; |
741 | int error; | |
742 | ||
f152f8ac | 743 | *out = NULL; |
950a7091 | 744 | |
f152f8ac | 745 | if ((error = rebase_movenext(rebase)) < 0) |
950a7091 ET |
746 | goto done; |
747 | ||
f152f8ac | 748 | operation = git_array_get(rebase->operations, rebase->current); |
443d5674 | 749 | |
f152f8ac ET |
750 | if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || |
751 | (error = git_commit_tree(¤t_tree, current_commit)) < 0 || | |
b6b636a7 | 752 | (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0) |
950a7091 ET |
753 | goto done; |
754 | ||
f152f8ac | 755 | if ((parent_count = git_commit_parentcount(current_commit)) > 1) { |
950a7091 ET |
756 | giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); |
757 | error = -1; | |
758 | goto done; | |
759 | } else if (parent_count) { | |
f152f8ac | 760 | if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || |
950a7091 ET |
761 | (error = git_commit_tree(&parent_tree, parent_commit)) < 0) |
762 | goto done; | |
763 | } | |
764 | ||
f152f8ac ET |
765 | git_oid_fmt(current_idstr, &operation->id); |
766 | ||
767 | if ((error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 || | |
768 | (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0) | |
950a7091 ET |
769 | goto done; |
770 | ||
b6b636a7 ET |
771 | if ((error = normalize_checkout_opts(rebase, &checkout_opts, given_checkout_opts)) < 0 || |
772 | (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 || | |
773 | (error = git_merge__check_result(rebase->repo, index)) < 0 || | |
774 | (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0) | |
950a7091 ET |
775 | goto done; |
776 | ||
f152f8ac | 777 | *out = operation; |
18b439b9 | 778 | |
950a7091 ET |
779 | done: |
780 | git_index_free(index); | |
781 | git_tree_free(current_tree); | |
782 | git_tree_free(head_tree); | |
783 | git_tree_free(parent_tree); | |
950a7091 | 784 | git_commit_free(parent_commit); |
f152f8ac | 785 | git_commit_free(current_commit); |
950a7091 | 786 | git_buf_free(&path); |
950a7091 ET |
787 | |
788 | return error; | |
789 | } | |
790 | ||
443d5674 | 791 | int git_rebase_next( |
f152f8ac | 792 | git_rebase_operation **out, |
b6b636a7 | 793 | git_rebase *rebase, |
443d5674 | 794 | git_checkout_options *checkout_opts) |
950a7091 | 795 | { |
950a7091 ET |
796 | int error; |
797 | ||
b6b636a7 | 798 | assert(out && rebase); |
18b439b9 | 799 | |
b6b636a7 | 800 | switch (rebase->type) { |
950a7091 | 801 | case GIT_REBASE_TYPE_MERGE: |
b6b636a7 | 802 | error = rebase_next_merge(out, rebase, checkout_opts); |
950a7091 ET |
803 | break; |
804 | default: | |
805 | abort(); | |
806 | } | |
807 | ||
a35a9890 ET |
808 | return error; |
809 | } | |
810 | ||
811 | static int rebase_commit_merge( | |
812 | git_oid *commit_id, | |
b6b636a7 | 813 | git_rebase *rebase, |
a35a9890 ET |
814 | const git_signature *author, |
815 | const git_signature *committer, | |
816 | const char *message_encoding, | |
817 | const char *message) | |
818 | { | |
819 | git_index *index = NULL; | |
820 | git_reference *head = NULL; | |
f152f8ac ET |
821 | git_commit *current_commit = NULL, *head_commit = NULL, *commit = NULL; |
822 | git_rebase_operation *operation; | |
93a7004c ET |
823 | git_tree *head_tree = NULL, *tree = NULL; |
824 | git_diff *diff = NULL; | |
a35a9890 | 825 | git_oid tree_id; |
a612a25f | 826 | git_buf reflog_msg = GIT_BUF_INIT; |
a35a9890 ET |
827 | char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; |
828 | int error; | |
829 | ||
f152f8ac ET |
830 | operation = git_array_get(rebase->operations, rebase->current); |
831 | assert(operation); | |
a35a9890 | 832 | |
b6b636a7 | 833 | if ((error = git_repository_index(&index, rebase->repo)) < 0) |
a35a9890 ET |
834 | goto done; |
835 | ||
836 | if (git_index_has_conflicts(index)) { | |
837 | giterr_set(GITERR_REBASE, "Conflicts have not been resolved"); | |
838 | error = GIT_EMERGECONFLICT; | |
839 | goto done; | |
840 | } | |
841 | ||
f152f8ac ET |
842 | if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || |
843 | (error = git_repository_head(&head, rebase->repo)) < 0 || | |
a35a9890 | 844 | (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 || |
93a7004c | 845 | (error = git_commit_tree(&head_tree, head_commit)) < 0 || |
b6b636a7 | 846 | (error = git_diff_tree_to_index(&diff, rebase->repo, head_tree, index, NULL)) < 0) |
93a7004c ET |
847 | goto done; |
848 | ||
849 | if (git_diff_num_deltas(diff) == 0) { | |
850 | giterr_set(GITERR_REBASE, "This patch has already been applied"); | |
851 | error = GIT_EAPPLIED; | |
852 | goto done; | |
853 | } | |
854 | ||
855 | if ((error = git_index_write_tree(&tree_id, index)) < 0 || | |
b6b636a7 | 856 | (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) |
a35a9890 ET |
857 | goto done; |
858 | ||
859 | if (!author) | |
f152f8ac | 860 | author = git_commit_author(current_commit); |
a35a9890 ET |
861 | |
862 | if (!message) { | |
f152f8ac ET |
863 | message_encoding = git_commit_message_encoding(current_commit); |
864 | message = git_commit_message(current_commit); | |
a35a9890 ET |
865 | } |
866 | ||
b6b636a7 | 867 | if ((error = git_commit_create(commit_id, rebase->repo, NULL, author, |
a35a9890 | 868 | committer, message_encoding, message, tree, 1, |
a612a25f | 869 | (const git_commit **)&head_commit)) < 0 || |
b6b636a7 | 870 | (error = git_commit_lookup(&commit, rebase->repo, commit_id)) < 0 || |
a612a25f | 871 | (error = git_reference__update_for_commit( |
b6b636a7 | 872 | rebase->repo, NULL, "HEAD", commit_id, committer, "rebase")) < 0) |
a35a9890 ET |
873 | goto done; |
874 | ||
f152f8ac | 875 | git_oid_fmt(old_idstr, git_commit_id(current_commit)); |
a35a9890 ET |
876 | git_oid_fmt(new_idstr, commit_id); |
877 | ||
b6b636a7 | 878 | error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, |
a35a9890 ET |
879 | "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr); |
880 | ||
881 | done: | |
a612a25f ET |
882 | git_buf_free(&reflog_msg); |
883 | git_commit_free(commit); | |
93a7004c | 884 | git_diff_free(diff); |
a35a9890 | 885 | git_tree_free(tree); |
93a7004c | 886 | git_tree_free(head_tree); |
a35a9890 | 887 | git_commit_free(head_commit); |
f152f8ac | 888 | git_commit_free(current_commit); |
a35a9890 ET |
889 | git_reference_free(head); |
890 | git_index_free(index); | |
891 | ||
892 | return error; | |
893 | } | |
894 | ||
895 | int git_rebase_commit( | |
896 | git_oid *id, | |
b6b636a7 | 897 | git_rebase *rebase, |
a35a9890 ET |
898 | const git_signature *author, |
899 | const git_signature *committer, | |
900 | const char *message_encoding, | |
901 | const char *message) | |
902 | { | |
a35a9890 ET |
903 | int error; |
904 | ||
b6b636a7 | 905 | assert(rebase && committer); |
a35a9890 | 906 | |
b6b636a7 | 907 | switch (rebase->type) { |
a35a9890 ET |
908 | case GIT_REBASE_TYPE_MERGE: |
909 | error = rebase_commit_merge( | |
b6b636a7 | 910 | id, rebase, author, committer, message_encoding, message); |
a35a9890 ET |
911 | break; |
912 | default: | |
913 | abort(); | |
914 | } | |
915 | ||
950a7091 ET |
916 | return error; |
917 | } | |
918 | ||
b6b636a7 | 919 | int git_rebase_abort(git_rebase *rebase, const git_signature *signature) |
4fe84d62 | 920 | { |
4fe84d62 ET |
921 | git_reference *orig_head_ref = NULL; |
922 | git_commit *orig_head_commit = NULL; | |
923 | int error; | |
924 | ||
b6b636a7 | 925 | assert(rebase && signature); |
4fe84d62 | 926 | |
b6b636a7 ET |
927 | error = rebase->head_detached ? |
928 | git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, | |
929 | &rebase->orig_head_id, 1, signature, "rebase: aborting") : | |
4fe84d62 | 930 | git_reference_symbolic_create( |
b6b636a7 | 931 | &orig_head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, |
4fe84d62 ET |
932 | signature, "rebase: aborting"); |
933 | ||
934 | if (error < 0) | |
935 | goto done; | |
936 | ||
937 | if ((error = git_commit_lookup( | |
b6b636a7 ET |
938 | &orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 || |
939 | (error = git_reset(rebase->repo, (git_object *)orig_head_commit, | |
4fe84d62 ET |
940 | GIT_RESET_HARD, NULL, signature, NULL)) < 0) |
941 | goto done; | |
942 | ||
b6b636a7 | 943 | error = rebase_cleanup(rebase); |
4fe84d62 ET |
944 | |
945 | done: | |
946 | git_commit_free(orig_head_commit); | |
947 | git_reference_free(orig_head_ref); | |
4fe84d62 ET |
948 | |
949 | return error; | |
950 | } | |
517644cc | 951 | |
5ae9d296 | 952 | static int rebase_copy_note( |
b6b636a7 | 953 | git_rebase *rebase, |
5ae9d296 ET |
954 | git_oid *from, |
955 | git_oid *to, | |
956 | const git_signature *committer, | |
957 | const git_rebase_options *opts) | |
517644cc | 958 | { |
5ae9d296 ET |
959 | git_note *note = NULL; |
960 | git_oid note_id; | |
961 | int error; | |
962 | ||
b6b636a7 | 963 | if ((error = git_note_read(¬e, rebase->repo, opts->rewrite_notes_ref, from)) < 0) { |
5ae9d296 ET |
964 | if (error == GIT_ENOTFOUND) { |
965 | giterr_clear(); | |
966 | error = 0; | |
967 | } | |
968 | ||
969 | goto done; | |
970 | } | |
971 | ||
b6b636a7 | 972 | error = git_note_create(¬e_id, rebase->repo, git_note_author(note), |
5ae9d296 ET |
973 | committer, opts->rewrite_notes_ref, to, git_note_message(note), 0); |
974 | ||
975 | done: | |
976 | git_note_free(note); | |
977 | ||
978 | return error; | |
979 | } | |
980 | ||
981 | static int rebase_copy_notes( | |
b6b636a7 | 982 | git_rebase *rebase, |
5ae9d296 ET |
983 | const git_signature *committer, |
984 | const git_rebase_options *opts) | |
985 | { | |
986 | git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT; | |
987 | char *pair_list, *fromstr, *tostr, *end; | |
988 | git_oid from, to; | |
989 | unsigned int linenum = 1; | |
990 | int error = 0; | |
991 | ||
992 | if (!opts->rewrite_notes_ref) | |
993 | goto done; | |
994 | ||
b6b636a7 | 995 | if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 || |
5ae9d296 ET |
996 | (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0) |
997 | goto done; | |
998 | ||
999 | pair_list = rewritten.ptr; | |
1000 | ||
1001 | while (*pair_list) { | |
1002 | fromstr = pair_list; | |
1003 | ||
1004 | if ((end = strchr(fromstr, '\n')) == NULL) | |
1005 | goto on_error; | |
1006 | ||
1007 | pair_list = end+1; | |
1008 | *end = '\0'; | |
1009 | ||
1010 | if ((end = strchr(fromstr, ' ')) == NULL) | |
1011 | goto on_error; | |
1012 | ||
1013 | tostr = end+1; | |
1014 | *end = '\0'; | |
1015 | ||
1016 | if (strlen(fromstr) != GIT_OID_HEXSZ || | |
1017 | strlen(tostr) != GIT_OID_HEXSZ || | |
1018 | git_oid_fromstr(&from, fromstr) < 0 || | |
1019 | git_oid_fromstr(&to, tostr) < 0) | |
1020 | goto on_error; | |
1021 | ||
b6b636a7 | 1022 | if ((error = rebase_copy_note(rebase, &from, &to, committer, opts)) < 0) |
5ae9d296 ET |
1023 | goto done; |
1024 | ||
1025 | linenum++; | |
1026 | } | |
1027 | ||
1028 | goto done; | |
1029 | ||
1030 | on_error: | |
1031 | giterr_set(GITERR_REBASE, "Invalid rewritten file at line %d", linenum); | |
1032 | error = -1; | |
1033 | ||
1034 | done: | |
1035 | git_buf_free(&rewritten); | |
1036 | git_buf_free(&path); | |
1037 | ||
1038 | return error; | |
1039 | } | |
1040 | ||
1041 | int git_rebase_finish( | |
b6b636a7 | 1042 | git_rebase *rebase, |
5ae9d296 ET |
1043 | const git_signature *signature, |
1044 | const git_rebase_options *given_opts) | |
1045 | { | |
1046 | git_rebase_options opts; | |
517644cc ET |
1047 | git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL; |
1048 | git_commit *terminal_commit = NULL; | |
1049 | git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT; | |
1050 | char onto[GIT_OID_HEXSZ]; | |
1051 | int error; | |
1052 | ||
b6b636a7 | 1053 | assert(rebase); |
517644cc | 1054 | |
b6b636a7 | 1055 | if ((error = rebase_normalize_opts(rebase->repo, &opts, given_opts)) < 0) |
517644cc ET |
1056 | goto done; |
1057 | ||
b6b636a7 | 1058 | git_oid_fmt(onto, &rebase->onto_id); |
517644cc ET |
1059 | |
1060 | if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s", | |
b6b636a7 | 1061 | rebase->orig_head_name, GIT_OID_HEXSZ, onto)) < 0 || |
517644cc | 1062 | (error = git_buf_printf(&head_msg, "rebase finished: returning to %s", |
b6b636a7 ET |
1063 | rebase->orig_head_name)) < 0 || |
1064 | (error = git_repository_head(&terminal_ref, rebase->repo)) < 0 || | |
517644cc | 1065 | (error = git_reference_peel((git_object **)&terminal_commit, |
5ae9d296 ET |
1066 | terminal_ref, GIT_OBJ_COMMIT)) < 0 || |
1067 | (error = git_reference_create_matching(&branch_ref, | |
b6b636a7 ET |
1068 | rebase->repo, rebase->orig_head_name, git_commit_id(terminal_commit), 1, |
1069 | &rebase->orig_head_id, signature, branch_msg.ptr)) < 0 || | |
517644cc | 1070 | (error = git_reference_symbolic_create(&head_ref, |
b6b636a7 | 1071 | rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, |
5ae9d296 | 1072 | signature, head_msg.ptr)) < 0 || |
b6b636a7 | 1073 | (error = rebase_copy_notes(rebase, signature, &opts)) < 0) |
517644cc | 1074 | goto done; |
5ae9d296 | 1075 | |
b6b636a7 | 1076 | error = rebase_cleanup(rebase); |
517644cc ET |
1077 | |
1078 | done: | |
1079 | git_buf_free(&head_msg); | |
1080 | git_buf_free(&branch_msg); | |
1081 | git_commit_free(terminal_commit); | |
1082 | git_reference_free(head_ref); | |
1083 | git_reference_free(branch_ref); | |
1084 | git_reference_free(terminal_ref); | |
5ae9d296 | 1085 | rebase_opts_free(&opts); |
517644cc ET |
1086 | |
1087 | return error; | |
1088 | } | |
1089 | ||
b6b636a7 ET |
1090 | void git_rebase_free(git_rebase *rebase) |
1091 | { | |
1092 | if (rebase == NULL) | |
1093 | return; | |
1094 | ||
f152f8ac | 1095 | git__free(rebase->onto_name); |
b6b636a7 ET |
1096 | git__free(rebase->orig_head_name); |
1097 | git__free(rebase->state_path); | |
f152f8ac ET |
1098 | git_array_clear(rebase->operations); |
1099 | git__free(rebase); | |
b6b636a7 | 1100 | } |