rebase: correctly finish rebasing detached heads When rebasing with IDs, we do not return to the `branch`, we remain in a detached HEAD state.
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
diff --git a/src/rebase.c b/src/rebase.c
index a2123ca..93a9154 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -1262,42 +1262,33 @@ done:
return error;
}
-int git_rebase_finish(
- git_rebase *rebase,
- const git_signature *signature)
+static int return_to_orig_head(git_rebase *rebase)
{
git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL;
git_commit *terminal_commit = NULL;
git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT;
char onto[GIT_OID_HEXSZ];
- int error;
-
- assert(rebase);
-
- if (rebase->inmemory)
- return 0;
+ int error = 0;
git_oid_fmt(onto, &rebase->onto_id);
- if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s",
- rebase->orig_head_name, GIT_OID_HEXSZ, onto)) < 0 ||
- (error = git_buf_printf(&head_msg, "rebase finished: returning to %s",
- rebase->orig_head_name)) < 0 ||
- (error = git_repository_head(&terminal_ref, rebase->repo)) < 0 ||
+ if ((error = git_buf_printf(&branch_msg,
+ "rebase finished: %s onto %.*s",
+ rebase->orig_head_name, GIT_OID_HEXSZ, onto)) == 0 &&
+ (error = git_buf_printf(&head_msg,
+ "rebase finished: returning to %s",
+ rebase->orig_head_name)) == 0 &&
+ (error = git_repository_head(&terminal_ref, rebase->repo)) == 0 &&
(error = git_reference_peel((git_object **)&terminal_commit,
- terminal_ref, GIT_OBJ_COMMIT)) < 0 ||
+ terminal_ref, GIT_OBJ_COMMIT)) == 0 &&
(error = git_reference_create_matching(&branch_ref,
- rebase->repo, rebase->orig_head_name, git_commit_id(terminal_commit), 1,
- &rebase->orig_head_id, branch_msg.ptr)) < 0 ||
- (error = git_reference_symbolic_create(&head_ref,
+ rebase->repo, rebase->orig_head_name,
+ git_commit_id(terminal_commit), 1,
+ &rebase->orig_head_id, branch_msg.ptr)) == 0)
+ error = git_reference_symbolic_create(&head_ref,
rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1,
- head_msg.ptr)) < 0 ||
- (error = rebase_copy_notes(rebase, signature)) < 0)
- goto done;
-
- error = rebase_cleanup(rebase);
+ head_msg.ptr);
-done:
git_buf_free(&head_msg);
git_buf_free(&branch_msg);
git_commit_free(terminal_commit);
@@ -1308,6 +1299,26 @@ done:
return error;
}
+int git_rebase_finish(
+ git_rebase *rebase,
+ const git_signature *signature)
+{
+ int error = 0;
+
+ assert(rebase);
+
+ if (rebase->inmemory)
+ return 0;
+
+ if (!rebase->head_detached)
+ error = return_to_orig_head(rebase);
+
+ if (error == 0 && (error = rebase_copy_notes(rebase, signature)) == 0)
+ error = rebase_cleanup(rebase);
+
+ return error;
+}
+
size_t git_rebase_operation_entrycount(git_rebase *rebase)
{
assert(rebase);
diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c
index d090e02..2d6df36 100644
--- a/tests/rebase/merge.c
+++ b/tests/rebase/merge.c
@@ -475,6 +475,56 @@ void test_rebase_merge__finish(void)
git_rebase_free(rebase);
}
+void test_rebase_merge__finish_with_ids(void)
+{
+ git_rebase *rebase;
+ git_reference *head_ref;
+ git_oid branch_id, upstream_id;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+ int error;
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "d616d97082eb7bb2dc6f180a7cca940993b7a56f"));
+ cl_git_pass(git_oid_fromstr(&upstream_id, "f87d14a4a236582a0278a916340a793714256864"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REF_OID, git_reference_type(head_ref));
+ cl_assert_equal_oid(&commit_id, git_reference_target(head_ref));
+
+ /* reflogs are not updated as if we were operating on proper
+ * branches. check that the last reflog entry is the rebase.
+ */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: Modification 3 to gravy", git_reflog_entry_message(reflog_entry));
+ git_reflog_free(reflog);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(head_ref);
+ git_rebase_free(rebase);
+}
+
static void test_copy_note(
const git_rebase_options *opts,
bool should_exist)