Commit a35a9890 by Edward Thomson Committed by Edward Thomson

Introduce git_rebase_commit

Commit the current patch of a rebase process.
parent 443d5674
......@@ -82,6 +82,34 @@ GIT_EXTERN(int) git_rebase_next(
git_checkout_options *checkout_opts);
/**
* Commits the current patch. You must have resolved any conflicts that
* were introduced during the patch application from the `git_rebase_next`
* invocation.
*
* @param id Pointer in which to store the OID of the newly created commit
* @param repo The repository with the in-progress rebase
* @param author The author of the updated commit, or NULL to keep the
* author from the original commit
* @param committer The committer of the rebase
* @param message_encoding The encoding for the message in the commit,
* represented with a standard encoding name. If message is NULL,
* this should also be NULL, and the encoding from the original
* commit will be maintained. If message is specified, this may be
* NULL to indicate that "UTF-8" is to be used.
* @param message The message for this commit, or NULL to keep the message
* from the original commit
* @return Zero on success, GIT_EUNMERGED if there are unmerged changes in
* the index, -1 on failure.
*/
GIT_EXTERN(int) git_rebase_commit(
git_oid *id,
git_repository *repo,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message);
/**
* Aborts a rebase that is currently in progress, resetting the repository
* and working directory to their state before rebase began.
*
......
......@@ -33,6 +33,7 @@
#define END_FILE "end"
#define CMT_FILE_FMT "cmt.%d"
#define CURRENT_FILE "current"
#define REWRITTEN_FILE "rewritten"
#define ORIG_DETACHED_HEAD "detached HEAD"
......@@ -284,7 +285,7 @@ static int rebase_finish(git_rebase_state *state)
0;
}
static int rebase_setupfile(git_repository *repo, const char *filename, const char *fmt, ...)
static int rebase_setupfile(git_repository *repo, const char *filename, int flags, const char *fmt, ...)
{
git_buf path = GIT_BUF_INIT,
contents = GIT_BUF_INIT;
......@@ -297,7 +298,7 @@ static int rebase_setupfile(git_repository *repo, const char *filename, const ch
if ((error = git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR)) == 0 &&
(error = git_buf_joinpath(&path, path.ptr, filename)) == 0)
error = git_futils_writebuffer(&contents, path.ptr, O_RDWR|O_CREAT, REBASE_FILE_MODE);
error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE);
git_buf_free(&path);
git_buf_free(&contents);
......@@ -361,16 +362,16 @@ static int rebase_setup_merge(
git_buf_printf(&commit_filename, CMT_FILE_FMT, commit_cnt);
git_oid_fmt(id_str, &id);
if ((error = rebase_setupfile(repo, commit_filename.ptr,
if ((error = rebase_setupfile(repo, commit_filename.ptr, -1,
"%.*s\n", GIT_OID_HEXSZ, id_str)) < 0)
goto done;
}
if (error != GIT_ITEROVER ||
(error = rebase_setupfile(repo, END_FILE, "%d\n", commit_cnt)) < 0)
(error = rebase_setupfile(repo, END_FILE, -1, "%d\n", commit_cnt)) < 0)
goto done;
error = rebase_setupfile(repo, ONTO_NAME_FILE, "%s\n",
error = rebase_setupfile(repo, ONTO_NAME_FILE, -1, "%s\n",
rebase_onto_name(onto));
done:
......@@ -405,10 +406,10 @@ static int rebase_setup(
orig_head_name = branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD;
if ((error = rebase_setupfile(repo, HEAD_NAME_FILE, "%s\n", orig_head_name)) < 0 ||
(error = rebase_setupfile(repo, ONTO_FILE, "%s\n", onto->oid_str)) < 0 ||
(error = rebase_setupfile(repo, ORIG_HEAD_FILE, "%s\n", branch->oid_str)) < 0 ||
(error = rebase_setupfile(repo, QUIET_FILE, opts->quiet ? "t\n" : "\n")) < 0)
if ((error = rebase_setupfile(repo, HEAD_NAME_FILE, -1, "%s\n", orig_head_name)) < 0 ||
(error = rebase_setupfile(repo, ONTO_FILE, -1, "%s\n", onto->oid_str)) < 0 ||
(error = rebase_setupfile(repo, ORIG_HEAD_FILE, -1, "%s\n", branch->oid_str)) < 0 ||
(error = rebase_setupfile(repo, QUIET_FILE, -1, opts->quiet ? "t\n" : "\n")) < 0)
goto done;
error = rebase_setup_merge(repo, branch, upstream, onto, opts);
......@@ -615,8 +616,8 @@ static int rebase_next_merge(
goto done;
}
if ((error = rebase_setupfile(repo, MSGNUM_FILE, "%d\n", state->merge.msgnum)) < 0 ||
(error = rebase_setupfile(repo, CURRENT_FILE, "%s\n", current.ptr)) < 0)
if ((error = rebase_setupfile(repo, MSGNUM_FILE, -1, "%d\n", state->merge.msgnum)) < 0 ||
(error = rebase_setupfile(repo, CURRENT_FILE, -1, "%s\n", current.ptr)) < 0)
goto done;
if ((error = normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts, state)) < 0 ||
......@@ -661,6 +662,104 @@ int git_rebase_next(
return error;
}
static int rebase_commit_merge(
git_oid *commit_id,
git_repository *repo,
git_rebase_state *state,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message)
{
git_index *index = NULL;
git_reference *head = NULL;
git_commit *head_commit = NULL;
git_tree *tree = NULL;
git_oid tree_id;
char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ];
int error;
if (!state->merge.msgnum || !state->merge.current) {
giterr_set(GITERR_REBASE, "No rebase-merge state files exist");
error = -1;
goto done;
}
if ((error = git_repository_index(&index, repo)) < 0)
goto done;
if (git_index_has_conflicts(index)) {
giterr_set(GITERR_REBASE, "Conflicts have not been resolved");
error = GIT_EMERGECONFLICT;
goto done;
}
/* TODO: if there are no changes, error with a useful code */
if ((error = git_repository_head(&head, repo)) < 0 ||
(error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 ||
(error = git_index_write_tree(&tree_id, index)) < 0 ||
(error = git_tree_lookup(&tree, repo, &tree_id)) < 0)
goto done;
if (!author)
author = git_commit_author(state->merge.current);
if (!message) {
message_encoding = git_commit_message_encoding(state->merge.current);
message = git_commit_message(state->merge.current);
}
if ((error = git_commit_create(commit_id, repo, "HEAD", author,
committer, message_encoding, message, tree, 1,
(const git_commit **)&head_commit)) < 0)
goto done;
git_oid_fmt(old_idstr, git_commit_id(state->merge.current));
git_oid_fmt(new_idstr, commit_id);
error = rebase_setupfile(repo, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND,
"%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr);
done:
git_tree_free(tree);
git_commit_free(head_commit);
git_reference_free(head);
git_index_free(index);
return error;
}
int git_rebase_commit(
git_oid *id,
git_repository *repo,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message)
{
git_rebase_state state = GIT_REBASE_STATE_INIT;
int error;
assert(repo && committer);
if ((error = rebase_state(&state, repo)) < 0)
goto done;
switch (state.type) {
case GIT_REBASE_TYPE_MERGE:
error = rebase_commit_merge(
id, repo, &state, author, committer, message_encoding, message);
break;
default:
abort();
}
done:
rebase_state_free(&state);
return error;
}
int git_rebase_abort(git_repository *repo, const git_signature *signature)
{
git_rebase_state state = GIT_REBASE_STATE_INIT;
......
......@@ -270,3 +270,14 @@ void git_signature__writebuf(git_buf *buf, const char *header, const git_signatu
(unsigned)sig->when.time, sign, hours, mins);
}
bool git_signature__equal(const git_signature *one, const git_signature *two)
{
assert(one && two);
return
git__strcmp(one->name, two->name) == 0 &&
git__strcmp(one->email, two->email) == 0 &&
one->when.time == two->when.time &&
one->when.offset == two->when.offset;
}
......@@ -14,6 +14,7 @@
int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);
void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig);
bool git_signature__equal(const git_signature *one, const git_signature *two);
int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool);
......
#include "clar_libgit2.h"
#include "git2/rebase.h"
#include "posix.h"
#include "signature.h"
#include <fcntl.h>
......@@ -11,10 +12,13 @@ static git_signature *signature;
void test_rebase_merge__initialize(void)
{
repo = cl_git_sandbox_init("rebase");
cl_git_pass(git_signature_new(&signature,
"Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
}
void test_rebase_merge__cleanup(void)
{
git_signature_free(signature);
cl_git_sandbox_cleanup();
}
......@@ -116,3 +120,90 @@ void test_rebase_merge__next_with_conflicts(void)
git_reference_free(upstream_ref);
}
void test_rebase_merge__commit(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id, tree_id, parent_id;
git_signature *author;
git_commit *commit;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
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_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_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
git_oid_fromstr(&parent_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_assert_equal_i(1, git_commit_parentcount(commit));
cl_assert_equal_oid(&parent_id, git_commit_parent_id(commit, 0));
git_oid_fromstr(&tree_id, "4461379789c777d2a6c1f2ee0e9d6c86731b9992");
cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
cl_assert_equal_s(NULL, git_commit_message_encoding(commit));
cl_assert_equal_s("Modification 1 to beef\n", git_commit_message(commit));
cl_git_pass(git_signature_new(&author,
"Edward Thomson", "ethomson@edwardthomson.com", 1405621769, 0-(4*60)));
cl_assert(git_signature__equal(author, git_commit_author(commit)));
cl_assert(git_signature__equal(signature, git_commit_committer(commit)));
cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94 776e4c48922799f903f03f5f6e51da8b01e4cce0\n", 82, "rebase/.git/rebase-merge/rewritten");
git_signature_free(author);
git_commit_free(commit);
git_merge_head_free(branch_head);
git_merge_head_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
}
void test_rebase_merge__commit_updates_rewritten(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
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_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_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
cl_git_pass(git_rebase_next(repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
cl_assert_equal_file(
"da9c51a23d02d931a486f45ad18cda05cf5d2b94 776e4c48922799f903f03f5f6e51da8b01e4cce0\n"
"8d1f13f93c4995760ac07d129246ac1ff64c0be9 ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a\n",
164, "rebase/.git/rebase-merge/rewritten");
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