Implement merging of index entries.
This allows us to generate a textual diff of conflicting files in bare repositories by performing a merge on the index followed by repo.merge_file_from_index on the resulting index entries.
This commit is contained in:
		@@ -657,8 +657,34 @@ typedef struct {
 | 
			
		||||
	git_merge_file_favor_t file_favor;
 | 
			
		||||
} git_merge_options;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	unsigned int automergeable;
 | 
			
		||||
	const char *path;
 | 
			
		||||
	unsigned int mode;
 | 
			
		||||
	const char *ptr;
 | 
			
		||||
	size_t len;
 | 
			
		||||
} git_merge_file_result;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
	GIT_MERGE_FILE_DEFAULT = 0,
 | 
			
		||||
	GIT_MERGE_FILE_STYLE_MERGE = 1,
 | 
			
		||||
	GIT_MERGE_FILE_STYLE_DIFF3 = 2,
 | 
			
		||||
	GIT_MERGE_FILE_SIMPLIFY_ALNUM = 4,
 | 
			
		||||
} git_merge_file_flags_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	unsigned int version;
 | 
			
		||||
	const char *ancestor_label;
 | 
			
		||||
	const char *our_label;
 | 
			
		||||
	const char *their_label;
 | 
			
		||||
	git_merge_file_favor_t favor;
 | 
			
		||||
	git_merge_file_flags_t flags;
 | 
			
		||||
} git_merge_file_options;
 | 
			
		||||
 | 
			
		||||
#define GIT_MERGE_OPTIONS_VERSION ...
 | 
			
		||||
 | 
			
		||||
int git_merge_init_options(git_merge_options *opts,	unsigned int version);
 | 
			
		||||
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);
 | 
			
		||||
int git_merge_trees(git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *opts);
 | 
			
		||||
int git_merge_file_from_index(git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts);
 | 
			
		||||
void git_merge_file_result_free(git_merge_file_result *result);
 | 
			
		||||
 
 | 
			
		||||
@@ -526,6 +526,40 @@ class Repository(_Repository):
 | 
			
		||||
 | 
			
		||||
        return opts
 | 
			
		||||
 | 
			
		||||
    def merge_file_from_index(self, ancestor, ours, theirs):
 | 
			
		||||
        """merge_file_from_index(ancestor, ours, theirs) -> str
 | 
			
		||||
 | 
			
		||||
        Merge files from index.
 | 
			
		||||
 | 
			
		||||
        Return a :py:class:`str` object with the merge result
 | 
			
		||||
        containing possible conflicts.
 | 
			
		||||
 | 
			
		||||
        ancestor
 | 
			
		||||
            The index entry which will be used as a common
 | 
			
		||||
            ancestor.
 | 
			
		||||
        ours
 | 
			
		||||
            The index entry to take as "ours" or base.
 | 
			
		||||
        theirs
 | 
			
		||||
            The index entry which will be merged into "ours"
 | 
			
		||||
        """
 | 
			
		||||
        cmergeresult = ffi.new('git_merge_file_result *')
 | 
			
		||||
 | 
			
		||||
        cancestor = ancestor._to_c()[0] if ancestor is not None else ffi.NULL
 | 
			
		||||
        cours = ours._to_c()[0] if ours is not None else ffi.NULL
 | 
			
		||||
        ctheirs = theirs._to_c()[0] if theirs is not None else ffi.NULL
 | 
			
		||||
 | 
			
		||||
        err = C.git_merge_file_from_index(
 | 
			
		||||
                cmergeresult, self._repo,
 | 
			
		||||
                cancestor, cours, ctheirs,
 | 
			
		||||
                ffi.NULL);
 | 
			
		||||
        check_error(err)
 | 
			
		||||
 | 
			
		||||
        ret = ffi.string(cmergeresult.ptr,
 | 
			
		||||
                cmergeresult.len).decode()
 | 
			
		||||
        C.git_merge_file_result_free(cmergeresult)
 | 
			
		||||
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def merge_commits(self, ours, theirs, favor='normal'):
 | 
			
		||||
        """Merge two arbitrary commits
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -198,6 +198,39 @@ class RepositoryTest(utils.BareRepoTestCase):
 | 
			
		||||
        written_sha1 = self.repo.create_blob(data)
 | 
			
		||||
        self.assertEqual(hashed_sha1, written_sha1)
 | 
			
		||||
 | 
			
		||||
    def test_conflicts_in_bare_repository(self):
 | 
			
		||||
        def create_conflict_file(repo, branch, content):
 | 
			
		||||
            oid = repo.create_blob(content)
 | 
			
		||||
            tb = repo.TreeBuilder()
 | 
			
		||||
            tb.insert('conflict', oid, pygit2.GIT_FILEMODE_BLOB)
 | 
			
		||||
            tree = tb.write()
 | 
			
		||||
 | 
			
		||||
            sig = pygit2.Signature('Author', 'author@example.com')
 | 
			
		||||
            commit = repo.create_commit(branch.name, sig, sig,
 | 
			
		||||
                    'Conflict', tree, [branch.target])
 | 
			
		||||
            self.assertIsNotNone(commit)
 | 
			
		||||
            return commit
 | 
			
		||||
 | 
			
		||||
        b1 = self.repo.create_branch('b1', self.repo.head.peel())
 | 
			
		||||
        c1 = create_conflict_file(self.repo, b1, 'Conflict 1')
 | 
			
		||||
        b2 = self.repo.create_branch('b2', self.repo.head.peel())
 | 
			
		||||
        c2 = create_conflict_file(self.repo, b2, 'Conflict 2')
 | 
			
		||||
 | 
			
		||||
        index = self.repo.merge_commits(c1, c2)
 | 
			
		||||
        self.assertIsNotNone(index.conflicts)
 | 
			
		||||
 | 
			
		||||
        # ConflictCollection does not allow calling len(...) on it directly so
 | 
			
		||||
        # we have to calculate length by iterating over its entries
 | 
			
		||||
        self.assertEqual(sum(1 for _ in index.conflicts), 1)
 | 
			
		||||
 | 
			
		||||
        (a, t, o) = index.conflicts['conflict']
 | 
			
		||||
        diff = self.repo.merge_file_from_index(a, t, o)
 | 
			
		||||
        self.assertEqual(diff, '''<<<<<<< conflict
 | 
			
		||||
Conflict 1
 | 
			
		||||
=======
 | 
			
		||||
Conflict 2
 | 
			
		||||
>>>>>>> conflict
 | 
			
		||||
''')
 | 
			
		||||
 | 
			
		||||
class RepositoryTest_II(utils.RepoTestCase):
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user