diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2014-07-17 11:59:01 -0400 |
---|---|---|
committer | Edward Thomson <ethomson@microsoft.com> | 2014-10-26 22:59:16 -0400 |
commit | 443d5674fe3f2edd4cb51c7657e7ad5063275bff (patch) | |
tree | 3531fd9f481183b492a2dfa3c69da6e413e4edb3 | |
parent | 950a70915930342d18286c6d6350929662a978e2 (diff) | |
download | libgit2-443d5674fe3f2edd4cb51c7657e7ad5063275bff.tar.gz |
git_rebase_next: write conflicts nicely during rebase
-rw-r--r-- | src/rebase.c | 120 | ||||
-rw-r--r-- | tests/rebase/merge.c | 59 |
2 files changed, 161 insertions, 18 deletions
diff --git a/src/rebase.c b/src/rebase.c index 1062b2a2b..676a803cc 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -49,6 +49,9 @@ typedef enum { struct git_rebase_state_merge { int32_t msgnum; int32_t end; + char *onto_name; + + git_commit *current; }; typedef struct { @@ -105,7 +108,9 @@ done: static int rebase_state_merge(git_rebase_state *state, git_repository *repo) { - git_buf path = GIT_BUF_INIT, msgnum = GIT_BUF_INIT, end = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT, msgnum = GIT_BUF_INIT, end = GIT_BUF_INIT, + onto_name = GIT_BUF_INIT, current = GIT_BUF_INIT; + git_oid current_id; int state_path_len, error; GIT_UNUSED(repo); @@ -115,10 +120,34 @@ static int rebase_state_merge(git_rebase_state *state, git_repository *repo) state_path_len = git_buf_len(&path); + /* Read 'end' */ + if ((error = git_buf_joinpath(&path, path.ptr, END_FILE)) < 0 || + (error = git_futils_readbuffer(&end, path.ptr)) < 0) + goto done; + + git_buf_rtrim(&end); + + if ((error = git__strtol32(&state->merge.end, end.ptr, NULL, 10)) < 0) + goto done; + + /* Read 'onto_name' */ + git_buf_truncate(&path, state_path_len); + + if ((error = git_buf_joinpath(&path, path.ptr, ONTO_NAME_FILE)) < 0 || + (error = git_futils_readbuffer(&onto_name, path.ptr)) < 0) + goto done; + + git_buf_rtrim(&onto_name); + + state->merge.onto_name = git_buf_detach(&onto_name); + + /* Read 'msgnum' if it exists, otherwise let msgnum = 0 */ + git_buf_truncate(&path, state_path_len); + if ((error = git_buf_joinpath(&path, path.ptr, MSGNUM_FILE)) < 0) goto done; - if (git_path_isfile(path.ptr)) { + if (git_path_exists(path.ptr)) { if ((error = git_futils_readbuffer(&msgnum, path.ptr)) < 0) goto done; @@ -128,21 +157,30 @@ static int rebase_state_merge(git_rebase_state *state, git_repository *repo) goto done; } + + /* Read 'current' if it exists, otherwise let current = null */ git_buf_truncate(&path, state_path_len); - if ((error = git_buf_joinpath(&path, path.ptr, END_FILE)) < 0 || - (error = git_futils_readbuffer(&end, path.ptr)) < 0) + if ((error = git_buf_joinpath(&path, path.ptr, CURRENT_FILE)) < 0) goto done; - git_buf_rtrim(&end); + if (git_path_exists(path.ptr)) { + if ((error = git_futils_readbuffer(¤t, path.ptr)) < 0) + goto done; - if ((error = git__strtol32(&state->merge.end, end.ptr, NULL, 10)) < 0) - goto done; + git_buf_rtrim(¤t); + + if ((error = git_oid_fromstr(¤t_id, current.ptr)) < 0 || + (error = git_commit_lookup(&state->merge.current, repo, ¤t_id)) < 0) + goto done; + } done: git_buf_free(&path); git_buf_free(&msgnum); git_buf_free(&end); + git_buf_free(&onto_name); + git_buf_free(¤t); return error; } @@ -230,6 +268,11 @@ static void rebase_state_free(git_rebase_state *state) if (state == NULL) return; + if (state->type == GIT_REBASE_TYPE_MERGE) { + git__free(state->merge.onto_name); + git_commit_free(state->merge.current); + } + git__free(state->orig_head_name); git__free(state->state_path); } @@ -492,14 +535,50 @@ done: return error; } +static int normalize_checkout_opts( + git_repository *repo, + git_checkout_options *checkout_opts, + const git_checkout_options *given_checkout_opts, + const git_rebase_state *state) +{ + int error = 0; + + GIT_UNUSED(repo); + + if (given_checkout_opts != NULL) + memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options)); + else { + git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options)); + } + + if (!checkout_opts->ancestor_label) + checkout_opts->ancestor_label = "ancestor"; + + if (state->type == GIT_REBASE_TYPE_MERGE) { + if (!checkout_opts->our_label) + checkout_opts->our_label = state->merge.onto_name; + + if (!checkout_opts->their_label) + checkout_opts->their_label = git_commit_summary(state->merge.current); + } else { + abort(); + } + + return error; +} + static int rebase_next_merge( git_repository *repo, git_rebase_state *state, - git_checkout_options *checkout_opts) + git_checkout_options *given_checkout_opts) { git_buf path = GIT_BUF_INIT, current = GIT_BUF_INIT; + git_checkout_options checkout_opts = {0}; git_oid current_id; - git_commit *current_commit = NULL, *parent_commit = NULL; + git_commit *parent_commit = NULL; git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; git_index *index = NULL; unsigned int parent_count; @@ -517,18 +596,21 @@ static int rebase_next_merge( git_buf_rtrim(¤t); + if (state->merge.current) + git_commit_free(state->merge.current); + if ((error = git_oid_fromstr(¤t_id, current.ptr)) < 0 || - (error = git_commit_lookup(¤t_commit, repo, ¤t_id)) < 0 || - (error = git_commit_tree(¤t_tree, current_commit)) < 0 || + (error = git_commit_lookup(&state->merge.current, repo, ¤t_id)) < 0 || + (error = git_commit_tree(¤t_tree, state->merge.current)) < 0 || (error = git_repository_head_tree(&head_tree, repo)) < 0) goto done; - if ((parent_count = git_commit_parentcount(current_commit)) > 1) { + if ((parent_count = git_commit_parentcount(state->merge.current)) > 1) { giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { - if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || + if ((error = git_commit_parent(&parent_commit, state->merge.current, 0)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0) goto done; } @@ -537,9 +619,10 @@ static int rebase_next_merge( (error = rebase_setupfile(repo, CURRENT_FILE, "%s\n", current.ptr)) < 0) goto done; - if ((error = git_merge_trees(&index, repo, parent_tree, head_tree, current_tree, NULL)) < 0 || + if ((error = normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts, state)) < 0 || + (error = git_merge_trees(&index, repo, parent_tree, head_tree, current_tree, NULL)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || - (error = git_checkout_index(repo, index, checkout_opts)) < 0) + (error = git_checkout_index(repo, index, &checkout_opts)) < 0) goto done; done: @@ -547,7 +630,6 @@ done: git_tree_free(current_tree); git_tree_free(head_tree); git_tree_free(parent_tree); - git_commit_free(current_commit); git_commit_free(parent_commit); git_buf_free(&path); git_buf_free(¤t); @@ -555,7 +637,9 @@ done: return error; } -int git_rebase_next(git_repository *repo, git_checkout_options *opts) +int git_rebase_next( + git_repository *repo, + git_checkout_options *checkout_opts) { git_rebase_state state = GIT_REBASE_STATE_INIT; int error; @@ -567,7 +651,7 @@ int git_rebase_next(git_repository *repo, git_checkout_options *opts) switch (state.type) { case GIT_REBASE_TYPE_MERGE: - error = rebase_next_merge(repo, &state, opts); + error = rebase_next_merge(repo, &state, checkout_opts); break; default: abort(); diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c index e44fdd3f6..506c411ed 100644 --- a/tests/rebase/merge.c +++ b/tests/rebase/merge.c @@ -57,3 +57,62 @@ void test_rebase_merge__next(void) git_reference_free(branch_ref); git_reference_free(upstream_ref); } + +void test_rebase_merge__next_with_conflicts(void) +{ + git_reference *branch_ref, *upstream_ref; + git_merge_head *branch_head, *upstream_head; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_status_list *status_list; + const git_status_entry *status_entry; + + const char *expected_merge = +"ASPARAGUS SOUP.\n" +"\n" +"<<<<<<< master\n" +"TAKE FOUR LARGE BUNCHES of asparagus, scrape it nicely, cut off one inch\n" +"OF THE TOPS, and lay them in water, chop the stalks and put them on the\n" +"FIRE WITH A PIECE OF BACON, a large onion cut up, and pepper and salt;\n" +"ADD TWO QUARTS OF WATER, boil them till the stalks are quite soft, then\n" +"PULP THEM THROUGH A SIEVE, and strain the water to it, which must be put\n" +"=======\n" +"Take four large bunches of asparagus, scrape it nicely, CUT OFF ONE INCH\n" +"of the tops, and lay them in water, chop the stalks and PUT THEM ON THE\n" +"fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" +"add two quarts of water, boil them till the stalks are quite soft, then\n" +"pulp them through a sieve, and strain the water to it, which must be put\n" +">>>>>>> Conflicting modification 1 to asparagus\n" +"back in the pot; put into it a chicken cut up, with the tops of\n" +"asparagus which had been laid by, boil it until these last articles are\n" +"sufficiently done, thicken with flour, butter and milk, and serve it up.\n"; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + 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_merge_head_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_merge_head_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase(repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(repo, &checkout_opts)); + + cl_assert_equal_file("33f915f9e4dbd9f4b24430e48731a59b45b15500\n", 41, "rebase/.git/rebase-merge/current"); + cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum"); + + cl_git_pass(git_status_list_new(&status_list, repo, NULL)); + cl_assert_equal_i(1, git_status_list_entrycount(status_list)); + cl_assert(status_entry = git_status_byindex(status_list, 0)); + + cl_assert_equal_s("asparagus.txt", status_entry->head_to_index->new_file.path); + + cl_assert_equal_file(expected_merge, strlen(expected_merge), "rebase/asparagus.txt"); + + git_status_list_free(status_list); + git_merge_head_free(branch_head); + git_merge_head_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); +} + |