Commit 443d5674 by Edward Thomson Committed by Edward Thomson

git_rebase_next: write conflicts nicely during rebase

parent 950a7091
......@@ -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(&current, path.ptr)) < 0)
goto done;
if ((error = git__strtol32(&state->merge.end, end.ptr, NULL, 10)) < 0)
git_buf_rtrim(&current);
if ((error = git_oid_fromstr(&current_id, current.ptr)) < 0 ||
(error = git_commit_lookup(&state->merge.current, repo, &current_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(&current);
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(&current);
if (state->merge.current)
git_commit_free(state->merge.current);
if ((error = git_oid_fromstr(&current_id, current.ptr)) < 0 ||
(error = git_commit_lookup(&current_commit, repo, &current_id)) < 0 ||
(error = git_commit_tree(&current_tree, current_commit)) < 0 ||
(error = git_commit_lookup(&state->merge.current, repo, &current_id)) < 0 ||
(error = git_commit_tree(&current_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(&current);
......@@ -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();
......
......@@ -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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment