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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
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