mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-20 07:52:43 +00:00
Introduce git_rebase_commit
Commit the current patch of a rebase process.
This commit is contained in:
parent
443d5674fe
commit
a35a9890b0
@ -81,6 +81,34 @@ GIT_EXTERN(int) git_rebase_next(
|
||||
git_repository *repo,
|
||||
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.
|
||||
|
121
src/rebase.c
121
src/rebase.c
@ -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);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user