mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-20 15:07:45 +00:00
Introduce git_rebase_abort
Abort an in-progress rebase and move the working directory and repository back to the ORIG_HEAD state.
This commit is contained in:
parent
daf395b795
commit
4fe84d624b
@ -68,6 +68,19 @@ GIT_EXTERN(int) git_rebase(
|
|||||||
const git_signature *signature,
|
const git_signature *signature,
|
||||||
const git_rebase_options *opts);
|
const git_rebase_options *opts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aborts a rebase that is currently in progress, resetting the repository
|
||||||
|
* and working directory to their state before rebase began.
|
||||||
|
*
|
||||||
|
* @param repo The repository with the in-progress rebase
|
||||||
|
* @param signature The identity that is aborting the rebase
|
||||||
|
* @return Zero on success; GIT_ENOTFOUND if a rebase is not in progress,
|
||||||
|
* -1 on other errors.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_rebase_abort(
|
||||||
|
git_repository *repo,
|
||||||
|
const git_signature *signature);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
128
src/rebase.c
128
src/rebase.c
@ -44,6 +44,18 @@ typedef enum {
|
|||||||
GIT_REBASE_TYPE_MERGE = 2,
|
GIT_REBASE_TYPE_MERGE = 2,
|
||||||
} git_rebase_type_t;
|
} git_rebase_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_rebase_type_t type;
|
||||||
|
char *state_path;
|
||||||
|
|
||||||
|
int head_detached : 1;
|
||||||
|
|
||||||
|
char *orig_head_name;
|
||||||
|
git_oid orig_head_id;
|
||||||
|
} git_rebase_state;
|
||||||
|
|
||||||
|
#define GIT_REBASE_STATE_INIT {0}
|
||||||
|
|
||||||
static int rebase_state_type(
|
static int rebase_state_type(
|
||||||
git_rebase_type_t *type_out,
|
git_rebase_type_t *type_out,
|
||||||
char **path_out,
|
char **path_out,
|
||||||
@ -80,6 +92,84 @@ done:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rebase_state(git_rebase_state *state, git_repository *repo)
|
||||||
|
{
|
||||||
|
git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT,
|
||||||
|
orig_head_id = GIT_BUF_INIT;
|
||||||
|
int state_path_len, error;
|
||||||
|
|
||||||
|
memset(state, 0x0, sizeof(git_rebase_state));
|
||||||
|
|
||||||
|
if ((error = rebase_state_type(&state->type, &state->state_path, repo)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (state->type == GIT_REBASE_TYPE_NONE) {
|
||||||
|
giterr_set(GITERR_REBASE, "There is no rebase in progress");
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = git_buf_puts(&path, state->state_path)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
state_path_len = git_buf_len(&path);
|
||||||
|
|
||||||
|
if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 ||
|
||||||
|
(error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
git_buf_rtrim(&orig_head_name);
|
||||||
|
|
||||||
|
if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0)
|
||||||
|
state->head_detached = 1;
|
||||||
|
|
||||||
|
git_buf_truncate(&path, state_path_len);
|
||||||
|
|
||||||
|
if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (!git_path_isfile(path.ptr)) {
|
||||||
|
/* Previous versions of git.git used 'head' here; support that. */
|
||||||
|
git_buf_truncate(&path, state_path_len);
|
||||||
|
|
||||||
|
if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
git_buf_rtrim(&orig_head_id);
|
||||||
|
|
||||||
|
if ((error = git_oid_fromstr(&state->orig_head_id, orig_head_id.ptr)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (!state->head_detached)
|
||||||
|
state->orig_head_name = git_buf_detach(&orig_head_name);
|
||||||
|
|
||||||
|
done:
|
||||||
|
git_buf_free(&path);
|
||||||
|
git_buf_free(&orig_head_name);
|
||||||
|
git_buf_free(&orig_head_id);
|
||||||
|
git_buf_free(&onto_id);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rebase_state_free(git_rebase_state *state)
|
||||||
|
{
|
||||||
|
if (state == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
git__free(state->orig_head_name);
|
||||||
|
git__free(state->state_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rebase_finish(git_rebase_state *state)
|
||||||
|
{
|
||||||
|
return git_path_isdir(state->state_path) ?
|
||||||
|
git_futils_rmdir_r(state->state_path, NULL, GIT_RMDIR_REMOVE_FILES) :
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
|
||||||
static int rebase_setupfile(git_repository *repo, const char *filename, const char *fmt, ...)
|
static int rebase_setupfile(git_repository *repo, const char *filename, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
git_buf path = GIT_BUF_INIT,
|
git_buf path = GIT_BUF_INIT,
|
||||||
@ -330,3 +420,41 @@ done:
|
|||||||
git_buf_free(&reflog);
|
git_buf_free(&reflog);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_rebase_abort(git_repository *repo, const git_signature *signature)
|
||||||
|
{
|
||||||
|
git_rebase_state state = GIT_REBASE_STATE_INIT;
|
||||||
|
git_reference *orig_head_ref = NULL;
|
||||||
|
git_commit *orig_head_commit = NULL;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
assert(repo && signature);
|
||||||
|
|
||||||
|
if ((error = rebase_state(&state, repo)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
error = state.head_detached ?
|
||||||
|
git_reference_create(&orig_head_ref, repo, GIT_HEAD_FILE,
|
||||||
|
&state.orig_head_id, 1, signature, "rebase: aborting") :
|
||||||
|
git_reference_symbolic_create(
|
||||||
|
&orig_head_ref, repo, GIT_HEAD_FILE, state.orig_head_name, 1,
|
||||||
|
signature, "rebase: aborting");
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if ((error = git_commit_lookup(
|
||||||
|
&orig_head_commit, repo, &state.orig_head_id)) < 0 ||
|
||||||
|
(error = git_reset(repo, (git_object *)orig_head_commit,
|
||||||
|
GIT_RESET_HARD, NULL, signature, NULL)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
error = rebase_finish(&state);
|
||||||
|
|
||||||
|
done:
|
||||||
|
git_commit_free(orig_head_commit);
|
||||||
|
git_reference_free(orig_head_ref);
|
||||||
|
rebase_state_free(&state);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
148
tests/rebase/abort.c
Normal file
148
tests/rebase/abort.c
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "git2/rebase.h"
|
||||||
|
#include "merge.h"
|
||||||
|
#include "posix.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
static git_repository *repo;
|
||||||
|
|
||||||
|
// Fixture setup and teardown
|
||||||
|
void test_rebase_abort__initialize(void)
|
||||||
|
{
|
||||||
|
repo = cl_git_sandbox_init("rebase");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rebase_abort__cleanup(void)
|
||||||
|
{
|
||||||
|
cl_git_sandbox_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_abort(git_merge_head *branch, git_merge_head *onto)
|
||||||
|
{
|
||||||
|
git_reference *head_ref, *branch_ref = NULL;
|
||||||
|
git_signature *signature;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_reflog *reflog;
|
||||||
|
const git_reflog_entry *reflog_entry;
|
||||||
|
|
||||||
|
cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
|
||||||
|
cl_git_pass(git_rebase_abort(repo, signature));
|
||||||
|
|
||||||
|
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
|
||||||
|
|
||||||
|
/* Make sure the refs are updated appropriately */
|
||||||
|
cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
|
||||||
|
|
||||||
|
if (branch->ref_name == NULL)
|
||||||
|
cl_assert_equal_oid(git_merge_head_id(branch), git_reference_target(head_ref));
|
||||||
|
else {
|
||||||
|
cl_assert_equal_s("refs/heads/beef", git_reference_symbolic_target(head_ref));
|
||||||
|
cl_git_pass(git_reference_lookup(&branch_ref, repo, git_reference_symbolic_target(head_ref)));
|
||||||
|
cl_assert_equal_oid(git_merge_head_id(branch), git_reference_target(branch_ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
git_status_list_new(&statuslist, repo, NULL);
|
||||||
|
cl_assert_equal_i(0, git_status_list_entrycount(statuslist));
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
/* Make sure the reflogs are updated appropriately */
|
||||||
|
cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
|
||||||
|
|
||||||
|
cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
|
||||||
|
cl_assert_equal_oid(git_merge_head_id(onto), git_reflog_entry_id_old(reflog_entry));
|
||||||
|
cl_assert_equal_oid(git_merge_head_id(branch), git_reflog_entry_id_new(reflog_entry));
|
||||||
|
cl_assert_equal_s("rebase: aborting", git_reflog_entry_message(reflog_entry));
|
||||||
|
|
||||||
|
git_reflog_free(reflog);
|
||||||
|
git_reference_free(head_ref);
|
||||||
|
git_reference_free(branch_ref);
|
||||||
|
git_signature_free(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rebase_abort__merge(void)
|
||||||
|
{
|
||||||
|
git_reference *branch_ref, *onto_ref;
|
||||||
|
git_signature *signature;
|
||||||
|
git_merge_head *branch_head, *onto_head;
|
||||||
|
|
||||||
|
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
|
||||||
|
cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
|
||||||
|
|
||||||
|
cl_git_pass(git_merge_head_from_ref(&branch_head, repo, branch_ref));
|
||||||
|
cl_git_pass(git_merge_head_from_ref(&onto_head, repo, onto_ref));
|
||||||
|
|
||||||
|
cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
|
||||||
|
|
||||||
|
cl_git_pass(git_rebase(repo, branch_head, NULL, onto_head, signature, NULL));
|
||||||
|
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
|
||||||
|
|
||||||
|
test_abort(branch_head, onto_head);
|
||||||
|
|
||||||
|
git_signature_free(signature);
|
||||||
|
|
||||||
|
git_merge_head_free(branch_head);
|
||||||
|
git_merge_head_free(onto_head);
|
||||||
|
|
||||||
|
git_reference_free(branch_ref);
|
||||||
|
git_reference_free(onto_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rebase_abort__detached_head(void)
|
||||||
|
{
|
||||||
|
git_oid branch_id;
|
||||||
|
git_reference *onto_ref;
|
||||||
|
git_signature *signature;
|
||||||
|
git_merge_head *branch_head, *onto_head;
|
||||||
|
|
||||||
|
git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64");
|
||||||
|
cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
|
||||||
|
|
||||||
|
cl_git_pass(git_merge_head_from_id(&branch_head, repo, &branch_id));
|
||||||
|
cl_git_pass(git_merge_head_from_ref(&onto_head, repo, onto_ref));
|
||||||
|
|
||||||
|
cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
|
||||||
|
|
||||||
|
cl_git_pass(git_rebase(repo, branch_head, NULL, onto_head, signature, NULL));
|
||||||
|
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
|
||||||
|
|
||||||
|
test_abort(branch_head, onto_head);
|
||||||
|
|
||||||
|
git_signature_free(signature);
|
||||||
|
|
||||||
|
git_merge_head_free(branch_head);
|
||||||
|
git_merge_head_free(onto_head);
|
||||||
|
|
||||||
|
git_reference_free(onto_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_rebase_abort__old_style_head_file(void)
|
||||||
|
{
|
||||||
|
git_reference *branch_ref, *onto_ref;
|
||||||
|
git_signature *signature;
|
||||||
|
git_merge_head *branch_head, *onto_head;
|
||||||
|
|
||||||
|
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
|
||||||
|
cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
|
||||||
|
|
||||||
|
cl_git_pass(git_merge_head_from_ref(&branch_head, repo, branch_ref));
|
||||||
|
cl_git_pass(git_merge_head_from_ref(&onto_head, repo, onto_ref));
|
||||||
|
|
||||||
|
cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
|
||||||
|
|
||||||
|
cl_git_pass(git_rebase(repo, branch_head, NULL, onto_head, signature, NULL));
|
||||||
|
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
|
||||||
|
|
||||||
|
p_rename("rebase-merge/.git/rebase-merge/orig-head",
|
||||||
|
"rebase-merge/.git/rebase-merge/head");
|
||||||
|
|
||||||
|
test_abort(branch_head, onto_head);
|
||||||
|
|
||||||
|
git_signature_free(signature);
|
||||||
|
|
||||||
|
git_merge_head_free(branch_head);
|
||||||
|
git_merge_head_free(onto_head);
|
||||||
|
|
||||||
|
git_reference_free(branch_ref);
|
||||||
|
git_reference_free(onto_ref);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user