feat(merge): add ability to pass options to fileFromIndex(...) API method

This commit is contained in:
Aleksey Kulikov 2022-06-17 15:31:02 +03:00
parent aa073c531e
commit 57f3f5fdb7
3 changed files with 85 additions and 14 deletions

View file

@ -186,30 +186,39 @@ String mergeFile({
libgit2.git_merge_file_input_init(theirsC, GIT_MERGE_FILE_INPUT_VERSION); libgit2.git_merge_file_input_init(theirsC, GIT_MERGE_FILE_INPUT_VERSION);
ancestorC.ref.ptr = ancestor.toChar(); ancestorC.ref.ptr = ancestor.toChar();
ancestorC.ref.size = ancestor.length; ancestorC.ref.size = ancestor.length;
Pointer<Char> ancestorLabelC = nullptr;
oursC.ref.ptr = ours.toChar(); oursC.ref.ptr = ours.toChar();
oursC.ref.size = ours.length; oursC.ref.size = ours.length;
Pointer<Char> oursLabelC = nullptr;
theirsC.ref.ptr = theirs.toChar(); theirsC.ref.ptr = theirs.toChar();
theirsC.ref.size = theirs.length; theirsC.ref.size = theirs.length;
Pointer<Char> theirsLabelC = nullptr;
final opts = calloc<git_merge_file_options>(); final opts = calloc<git_merge_file_options>();
libgit2.git_merge_file_options_init(opts, GIT_MERGE_FILE_OPTIONS_VERSION); libgit2.git_merge_file_options_init(opts, GIT_MERGE_FILE_OPTIONS_VERSION);
opts.ref.favor = favor; opts.ref.favor = favor;
opts.ref.flags = flags; opts.ref.flags = flags;
if (ancestorLabel.isNotEmpty) { if (ancestorLabel.isNotEmpty) {
opts.ref.ancestor_label = ancestorLabel.toChar(); ancestorLabelC = ancestorLabel.toChar();
opts.ref.ancestor_label = ancestorLabelC;
} }
if (oursLabel.isNotEmpty) { if (oursLabel.isNotEmpty) {
opts.ref.our_label = oursLabel.toChar(); oursLabelC = oursLabel.toChar();
opts.ref.our_label = oursLabelC;
} }
if (theirsLabel.isNotEmpty) { if (theirsLabel.isNotEmpty) {
opts.ref.their_label = theirsLabel.toChar(); theirsLabelC = theirsLabel.toChar();
opts.ref.their_label = theirsLabelC;
} }
libgit2.git_merge_file(out, ancestorC, oursC, theirsC, opts); libgit2.git_merge_file(out, ancestorC, oursC, theirsC, opts);
calloc.free(ancestorC); calloc.free(ancestorC);
calloc.free(ancestorLabelC);
calloc.free(oursC); calloc.free(oursC);
calloc.free(oursLabelC);
calloc.free(theirsC); calloc.free(theirsC);
calloc.free(theirsLabelC);
calloc.free(opts); calloc.free(opts);
final result = out.ref.ptr.toDartString(length: out.ref.len); final result = out.ref.ptr.toDartString(length: out.ref.len);
@ -226,17 +235,43 @@ String mergeFile({
String mergeFileFromIndex({ String mergeFileFromIndex({
required Pointer<git_repository> repoPointer, required Pointer<git_repository> repoPointer,
required Pointer<git_index_entry>? ancestorPointer, required Pointer<git_index_entry>? ancestorPointer,
required String ancestorLabel,
required Pointer<git_index_entry> oursPointer, required Pointer<git_index_entry> oursPointer,
required String oursLabel,
required Pointer<git_index_entry> theirsPointer, required Pointer<git_index_entry> theirsPointer,
required String theirsLabel,
required int favor,
required int flags,
}) { }) {
final out = calloc<git_merge_file_result>(); final out = calloc<git_merge_file_result>();
final opts = calloc<git_merge_file_options>();
Pointer<Char> ancestorLabelC = nullptr;
Pointer<Char> oursLabelC = nullptr;
Pointer<Char> theirsLabelC = nullptr;
libgit2.git_merge_file_options_init(opts, GIT_MERGE_FILE_OPTIONS_VERSION);
opts.ref.favor = favor;
opts.ref.flags = flags;
if (ancestorLabel.isNotEmpty) {
ancestorLabelC = ancestorLabel.toChar();
opts.ref.ancestor_label = ancestorLabelC;
}
if (oursLabel.isNotEmpty) {
oursLabelC = oursLabel.toChar();
opts.ref.our_label = oursLabelC;
}
if (theirsLabel.isNotEmpty) {
theirsLabelC = theirsLabel.toChar();
opts.ref.their_label = theirsLabelC;
}
final error = libgit2.git_merge_file_from_index( final error = libgit2.git_merge_file_from_index(
out, out,
repoPointer, repoPointer,
ancestorPointer ?? nullptr, ancestorPointer ?? nullptr,
oursPointer, oursPointer,
theirsPointer, theirsPointer,
nullptr, opts,
); );
late final String result; late final String result;
@ -244,6 +279,10 @@ String mergeFileFromIndex({
result = out.ref.ptr.toDartString(length: out.ref.len); result = out.ref.ptr.toDartString(length: out.ref.len);
} }
calloc.free(ancestorLabelC);
calloc.free(oursLabelC);
calloc.free(theirsLabelC);
calloc.free(opts);
calloc.free(out); calloc.free(out);
if (error < 0) { if (error < 0) {

View file

@ -265,18 +265,43 @@ class Merge {
/// given common [ancestor] as the baseline, producing a string that reflects /// given common [ancestor] as the baseline, producing a string that reflects
/// the merge result containing possible conflicts. /// the merge result containing possible conflicts.
/// ///
/// [ancestorLabel] is optional label for the ancestor file side of the
/// conflict which will be prepended to labels in diff3-format merge files.
///
/// [oursLabel] is optional label for our file side of the conflict which
/// will be prepended to labels in merge files.
///
/// [theirsLabel] is optional label for their file side of the conflict which
/// will be prepended to labels in merge files.
///
/// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting
/// content. Defaults to [GitMergeFileFavor.normal].
///
/// [flags] is a combination of [GitMergeFileFlag] flags. Defaults to
/// [GitMergeFileFlag.defaults].
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
static String fileFromIndex({ static String fileFromIndex({
required Repository repo, required Repository repo,
required IndexEntry? ancestor, required IndexEntry? ancestor,
String ancestorLabel = '',
required IndexEntry ours, required IndexEntry ours,
String oursLabel = '',
required IndexEntry theirs, required IndexEntry theirs,
String theirsLabel = '',
GitMergeFileFavor favor = GitMergeFileFavor.normal,
Set<GitMergeFileFlag> flags = const {GitMergeFileFlag.defaults},
}) { }) {
return bindings.mergeFileFromIndex( return bindings.mergeFileFromIndex(
repoPointer: repo.pointer, repoPointer: repo.pointer,
ancestorPointer: ancestor?.pointer, ancestorPointer: ancestor?.pointer,
ancestorLabel: ancestorLabel,
oursPointer: ours.pointer, oursPointer: ours.pointer,
oursLabel: oursLabel,
theirsPointer: theirs.pointer, theirsPointer: theirs.pointer,
theirsLabel: theirsLabel,
favor: favor.value,
flags: flags.fold(0, (acc, e) => acc | e.value),
); );
} }

View file

@ -168,31 +168,38 @@ Another feature edit
expect(diff, diffExpected); expect(diff, diffExpected);
}); });
test('merges with provided merge flags and file flags', () { test('merges with provided options', () {
const diffExpected = """ const diffExpected = """
\<<<<<<< conflict_file \<<<<<<< ours
master conflict edit Feature edit on feature branch
||||||| ancestor
Feature edit
======= =======
conflict branch edit Another feature edit
>>>>>>> conflict_file >>>>>>> theirs
"""; """;
Checkout.reference(repo: repo, name: 'refs/heads/feature');
repo.setHead('refs/heads/feature');
Merge.commit( Merge.commit(
repo: repo, repo: repo,
commit: AnnotatedCommit.lookup( commit: AnnotatedCommit.lookup(
repo: repo, repo: repo,
oid: Branch.lookup(repo: repo, name: 'conflict-branch').target, oid: Branch.lookup(repo: repo, name: 'ancestor-conflict').target,
), ),
mergeFlags: {GitMergeFlag.noRecursive},
fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL},
); );
final conflictedFile = repo.index.conflicts['conflict_file']!; final conflictedFile = repo.index.conflicts['feature_file']!;
final diff = Merge.fileFromIndex( final diff = Merge.fileFromIndex(
repo: repo, repo: repo,
ancestor: null, ancestor: conflictedFile.ancestor,
ancestorLabel: 'ancestor',
ours: conflictedFile.our!, ours: conflictedFile.our!,
oursLabel: 'ours',
theirs: conflictedFile.their!, theirs: conflictedFile.their!,
theirsLabel: 'theirs',
flags: {GitMergeFileFlag.styleDiff3},
); );
expect(diff, diffExpected); expect(diff, diffExpected);