Commit ee667307 by Edward Thomson

rebase: introduce inmemory rebasing

Introduce the ability to rebase in-memory or in a bare repository.

When `rebase_options.inmemory` is specified, the resultant `git_rebase`
session will not be persisted to disk.  Callers may still analyze
the rebase operations, resolve any conflicts against the in-memory
index and create the commits.  Neither `HEAD` nor the working
directory will be updated during this process.
parent 488e2b85
......@@ -39,6 +39,15 @@ typedef struct {
int quiet;
/**
* Used by `git_rebase_init`, this will begin an in-memory rebase,
* which will allow callers to step through the rebase operations and
* commit the rebased changes, but will not rewind HEAD or update the
* repository to be in a rebasing state. This will not interfere with
* the working directory (if there is one).
*/
int inmemory;
/**
* Used by `git_rebase_finish`, this is the name of the notes reference
* used to rewrite notes for rebased commits when finishing the rebase;
* if NULL, the contents of the configuration option `notes.rewriteRef`
......@@ -101,7 +110,7 @@ typedef enum {
#define GIT_REBASE_OPTIONS_VERSION 1
#define GIT_REBASE_OPTIONS_INIT \
{GIT_REBASE_OPTIONS_VERSION, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT}
{GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT}
/** Indicates that a rebase operation is not (yet) in progress. */
#define GIT_REBASE_NO_OPERATION SIZE_MAX
......@@ -127,6 +136,12 @@ typedef struct {
* be populated for operations of type `GIT_REBASE_OPERATION_EXEC`.
*/
const char *exec;
/**
* The index that is the result of an operation.
* This is set only for in-memory rebases.
*/
git_index *index;
} git_rebase_operation;
/**
......
#include "clar_libgit2.h"
#include "git2/rebase.h"
#include "posix.h"
#include <fcntl.h>
static git_repository *repo;
// Fixture setup and teardown
void test_rebase_inmemory__initialize(void)
{
repo = cl_git_sandbox_init("rebase");
}
void test_rebase_inmemory__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_rebase_inmemory__not_in_rebase_state(void)
{
git_rebase *rebase;
git_reference *branch_ref, *upstream_ref;
git_annotated_commit *branch_head, *upstream_head;
git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
opts.inmemory = true;
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
git_rebase_free(rebase);
git_annotated_commit_free(branch_head);
git_annotated_commit_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
}
void test_rebase_inmemory__can_resolve_conflicts(void)
{
git_rebase *rebase;
git_reference *branch_ref, *upstream_ref;
git_annotated_commit *branch_head, *upstream_head;
git_rebase_operation *rebase_operation;
git_status_list *status_list;
git_oid pick_id, commit_id, expected_commit_id;
git_signature *signature;
git_index *repo_index;
git_index_entry resolution = {{0}};
git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
cl_git_pass(git_signature_new(&signature,
"Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
opts.inmemory = true;
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
cl_git_pass(git_rebase_next(&rebase_operation, rebase));
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);
/* ensure that we did not do anything stupid to the workdir or repo index */
cl_git_pass(git_repository_index(&repo_index, repo));
cl_assert(!git_index_has_conflicts(repo_index));
cl_git_pass(git_status_list_new(&status_list, repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(status_list));
/* but that the index returned from rebase does have conflicts */
cl_assert(git_index_has_conflicts(rebase_operation->index));
cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
/* ensure that we can work with the in-memory index to resolve the conflict */
resolution.path = "asparagus.txt";
resolution.mode = GIT_FILEMODE_BLOB;
git_oid_fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9");
cl_git_pass(git_index_conflict_remove(rebase_operation->index, "asparagus.txt"));
cl_git_pass(git_index_add(rebase_operation->index, &resolution));
/* and finally create a commit for the resolved rebase operation */
cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
cl_git_pass(git_oid_fromstr(&expected_commit_id, "db7af47222181e548810da2ab5fec0e9357c5637"));
cl_assert_equal_oid(&commit_id, &expected_commit_id);
git_signature_free(signature);
git_status_list_free(status_list);
git_annotated_commit_free(branch_head);
git_annotated_commit_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
git_index_free(repo_index);
git_rebase_free(rebase);
}
......@@ -46,26 +46,32 @@ static void test_operations(git_rebase *rebase, size_t expected_current)
}
}
void test_rebase_iterator__iterates(void)
void test_iterator(bool inmemory)
{
git_rebase *rebase;
git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
git_reference *branch_ref, *upstream_ref;
git_annotated_commit *branch_head, *upstream_head;
git_rebase_operation *rebase_operation;
git_oid commit_id;
int error;
opts.inmemory = inmemory;
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
test_operations(rebase, GIT_REBASE_NO_OPERATION);
git_rebase_free(rebase);
if (!inmemory) {
git_rebase_free(rebase);
cl_git_pass(git_rebase_open(&rebase, repo, NULL));
}
cl_git_pass(git_rebase_next(&rebase_operation, rebase));
cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
NULL, NULL));
......@@ -81,8 +87,10 @@ void test_rebase_iterator__iterates(void)
NULL, NULL));
test_operations(rebase, 2);
if (!inmemory) {
git_rebase_free(rebase);
cl_git_pass(git_rebase_open(&rebase, repo, NULL));
}
cl_git_pass(git_rebase_next(&rebase_operation, rebase));
cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
......@@ -104,3 +112,13 @@ void test_rebase_iterator__iterates(void)
git_reference_free(upstream_ref);
git_rebase_free(rebase);
}
void test_rebase_iterator__iterates(void)
{
test_iterator(false);
}
void test_rebase_iterator__iterates_inmemory(void)
{
test_iterator(true);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment