Introduce git_rebase to set up a rebase session

Introduce `git_rebase` to set up a rebase session that can
then be continued.  Immediately, only merge-type rebase is
supported.
This commit is contained in:
Edward Thomson 2014-07-14 14:35:01 -04:00
parent 9e44289c8d
commit 867a36f3a6
131 changed files with 759 additions and 24 deletions

View File

@ -90,6 +90,7 @@ typedef enum {
GITERR_CALLBACK,
GITERR_CHERRYPICK,
GITERR_DESCRIBE,
GITERR_REBASE,
} git_error_t;
/**

73
include/git2/rebase.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_rebase_h__
#define INCLUDE_git_rebase_h__
#include "common.h"
#include "types.h"
#include "oid.h"
/**
* @file git2/rebase.h
* @brief Git rebase routines
* @defgroup git_rebase Git merge routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef struct {
unsigned int version;
/**
* Provide a quiet rebase experience; unused by libgit2 but provided for
* interoperability with other clients.
*/
int quiet;
} git_rebase_options;
#define GIT_REBASE_OPTIONS_VERSION 1
#define GIT_REBASE_OPTIONS_INIT {GIT_REBASE_OPTIONS_VERSION}
/**
* Initializes a `git_rebase_options` with default values. Equivalent to
* creating an instance with GIT_REBASE_OPTIONS_INIT.
*
* @param opts the `git_rebase_options` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_REBASE_OPTIONS_VERSION` here.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_rebase_init_options(
git_rebase_options *opts,
unsigned int version);
/**
* Sets up a rebase operation to rebase the changes in ours relative to
* upstream onto another branch.
*
* @param repo The repository to perform the rebase
* @param branch The terminal commit to rebase
* @param upstream The commit to begin rebasing from, or NULL to rebase all
* reachable commits
* @param onto The branch to rebase onto, or NULL to rebase onto the given
* upstream
* @param signature The signature of the rebaser
* @param opts Options to specify how rebase is performed
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_rebase(
git_repository *repo,
const git_merge_head *branch,
const git_merge_head *upstream,
const git_merge_head *onto,
const git_signature *signature,
const git_rebase_options *opts);
/** @} */
GIT_END_DECL
#endif

View File

