diff --git a/lib/libgit2dart.dart b/lib/libgit2dart.dart index 916aa7f..38c904e 100644 --- a/lib/libgit2dart.dart +++ b/lib/libgit2dart.dart @@ -1,3 +1,4 @@ +export 'src/annotated.dart'; export 'src/blame.dart'; export 'src/blob.dart'; export 'src/branch.dart'; diff --git a/lib/src/annotated.dart b/lib/src/annotated.dart new file mode 100644 index 0000000..b5958f9 --- /dev/null +++ b/lib/src/annotated.dart @@ -0,0 +1,102 @@ +import 'dart:ffi'; + +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:libgit2dart/src/bindings/annotated.dart' as bindings; +import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; + +/// An annotated commit contains information about how it was looked up, which +/// may be useful for functions like merge or rebase to provide context to the +/// operation. For example, conflict files will include the name of the source +/// or target branches being merged. +class AnnotatedCommit { + /// Lookups an annotated commit from the given commit [oid]. + /// + /// It is preferable to use [AnnotatedCommit.fromReference] instead of this + /// one, for commit to contain more information about how it was looked up. + /// + /// **IMPORTANT**: Should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + AnnotatedCommit.lookup({required Repository repo, required Oid oid}) { + _annotatedCommitPointer = bindings.lookup( + repoPointer: repo.pointer, + oidPointer: oid.pointer, + ); + } + + /// Creates an annotated commit from the given [reference]. + /// + /// **IMPORTANT**: Should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + AnnotatedCommit.fromReference({ + required Repository repo, + required Reference reference, + }) { + _annotatedCommitPointer = bindings.fromRef( + repoPointer: repo.pointer, + referencePointer: reference.pointer, + ); + } + + /// Creates an annotated commit from a revision string. + /// + /// See `man gitrevisions`, or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions + /// for information on the syntax accepted. + /// + /// **IMPORTANT**: Should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + AnnotatedCommit.fromRevSpec({ + required Repository repo, + required String spec, + }) { + _annotatedCommitPointer = bindings.fromRevSpec( + repoPointer: repo.pointer, + revspec: spec, + ); + } + + /// Creates an annotated commit from the given fetch head data. + /// + /// [repo] is repository that contains the given commit. + /// + /// [branchName] is name of the (remote) branch. + /// + /// [remoteUrl] is url of the remote. + /// + /// [oid] is the commit object id of the remote branch. + /// + /// **IMPORTANT**: Should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + AnnotatedCommit.fromFetchHead({ + required Repository repo, + required String branchName, + required String remoteUrl, + required Oid oid, + }) { + _annotatedCommitPointer = bindings.fromFetchHead( + repoPointer: repo.pointer, + branchName: branchName, + remoteUrl: remoteUrl, + oid: oid.pointer, + ); + } + + late final Pointer _annotatedCommitPointer; + + /// Pointer to pointer to memory address for allocated commit object. + Pointer get pointer => _annotatedCommitPointer; + + /// Commit oid that the given annotated commit refers to. + Oid get oid => Oid.fromRaw(bindings.oid(_annotatedCommitPointer).ref); + + /// Reference name that the annotated commit refers to. + /// + /// Returns empty string if no information found. + String get refName => bindings.refName(_annotatedCommitPointer); + + /// Releases memory allocated for commit object. + void free() => bindings.free(_annotatedCommitPointer); +} diff --git a/lib/src/bindings/annotated.dart b/lib/src/bindings/annotated.dart new file mode 100644 index 0000000..30bc0d3 --- /dev/null +++ b/lib/src/bindings/annotated.dart @@ -0,0 +1,132 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:libgit2dart/src/error.dart'; +import 'package:libgit2dart/src/util.dart'; + +/// Creates an annotated commit from the given commit id. The resulting +/// annotated commit must be freed with [annotatedFree]. +/// +/// An annotated commit contains information about how it was looked up, which +/// may be useful for functions like merge or rebase to provide context to the +/// operation. For example, conflict files will include the name of the source +/// or target branches being merged. It is therefore preferable to use the most +/// specific function (e.g. [annotatedFromRef]) instead of this one when that +/// data is known. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer lookup({ + required Pointer repoPointer, + required Pointer oidPointer, +}) { + final out = calloc>(); + final error = libgit2.git_annotated_commit_lookup( + out, + repoPointer, + oidPointer, + ); + + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Creates an annotated commit from the given reference. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer fromRef({ + required Pointer repoPointer, + required Pointer referencePointer, +}) { + final out = calloc>(); + final error = libgit2.git_annotated_commit_from_ref( + out, + repoPointer, + referencePointer, + ); + + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Creates an annotated commit from a revision string. +/// +/// See `man gitrevisions`, or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions +/// for information on the syntax accepted. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer fromRevSpec({ + required Pointer repoPointer, + required String revspec, +}) { + final out = calloc>(); + final revspecC = revspec.toNativeUtf8().cast(); + final error = libgit2.git_annotated_commit_from_revspec( + out, + repoPointer, + revspecC, + ); + + calloc.free(revspecC); + + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Creates an annotated commit from the given fetch head data. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer fromFetchHead({ + required Pointer repoPointer, + required String branchName, + required String remoteUrl, + required Pointer oid, +}) { + final out = calloc>(); + final branchNameC = branchName.toNativeUtf8().cast(); + final remoteUrlC = remoteUrl.toNativeUtf8().cast(); + final error = libgit2.git_annotated_commit_from_fetchhead( + out, + repoPointer, + branchNameC, + remoteUrlC, + oid, + ); + + calloc.free(branchNameC); + calloc.free(remoteUrlC); + + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Gets the commit ID that the given annotated commit refers to. +Pointer oid(Pointer commit) => + libgit2.git_annotated_commit_id(commit); + +/// Get the refname that the given annotated commit refers to. +String refName(Pointer commit) { + final result = libgit2.git_annotated_commit_ref(commit); + return result == nullptr ? '' : result.cast().toDartString(); +} + +/// Frees an annotated commit. +void free(Pointer commit) { + libgit2.git_annotated_commit_free(commit); +} diff --git a/lib/src/bindings/commit.dart b/lib/src/bindings/commit.dart index 10d7c0e..1267c96 100644 --- a/lib/src/bindings/commit.dart +++ b/lib/src/bindings/commit.dart @@ -25,41 +25,6 @@ Pointer lookup({ } } -/// Creates an annotated commit from the given commit id. The resulting -/// annotated commit must be freed with [annotatedFree]. -/// -/// An annotated commit contains information about how it was looked up, which -/// may be useful for functions like merge or rebase to provide context to the -/// operation. For example, conflict files will include the name of the source -/// or target branches being merged. It is therefore preferable to use the most -/// specific function (eg git_annotated_commit_from_ref) instead of this one -/// when that data is known. -/// -/// Throws a [LibGit2Error] if error occured. -Pointer> annotatedLookup({ - required Pointer repoPointer, - required Pointer oidPointer, -}) { - final out = calloc>(); - final error = libgit2.git_annotated_commit_lookup( - out, - repoPointer, - oidPointer, - ); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } -} - -/// Frees an annotated commit. -void annotatedFree(Pointer commit) { - libgit2.git_annotated_commit_free(commit); -} - /// Create new commit in the repository. /// /// Throws a [LibGit2Error] if error occured. diff --git a/lib/src/bindings/merge.dart b/lib/src/bindings/merge.dart index f1ea634..aa256ae 100644 --- a/lib/src/bindings/merge.dart +++ b/lib/src/bindings/merge.dart @@ -89,17 +89,20 @@ Pointer mergeBaseOctopus({ List analysis({ required Pointer repoPointer, required Pointer ourRefPointer, - required Pointer> theirHeadPointer, + required Pointer theirHeadPointer, required int theirHeadsLen, }) { final analysisOut = calloc(); final preferenceOut = calloc(); + final theirHead = calloc>(); + theirHead[0] = theirHeadPointer; + libgit2.git_merge_analysis_for_ref( analysisOut, preferenceOut, repoPointer, ourRefPointer, - theirHeadPointer, + theirHead, theirHeadsLen, ); @@ -107,6 +110,7 @@ List analysis({ calloc.free(analysisOut); calloc.free(preferenceOut); + calloc.free(theirHead); return result; } @@ -117,12 +121,15 @@ List analysis({ /// completes, resolve any conflicts and prepare a commit. void merge({ required Pointer repoPointer, - required Pointer> theirHeadsPointer, + required Pointer theirHeadPointer, required int theirHeadsLen, required int favor, required int mergeFlags, required int fileFlags, }) { + final theirHead = calloc>(); + theirHead[0] = theirHeadPointer; + final mergeOpts = _initMergeOptions( favor: favor, mergeFlags: mergeFlags, @@ -138,7 +145,7 @@ void merge({ libgit2.git_merge( repoPointer, - theirHeadsPointer, + theirHead, theirHeadsLen, mergeOpts, checkoutOpts, @@ -146,6 +153,7 @@ void merge({ calloc.free(mergeOpts); calloc.free(checkoutOpts); + calloc.free(theirHead); } /// Merge two files as they exist in the in-memory data structures, using the diff --git a/lib/src/commit.dart b/lib/src/commit.dart index add02fb..21d9797 100644 --- a/lib/src/commit.dart +++ b/lib/src/commit.dart @@ -292,31 +292,3 @@ class Commit { ' author: $author}'; } } - -/// An annotated commit contains information about how it was looked up, which -/// may be useful for functions like merge or rebase to provide context to the -/// operation. For example, conflict files will include the name of the source -/// or target branches being merged. -/// -/// Note: for internal use. -class AnnotatedCommit { - /// Lookups an annotated commit from the given commit [oid]. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// - /// Throws a [LibGit2Error] if error occured. - AnnotatedCommit.lookup({required Repository repo, required Oid oid}) { - _annotatedCommitPointer = bindings.annotatedLookup( - repoPointer: repo.pointer, - oidPointer: oid.pointer, - ); - } - - late final Pointer> _annotatedCommitPointer; - - /// Pointer to pointer to memory address for allocated commit object. - Pointer> get pointer => _annotatedCommitPointer; - - /// Releases memory allocated for commit object. - void free() => bindings.annotatedFree(_annotatedCommitPointer.value); -} diff --git a/lib/src/rebase.dart b/lib/src/rebase.dart index 7754481..f5280f6 100644 --- a/lib/src/rebase.dart +++ b/lib/src/rebase.dart @@ -15,44 +15,23 @@ class Rebase { /// reachable commits. /// /// [onto] is the branch to rebase onto, default is to rebase onto the given - /// [upstream] (throws if [upstream] is not provided). + /// [upstream]. /// /// **IMPORTANT**: Should be freed to release allocated memory. /// /// Throws a [LibGit2Error] if error occured. Rebase.init({ required Repository repo, - Oid? branch, - Oid? upstream, - Oid? onto, + AnnotatedCommit? branch, + AnnotatedCommit? upstream, + AnnotatedCommit? onto, }) { - AnnotatedCommit? _branch, _upstream, _onto; - if (branch != null) { - _branch = AnnotatedCommit.lookup(repo: repo, oid: branch); - } - if (upstream != null) { - _upstream = AnnotatedCommit.lookup(repo: repo, oid: upstream); - } - if (onto != null) { - _onto = AnnotatedCommit.lookup(repo: repo, oid: onto); - } - _rebasePointer = bindings.init( repoPointer: repo.pointer, - branchPointer: _branch?.pointer.value, - upstreamPointer: _upstream?.pointer.value, - ontoPointer: _onto?.pointer.value, + branchPointer: branch?.pointer, + upstreamPointer: upstream?.pointer, + ontoPointer: onto?.pointer, ); - - if (branch != null) { - _branch!.free(); - } - if (upstream != null) { - _upstream!.free(); - } - if (onto != null) { - _onto!.free(); - } } /// Pointer to memory address for allocated rebase object. diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 348e15b..736dce0 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -1018,7 +1018,7 @@ class Repository { return [analysisSet, mergePreference]; } - /// Merges the given commit [oid] into HEAD, writing the results into the + /// Merges the given [commit] into HEAD, writing the results into the /// working directory. Any changes are staged for commit and any conflicts /// are written to the index. Callers should inspect the repository's index /// after this completes, resolve any conflicts and prepare a commit. @@ -1036,26 +1036,19 @@ class Repository { /// /// Throws a [LibGit2Error] if error occured. void merge({ - required Oid oid, + required AnnotatedCommit commit, GitMergeFileFavor favor = GitMergeFileFavor.normal, Set mergeFlags = const {GitMergeFlag.findRenames}, Set fileFlags = const {GitMergeFileFlag.defaults}, }) { - final theirHead = AnnotatedCommit.lookup( - repo: this, - oid: oid, - ); - merge_bindings.merge( repoPointer: _repoPointer, - theirHeadsPointer: theirHead.pointer, + theirHeadPointer: commit.pointer, theirHeadsLen: 1, favor: favor.value, mergeFlags: mergeFlags.fold(0, (acc, e) => acc | e.value), fileFlags: fileFlags.fold(0, (acc, e) => acc | e.value), ); - - theirHead.free(); } /// Merges two files as they exist in the in-memory data structures, using the diff --git a/test/annotated_test.dart b/test/annotated_test.dart new file mode 100644 index 0000000..fb0a57a --- /dev/null +++ b/test/annotated_test.dart @@ -0,0 +1,118 @@ +import 'dart:ffi'; +import 'dart:io'; + +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:test/test.dart'; + +import 'helpers/util.dart'; + +void main() { + late Repository repo; + late Directory tmpDir; + late Oid tip; + + setUp(() { + tmpDir = setupRepo(Directory('test/assets/test_repo/')); + repo = Repository.open(tmpDir.path); + tip = repo['821ed6e80627b8769d170a293862f9fc60825226']; + }); + + tearDown(() { + repo.free(); + tmpDir.deleteSync(recursive: true); + }); + + group('AnnotatedCommit', () { + test('lookups annotated commit from provided oid', () { + final annotated = AnnotatedCommit.lookup(repo: repo, oid: tip); + + expect(annotated.oid, tip); + expect(annotated.refName, ''); + + annotated.free(); + }); + + test('throws when trying to lookup annotated commit with invalid oid', () { + expect( + () => AnnotatedCommit.lookup(repo: repo, oid: repo['0' * 40]), + throwsA(isA()), + ); + }); + + test('creates annotated commit from provided reference', () { + final reference = repo.lookupReference('refs/heads/master'); + final annotated = AnnotatedCommit.fromReference( + repo: repo, + reference: reference, + ); + + expect(annotated.oid, reference.target); + expect(annotated.refName, 'refs/heads/master'); + + annotated.free(); + reference.free(); + }); + + test( + 'throws when trying to create annotated commit from provided ' + 'reference and error occurs', () { + final reference = repo.lookupReference('refs/heads/master'); + + expect( + () => AnnotatedCommit.fromReference( + repo: Repository(nullptr), + reference: reference, + ), + throwsA(isA()), + ); + + reference.free(); + }); + + test('creates annotated commit from provided revspec', () { + final annotated = AnnotatedCommit.fromRevSpec(repo: repo, spec: '@{-1}'); + + expect(annotated.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); + expect(annotated.refName, ''); + + annotated.free(); + }); + + test('throws when trying to create annotated commit from invalid revspec', + () { + expect( + () => AnnotatedCommit.fromRevSpec(repo: repo, spec: 'invalid'), + throwsA(isA()), + ); + }); + + test('creates annotated commit from provided fetch head data', () { + final oid = repo['821ed6e']; + final annotated = AnnotatedCommit.fromFetchHead( + repo: repo, + branchName: 'master', + remoteUrl: 'git://github.com/SkinnyMind/libgit2dart.git', + oid: oid, + ); + + expect(annotated.oid, oid); + expect(annotated.refName, 'master'); + + annotated.free(); + }); + + test( + 'throws when trying to create annotated commit from fetch head and ' + 'error occurs', () { + expect( + () => AnnotatedCommit.fromFetchHead( + repo: repo, + branchName: '', + remoteUrl: '', + oid: Oid(nullptr), + ), + throwsA(isA()), + ); + }); + }); +} diff --git a/test/commit_test.dart b/test/commit_test.dart index c277894..4543498 100644 --- a/test/commit_test.dart +++ b/test/commit_test.dart @@ -57,19 +57,6 @@ void main() { ); }); - test('successfully lookups annotated commit for provided oid', () { - final annotated = AnnotatedCommit.lookup(repo: repo, oid: tip); - expect(annotated, isA()); - annotated.free(); - }); - - test('throws when trying to lookup annotated commit with invalid oid', () { - expect( - () => AnnotatedCommit.lookup(repo: repo, oid: repo['0' * 40]), - throwsA(isA()), - ); - }); - test( 'throws when trying to get the summary of the commit message and error ' 'occurs', () { diff --git a/test/index_test.dart b/test/index_test.dart index b247c34..c288444 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -311,10 +311,16 @@ void main() { final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); final index = repo.index; - repo.merge(oid: conflictBranch.target); + final commit = AnnotatedCommit.lookup( + repo: repo, + oid: conflictBranch.target, + ); + + repo.merge(commit: commit); expect(() => index.writeTree(), throwsA(isA())); + commit.free(); conflictBranch.free(); index.free(); repo.free(); @@ -341,10 +347,14 @@ void main() { final conflictBranch = conflictRepo.lookupBranch( name: 'ancestor-conflict', ); + final commit = AnnotatedCommit.lookup( + repo: conflictRepo, + oid: conflictBranch.target, + ); conflictRepo.checkout(refName: 'refs/heads/feature'); - conflictRepo.merge(oid: conflictBranch.target); + conflictRepo.merge(commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['feature_file']!; @@ -354,6 +364,7 @@ void main() { expect(conflictedFile.toString(), contains('ConflictEntry{')); index.free(); + commit.free(); conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); @@ -364,8 +375,12 @@ void main() { final conflictRepo = Repository.open(repoDir.path); final conflictBranch = conflictRepo.lookupBranch(name: 'conflict-branch'); + final commit = AnnotatedCommit.lookup( + repo: conflictRepo, + oid: conflictBranch.target, + ); - conflictRepo.merge(oid: conflictBranch.target); + conflictRepo.merge(commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['conflict_file']!; @@ -375,6 +390,7 @@ void main() { expect(conflictedFile.toString(), contains('ConflictEntry{')); index.free(); + commit.free(); conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); @@ -387,10 +403,14 @@ void main() { final conflictBranch = conflictRepo.lookupBranch( name: 'ancestor-conflict', ); + final commit = AnnotatedCommit.lookup( + repo: conflictRepo, + oid: conflictBranch.target, + ); conflictRepo.checkout(refName: 'refs/heads/our-conflict'); - conflictRepo.merge(oid: conflictBranch.target); + conflictRepo.merge(commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['feature_file']!; @@ -400,6 +420,7 @@ void main() { expect(conflictedFile.toString(), contains('ConflictEntry{')); index.free(); + commit.free(); conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); @@ -410,10 +431,14 @@ void main() { final conflictRepo = Repository.open(repoDir.path); final conflictBranch = conflictRepo.lookupBranch(name: 'their-conflict'); + final commit = AnnotatedCommit.lookup( + repo: conflictRepo, + oid: conflictBranch.target, + ); conflictRepo.checkout(refName: 'refs/heads/feature'); - conflictRepo.merge(oid: conflictBranch.target); + conflictRepo.merge(commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['feature_file']!; @@ -423,6 +448,7 @@ void main() { expect(conflictedFile.toString(), contains('ConflictEntry{')); index.free(); + commit.free(); conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); @@ -433,9 +459,13 @@ void main() { final conflictRepo = Repository.open(repoDir.path); final conflictBranch = conflictRepo.lookupBranch(name: 'conflict-branch'); + final commit = AnnotatedCommit.lookup( + repo: conflictRepo, + oid: conflictBranch.target, + ); final index = conflictRepo.index; - conflictRepo.merge(oid: conflictBranch.target); + conflictRepo.merge(commit: commit); expect(index.hasConflicts, true); expect(index['.gitignore'].isConflict, false); expect(index.conflicts['conflict_file']!.our!.isConflict, true); @@ -448,6 +478,7 @@ void main() { expect(index.conflicts['conflict_file'], null); index.free(); + commit.free(); conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); @@ -466,9 +497,13 @@ void main() { final conflictRepo = Repository.open(repoDir.path); final conflictBranch = conflictRepo.lookupBranch(name: 'conflict-branch'); + final commit = AnnotatedCommit.lookup( + repo: conflictRepo, + oid: conflictBranch.target, + ); final index = conflictRepo.index; - conflictRepo.merge(oid: conflictBranch.target); + conflictRepo.merge(commit: commit); expect(index.hasConflicts, true); expect(index.conflicts.length, 1); @@ -477,6 +512,7 @@ void main() { expect(index.conflicts, isEmpty); index.free(); + commit.free(); conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); diff --git a/test/merge_test.dart b/test/merge_test.dart index 108b6a7..ea6f52a 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -72,12 +72,16 @@ void main() { test('writes conflicts to index', () { final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); + final commit = AnnotatedCommit.lookup( + repo: repo, + oid: conflictBranch.target, + ); final index = repo.index; final result = repo.mergeAnalysis(theirHead: conflictBranch.target); expect(result[0], {GitMergeAnalysis.normal}); - repo.merge(oid: conflictBranch.target); + repo.merge(commit: commit); expect(index.hasConflicts, true); expect(index.conflicts.length, 1); expect(repo.state, GitRepositoryState.merge); @@ -105,6 +109,7 @@ void main() { ); index.free(); + commit.free(); conflictBranch.free(); }); @@ -118,8 +123,13 @@ conflict branch edit >>>>>>> conflict_file """; final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); + final commit = AnnotatedCommit.lookup( + repo: repo, + oid: conflictBranch.target, + ); final index = repo.index; - repo.merge(oid: conflictBranch.target); + + repo.merge(commit: commit); final conflictedFile = index.conflicts['conflict_file']!; final diff = repo.mergeFileFromIndex( @@ -131,6 +141,7 @@ conflict branch edit expect(diff, diffExpected); index.free(); + commit.free(); conflictBranch.free(); }); @@ -143,9 +154,14 @@ Another feature edit >>>>>>> feature_file """; final conflictBranch = repo.lookupBranch(name: 'ancestor-conflict'); + final commit = AnnotatedCommit.lookup( + repo: repo, + oid: conflictBranch.target, + ); repo.checkout(refName: 'refs/heads/feature'); final index = repo.index; - repo.merge(oid: conflictBranch.target); + + repo.merge(commit: commit); final conflictedFile = index.conflicts['feature_file']!; final diff = repo.mergeFileFromIndex( @@ -157,6 +173,7 @@ Another feature edit expect(diff, diffExpected); index.free(); + commit.free(); conflictBranch.free(); }); @@ -169,9 +186,14 @@ conflict branch edit >>>>>>> conflict_file """; final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); - final index = repo.index; - repo.merge( + final commit = AnnotatedCommit.lookup( + repo: repo, oid: conflictBranch.target, + ); + final index = repo.index; + + repo.merge( + commit: commit, mergeFlags: {GitMergeFlag.noRecursive}, fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL}, ); @@ -186,14 +208,20 @@ conflict branch edit expect(diff, diffExpected); index.free(); + commit.free(); conflictBranch.free(); }); test('merges with provided merge favor', () { final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); + final commit = AnnotatedCommit.lookup( + repo: repo, + oid: conflictBranch.target, + ); final index = repo.index; - repo.merge(oid: conflictBranch.target, favor: GitMergeFileFavor.ours); + repo.merge(commit: commit, favor: GitMergeFileFavor.ours); + expect(index.conflicts, isEmpty); expect( File('${repo.workdir}conflict_file').readAsStringSync(), @@ -201,6 +229,7 @@ conflict branch edit ); index.free(); + commit.free(); conflictBranch.free(); }); @@ -274,6 +303,10 @@ theirs content group('merge commits', () { test('successfully merges with default values', () { final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final theirCommitAnnotated = AnnotatedCommit.lookup( + repo: repo, + oid: theirCommit.oid, + ); final ourCommit = repo.lookupCommit(repo['1490545']); final mergeIndex = repo.mergeCommits( @@ -283,7 +316,7 @@ theirs content expect(mergeIndex.conflicts, isEmpty); final mergeCommitsTree = mergeIndex.writeTree(repo); - repo.merge(oid: theirCommit.oid); + repo.merge(commit: theirCommitAnnotated); final index = repo.index; expect(index.conflicts, isEmpty); final mergeTree = index.writeTree(); @@ -293,6 +326,7 @@ theirs content index.free(); mergeIndex.free(); ourCommit.free(); + theirCommitAnnotated.free(); theirCommit.free(); }); @@ -420,6 +454,10 @@ theirs content group('merge trees', () { test('successfully merges with default values', () { final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final theirCommitAnnotated = AnnotatedCommit.lookup( + repo: repo, + oid: theirCommit.oid, + ); final ourCommit = repo.lookupCommit(repo['1490545']); final baseCommit = repo.lookupCommit( repo.mergeBase([ourCommit.oid, theirCommit.oid]), @@ -437,7 +475,7 @@ theirs content final mergeTreesTree = mergeIndex.writeTree(repo); repo.setHead(ourCommit.oid); - repo.merge(oid: theirCommit.oid); + repo.merge(commit: theirCommitAnnotated); final index = repo.index; expect(index.conflicts, isEmpty); final mergeTree = index.writeTree(); @@ -451,6 +489,7 @@ theirs content theirTree.free(); baseCommit.free(); ourCommit.free(); + theirCommitAnnotated.free(); theirCommit.free(); }); diff --git a/test/rebase_test.dart b/test/rebase_test.dart index 5394a4f..c683800 100644 --- a/test/rebase_test.dart +++ b/test/rebase_test.dart @@ -32,15 +32,17 @@ void main() { time: 1234, ); final master = repo.lookupReference('refs/heads/master'); + final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final feature = repo.lookupReference('refs/heads/feature'); + final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target); repo.checkout(refName: feature.name); expect(() => repo.index['.gitignore'], throwsA(isA())); final rebase = Rebase.init( repo: repo, - branch: master.target, - onto: feature.target, + branch: branchHead, + onto: ontoHead, ); final operationsCount = rebase.operationsCount; @@ -63,6 +65,8 @@ void main() { expect(repo.index['.gitignore'], isA()); rebase.free(); + ontoHead.free(); + branchHead.free(); feature.free(); master.free(); signature.free(); @@ -75,10 +79,11 @@ void main() { time: 1234, ); final feature = repo.lookupReference('refs/heads/feature'); + final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target); final rebase = Rebase.init( repo: repo, - onto: feature.target, + onto: ontoHead, ); final operationsCount = rebase.operationsCount; @@ -100,6 +105,7 @@ void main() { rebase.finish(); rebase.free(); + ontoHead.free(); feature.free(); signature.free(); }); @@ -111,16 +117,17 @@ void main() { time: 1234, ); final master = repo.lookupReference('refs/heads/master'); + final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final feature = repo.lookupReference('refs/heads/feature'); - final startCommit = repo.lookupCommit(repo[shas[1]]); + final upstream = AnnotatedCommit.lookup(repo: repo, oid: repo[shas[1]]); repo.checkout(refName: feature.name); expect(() => repo.index['conflict_file'], throwsA(isA())); final rebase = Rebase.init( repo: repo, - branch: master.target, - upstream: startCommit.oid, + branch: branchHead, + upstream: upstream, ); final operationsCount = rebase.operationsCount; @@ -135,7 +142,8 @@ void main() { expect(repo.index['conflict_file'], isA()); rebase.free(); - startCommit.free(); + upstream.free(); + branchHead.free(); feature.free(); master.free(); signature.free(); @@ -154,14 +162,16 @@ void main() { time: 1234, ); final master = repo.lookupReference('refs/heads/master'); + final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = repo.lookupReference('refs/heads/conflict-branch'); + final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target); repo.checkout(refName: conflict.name); final rebase = Rebase.init( repo: repo, - branch: master.target, - onto: conflict.target, + branch: branchHead, + onto: ontoHead, ); expect(rebase.operationsCount, 1); @@ -174,6 +184,8 @@ void main() { ); rebase.free(); + ontoHead.free(); + branchHead.free(); conflict.free(); master.free(); signature.free(); @@ -187,14 +199,16 @@ void main() { time: 1234, ); final master = repo.lookupReference('refs/heads/master'); + final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = repo.lookupReference('refs/heads/conflict-branch'); + final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target); repo.checkout(refName: conflict.name); final rebase = Rebase.init( repo: repo, - branch: master.target, - onto: conflict.target, + branch: branchHead, + onto: ontoHead, ); expect(rebase.operationsCount, 1); @@ -202,6 +216,8 @@ void main() { expect(() => rebase.next(), throwsA(isA())); rebase.free(); + ontoHead.free(); + branchHead.free(); conflict.free(); master.free(); signature.free(); @@ -209,14 +225,16 @@ void main() { test('successfully aborts rebase in progress', () { final master = repo.lookupReference('refs/heads/master'); + final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = repo.lookupReference('refs/heads/conflict-branch'); + final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target); repo.checkout(refName: conflict.name); final rebase = Rebase.init( repo: repo, - branch: master.target, - onto: conflict.target, + branch: branchHead, + onto: ontoHead, ); expect(rebase.operationsCount, 1); @@ -229,6 +247,8 @@ void main() { expect(repo.state, GitRepositoryState.none); rebase.free(); + ontoHead.free(); + branchHead.free(); conflict.free(); master.free(); });