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