diff --git a/include/git2/rebase.h b/include/git2/rebase.h index cf1b00fd9..4fda1fd2f 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -38,6 +38,15 @@ typedef struct { */ int quiet; + /** + * Used by `git_rebase_init`, this will begin an in-memory rebase, + * which will allow callers to step through the rebase operations and + * commit the rebased changes, but will not rewind HEAD or update the + * repository to be in a rebasing state. This will not interfere with + * the working directory (if there is one). + */ + int inmemory; + /** * Used by `git_rebase_finish`, this is the name of the notes reference * used to rewrite notes for rebased commits when finishing the rebase; @@ -48,6 +57,11 @@ typedef struct { */ const char *rewrite_notes_ref; + /** + * Options to control how trees are merged during `git_rebase_next`. + */ + git_merge_options merge_options; + /** * Options to control how files are written during `git_rebase_init`, * `git_checkout_next` and `git_checkout_abort`. Note that a minimum @@ -101,7 +115,8 @@ typedef enum { #define GIT_REBASE_OPTIONS_VERSION 1 #define GIT_REBASE_OPTIONS_INIT \ - {GIT_REBASE_OPTIONS_VERSION, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT} + { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \ + GIT_CHECKOUT_OPTIONS_INIT} /** Indicates that a rebase operation is not (yet) in progress. */ #define GIT_REBASE_NO_OPERATION SIZE_MAX @@ -127,6 +142,12 @@ typedef struct { * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`. */ const char *exec; + + /** + * The index that is the result of an operation. + * This is set only for in-memory rebases. + */ + git_index *index; } git_rebase_operation; /** diff --git a/src/rebase.c b/src/rebase.c index 17536c030..52a6df032 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -63,17 +63,23 @@ struct git_rebase { char *state_path; int head_detached : 1, + inmemory : 1, quiet : 1, started : 1; - char *orig_head_name; + git_array_t(git_rebase_operation) operations; + size_t current; + + /* Used by in-memory rebase */ + git_commit *last_commit; + git_index *last_index; + + /* Used by regular (not in-memory) merge-style rebase */ git_oid orig_head_id; + char *orig_head_name; git_oid onto_id; char *onto_name; - - git_array_t(git_rebase_operation) operations; - size_t current; }; #define GIT_REBASE_STATE_INIT {0} @@ -393,6 +399,9 @@ done: static int rebase_cleanup(git_rebase *rebase) { + if (!rebase || rebase->inmemory) + return 0; + return git_path_isdir(rebase->state_path) ? git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : 0; @@ -601,59 +610,63 @@ static int rebase_init_merge( const git_annotated_commit *upstream, const git_annotated_commit *onto) { - if (rebase_init_operations(rebase, repo, branch, upstream, onto) < 0) - return -1; + git_reference *head_ref = NULL; + git_commit *onto_commit = NULL; + git_buf reflog = GIT_BUF_INIT; + git_buf state_path = GIT_BUF_INIT; + int error; + + GIT_UNUSED(upstream); + + if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) + goto done; + + rebase->state_path = git_buf_detach(&state_path); + GITERR_CHECK_ALLOC(rebase->state_path); + + rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); + GITERR_CHECK_ALLOC(rebase->orig_head_name); rebase->onto_name = git__strdup(rebase_onto_name(onto)); GITERR_CHECK_ALLOC(rebase->onto_name); - return 0; + rebase->quiet = rebase->options.quiet; + + git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); + git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); + + if ((error = rebase_setupfiles(rebase)) < 0 || + (error = git_buf_printf(&reflog, + "rebase: checkout %s", rebase_onto_name(onto))) < 0 || + (error = git_commit_lookup( + &onto_commit, repo, git_annotated_commit_id(onto))) < 0 || + (error = git_checkout_tree(repo, + (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || + (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, + git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) + goto done; + +done: + git_reference_free(head_ref); + git_commit_free(onto_commit); + git_buf_free(&reflog); + git_buf_free(&state_path); + + return error; } -static int rebase_init( +static int rebase_init_inmemory( git_rebase *rebase, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto) { - git_reference *head_ref = NULL; - git_annotated_commit *head_branch = NULL; - git_buf state_path = GIT_BUF_INIT; - int error; + GIT_UNUSED(branch); + GIT_UNUSED(upstream); - if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) - goto done; - - if (!branch) { - if ((error = git_repository_head(&head_ref, repo)) < 0 || - (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) - goto done; - - branch = head_branch; - } - - rebase->repo = repo; - rebase->type = GIT_REBASE_TYPE_MERGE; - rebase->state_path = git_buf_detach(&state_path); - rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); - rebase->quiet = rebase->options.quiet; - - git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); - git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); - - if (!rebase->orig_head_name || !rebase->state_path) - return -1; - - error = rebase_init_merge(rebase, repo, branch, upstream, onto); - - git_buf_free(&state_path); - -done: - git_reference_free(head_ref); - git_annotated_commit_free(head_branch); - - return error; + return git_commit_lookup( + &rebase->last_commit, repo, git_annotated_commit_id(onto)); } int git_rebase_init( @@ -665,9 +678,9 @@ int git_rebase_init( const git_rebase_options *given_opts) { git_rebase *rebase = NULL; - git_buf reflog = GIT_BUF_INIT; - git_commit *onto_commit = NULL; + git_annotated_commit *head_branch = NULL; git_reference *head_ref = NULL; + bool inmemory = (given_opts && given_opts->inmemory); int error; assert(repo && (upstream || onto)); @@ -677,39 +690,51 @@ int git_rebase_init( if (!onto) onto = upstream; - if ((error = rebase_check_versions(given_opts)) < 0 || - (error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || - (error = rebase_ensure_not_in_progress(repo)) < 0 || - (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0 || - (error = git_commit_lookup( - &onto_commit, repo, git_annotated_commit_id(onto))) < 0) - return error; - - rebase = rebase_alloc(given_opts); - - if ((error = rebase_init( - rebase, repo, branch, upstream, onto)) < 0 || - (error = rebase_setupfiles(rebase)) < 0 || - (error = git_buf_printf(&reflog, - "rebase: checkout %s", rebase_onto_name(onto))) < 0 || - (error = git_checkout_tree( - repo, (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || - (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, - git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) + if ((error = rebase_check_versions(given_opts)) < 0) goto done; - *out = rebase; + if (!inmemory) { + if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || + (error = rebase_ensure_not_in_progress(repo)) < 0 || + (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0) + goto done; + } + + if (!branch) { + if ((error = git_repository_head(&head_ref, repo)) < 0 || + (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) + goto done; + + branch = head_branch; + } + + rebase = rebase_alloc(given_opts); + GITERR_CHECK_ALLOC(rebase); + + rebase->repo = repo; + rebase->inmemory = inmemory; + rebase->type = GIT_REBASE_TYPE_MERGE; + + if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0) + goto done; + + if (inmemory) + error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); + else + rebase_init_merge(rebase, repo, branch ,upstream, onto); + + if (error == 0) + *out = rebase; done: git_reference_free(head_ref); + git_annotated_commit_free(head_branch); + if (error < 0) { rebase_cleanup(rebase); git_rebase_free(rebase); } - git_commit_free(onto_commit); - git_buf_free(&reflog); - return error; } @@ -764,9 +789,6 @@ static int rebase_next_merge( *out = NULL; - if ((error = rebase_movenext(rebase)) < 0) - goto done; - operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || @@ -791,7 +813,7 @@ static int rebase_next_merge( if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || - (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 || + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) @@ -812,6 +834,46 @@ done: return error; } +static int rebase_next_inmemory( + git_rebase_operation **out, + git_rebase *rebase) +{ + git_commit *current_commit = NULL, *parent_commit = NULL; + git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; + git_rebase_operation *operation; + git_index *index = NULL; + int error; + + *out = NULL; + + operation = git_array_get(rebase->operations, rebase->current); + + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(¤t_tree, current_commit)) < 0 || + (error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || + (error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) + goto done; + + git_index_free(rebase->last_index); + rebase->last_index = index; + operation->index = index; + index = NULL; + + *out = operation; + +done: + git_commit_free(current_commit); + git_commit_free(parent_commit); + git_tree_free(current_tree); + git_tree_free(head_tree); + git_tree_free(parent_tree); + git_index_free(index); + + return error; +} + int git_rebase_next( git_rebase_operation **out, git_rebase *rebase) @@ -820,14 +882,79 @@ int git_rebase_next( assert(out && rebase); - switch (rebase->type) { - case GIT_REBASE_TYPE_MERGE: + if ((error = rebase_movenext(rebase)) < 0) + return error; + + if (rebase->inmemory) + error = rebase_next_inmemory(out, rebase); + else if (rebase->type == GIT_REBASE_TYPE_MERGE) error = rebase_next_merge(out, rebase); - break; - default: + else abort(); + + return error; +} + +static int rebase_commit__create( + git_commit **out, + git_rebase *rebase, + git_index *index, + git_commit *parent_commit, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_rebase_operation *operation; + git_commit *current_commit = NULL, *commit = NULL; + git_tree *parent_tree = NULL, *tree = NULL; + git_oid tree_id, commit_id; + int error; + + operation = git_array_get(rebase->operations, rebase->current); + + if (git_index_has_conflicts(index)) { + giterr_set(GITERR_REBASE, "conflicts have not been resolved"); + error = GIT_EUNMERGED; + goto done; } + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || + (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 || + (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) + goto done; + + if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) { + giterr_set(GITERR_REBASE, "this patch has already been applied"); + error = GIT_EAPPLIED; + goto done; + } + + if (!author) + author = git_commit_author(current_commit); + + if (!message) { + message_encoding = git_commit_message_encoding(current_commit); + message = git_commit_message(current_commit); + } + + if ((error = git_commit_create(&commit_id, rebase->repo, NULL, author, + committer, message_encoding, message, tree, 1, + (const git_commit **)&parent_commit)) < 0 || + (error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0) + goto done; + + *out = commit; + +done: + if (error < 0) + git_commit_free(commit); + + git_commit_free(current_commit); + git_tree_free(parent_tree); + git_tree_free(tree); + return error; } @@ -839,79 +966,77 @@ static int rebase_commit_merge( const char *message_encoding, const char *message) { - git_index *index = NULL; - git_reference *head = NULL; - git_commit *current_commit = NULL, *head_commit = NULL, *commit = NULL; git_rebase_operation *operation; - git_tree *head_tree = NULL, *tree = NULL; - git_diff *diff = NULL; - git_oid tree_id; - git_buf reflog_msg = GIT_BUF_INIT; + git_reference *head = NULL; + git_commit *head_commit = NULL, *commit = NULL; + git_index *index = NULL; char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; int error; operation = git_array_get(rebase->operations, rebase->current); assert(operation); - if ((error = git_repository_index(&index, rebase->repo)) < 0) - goto done; - - if (git_index_has_conflicts(index)) { - giterr_set(GITERR_REBASE, "Conflicts have not been resolved"); - error = GIT_EUNMERGED; - goto done; - } - if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || - (error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_repository_head(&head, rebase->repo)) < 0 || (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 || - (error = git_commit_tree(&head_tree, head_commit)) < 0 || - (error = git_diff_tree_to_index(&diff, rebase->repo, head_tree, index, NULL)) < 0) - goto done; - - if (git_diff_num_deltas(diff) == 0) { - giterr_set(GITERR_REBASE, "This patch has already been applied"); - error = GIT_EAPPLIED; - goto done; - } - - if ((error = git_index_write_tree(&tree_id, index)) < 0 || - (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) - goto done; - - if (!author) - author = git_commit_author(current_commit); - - if (!message) { - message_encoding = git_commit_message_encoding(current_commit); - message = git_commit_message(current_commit); - } - - if ((error = git_commit_create(commit_id, rebase->repo, NULL, author, - committer, message_encoding, message, tree, 1, - (const git_commit **)&head_commit)) < 0 || - (error = git_commit_lookup(&commit, rebase->repo, commit_id)) < 0 || + (error = git_repository_index(&index, rebase->repo)) < 0 || + (error = rebase_commit__create(&commit, rebase, index, head_commit, + author, committer, message_encoding, message)) < 0 || (error = git_reference__update_for_commit( - rebase->repo, NULL, "HEAD", commit_id, "rebase")) < 0) + rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0) goto done; - git_oid_fmt(old_idstr, git_commit_id(current_commit)); - git_oid_fmt(new_idstr, commit_id); + git_oid_fmt(old_idstr, &operation->id); + git_oid_fmt(new_idstr, git_commit_id(commit)); - error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, - "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr); + if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, + "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr)) < 0) + goto done; + + git_oid_cpy(commit_id, git_commit_id(commit)); done: - git_buf_free(&reflog_msg); - git_commit_free(commit); - git_diff_free(diff); - git_tree_free(tree); - git_tree_free(head_tree); - git_commit_free(head_commit); - git_commit_free(current_commit); - git_reference_free(head); git_index_free(index); + git_reference_free(head); + git_commit_free(head_commit); + git_commit_free(commit); + return error; +} + +static int rebase_commit_inmemory( + git_oid *commit_id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_rebase_operation *operation; + git_commit *commit = NULL; + int error = 0; + + operation = git_array_get(rebase->operations, rebase->current); + + assert(operation); + assert(operation->index); + assert(rebase->last_commit); + + if ((error = rebase_commit__create(&commit, rebase, operation->index, + rebase->last_commit, author, committer, message_encoding, message)) < 0) + goto done; + + git_index_free(rebase->last_index); + operation->index = NULL; + rebase->last_index = NULL; + + git_commit_free(rebase->last_commit); + rebase->last_commit = commit; + + git_oid_cpy(commit_id, git_commit_id(commit)); + +done: + if (error < 0) + git_commit_free(commit); return error; } @@ -928,14 +1053,14 @@ int git_rebase_commit( assert(rebase && committer); - switch (rebase->type) { - case GIT_REBASE_TYPE_MERGE: + if (rebase->inmemory) + error = rebase_commit_inmemory( + id, rebase, author, committer, message_encoding, message); + else if (rebase->type == GIT_REBASE_TYPE_MERGE) error = rebase_commit_merge( id, rebase, author, committer, message_encoding, message); - break; - default: + else abort(); - } return error; } @@ -948,6 +1073,9 @@ int git_rebase_abort(git_rebase *rebase) assert(rebase); + if (rebase->inmemory) + return 0; + error = rebase->head_detached ? git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, &rebase->orig_head_id, 1, "rebase: aborting") : @@ -1125,6 +1253,9 @@ int git_rebase_finish( assert(rebase); + if (rebase->inmemory) + return 0; + git_oid_fmt(onto, &rebase->onto_id); if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s", @@ -1182,6 +1313,8 @@ void git_rebase_free(git_rebase *rebase) if (rebase == NULL) return; + git_index_free(rebase->last_index); + git_commit_free(rebase->last_commit); git__free(rebase->onto_name); git__free(rebase->orig_head_name); git__free(rebase->state_path); diff --git a/tests/rebase/inmemory.c b/tests/rebase/inmemory.c new file mode 100644 index 000000000..c7942d6af --- /dev/null +++ b/tests/rebase/inmemory.c @@ -0,0 +1,114 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "posix.h" + +#include + +static git_repository *repo; + +// Fixture setup and teardown +void test_rebase_inmemory__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); +} + +void test_rebase_inmemory__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_rebase_inmemory__not_in_rebase_state(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + + opts.inmemory = true; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + git_rebase_free(rebase); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + + git_reference_free(branch_ref); + git_reference_free(upstream_ref); +} + +void test_rebase_inmemory__can_resolve_conflicts(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_status_list *status_list; + git_oid pick_id, commit_id, expected_commit_id; + git_signature *signature; + git_index *repo_index; + git_index_entry resolution = {{0}}; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + + cl_git_pass(git_signature_new(&signature, + "Rebaser", "rebaser@rebaser.rb", 1405694510, 0)); + + opts.inmemory = true; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); + + git_oid_fromstr(&pick_id, "33f915f9e4dbd9f4b24430e48731a59b45b15500"); + + cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type); + cl_assert_equal_oid(&pick_id, &rebase_operation->id); + + /* ensure that we did not do anything stupid to the workdir or repo index */ + cl_git_pass(git_repository_index(&repo_index, repo)); + cl_assert(!git_index_has_conflicts(repo_index)); + + cl_git_pass(git_status_list_new(&status_list, repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(status_list)); + + /* but that the index returned from rebase does have conflicts */ + cl_assert(git_index_has_conflicts(rebase_operation->index)); + + cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); + + /* ensure that we can work with the in-memory index to resolve the conflict */ + resolution.path = "asparagus.txt"; + resolution.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9"); + cl_git_pass(git_index_conflict_remove(rebase_operation->index, "asparagus.txt")); + cl_git_pass(git_index_add(rebase_operation->index, &resolution)); + + /* and finally create a commit for the resolved rebase operation */ + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); + + cl_git_pass(git_oid_fromstr(&expected_commit_id, "db7af47222181e548810da2ab5fec0e9357c5637")); + cl_assert_equal_oid(&commit_id, &expected_commit_id); + + git_signature_free(signature); + git_status_list_free(status_list); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_index_free(repo_index); + git_rebase_free(rebase); +} diff --git a/tests/rebase/iterator.c b/tests/rebase/iterator.c index acf2a92db..e761d39d3 100644 --- a/tests/rebase/iterator.c +++ b/tests/rebase/iterator.c @@ -46,26 +46,32 @@ static void test_operations(git_rebase *rebase, size_t expected_current) } } -void test_rebase_iterator__iterates(void) +void test_iterator(bool inmemory) { git_rebase *rebase; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; git_reference *branch_ref, *upstream_ref; git_annotated_commit *branch_head, *upstream_head; git_rebase_operation *rebase_operation; git_oid commit_id; int error; + opts.inmemory = inmemory; + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); - cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL)); + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); test_operations(rebase, GIT_REBASE_NO_OPERATION); - git_rebase_free(rebase); - cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + if (!inmemory) { + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + } + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); @@ -81,8 +87,10 @@ void test_rebase_iterator__iterates(void) NULL, NULL)); test_operations(rebase, 2); - git_rebase_free(rebase); - cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + if (!inmemory) { + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + } cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, @@ -104,3 +112,13 @@ void test_rebase_iterator__iterates(void) git_reference_free(upstream_ref); git_rebase_free(rebase); } + +void test_rebase_iterator__iterates(void) +{ + test_iterator(false); +} + +void test_rebase_iterator__iterates_inmemory(void) +{ + test_iterator(true); +} diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c index 33eadb7ed..c60113b64 100644 --- a/tests/rebase/merge.c +++ b/tests/rebase/merge.c @@ -565,3 +565,33 @@ void test_rebase_merge__custom_checkout_options(void) git_reference_free(upstream_ref); git_rebase_free(rebase); } + +void test_rebase_merge__custom_merge_options(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_options rebase_options = GIT_REBASE_OPTIONS_INIT; + git_rebase_operation *rebase_operation; + + rebase_options.merge_options.flags |= + GIT_MERGE_FAIL_ON_CONFLICT | + GIT_MERGE_SKIP_REUC; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_options)); + + cl_git_fail_with(GIT_EMERGECONFLICT, git_rebase_next(&rebase_operation, rebase)); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} +