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