summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/rebase.h74
-rw-r--r--src/rebase.c11
-rw-r--r--tests/rebase/merge.c51
3 files changed, 111 insertions, 25 deletions
diff --git a/include/git2/rebase.h b/include/git2/rebase.h
index d9cf2318e..3b4f44709 100644
--- a/include/git2/rebase.h
+++ b/include/git2/rebase.h
@@ -39,9 +39,67 @@ typedef struct {
const char *rewrite_notes_ref;
} git_rebase_options;
+/** Type of rebase operation in-progress after calling `git_rebase_next`. */
+typedef enum {
+ /**
+ * The given commit is to be cherry-picked. The client should commit
+ * the changes and continue if there are no conflicts.
+ */
+ GIT_REBASE_OPERATION_PICK = 0,
+
+ /**
+ * The given commit is to be cherry-picked, but the client should prompt
+ * the user to provide an updated commit message.
+ */
+ GIT_REBASE_OPERATION_REWORD,
+
+ /**
+ * The given commit is to be cherry-picked, but the client should stop
+ * to allow the user to edit the changes before committing them.
+ */
+ GIT_REBASE_OPERATION_EDIT,
+
+ /**
+ * The given commit is to be squashed into the previous commit. The
+ * commit message will be merged with the previous message.
+ */
+ GIT_REBASE_OPERATION_SQUASH,
+
+ /**
+ * The given commit is to be squashed into the previous commit. The
+ * commit message from this commit will be discarded.
+ */
+ GIT_REBASE_OPERATION_FIXUP,
+
+ /**
+ * No commit will be cherry-picked. The client should run the given
+ * command and (if successful) continue.
+ */
+ GIT_REBASE_OPERATION_EXEC,
+} git_rebase_operation_t;
+
#define GIT_REBASE_OPTIONS_VERSION 1
#define GIT_REBASE_OPTIONS_INIT {GIT_REBASE_OPTIONS_VERSION}
+typedef struct {
+ /** The type of rebase operation. */
+ unsigned int type;
+
+ union {
+ /**
+ * The commit ID being cherry-picked. This will be populated for
+ * all operations except those of type `GIT_REBASE_OPERATION_EXEC`.
+ */
+ git_oid id;
+
+ /**
+ * The executable the user has requested be run. This will only
+ * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`.
+ */
+ const char *exec;
+ };
+} git_rebase_operation;
+
/**
* Initializes a `git_rebase_options` with default values. Equivalent to
* creating an instance with GIT_REBASE_OPTIONS_INIT.
@@ -78,15 +136,19 @@ GIT_EXTERN(int) git_rebase(
const git_rebase_options *opts);
/**
- * Applies the next patch, updating the index and working directory with the
- * changes. If there are conflicts, you will need to address those before
- * committing the changes.
+ * Performs the next rebase operation and returns the information about it.
+ * If the operation is one that applies a patch (which is any operation except
+ * GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and
+ * working directory will be updated with the changes. If there are conflicts,
+ * you will need to address those before committing the changes.
*
+ * @param out The rebase operation that is to be performed next
* @param repo The repository with a rebase in progress
* @param checkout_opts Options to specify how the patch should be checked out
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_rebase_next(
+ git_rebase_operation *operation,
git_repository *repo,
git_checkout_options *checkout_opts);
@@ -94,7 +156,7 @@ GIT_EXTERN(int) git_rebase_next(
* 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
@@ -105,8 +167,8 @@ GIT_EXTERN(int) git_rebase_next(
* 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
+ * @param message The message for this commit, or NULL to use the message
+ * from the original commit.
* @return Zero on success, GIT_EUNMERGED if there are unmerged changes in
* the index, GIT_EAPPLIED if the current commit has already
* been applied to the upstream and there is nothing to commit,
diff --git a/src/rebase.c b/src/rebase.c
index 3384a14f2..c6bbfbfb8 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -624,6 +624,7 @@ static int normalize_checkout_opts(
}
static int rebase_next_merge(
+ git_rebase_operation *out,
git_repository *repo,
git_rebase_state *state,
git_checkout_options *given_checkout_opts)
@@ -678,6 +679,9 @@ static int rebase_next_merge(
(error = git_checkout_index(repo, index, &checkout_opts)) < 0)
goto done;
+ out->type = GIT_REBASE_OPERATION_PICK;
+ memcpy(&out->id, &current_id, sizeof(git_oid));
+
done:
git_index_free(index);
git_tree_free(current_tree);
@@ -691,20 +695,23 @@ done:
}
int git_rebase_next(
+ git_rebase_operation *out,
git_repository *repo,
git_checkout_options *checkout_opts)
{
git_rebase_state state = GIT_REBASE_STATE_INIT;
int error;
- assert(repo);
+ assert(out && repo);
+
+ memset(out, 0, sizeof(git_rebase_operation));
if ((error = rebase_state(&state, repo)) < 0)
return -1;
switch (state.type) {
case GIT_REBASE_TYPE_MERGE:
- error = rebase_next_merge(repo, &state, checkout_opts);
+ error = rebase_next_merge(out, repo, &state, checkout_opts);
break;
default:
abort();
diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c
index fddab8397..a3bb711b2 100644
--- a/tests/rebase/merge.c
+++ b/tests/rebase/merge.c
@@ -26,10 +26,11 @@ void test_rebase_merge__next(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_status_list *status_list;
const git_status_entry *status_entry;
- git_oid file1_id;
+ git_oid pick_id, file1_id;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
@@ -41,8 +42,12 @@ void test_rebase_merge__next(void)
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_next(&rebase_operation, repo, &checkout_opts));
+ git_oid_fromstr(&pick_id, "da9c51a23d02d931a486f45ad18cda05cf5d2b94");
+
+ cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation.type);
+ cl_assert_equal_oid(&pick_id, &rebase_operation.id);
cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/current");
cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum");
@@ -66,9 +71,11 @@ void test_rebase_merge__next_with_conflicts(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_status_list *status_list;
const git_status_entry *status_entry;
+ git_oid pick_id;
const char *expected_merge =
"ASPARAGUS SOUP.\n"
@@ -100,8 +107,12 @@ void test_rebase_merge__next_with_conflicts(void)
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_next(&rebase_operation, repo, &checkout_opts));
+
+ 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);
cl_assert_equal_file("33f915f9e4dbd9f4b24430e48731a59b45b15500\n", 41, "rebase/.git/rebase-merge/current");
cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum");
@@ -124,6 +135,7 @@ void test_rebase_merge__next_stops_with_iterover(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id;
int error;
@@ -138,27 +150,27 @@ void test_rebase_merge__next_stops_with_iterover(void)
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_next(&rebase_operation, 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_next(&rebase_operation, 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_next(&rebase_operation, 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_next(&rebase_operation, 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_next(&rebase_operation, repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
- cl_git_fail(error = git_rebase_next(repo, &checkout_opts));
+ cl_git_fail(error = git_rebase_next(&rebase_operation, repo, &checkout_opts));
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
@@ -174,6 +186,7 @@ void test_rebase_merge__commit(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id, tree_id, parent_id;
git_signature *author;
@@ -191,7 +204,7 @@ void test_rebase_merge__commit(void)
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_next(&rebase_operation, repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
@@ -233,6 +246,7 @@ void test_rebase_merge__commit_updates_rewritten(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id;
@@ -246,11 +260,11 @@ void test_rebase_merge__commit_updates_rewritten(void)
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_next(&rebase_operation, 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_next(&rebase_operation, repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
@@ -269,6 +283,7 @@ void test_rebase_merge__commit_drops_already_applied(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id;
int error;
@@ -283,13 +298,13 @@ void test_rebase_merge__commit_drops_already_applied(void)
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_next(&rebase_operation, repo, &checkout_opts));
cl_git_fail(error = git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
cl_assert_equal_i(GIT_EAPPLIED, error);
- cl_git_pass(git_rebase_next(repo, &checkout_opts));
+ cl_git_pass(git_rebase_next(&rebase_operation, repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
@@ -307,6 +322,7 @@ void test_rebase_merge__finish(void)
{
git_reference *branch_ref, *upstream_ref, *head_ref;
git_merge_head *branch_head, *upstream_head;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid commit_id;
git_reflog *reflog;
@@ -323,11 +339,11 @@ void test_rebase_merge__finish(void)
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_next(&rebase_operation, repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));
- cl_git_fail(error = git_rebase_next(repo, &checkout_opts));
+ cl_git_fail(error = git_rebase_next(&rebase_operation, repo, &checkout_opts));
cl_assert_equal_i(GIT_ITEROVER, error);
cl_git_pass(git_rebase_finish(repo, signature, NULL));
@@ -367,6 +383,7 @@ static void test_copy_note(
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
git_commit *branch_commit;
+ git_rebase_operation rebase_operation;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_oid note_id, commit_id;
git_note *note = NULL;
@@ -391,7 +408,7 @@ static void test_copy_note(
cl_git_pass(git_rebase(repo, branch_head, upstream_head, NULL, signature, opts));
- cl_git_pass(git_rebase_next(repo, &checkout_opts));
+ cl_git_pass(git_rebase_next(&rebase_operation, repo, &checkout_opts));
cl_git_pass(git_rebase_commit(&commit_id, repo, NULL, signature,
NULL, NULL));