Commit 93a7004cc234da31d912bb0f266c39b99ab8c8db

Edward Thomson 2014-07-18T14:50:06

git_rebase_commit: drop already-picked commits Already cherry-picked commits should not be re-included. If all changes included in a commit exist in the upstream, then we should error with GIT_EAPPLIED.

diff --git a/include/git2/errors.h b/include/git2/errors.h
index 5dfa72a..b33118e 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -43,6 +43,7 @@ typedef enum {
 	GIT_EMODIFIED       = -15,	/**< Reference value does not match expected */
 	GIT_EAUTH           = -16,      /**< Authentication error */
 	GIT_ECERTIFICATE    = -17,      /**< Server certificate is invalid */
+	GIT_EAPPLIED        = -18,	/**< Patch/merge has already been applied */
 
 	GIT_PASSTHROUGH     = -30,	/**< Internal only */
 	GIT_ITEROVER        = -31,	/**< Signals end of iteration with iterator */
diff --git a/include/git2/rebase.h b/include/git2/rebase.h
index 0fe2b26..32b4ff6 100644
--- a/include/git2/rebase.h
+++ b/include/git2/rebase.h
@@ -99,7 +99,9 @@ GIT_EXTERN(int) git_rebase_next(
  * @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.
+ *        the index, GIT_EAPPLIED if the current commit has already
+ *        been applied to the upstream and there is nothing to commit,
+ *        -1 on failure.
  */
 GIT_EXTERN(int) git_rebase_commit(
 	git_oid *id,
diff --git a/src/rebase.c b/src/rebase.c
index 9245dca..a28a928 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -674,7 +674,8 @@ static int rebase_commit_merge(
 	git_index *index = NULL;
 	git_reference *head = NULL;
 	git_commit *head_commit = NULL;
-	git_tree *tree = NULL;
+	git_tree *head_tree = NULL, *tree = NULL;
+	git_diff *diff = NULL;
 	git_oid tree_id;
 	char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ];
 	int error;
@@ -694,11 +695,19 @@ static int rebase_commit_merge(
 		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_commit_tree(&head_tree, head_commit)) < 0 ||
+		(error = git_diff_tree_to_index(&diff, repo, head_tree, index, NULL)) < 0)
+		goto done;
+
+	if (git_diff_num_deltas(diff) == 0) {
+		giterr_set(GITERR_REBASE, "This patch has already been applied");
+		error = GIT_EAPPLIED;
+		goto done;
+	}
+
+	if ((error = git_index_write_tree(&tree_id, index)) < 0 ||
 		(error = git_tree_lookup(&tree, repo, &tree_id)) < 0)
 		goto done;
 
@@ -722,7 +731,9 @@ static int rebase_commit_merge(
 		"%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr);
 
 done:
+	git_diff_free(diff);
 	git_tree_free(tree);
+	git_tree_free(head_tree);
 	git_commit_free(head_commit);
 	git_reference_free(head);
 	git_index_free(index);
diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c
index e578bef..04d06d2 100644
--- a/tests/rebase/merge.c
+++ b/tests/rebase/merge.c
@@ -207,3 +207,41 @@ void test_rebase_merge__commit_updates_rewritten(void)
 	git_reference_free(upstream_ref);
 }
 
+void test_rebase_merge__commit_drops_already_applied(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;
+	int error;
+
+	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/green_pea"));
+
+	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_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_commit(&commit_id, repo, NULL, signature,
+		NULL, NULL));
+
+	cl_assert_equal_file(
+		"8d1f13f93c4995760ac07d129246ac1ff64c0be9 2ac4fb7b74c1287f6c792acad759e1ec01e18dae\n",
+		82, "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);
+}
+
diff --git a/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac b/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac
new file mode 100644
index 0000000..c38c5b2
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac differ
diff --git a/tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881f b/tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881f
new file mode 100644
index 0000000..7ce4452
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881f differ
diff --git a/tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47 b/tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47
new file mode 100644
index 0000000..b096a96
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47 differ
diff --git a/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf b/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf
new file mode 100644
index 0000000..cc7ccd0
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf differ
diff --git a/tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8e b/tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8e
new file mode 100644
index 0000000..acd6bdc
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8e differ
diff --git a/tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12af b/tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12af
new file mode 100644
index 0000000..af3bd17
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12af differ
diff --git a/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a b/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a
new file mode 100644
index 0000000..9907248
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a
@@ -0,0 +1 @@
+xMJ1]ϤAd@\UICwLDQ/mUAI1eqU<hKfOg$#2/]/%.(-.
uxɟ3mu?鬿==
x$Dc@4sCycW5]זkGJFQ-
Zy
\ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9 b/tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9
new file mode 100644
index 0000000..da96e9d
Binary files /dev/null and b/tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9 differ
diff --git a/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0 b/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0
new file mode 100644
index 0000000..e2e98d6
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0
@@ -0,0 +1,3 @@
+xOKN!u)h
+ct
f?8fb8{}o:`I0U…ZbH-PKўhEk=t@LrBق>!EnDph~K?C~!pSCoz3
+!D <轛;1]d~juZU
\ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/refs/heads/green_pea b/tests/resources/rebase/.gitted/refs/heads/green_pea
new file mode 100644
index 0000000..3bffe27
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/green_pea
@@ -0,0 +1 @@
+d482e77aecb8e07da43e4cad6e0dcb59219e12af