From 570c696269513cdbc212646c7e337362c2caa6cc Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Tue, 25 Jan 2022 18:53:04 +0300 Subject: [PATCH] refactor(merge)!: move merge related methods into Merge class (#41) --- lib/libgit2dart.dart | 1 + lib/src/bindings/merge.dart | 4 +- lib/src/merge.dart | 301 ++++++++++++++++++++++++++++++++++++ lib/src/repository.dart | 274 -------------------------------- test/index_test.dart | 14 +- test/merge_test.dart | 143 +++++++++-------- test/repository_test.dart | 2 +- test/revparse_test.dart | 2 +- 8 files changed, 393 insertions(+), 348 deletions(-) create mode 100644 lib/src/merge.dart diff --git a/lib/libgit2dart.dart b/lib/libgit2dart.dart index 38c904e..ff118d8 100644 --- a/lib/libgit2dart.dart +++ b/lib/libgit2dart.dart @@ -12,6 +12,7 @@ export 'src/git_types.dart'; export 'src/index.dart'; export 'src/libgit2.dart'; export 'src/mailmap.dart'; +export 'src/merge.dart'; export 'src/note.dart'; export 'src/odb.dart'; export 'src/oid.dart'; diff --git a/lib/src/bindings/merge.dart b/lib/src/bindings/merge.dart index aa256ae..45d98e9 100644 --- a/lib/src/bindings/merge.dart +++ b/lib/src/bindings/merge.dart @@ -115,7 +115,7 @@ List analysis({ return result; } -/// Merges the given commit(s) into HEAD, writing the results into the working +/// 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. @@ -285,7 +285,7 @@ Pointer mergeCommits({ } } -/// Merge two trees, producing a git_index that reflects the result of the +/// Merge two trees, 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 part of the merge. diff --git a/lib/src/merge.dart b/lib/src/merge.dart new file mode 100644 index 0000000..fefc36d --- /dev/null +++ b/lib/src/merge.dart @@ -0,0 +1,301 @@ +import 'dart:ffi'; + +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:libgit2dart/src/bindings/merge.dart' as bindings; +import 'package:libgit2dart/src/util.dart'; + +class Merge { + const Merge._(); + + /// Finds a merge base between [commits]. + /// + /// Throws a [LibGit2Error] if error occured. + static Oid base({required Repository repo, required List commits}) { + return commits.length == 2 + ? Oid( + bindings.mergeBase( + repoPointer: repo.pointer, + aPointer: commits[0].pointer, + bPointer: commits[1].pointer, + ), + ) + : Oid( + bindings.mergeBaseMany( + repoPointer: repo.pointer, + commits: commits.map((e) => e.pointer.ref).toList(), + ), + ); + } + + /// Finds a merge base in preparation for an octopus merge. + /// + /// Throws a [LibGit2Error] if error occured. + static Oid octopusBase({ + required Repository repo, + required List commits, + }) { + return Oid( + bindings.mergeBaseOctopus( + repoPointer: repo.pointer, + commits: commits.map((e) => e.pointer.ref).toList(), + ), + ); + } + + /// Analyzes the given branch's [theirHead] oid and determines the + /// opportunities for merging them into [ourRef] reference (default is + /// 'HEAD'). + /// + /// Returns list with analysis result and preference for fast forward merge + /// values respectively. + /// + /// Throws a [LibGit2Error] if error occured. + static List analysis({ + required Repository repo, + required Oid theirHead, + String ourRef = 'HEAD', + }) { + final ref = Reference.lookup(repo: repo, name: ourRef); + final head = AnnotatedCommit.lookup( + repo: repo, + oid: theirHead, + ); + final analysisInt = bindings.analysis( + repoPointer: repo.pointer, + ourRefPointer: ref.pointer, + theirHeadPointer: head.pointer, + theirHeadsLen: 1, + ); + + final analysisSet = GitMergeAnalysis.values + .where((e) => analysisInt[0] & e.value == e.value) + .toSet(); + final mergePreference = GitMergePreference.values.singleWhere( + (e) => analysisInt[1] == e.value, + ); + + head.free(); + ref.free(); + + return [analysisSet, mergePreference]; + } + + /// 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. + /// + /// [repo] is the repository to merge. + /// + /// [commit] is the commit to merge. + /// + /// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting + /// content. Defaults to [GitMergeFileFavor.normal], recording conflict to t + /// he index. + /// + /// [mergeFlags] is a combination of [GitMergeFlag] flags. Defaults to + /// [GitMergeFlag.findRenames] enabling the ability to merge between a + /// modified and renamed file. + /// + /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to + /// [GitMergeFileFlag.defaults]. + /// + /// Throws a [LibGit2Error] if error occured. + static void commit({ + required Repository repo, + required AnnotatedCommit commit, + GitMergeFileFavor favor = GitMergeFileFavor.normal, + Set mergeFlags = const {GitMergeFlag.findRenames}, + Set fileFlags = const {GitMergeFileFlag.defaults}, + }) { + bindings.merge( + repoPointer: repo.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), + ); + } + + /// 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 part of the merge. + /// + /// [repo] is the repository that contains the given commits. + /// + /// [ourCommit] is the commit that reflects the destination tree. + /// + /// [theirCommit] is the commit to merge into [ourCommit]. + /// + /// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting + /// content. Defaults to [GitMergeFileFavor.normal], recording conflict to t + /// he index. + /// + /// [mergeFlags] is a combination of [GitMergeFlag] flags. Defaults to + /// [GitMergeFlag.findRenames] enabling the ability to merge between a + /// modified and renamed file. + /// + /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to + /// [GitMergeFileFlag.defaults]. + /// + /// **IMPORTANT**: returned index should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + static Index commits({ + required Repository repo, + required Commit ourCommit, + required Commit theirCommit, + GitMergeFileFavor favor = GitMergeFileFavor.normal, + Set mergeFlags = const {GitMergeFlag.findRenames}, + Set fileFlags = const {GitMergeFileFlag.defaults}, + }) { + return Index( + bindings.mergeCommits( + repoPointer: repo.pointer, + ourCommitPointer: ourCommit.pointer, + theirCommitPointer: theirCommit.pointer, + favor: favor.value, + mergeFlags: mergeFlags.fold(0, (acc, e) => acc | e.value), + fileFlags: fileFlags.fold(0, (acc, e) => acc | e.value), + ), + ); + } + + /// Merges two trees, 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 part of the merge. + /// + /// [repo] is the repository that contains the given trees. + /// + /// [ancestorTree] is the common ancestor between the trees, or null if none + /// (default). + /// + /// [ourTree] is the tree that reflects the destination tree. + /// + /// [theirTree] is the tree to merge into [ourTree]. + /// + /// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting + /// content. Defaults to [GitMergeFileFavor.normal], recording conflict to + /// the index. + /// + /// [mergeFlags] is a combination of [GitMergeFlag] flags. Defaults to + /// [GitMergeFlag.findRenames] enabling the ability to merge between a + /// modified and renamed file. + /// + /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to + /// [GitMergeFileFlag.defaults]. + /// + /// **IMPORTANT**: returned index should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + static Index trees({ + required Repository repo, + Tree? ancestorTree, + required Tree ourTree, + required Tree theirTree, + GitMergeFileFavor favor = GitMergeFileFavor.normal, + List mergeFlags = const [GitMergeFlag.findRenames], + List fileFlags = const [GitMergeFileFlag.defaults], + }) { + return Index( + bindings.mergeTrees( + repoPointer: repo.pointer, + ancestorTreePointer: ancestorTree?.pointer ?? nullptr, + ourTreePointer: ourTree.pointer, + theirTreePointer: theirTree.pointer, + favor: favor.value, + mergeFlags: mergeFlags.fold(0, (acc, element) => acc | element.value), + fileFlags: fileFlags.fold(0, (acc, element) => acc | element.value), + ), + ); + } + + /// Merges two files as they exist in the in-memory data structures, using the + /// given common ancestor as the baseline, producing a string that reflects + /// the merge result. + /// + /// Note that this function does not reference a repository and configuration + /// must be passed as [favor] and [flags]. + /// + /// [ancestor] is the contents of the ancestor file. + /// + /// [ancestorLabel] is optional label for the ancestor file side of the + /// conflict which will be prepended to labels in diff3-format merge files. + /// Defaults to "file.txt". + /// + /// [ours] is the contents of the file in "our" side. + /// + /// [oursLabel] is optional label for our file side of the conflict which + /// will be prepended to labels in merge files. Defaults to "file.txt". + /// + /// [theirs] is the contents of the file in "their" side. + /// + /// [theirsLabel] is optional label for their file side of the conflict which + /// will be prepended to labels in merge files. Defaults to "file.txt". + /// + /// [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]. + static String file({ + required String ancestor, + String ancestorLabel = '', + required String ours, + String oursLabel = '', + required String theirs, + String theirsLabel = '', + GitMergeFileFavor favor = GitMergeFileFavor.normal, + Set flags = const {GitMergeFileFlag.defaults}, + }) { + libgit2.git_libgit2_init(); + + return bindings.mergeFile( + ancestor: ancestor, + ancestorLabel: ancestorLabel, + ours: ours, + oursLabel: oursLabel, + theirs: theirs, + theirsLabel: theirsLabel, + favor: favor.value, + flags: flags.fold(0, (acc, e) => acc | e.value), + ); + } + + /// Merges two files [ours] and [theirs] 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. + static String fileFromIndex({ + required Repository repo, + required IndexEntry? ancestor, + required IndexEntry ours, + required IndexEntry theirs, + }) { + return bindings.mergeFileFromIndex( + repoPointer: repo.pointer, + ancestorPointer: ancestor?.pointer, + oursPointer: ours.pointer, + theirsPointer: theirs.pointer, + ); + } + + /// Cherry-picks the provided [commit], producing changes in the index and + /// 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. + /// + /// Throws a [LibGit2Error] if error occured. + static void cherryPick({required Repository repo, required Commit commit}) { + bindings.cherryPick( + repoPointer: repo.pointer, + commitPointer: commit.pointer, + ); + } +} diff --git a/lib/src/repository.dart b/lib/src/repository.dart index cc31577..72b9986 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -8,7 +8,6 @@ import 'package:libgit2dart/src/bindings/checkout.dart' as checkout_bindings; import 'package:libgit2dart/src/bindings/describe.dart' as describe_bindings; import 'package:libgit2dart/src/bindings/graph.dart' as graph_bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; -import 'package:libgit2dart/src/bindings/merge.dart' as merge_bindings; import 'package:libgit2dart/src/bindings/object.dart' as object_bindings; import 'package:libgit2dart/src/bindings/repository.dart' as bindings; import 'package:libgit2dart/src/bindings/reset.dart' as reset_bindings; @@ -553,279 +552,6 @@ class Repository { } } - /// Finds a merge base between [commits]. - /// - /// Throws a [LibGit2Error] if error occured. - Oid mergeBase(List commits) { - return commits.length == 2 - ? Oid( - merge_bindings.mergeBase( - repoPointer: _repoPointer, - aPointer: commits[0].pointer, - bPointer: commits[1].pointer, - ), - ) - : Oid( - merge_bindings.mergeBaseMany( - repoPointer: _repoPointer, - commits: commits.map((e) => e.pointer.ref).toList(), - ), - ); - } - - /// Finds a merge base in preparation for an octopus merge. - /// - /// Throws a [LibGit2Error] if error occured. - Oid mergeBaseOctopus(List commits) { - return Oid( - merge_bindings.mergeBaseOctopus( - repoPointer: _repoPointer, - commits: commits.map((e) => e.pointer.ref).toList(), - ), - ); - } - - /// Analyzes the given branch's [theirHead] oid and determines the - /// opportunities for merging them into [ourRef] reference (default is - /// 'HEAD'). - /// - /// Returns list with analysis result and preference for fast forward merge - /// values respectively. - /// - /// Throws a [LibGit2Error] if error occured. - List mergeAnalysis({ - required Oid theirHead, - String ourRef = 'HEAD', - }) { - final ref = Reference.lookup(repo: this, name: ourRef); - final head = AnnotatedCommit.lookup( - repo: this, - oid: theirHead, - ); - final analysisInt = merge_bindings.analysis( - repoPointer: _repoPointer, - ourRefPointer: ref.pointer, - theirHeadPointer: head.pointer, - theirHeadsLen: 1, - ); - - final analysisSet = GitMergeAnalysis.values - .where((e) => analysisInt[0] & e.value == e.value) - .toSet(); - final mergePreference = GitMergePreference.values.singleWhere( - (e) => analysisInt[1] == e.value, - ); - - head.free(); - ref.free(); - - return [analysisSet, mergePreference]; - } - - /// 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. - /// - /// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting - /// content. Defaults to [GitMergeFileFavor.normal], recording conflict to t - /// he index. - /// - /// [mergeFlags] is a combination of [GitMergeFlag] flags. Defaults to - /// [GitMergeFlag.findRenames] enabling the ability to merge between a - /// modified and renamed file. - /// - /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to - /// [GitMergeFileFlag.defaults]. - /// - /// Throws a [LibGit2Error] if error occured. - void merge({ - required AnnotatedCommit commit, - GitMergeFileFavor favor = GitMergeFileFavor.normal, - Set mergeFlags = const {GitMergeFlag.findRenames}, - Set fileFlags = const {GitMergeFileFlag.defaults}, - }) { - merge_bindings.merge( - repoPointer: _repoPointer, - 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), - ); - } - - /// Merges two files as they exist in the in-memory data structures, using the - /// given common ancestor as the baseline, producing a string that reflects - /// the merge result. - /// - /// Note that this function does not reference a repository and configuration - /// must be passed as [favor] and [flags]. - /// - /// [ancestor] is the contents of the ancestor file. - /// - /// [ancestorLabel] is optional label for the ancestor file side of the - /// conflict which will be prepended to labels in diff3-format merge files. - /// Defaults to "file.txt". - /// - /// [ours] is the contents of the file in "our" side. - /// - /// [oursLabel] is optional label for our file side of the conflict which - /// will be prepended to labels in merge files. Defaults to "file.txt". - /// - /// [theirs] is the contents of the file in "their" side. - /// - /// [theirsLabel] is optional label for their file side of the conflict which - /// will be prepended to labels in merge files. Defaults to "file.txt". - /// - /// [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]. - String mergeFile({ - required String ancestor, - String ancestorLabel = '', - required String ours, - String oursLabel = '', - required String theirs, - String theirsLabel = '', - GitMergeFileFavor favor = GitMergeFileFavor.normal, - Set flags = const {GitMergeFileFlag.defaults}, - }) { - return merge_bindings.mergeFile( - ancestor: ancestor, - ancestorLabel: ancestorLabel, - ours: ours, - oursLabel: oursLabel, - theirs: theirs, - theirsLabel: theirsLabel, - favor: favor.value, - flags: flags.fold(0, (acc, e) => acc | e.value), - ); - } - - /// Merges two files [ours] and [theirs] 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 part of the merge. - /// - /// [ourCommit] is the commit that reflects the destination tree. - /// - /// [theirCommit] is the commit to merge into [ourCommit]. - /// - /// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting - /// content. Defaults to [GitMergeFileFavor.normal], recording conflict to t - /// he index. - /// - /// [mergeFlags] is a combination of [GitMergeFlag] flags. Defaults to - /// [GitMergeFlag.findRenames] enabling the ability to merge between a - /// modified and renamed file. - /// - /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to - /// [GitMergeFileFlag.defaults]. - /// - /// **IMPORTANT**: returned index should be freed to release allocated memory. - /// - /// Throws a [LibGit2Error] if error occured. - Index mergeCommits({ - required Commit ourCommit, - required Commit theirCommit, - GitMergeFileFavor favor = GitMergeFileFavor.normal, - Set mergeFlags = const {GitMergeFlag.findRenames}, - Set fileFlags = const {GitMergeFileFlag.defaults}, - }) { - return Index( - merge_bindings.mergeCommits( - repoPointer: _repoPointer, - ourCommitPointer: ourCommit.pointer, - theirCommitPointer: theirCommit.pointer, - favor: favor.value, - mergeFlags: mergeFlags.fold(0, (acc, e) => acc | e.value), - fileFlags: fileFlags.fold(0, (acc, e) => acc | e.value), - ), - ); - } - - /// Merges two trees, 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 part of the merge. - /// - /// [ancestorTree] is the common ancestor between the trees, or null if none. - /// - /// [ourTree] is the tree that reflects the destination tree. - /// - /// [theirTree] is the tree to merge into [ourTree]. - /// - /// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting - /// content. Defaults to [GitMergeFileFavor.normal], recording conflict to - /// the index. - /// - /// [mergeFlags] is a combination of [GitMergeFlag] flags. Defaults to - /// [GitMergeFlag.findRenames] enabling the ability to merge between a - /// modified and renamed file. - /// - /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to - /// [GitMergeFileFlag.defaults]. - /// - /// **IMPORTANT**: returned index should be freed to release allocated memory. - /// - /// Throws a [LibGit2Error] if error occured. - Index mergeTrees({ - Tree? ancestorTree, - required Tree ourTree, - required Tree theirTree, - GitMergeFileFavor favor = GitMergeFileFavor.normal, - List mergeFlags = const [GitMergeFlag.findRenames], - List fileFlags = const [GitMergeFileFlag.defaults], - }) { - return Index( - merge_bindings.mergeTrees( - repoPointer: _repoPointer, - ancestorTreePointer: ancestorTree?.pointer ?? nullptr, - ourTreePointer: ourTree.pointer, - theirTreePointer: theirTree.pointer, - favor: favor.value, - mergeFlags: mergeFlags.fold(0, (acc, element) => acc | element.value), - fileFlags: fileFlags.fold(0, (acc, element) => acc | element.value), - ), - ); - } - - /// Cherry-picks the provided [commit], producing changes in the index and - /// 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. - /// - /// Throws a [LibGit2Error] if error occured. - void cherryPick(Commit commit) { - merge_bindings.cherryPick( - repoPointer: _repoPointer, - commitPointer: commit.pointer, - ); - } - /// Checkouts the provided [target] using the given strategy, and updates the /// HEAD. /// diff --git a/test/index_test.dart b/test/index_test.dart index 8cc6622..9dc6317 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -327,7 +327,7 @@ void main() { oid: conflictBranch.target, ); - repo.merge(commit: commit); + Merge.commit(repo: repo, commit: commit); expect(() => index.writeTree(), throwsA(isA())); @@ -366,7 +366,7 @@ void main() { conflictRepo.checkout(target: 'refs/heads/feature'); - conflictRepo.merge(commit: commit); + Merge.commit(repo: conflictRepo, commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['feature_file']!; @@ -395,7 +395,7 @@ void main() { oid: conflictBranch.target, ); - conflictRepo.merge(commit: commit); + Merge.commit(repo: conflictRepo, commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['conflict_file']!; @@ -426,7 +426,7 @@ void main() { conflictRepo.checkout(target: 'refs/heads/our-conflict'); - conflictRepo.merge(commit: commit); + Merge.commit(repo: conflictRepo, commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['feature_file']!; @@ -457,7 +457,7 @@ void main() { conflictRepo.checkout(target: 'refs/heads/feature'); - conflictRepo.merge(commit: commit); + Merge.commit(repo: conflictRepo, commit: commit); final index = conflictRepo.index; final conflictedFile = index.conflicts['feature_file']!; @@ -487,7 +487,7 @@ void main() { ); final index = conflictRepo.index; - conflictRepo.merge(commit: commit); + Merge.commit(repo: conflictRepo, commit: commit); expect(index.hasConflicts, true); expect(index['.gitignore'].isConflict, false); expect(index.conflicts['conflict_file']!.our!.isConflict, true); @@ -528,7 +528,7 @@ void main() { ); final index = conflictRepo.index; - conflictRepo.merge(commit: commit); + Merge.commit(repo: conflictRepo, commit: commit); expect(index.hasConflicts, true); expect(index.conflicts.length, 1); diff --git a/test/merge_test.dart b/test/merge_test.dart index a015966..34e25e6 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -26,7 +26,7 @@ void main() { group('Merge', () { group('analysis', () { test('is up to date when no reference is provided', () { - final result = repo.mergeAnalysis(theirHead: repo['c68ff54']); + final result = Merge.analysis(repo: repo, theirHead: repo['c68ff54']); expect(result, [ {GitMergeAnalysis.upToDate}, GitMergePreference.none, @@ -35,7 +35,8 @@ void main() { }); test('is up to date for provided ref', () { - final result = repo.mergeAnalysis( + final result = Merge.analysis( + repo: repo, theirHead: repo['c68ff54'], ourRef: 'refs/tags/v0.1', ); @@ -51,7 +52,8 @@ void main() { target: ffCommit, ); - final result = repo.mergeAnalysis( + final result = Merge.analysis( + repo: repo, theirHead: repo['6cbc22e'], ourRef: 'refs/heads/${ffBranch.name}', ); @@ -66,7 +68,7 @@ void main() { }); test('is not fast forward and there is no conflicts', () { - final result = repo.mergeAnalysis(theirHead: repo['5aecfa0']); + final result = Merge.analysis(repo: repo, theirHead: repo['5aecfa0']); expect(result[0], {GitMergeAnalysis.normal}); expect(repo.status, isEmpty); }); @@ -80,10 +82,13 @@ void main() { ); final index = repo.index; - final result = repo.mergeAnalysis(theirHead: conflictBranch.target); + final result = Merge.analysis( + repo: repo, + theirHead: conflictBranch.target, + ); expect(result[0], {GitMergeAnalysis.normal}); - repo.merge(commit: commit); + Merge.commit(repo: repo, commit: commit); expect(index.hasConflicts, true); expect(index.conflicts.length, 1); expect(repo.state, GitRepositoryState.merge); @@ -134,10 +139,11 @@ conflict branch edit ); final index = repo.index; - repo.merge(commit: commit); + Merge.commit(repo: repo, commit: commit); final conflictedFile = index.conflicts['conflict_file']!; - final diff = repo.mergeFileFromIndex( + final diff = Merge.fileFromIndex( + repo: repo, ancestor: null, ours: conflictedFile.our!, theirs: conflictedFile.their!, @@ -169,10 +175,11 @@ Another feature edit repo.checkout(target: 'refs/heads/feature'); final index = repo.index; - repo.merge(commit: commit); + Merge.commit(repo: repo, commit: commit); final conflictedFile = index.conflicts['feature_file']!; - final diff = repo.mergeFileFromIndex( + final diff = Merge.fileFromIndex( + repo: repo, ancestor: conflictedFile.ancestor, ours: conflictedFile.our!, theirs: conflictedFile.their!, @@ -203,14 +210,16 @@ conflict branch edit ); final index = repo.index; - repo.merge( + Merge.commit( + repo: repo, commit: commit, mergeFlags: {GitMergeFlag.noRecursive}, fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL}, ); final conflictedFile = index.conflicts['conflict_file']!; - final diff = repo.mergeFileFromIndex( + final diff = Merge.fileFromIndex( + repo: repo, ancestor: null, ours: conflictedFile.our!, theirs: conflictedFile.their!, @@ -234,7 +243,7 @@ conflict branch edit ); final index = repo.index; - repo.merge(commit: commit, favor: GitMergeFileFavor.ours); + Merge.commit(repo: repo, commit: commit, favor: GitMergeFileFavor.ours); expect(index.conflicts, isEmpty); expect( @@ -249,7 +258,8 @@ conflict branch edit test('throws when error occurs', () { expect( - () => repo.mergeFileFromIndex( + () => Merge.fileFromIndex( + repo: repo, ancestor: null, ours: IndexEntry(nullptr), theirs: IndexEntry(nullptr), @@ -268,7 +278,7 @@ ours content theirs content >>>>>>> file.txt """; - final diff = repo.mergeFile( + final diff = Merge.file( ancestor: '', ours: 'ours content', theirs: 'theirs content', @@ -287,7 +297,7 @@ ancestor content theirs content >>>>>>> theirs.txt """; - final diff = repo.mergeFile( + final diff = Merge.file( ancestor: 'ancestor content', ancestorLabel: 'ancestor.txt', ours: 'ours content', @@ -303,7 +313,7 @@ theirs content test('merges file with provided favor', () { const diffExpected = 'ours content'; - final diff = repo.mergeFile( + final diff = Merge.file( ancestor: 'ancestor content', ours: 'ours content', theirs: 'theirs content', @@ -323,14 +333,15 @@ theirs content ); final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); - final mergeIndex = repo.mergeCommits( + final mergeIndex = Merge.commits( + repo: repo, ourCommit: ourCommit, theirCommit: theirCommit, ); expect(mergeIndex.conflicts, isEmpty); final mergeCommitsTree = mergeIndex.writeTree(repo); - repo.merge(commit: theirCommitAnnotated); + Merge.commit(repo: repo, commit: theirCommitAnnotated); final index = repo.index; expect(index.conflicts, isEmpty); final mergeTree = index.writeTree(); @@ -348,7 +359,8 @@ theirs content final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); - final mergeIndex = repo.mergeCommits( + final mergeIndex = Merge.commits( + repo: repo, ourCommit: ourCommit, theirCommit: theirCommit, favor: GitMergeFileFavor.ours, @@ -364,7 +376,8 @@ theirs content final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); - final mergeIndex = repo.mergeCommits( + final mergeIndex = Merge.commits( + repo: repo, ourCommit: ourCommit, theirCommit: theirCommit, mergeFlags: { @@ -386,7 +399,8 @@ theirs content test('throws when error occurs', () { expect( - () => repo.mergeCommits( + () => Merge.commits( + repo: repo, ourCommit: Commit(nullptr), theirCommit: Commit(nullptr), ), @@ -396,70 +410,64 @@ theirs content }); test('finds merge base for two commits', () { - var base = repo.mergeBase([repo['1490545'], repo['5aecfa0']]); + var base = Merge.base( + repo: repo, + commits: [repo['1490545'], repo['5aecfa0']], + ); expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - base = repo.mergeBase([repo['f17d0d4'], repo['5aecfa0']]); + base = Merge.base( + repo: repo, + commits: [repo['f17d0d4'], repo['5aecfa0']], + ); expect(base.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); }); test('finds merge base for many commits', () { - var base = repo.mergeBase( - [ - repo['1490545'], - repo['0e409d6'], - repo['5aecfa0'], - ], + var base = Merge.base( + repo: repo, + commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], ); expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - base = repo.mergeBase( - [ - repo['f17d0d4'], - repo['5aecfa0'], - repo['0e409d6'], - ], + base = Merge.base( + repo: repo, + commits: [repo['f17d0d4'], repo['5aecfa0'], repo['0e409d6']], ); expect(base.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); }); test('throws when trying to find merge base for invalid oid', () { expect( - () => repo.mergeBase([repo['0' * 40], repo['5aecfa0']]), + () => Merge.base( + repo: repo, + commits: [repo['0' * 40], repo['5aecfa0']], + ), throwsA(isA()), ); expect( - () => repo.mergeBase( - [ - repo['0' * 40], - repo['5aecfa0'], - repo['0e409d6'], - ], + () => Merge.base( + repo: repo, + commits: [repo['0' * 40], repo['5aecfa0'], repo['0e409d6']], ), throwsA(isA()), ); }); test('finds octopus merge base', () { - final base = repo.mergeBaseOctopus( - [ - repo['1490545'], - repo['0e409d6'], - repo['5aecfa0'], - ], + final base = Merge.octopusBase( + repo: repo, + commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], ); expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); }); test('throws when trying to find octopus merge base for invalid oid', () { expect( - () => repo.mergeBaseOctopus( - [ - repo['0' * 40], - repo['5aecfa0'], - repo['0e409d6'], - ], + () => Merge.octopusBase( + repo: repo, + commits: [repo['0' * 40], repo['5aecfa0'], repo['0e409d6']], ), throwsA(isA()), ); @@ -475,13 +483,17 @@ theirs content final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); final baseCommit = Commit.lookup( repo: repo, - oid: repo.mergeBase([ourCommit.oid, theirCommit.oid]), + oid: Merge.base( + repo: repo, + commits: [ourCommit.oid, theirCommit.oid], + ), ); final theirTree = theirCommit.tree; final ourTree = ourCommit.tree; final ancestorTree = baseCommit.tree; - final mergeIndex = repo.mergeTrees( + final mergeIndex = Merge.trees( + repo: repo, ancestorTree: ancestorTree, ourTree: ourTree, theirTree: theirTree, @@ -490,7 +502,7 @@ theirs content final mergeTreesTree = mergeIndex.writeTree(repo); repo.setHead(ourCommit.oid); - repo.merge(commit: theirCommitAnnotated); + Merge.commit(repo: repo, commit: theirCommitAnnotated); final index = repo.index; expect(index.conflicts, isEmpty); final mergeTree = index.writeTree(); @@ -513,13 +525,17 @@ theirs content final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); final baseCommit = Commit.lookup( repo: repo, - oid: repo.mergeBase([ourCommit.oid, theirCommit.oid]), + oid: Merge.base( + repo: repo, + commits: [ourCommit.oid, theirCommit.oid], + ), ); final theirTree = theirCommit.tree; final ourTree = ourCommit.tree; final ancestorTree = baseCommit.tree; - final mergeIndex = repo.mergeTrees( + final mergeIndex = Merge.trees( + repo: repo, ancestorTree: ancestorTree, ourTree: ourTree, theirTree: theirTree, @@ -538,7 +554,8 @@ theirs content test('throws when error occurs', () { expect( - () => Repository(nullptr).mergeTrees( + () => Merge.trees( + repo: Repository(nullptr), ancestorTree: Tree(nullptr), ourTree: Tree(nullptr), theirTree: Tree(nullptr), @@ -550,7 +567,7 @@ theirs content test('cherry-picks commit', () { final cherry = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - repo.cherryPick(cherry); + Merge.cherryPick(repo: repo, commit: cherry); expect(repo.state, GitRepositoryState.cherrypick); expect(repo.message, 'add another feature file\n'); final index = repo.index; @@ -568,7 +585,7 @@ theirs content test('throws when error occurs', () { expect( - () => repo.cherryPick(Commit(nullptr)), + () => Merge.cherryPick(repo: repo, commit: Commit(nullptr)), throwsA(isA()), ); }); diff --git a/test/repository_test.dart b/test/repository_test.dart index b582cb5..30a9bf5 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -189,7 +189,7 @@ void main() { test('cleans up state', () { expect(repo.state, GitRepositoryState.none); final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - repo.cherryPick(commit); + Merge.cherryPick(repo: repo, commit: commit); expect(repo.state, GitRepositoryState.cherrypick); repo.stateCleanup(); diff --git a/test/revparse_test.dart b/test/revparse_test.dart index 11e7498..9f0df3e 100644 --- a/test/revparse_test.dart +++ b/test/revparse_test.dart @@ -125,7 +125,7 @@ void main() { expect(revspec.to?.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(revspec.flags, {GitRevSpec.range, GitRevSpec.mergeBase}); expect( - repo.mergeBase([revspec.from.oid, revspec.to!.oid]), + Merge.base(repo: repo, commits: [revspec.from.oid, revspec.to!.oid]), isA(), );