Add Repository.merge_commits()

This allows you to merge arbitrary commits, returning an index, which is
useful when dealing with bare repos in which we want to merge.
This commit is contained in:
Carlos Martín Nieto
2014-10-30 15:01:37 +01:00
parent 01067cb77f
commit 3b27e16d08
3 changed files with 89 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ typedef ... git_push;
typedef ... git_cred;
typedef ... git_object;
typedef ... git_tree;
typedef ... git_commit;
typedef ... git_index;
typedef ... git_diff;
typedef ... git_index_conflict_iterator;
@@ -274,6 +275,18 @@ typedef struct {
const char *new_prefix;
} git_diff_options;
typedef struct {
int (*file_signature)(
void **out, const git_diff_file *file,
const char *fullpath, void *payload);
int (*buffer_signature)(
void **out, const git_diff_file *file,
const char *buf, size_t buflen, void *payload);
void (*free_signature)(void *sig, void *payload);
int (*similarity)(int *score, void *siga, void *sigb, void *payload);
void *payload;
} git_diff_similarity_metric;
int git_diff_init_options(git_diff_options *opts, unsigned int version);
int git_diff_index_to_workdir(git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts);
int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts);
@@ -578,3 +591,23 @@ const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t inde
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno);
int git_blame_file(git_blame **out, git_repository *repo, const char *path, git_blame_options *options);
void git_blame_free(git_blame *blame);
/*
* Merging
*/
typedef enum { ... } git_merge_tree_flag_t;
typedef enum { ... } git_merge_file_favor_t;
typedef struct {
unsigned int version;
git_merge_tree_flag_t flags;
unsigned int rename_threshold;
unsigned int target_limit;
git_diff_similarity_metric *metric;
git_merge_file_favor_t file_favor;
} git_merge_options;
int git_merge_commits(git_index **out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, const git_merge_options *opts);

View File

@@ -508,6 +508,45 @@ class Repository(_Repository):
return Index.from_c(self, cindex)
#
# Merging
#
def merge_commits(self, ours, theirs):
"""Merge two arbitrary commits
Arguments:
ours
The commit to take as "ours" or base.
theirs
The commit which will be merged into "ours"
Both can be any object which peels to a commit or the id
(string or Oid) of an object which peels to a commit.
Returns an index with the result of the merge
"""
ours_ptr = ffi.new('git_commit **')
theirs_ptr = ffi.new('git_commit **')
cindex = ffi.new('git_index **')
if is_string(ours) or isinstance(ours, Oid):
ours = self[ours]
if is_string(theirs) or isinstance(theirs, Oid):
theirs = self[theirs]
ours = ours.peel(Commit)
theirs = theirs.peel(Commit)
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]
err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], ffi.NULL)
check_error(err)
return Index.from_c(self, cindex)
#
# Utility for writing a tree into an archive
#
def write_archive(self, treeish, archive, timestamp=None):

View File

@@ -141,3 +141,20 @@ class MergeTestWithConflicts(utils.RepoTestCaseForMerging):
del idx.conflicts['.gitignore']
self.assertRaises(KeyError, conflicts.__getitem__, '.gitignore')
self.assertTrue(idx.conflicts is None)
class MergeCommitsTest(utils.RepoTestCaseForMerging):
def test_merge_commits(self):
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
branch_id = self.repo.get(branch_head_hex).id
merge_index = self.repo.merge_commits(self.repo.head.target, branch_head_hex)
self.assertTrue(merge_index.conflicts is None)
merge_commits_tree = merge_index.write_tree(self.repo)
self.repo.merge(branch_id)
index = self.repo.index
self.assertTrue(index.conflicts is None)
merge_tree = index.write_tree()
self.assertEqual(merge_tree, merge_commits_tree)