Skip to content

Commit bc8b29b

Browse files
committed
Repository: allow passing a favor option to merge_commits
I've gone with taking a string and converting it because the depth of the namespacing in the libgit2 name is rather large and we don't care about the global namespace that C has; and this also lets us pass the same as the '-X' option to git-merge.
1 parent 3b27e16 commit bc8b29b

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

pygit2/decl.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,12 @@ void git_blame_free(git_blame *blame);
598598

599599
typedef enum { ... } git_merge_tree_flag_t;
600600

601-
typedef enum { ... } git_merge_file_favor_t;
601+
typedef enum {
602+
GIT_MERGE_FILE_FAVOR_NORMAL = 0,
603+
GIT_MERGE_FILE_FAVOR_OURS = 1,
604+
GIT_MERGE_FILE_FAVOR_THEIRS = 2,
605+
GIT_MERGE_FILE_FAVOR_UNION = 3,
606+
} git_merge_file_favor_t;
602607

603608
typedef struct {
604609
unsigned int version;
@@ -609,5 +614,7 @@ typedef struct {
609614
git_merge_file_favor_t file_favor;
610615
} git_merge_options;
611616

617+
#define GIT_MERGE_OPTIONS_VERSION ...
612618

619+
int git_merge_init_options(git_merge_options *opts, unsigned int version);
613620
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

+33-3
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ def index(self):
510510
#
511511
# Merging
512512
#
513-
def merge_commits(self, ours, theirs):
513+
def merge_commits(self, ours, theirs, favor='normal'):
514514
"""Merge two arbitrary commits
515515
516516
Arguments:
@@ -519,16 +519,37 @@ def merge_commits(self, ours, theirs):
519519
The commit to take as "ours" or base.
520520
theirs
521521
The commit which will be merged into "ours"
522+
favor
523+
How to deal with file-level conflicts. Can be one of
522524
523-
Both can be any object which peels to a commit or the id
525+
* normal (default). Conflicts will be preserved.
526+
* ours. The "ours" side of the conflict region is used.
527+
* theirs. The "theirs" side of the conflict region is used.
528+
* union. Unique lines from each side will be used.
529+
530+
for all but NORMAL, the index will not record a conflict.
531+
532+
Both "ours" and "theirs" can be any object which peels to a commit or the id
524533
(string or Oid) of an object which peels to a commit.
525534
526535
Returns an index with the result of the merge
527536
528537
"""
538+
def favor_to_enum(favor):
539+
if favor == 'normal':
540+
return C.GIT_MERGE_FILE_FAVOR_NORMAL
541+
elif favor == 'ours':
542+
return C.GIT_MERGE_FILE_FAVOR_OURS
543+
elif favor == 'theirs':
544+
return C.GIT_MERGE_FILE_FAVOR_THEIRS
545+
elif favor == 'union':
546+
return C.GIT_MERGE_FILE_FAVOR_UNION
547+
else:
548+
return None
529549

530550
ours_ptr = ffi.new('git_commit **')
531551
theirs_ptr = ffi.new('git_commit **')
552+
opts = ffi.new('git_merge_options *')
532553
cindex = ffi.new('git_index **')
533554

534555
if is_string(ours) or isinstance(ours, Oid):
@@ -539,10 +560,19 @@ def merge_commits(self, ours, theirs):
539560
ours = ours.peel(Commit)
540561
theirs = theirs.peel(Commit)
541562

563+
err = C.git_merge_init_options(opts, C.GIT_MERGE_OPTIONS_VERSION)
564+
check_error(err)
565+
566+
favor_val = favor_to_enum(favor)
567+
if favor_val is None:
568+
raise ValueError("unkown favor value %s" % favor)
569+
570+
opts.file_favor = favor_val
571+
542572
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
543573
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]
544574

545-
err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], ffi.NULL)
575+
err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], opts)
546576
check_error(err)
547577

548578
return Index.from_c(self, cindex)

test/test_merge.py

+7
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,10 @@ def test_merge_commits(self):
158158
merge_tree = index.write_tree()
159159

160160
self.assertEqual(merge_tree, merge_commits_tree)
161+
162+
def test_merge_commits_favor(self):
163+
branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
164+
merge_index = self.repo.merge_commits(self.repo.head.target, branch_head_hex, favor='ours')
165+
self.assertTrue(merge_index.conflicts is None)
166+
167+
self.assertRaises(ValueError, self.repo.merge_commits, self.repo.head.target, branch_head_hex, favor='foo')

0 commit comments

Comments
 (0)