Commit 18b439b9beee050de0dbf2f4649609764a69fb3c

Edward Thomson 2014-08-23T18:19:34

git_rebase_next: provide info about the operation

diff --git a/include/git2/rebase.h b/include/git2/rebase.h
index d9cf231..3b4f447 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 3384a14..c6bbfbf 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 fddab83..a3bb711 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));