From 52707dcc630261411bc996a91cb69f8be624ca8d Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Mon, 4 Oct 2021 12:23:48 +0300 Subject: [PATCH] feat(merge): add ability to merge file from index --- lib/src/bindings/merge.dart | 30 ++++++++++++++++++++++++++++++ lib/src/index.dart | 9 ++++++++- lib/src/repository.dart | 18 ++++++++++++++++++ test/merge_test.dart | 31 +++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/lib/src/bindings/merge.dart b/lib/src/bindings/merge.dart index d1d29b1..2a671d4 100644 --- a/lib/src/bindings/merge.dart +++ b/lib/src/bindings/merge.dart @@ -89,6 +89,36 @@ void merge({ } } +/// Merge two files as they exist in the index, using the given common ancestor +/// as the baseline, producing a string that reflects the merge result containing +/// possible conflicts. +/// +/// Throws a [LibGit2Error] if error occured. +String mergeFileFromIndex({ + required Pointer repoPointer, + required Pointer? ancestorPointer, + required Pointer? oursPointer, + required Pointer? theirsPointer, +}) { + final out = calloc(); + final error = libgit2.git_merge_file_from_index( + out, + repoPointer, + ancestorPointer ?? nullptr, + oursPointer ?? nullptr, + theirsPointer ?? nullptr, + nullptr, + ); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + final result = out.ref.ptr.cast().toDartString(length: out.ref.len); + calloc.free(out); + return result; + } +} + /// Merge two commits, producing a git_index that reflects the result of the merge. /// The index may be written as-is to the working directory or checked out. If the index /// is to be converted to a tree, the caller should resolve any conflicts that arose as diff --git a/lib/src/index.dart b/lib/src/index.dart index 7a20b52..b6ba8ea 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -256,9 +256,11 @@ class IndexEntry { /// Initializes a new instance of [IndexEntry] class. const IndexEntry(this._indexEntryPointer); - /// Pointer to memory address for allocated index entry object. final Pointer _indexEntryPointer; + /// Pointer to memory address for allocated index entry object. + Pointer get pointer => _indexEntryPointer; + /// Unique identity of the index entry. Oid get id => Oid.fromRaw(_indexEntryPointer.ref.id); @@ -282,6 +284,11 @@ class IndexEntry { /// Sets the UNIX file attributes of a index entry. set mode(GitFilemode mode) => _indexEntryPointer.ref.mode = mode.value; + @override + String toString() { + return 'IndexEntry{path: $path, sha: $sha}'; + } + String _oidToHex(git_oid oid) { var hex = StringBuffer(); for (var i = 0; i < 20; i++) { diff --git a/lib/src/repository.dart b/lib/src/repository.dart index ad1651c..145dead 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -677,6 +677,24 @@ class Repository { commit_bindings.annotatedFree(theirHead.value); } + /// Merges two files as they exist in the index, using the given common ancestor + /// as the baseline, producing a string that reflects the merge result containing + /// possible conflicts. + /// + /// Throws a [LibGit2Error] if error occured. + String mergeFileFromIndex({ + required IndexEntry? ancestor, + required IndexEntry? ours, + required IndexEntry? theirs, + }) { + return merge_bindings.mergeFileFromIndex( + repoPointer: _repoPointer, + ancestorPointer: ancestor?.pointer, + oursPointer: ours?.pointer, + theirsPointer: theirs?.pointer, + ); + } + /// Merges two commits, producing an index that reflects the result of the merge. /// The index may be written as-is to the working directory or checked out. If the index /// is to be converted to a tree, the caller should resolve any conflicts that arose as diff --git a/test/merge_test.dart b/test/merge_test.dart index 2bc31ac..5ff3895 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unnecessary_string_escapes + import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -139,6 +141,35 @@ void main() { conflictBranch.free(); }); + group('merge file from index', () { + test('successfully merges', () { + const diffExpected = """ +\<<<<<<< conflict_file +master conflict edit +======= +conflict branch edit +>>>>>>> conflict_file +"""; + final conflictBranch = repo.branches['conflict-branch']; + final index = repo.index; + repo.merge(conflictBranch.target); + + final diff = repo.mergeFileFromIndex( + ancestor: index.conflicts['conflict_file']!.ancestor, + ours: index.conflicts['conflict_file']!.our, + theirs: index.conflicts['conflict_file']!.their, + ); + + expect( + diff, + diffExpected, + ); + + index.free(); + conflictBranch.free(); + }); + }); + group('merge commits', () { test('successfully merges with default values', () { final theirCommit =