Skip to content

Commit 046f1f8

Browse files
committed
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.
1 parent 01067cb commit 046f1f8

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

pygit2/decl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ typedef ... git_push;
55
typedef ... git_cred;
66
typedef ... git_object;
77
typedef ... git_tree;
8+
typedef ... git_commit;
89
typedef ... git_index;
910
typedef ... git_diff;
1011
typedef ... git_index_conflict_iterator;
@@ -274,6 +275,18 @@ typedef struct {
274275
const char *new_prefix;
275276
} git_diff_options;
276277

278+
typedef struct {
279+
int (*file_signature)(
280+
void **out, const git_diff_file *file,
281+
const char *fullpath, void *payload);
282+
int (*buffer_signature)(
283+
void **out, const git_diff_file *file,
284+
const char *buf, size_t buflen, void *payload);
285+
void (*free_signature)(void *sig, void *payload);
286+
int (*similarity)(int *score, void *siga, void *sigb, void *payload);
287+
void *payload;
288+
} git_diff_similarity_metric;
289+
277290
int git_diff_init_options(git_diff_options *opts, unsigned int version);
278291
int git_diff_index_to_workdir(git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts);
279292
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
578591
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno);
579592
int git_blame_file(git_blame **out, git_repository *repo, const char *path, git_blame_options *options);
580593
void git_blame_free(git_blame *blame);
594+
595+
/*
596+
* Merging
597+
*/
598+
599+
typedef enum { ... } git_merge_tree_flag_t;
600+
601+
typedef enum { ... } git_merge_file_favor_t;
602+
603+
typedef struct {
604+
unsigned int version;
605+
git_merge_tree_flag_t flags;
606+
unsigned int rename_threshold;
607+
unsigned int target_limit;
608+
git_diff_similarity_metric *metric;
609+
git_merge_file_favor_t file_favor;
610+
} git_merge_options;
611+
612+
613+
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);

pygit2/repository.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,44 @@ def index(self):
508508
return Index.from_c(self, cindex)
509509

510510
#
511+
# Merging
512+
#
513+
def merge_commits(self, ours, theirs):
514+
"""Merge two arbitrary commits
515+
516+
Arguments:
517+
518+
ours
519+
The commit to take as "ours" or base.
520+
theirs
521+
The commit which will be merged into "ours"
522+
523+
Both can be any object which peels to a commit.
524+
525+
Returns an index with the result of the merge
526+
527+
"""
528+
529+
ours_ptr = ffi.new('git_commit **')
530+
theirs_ptr = ffi.new('git_commit **')
531+
cindex = ffi.new('git_index **')
532+
533+
if is_string(ours) or isinstance(ours, Oid):
534+
ours = self[ours]
535+
if is_string(theirs) or isinstance(theirs, Oid):
536+
theirs = self[theirs]
537+
538+
ours = ours.peel(Commit)
539+
theirs = theirs.peel(Commit)
540+
541+
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
542+
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]
543+
544+
err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], ffi.NULL)
545+
check_error(err)
546+
547+
return Index.from_c(self, cindex)
548+
#
511549
# Utility for writing a tree into an archive
512550
#
513551
def write_archive(self, treeish, archive, timestamp=None):

test/test_merge.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,20 @@ def test_merge_remove_conflicts(self):
141141
del idx.conflicts['.gitignore']
142142
self.assertRaises(KeyError, conflicts.__getitem__, '.gitignore')
143143
self.assertTrue(idx.conflicts is None)
144+
145+
class MergeCommitsTest(utils.RepoTestCaseForMerging):
146+
147+
def test_merge_commits(self):
148+
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
149+
branch_id = self.repo.get(branch_head_hex).id
150+
151+
merge_index = self.repo.merge_commits(self.repo.head.target, branch_head_hex)
152+
self.assertTrue(merge_index.conflicts is None)
153+
merge_commits_tree = merge_index.write_tree(self.repo)
154+
155+
self.repo.merge(branch_id)
156+
index = self.repo.index
157+
self.assertTrue(index.conflicts is None)
158+
merge_tree = index.write_tree()
159+
160+
self.assertEqual(merge_tree, merge_commits_tree)

0 commit comments

Comments
 (0)