@ -1835,29 +1835,6 @@ done:
/* Merge setup / cleanup */
static int write_orig_head(
git_repository *repo,
const git_merge_head *our_head)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
int error = 0;
assert(repo && our_head);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
(error = git_filebuf_printf(&file, "%s\n", our_head->oid_str)) == 0)
error = git_filebuf_commit(&file);
if (error < 0)
git_filebuf_cleanup(&file);
git_buf_free(&file_path);
return error;
}
static int write_merge_head(
git_repository *repo,
const git_merge_head *heads[],
@ -2229,7 +2206,7 @@ int git_merge__setup(
assert (repo && our_head && heads);
if ((error = write_orig_head(repo, our_head)) == 0 &&
if ((error = git_repository__set_orig_head(repo, &our_head->oid)) == 0 &&
(error = write_merge_head(repo, heads, heads_len)) == 0 &&
(error = write_merge_mode(repo)) == 0) {
error = write_merge_msg(repo, heads, heads_len);

332
src/rebase.c Normal file
View File

@ -0,0 +1,332 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "buffer.h"
#include "repository.h"
#include "posix.h"
#include "filebuf.h"
#include "merge.h"
#include "array.h"
#include <git2/types.h>
#include <git2/rebase.h>
#include <git2/commit.h>
#include <git2/reset.h>
#include <git2/revwalk.h>
#define REBASE_APPLY_DIR "rebase-apply"
#define REBASE_MERGE_DIR "rebase-merge"
#define HEAD_NAME_FILE "head-name"
#define ORIG_HEAD_FILE "orig-head"
#define HEAD_FILE "head"
#define ONTO_FILE "onto"
#define ONTO_NAME_FILE "onto_name"
#define QUIET_FILE "quiet"
#define MSGNUM_FILE "msgnum"
#define END_FILE "end"
#define CMT_FILE_FMT "cmt.%d"
#define ORIG_DETACHED_HEAD "detached HEAD"
#define REBASE_DIR_MODE 0777
#define REBASE_FILE_MODE 0666
typedef enum {
GIT_REBASE_TYPE_NONE = 0,
GIT_REBASE_TYPE_APPLY = 1,
GIT_REBASE_TYPE_MERGE = 2,
} git_rebase_type_t;
static int rebase_state_type(
git_rebase_type_t *type_out,
char **path_out,
git_repository *repo)
{
git_buf path = GIT_BUF_INIT;
git_rebase_type_t type = GIT_REBASE_TYPE_NONE;
if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0)
return -1;
if (git_path_isdir(git_buf_cstr(&path))) {
type = GIT_REBASE_TYPE_APPLY;
goto done;
}
git_buf_clear(&path);
if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0)
return -1;
if (git_path_isdir(git_buf_cstr(&path))) {
type = GIT_REBASE_TYPE_MERGE;
goto done;
}
done:
*type_out = type;
if (type != GIT_REBASE_TYPE_NONE && path_out)
*path_out = git_buf_detach(&path);
git_buf_free(&path);
return 0;
}
static int rebase_setupfile(git_repository *repo, const char *filename, const char *fmt, ...)
{
git_buf path = GIT_BUF_INIT,
contents = GIT_BUF_INIT;
va_list ap;
int error;
va_start(ap, fmt);
git_buf_vprintf(&contents, fmt, ap);
va_end(ap);
if ((error = git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR)) == 0 &&
(error = git_buf_joinpath(&path, path.ptr, filename)) == 0)
error = git_futils_writebuffer(&contents, path.ptr, O_RDWR|O_CREAT, REBASE_FILE_MODE);
git_buf_free(&path);
git_buf_free(&contents);
return error;
}
/* TODO: git.git actually uses the literal argv here, this is an attempt
* to emulate that.
*/
static const char *rebase_onto_name(const git_merge_head *onto)
{
if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0)
return onto->ref_name + 11;
else if (onto->ref_name)
return onto->ref_name;
else
return onto->oid_str;
}
static int rebase_setup_merge(
git_repository *repo,
const git_merge_head *branch,
const git_merge_head *upstream,
const git_merge_head *onto,
const git_rebase_options *opts)
{
git_revwalk *revwalk = NULL;
git_commit *commit;
git_buf commit_filename = GIT_BUF_INIT;
git_oid id;
char id_str[GIT_OID_HEXSZ];
bool merge;
int commit_cnt = 0, error;
GIT_UNUSED(opts);
if (!upstream)
upstream = onto;
if ((error = git_revwalk_new(&revwalk, repo)) < 0 ||
(error = git_revwalk_push(revwalk, &branch->oid)) < 0 ||
(error = git_revwalk_hide(revwalk, &upstream->oid)) < 0)
goto done;
git_revwalk_sorting(revwalk, GIT_SORT_REVERSE | GIT_SORT_TIME);
while ((error = git_revwalk_next(&id, revwalk)) == 0) {
if ((error = git_commit_lookup(&commit, repo, &id)) < 0)
goto done;
merge = (git_commit_parentcount(commit) > 1);
git_commit_free(commit);
if (merge)
continue;
commit_cnt++;
git_buf_clear(&commit_filename);
git_buf_printf(&commit_filename, CMT_FILE_FMT, commit_cnt);
git_oid_fmt(id_str, &id);
if ((error = rebase_setupfile(repo, commit_filename.ptr,
"%.*s\n", GIT_OID_HEXSZ, id_str)) < 0)
goto done;
}
if (error != GIT_ITEROVER ||
(error = rebase_setupfile(repo, END_FILE, "%d\n", commit_cnt)) < 0)
goto done;
error = rebase_setupfile(repo, ONTO_NAME_FILE, "%s\n",
rebase_onto_name(onto));
done:
git_revwalk_free(revwalk);
git_buf_free(&commit_filename);
return error;
}
static int rebase_setup(
git_repository *repo,
const git_merge_head *branch,
const git_merge_head *upstream,
const git_merge_head *onto,
const git_rebase_options *opts)
{
git_buf state_path = GIT_BUF_INIT;
const char *orig_head_name;
int error;
if (git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR) < 0)
return -1;
if ((error = p_mkdir(state_path.ptr, REBASE_DIR_MODE)) < 0) {
giterr_set(GITERR_OS, "Failed to create rebase directory '%s'",
state_path.ptr);
goto done;
}
if ((error = git_repository__set_orig_head(repo, &branch->oid)) < 0)
goto done;
orig_head_name = branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD;
if ((error = rebase_setupfile(repo, HEAD_NAME_FILE, "%s\n", orig_head_name)) < 0 ||
(error = rebase_setupfile(repo, ONTO_FILE, "%s\n", onto->oid_str)) < 0 ||
(error = rebase_setupfile(repo, ORIG_HEAD_FILE, "%s\n", branch->oid_str)) < 0 ||
(error = rebase_setupfile(repo, QUIET_FILE, opts->quiet ? "t\n" : "\n")) < 0)
goto done;
error = rebase_setup_merge(repo, branch, upstream, onto, opts);
done:
if (error < 0)
git_repository__cleanup_files(repo, (const char **)&state_path.ptr, 1);
git_buf_free(&state_path);
return error;
}
int git_rebase_init_options(git_rebase_options *opts, unsigned int version)
{
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT);
return 0;
}
static void rebase_normalize_options(
git_rebase_options *opts,
const git_rebase_options *given_opts)
{
if (given_opts)
memcpy(&opts, given_opts, sizeof(git_rebase_options));
}
static int rebase_ensure_not_in_progress(git_repository *repo)
{
int error;
git_rebase_type_t type;
if ((error = rebase_state_type(&type, NULL, repo)) < 0)
return error;
if (type != GIT_REBASE_TYPE_NONE) {
giterr_set(GITERR_REBASE, "There is an existing rebase in progress");
return -1;
}
return 0;
}
static int rebase_ensure_not_dirty(git_repository *repo)
{
git_tree *head = NULL;
git_index *index = NULL;
git_diff *diff = NULL;
int error;
if ((error = git_repository_head_tree(&head, repo)) < 0 ||
(error = git_repository_index(&index, repo)) < 0 ||
(error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0)
goto done;
if (git_diff_num_deltas(diff) > 0) {
giterr_set(GITERR_REBASE, "Uncommitted changes exist in index");
error = -1;
goto done;
}
git_diff_free(diff);
diff = NULL;
if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0)
goto done;
if (git_diff_num_deltas(diff) > 0) {
giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir");
error = -1;
}
done:
git_diff_free(diff);
git_index_free(index);
git_tree_free(head);
return error;
}
int git_rebase(
git_repository *repo,
const git_merge_head *branch,
const git_merge_head *upstream,
const git_merge_head *onto,
const git_signature *signature,
const git_rebase_options *given_opts)
{
git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
git_reference *head_ref = NULL;
git_buf reflog = GIT_BUF_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
int error;
assert(repo && branch && (upstream || onto));
GITERR_CHECK_VERSION(given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options");
rebase_normalize_options(&opts, given_opts);
if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 ||
(error = rebase_ensure_not_in_progress(repo)) < 0 ||
(error = rebase_ensure_not_dirty(repo)) < 0)
goto done;
if (!onto)
onto = upstream;
if ((error = rebase_setup(repo, branch, upstream, onto, &opts)) < 0)
goto done;
if ((error = git_buf_printf(&reflog,
"rebase: checkout %s", rebase_onto_name(onto))) < 0 ||
(error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE,
&onto->oid, 1, signature, reflog.ptr)) < 0)
goto done;
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
error = git_checkout_head(repo, &checkout_opts);
done:
git_reference_free(head_ref);
git_buf_free(&reflog);
return error;
}

View File

@ -1728,6 +1728,28 @@ cleanup:
return error;
}
int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
char orig_head_str[GIT_OID_HEXSZ];
int error = 0;
git_oid_fmt(orig_head_str, orig_head);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
(error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0)
error = git_filebuf_commit(&file);
if (error < 0)
git_filebuf_cleanup(&file);
git_buf_free(&file_path);
return error;
}
int git_repository_message(git_buf *out, git_repository *repo)
{
git_buf path = GIT_BUF_INIT;

View File

@ -170,6 +170,8 @@ GIT_INLINE(int) git_repository__ensure_not_bare(
return GIT_EBAREREPO;
}
int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head);
int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
#endif

328
tests/rebase/setup.c Normal file
View File

@ -0,0 +1,328 @@
#include "clar_libgit2.h"
#include "git2/rebase.h"
#include "posix.h"
#include <fcntl.h>
static git_repository *repo;
static git_index *_index;
static git_signature *signature;
// Fixture setup and teardown
void test_rebase_setup__initialize(void)
{
repo = cl_git_sandbox_init("rebase");
cl_git_pass(git_repository_index(&_index, repo));
cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb"));
}
void test_rebase_setup__cleanup(void)
{
git_signature_free(signature);
git_index_free(_index);
cl_git_sandbox_cleanup();
}
/* git checkout beef ; git rebase --merge master
* git checkout beef ; git rebase --merge master */
void test_rebase_setup__blocked_when_in_progress(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
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_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_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
cl_git_fail(git_rebase(repo, branch_head, upstream_head, NULL, signature, NULL));
git_merge_head_free(branch_head);
git_merge_head_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
}
/* git checkout beef ; git rebase --merge master */
void test_rebase_setup__merge(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
git_reference *head;
git_commit *head_commit;
git_oid head_id;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
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_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_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
git_commit_free(head_commit);
git_reference_free(head);
git_merge_head_free(branch_head);
git_merge_head_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
}
/* git checkout beef && git rebase --merge --root --onto master */
void test_rebase_setup__merge_root(void)
{
git_reference *branch_ref, *onto_ref;
git_merge_head *branch_head, *onto_head;
git_reference *head;
git_commit *head_commit;
git_oid head_id;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
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_rebase(repo, branch_head, NULL, onto_head, signature, NULL));
git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
git_commit_free(head_commit);
git_reference_free(head);
git_merge_head_free(branch_head);
git_merge_head_free(onto_head);
git_reference_free(branch_ref);
git_reference_free(onto_ref);
}
/* git checkout gravy && git rebase --merge --onto master veal */
void test_rebase_setup__merge_onto_and_upstream(void)
{
git_reference *branch1_ref, *branch2_ref, *onto_ref;
git_merge_head *branch1_head, *branch2_head, *onto_head;
git_reference *head;
git_commit *head_commit;
git_oid head_id;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
cl_git_pass(git_reference_lookup(&branch1_ref, repo, "refs/heads/gravy"));
cl_git_pass(git_reference_lookup(&branch2_ref, repo, "refs/heads/veal"));
cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
cl_git_pass(git_merge_head_from_ref(&branch1_head, repo, branch1_ref));
cl_git_pass(git_merge_head_from_ref(&branch2_head, repo, branch2_ref));
cl_git_pass(git_merge_head_from_ref(&onto_head, repo, onto_ref));
cl_git_pass(git_rebase(repo, branch1_head, branch2_head, onto_head, signature, NULL));
git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/ORIG_HEAD");
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/cmt.1");
cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/end");
cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/orig-head");
git_commit_free(head_commit);
git_reference_free(head);
git_merge_head_free(branch1_head);
git_merge_head_free(branch2_head);
git_merge_head_free(onto_head);
git_reference_free(branch1_ref);
git_reference_free(branch2_ref);
git_reference_free(onto_ref);
}
/* Ensure merge commits are dropped in a rebase */
/* git checkout veal && git rebase --merge master */
void test_rebase_setup__branch_with_merges(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
git_reference *head;
git_commit *head_commit;
git_oid head_id;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/veal"));
cl_git_pass(git_reference_lookup(&upstream_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(&upstream_head, repo, upstream_ref));
cl_git_pass(git_rebase(repo, branch_head, upstream_head, NULL, signature, NULL));
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/ORIG_HEAD");
cl_assert_equal_file("4bed71df7017283cac61bbf726197ad6a5a18b84\n", 41, "rebase/.git/rebase-merge/cmt.1");
cl_assert_equal_file("2aa3ce842094e08ebac152b3d6d5b0fff39f9c6e\n", 41, "rebase/.git/rebase-merge/cmt.2");
cl_assert_equal_file("3e8989b5a16d5258c935d998ef0e6bb139cc4757\n", 41, "rebase/.git/rebase-merge/cmt.3");
cl_assert_equal_file("4cacc6f6e740a5bc64faa33e04b8ef0733d8a127\n", 41, "rebase/.git/rebase-merge/cmt.4");
cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/cmt.5");
cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/orig-head");
git_commit_free(head_commit);
git_reference_free(head);
git_merge_head_free(branch_head);
git_merge_head_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
}
/* git checkout barley && git rebase --merge master */
void test_rebase_setup__orphan_branch(void)
{
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
git_reference *head;
git_commit *head_commit;
git_oid head_id;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/barley"));
cl_git_pass(git_reference_lookup(&upstream_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(&upstream_head, repo, upstream_ref));
cl_git_pass(git_rebase(repo, branch_head, upstream_head, NULL, signature, NULL));
cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/ORIG_HEAD");
cl_assert_equal_file("aa4c42aecdfc7cd989bbc3209934ea7cda3f4d88\n", 41, "rebase/.git/rebase-merge/cmt.1");
cl_assert_equal_file("e4f809f826c1a9fc929874bc0e4644dd2f2a1af4\n", 41, "rebase/.git/rebase-merge/cmt.2");
cl_assert_equal_file("9539b2cc291d6a6b1b266df8474d31fdd344dd79\n", 41, "rebase/.git/rebase-merge/cmt.3");
cl_assert_equal_file("013cc32d341bab0e6f039f50f153c18986f16c58\n", 41, "rebase/.git/rebase-merge/cmt.4");
cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/rebase-merge/cmt.5");
cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/rebase-merge/orig-head");
git_commit_free(head_commit);
git_reference_free(head);
git_merge_head_free(branch_head);
git_merge_head_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
}
static int rebase_is_blocked(void)
{
int error;
git_reference *branch_ref, *upstream_ref;
git_merge_head *branch_head, *upstream_head;
cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
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_merge_head_from_ref(&branch_head, repo, branch_ref));
cl_git_pass(git_merge_head_from_ref(&upstream_head, repo, upstream_ref));
error = git_rebase(repo, branch_head, upstream_head, NULL, signature, NULL);
git_merge_head_free(branch_head);
git_merge_head_free(upstream_head);
git_reference_free(branch_ref);
git_reference_free(upstream_ref);
return error;
}
void test_rebase_setup__blocked_for_staged_change(void)
{
cl_git_rewritefile("rebase/newfile.txt", "Stage an add");
git_index_add_bypath(_index, "newfile.txt");
cl_git_fail(rebase_is_blocked());
}
void test_rebase_setup__blocked_for_unstaged_change(void)
{
cl_git_rewritefile("rebase/asparagus.txt", "Unstaged change");
cl_git_fail(rebase_is_blocked());
}
void test_rebase_setup__not_blocked_for_untracked_add(void)
{
cl_git_rewritefile("rebase/newfile.txt", "Untracked file");
cl_git_pass(rebase_is_blocked());
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More