From 3a0fa7592956a8b3a3fea31766fbe43287c3825a Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Mon, 11 Oct 2021 20:06:36 +0300 Subject: [PATCH] feat(repository)!: add more aliases for api methods BREAKING CHANGE: Make repository entry point for most operations --- example/config_example.dart | 6 +- example/reference_example.dart | 16 +- lib/src/bindings/branch.dart | 16 +- lib/src/bindings/commit.dart | 17 +- lib/src/bindings/note.dart | 4 +- lib/src/bindings/treebuilder.dart | 2 +- lib/src/bindings/worktree.dart | 18 +- lib/src/blob.dart | 12 +- lib/src/branch.dart | 176 +++++++------- lib/src/commit.dart | 26 +- lib/src/index.dart | 20 +- lib/src/note.dart | 98 ++++---- lib/src/reference.dart | 177 +++++++------- lib/src/remote.dart | 121 +++++----- lib/src/repository.dart | 382 +++++++++++++++++++++++++----- lib/src/tag.dart | 42 ++-- lib/src/tree.dart | 12 +- lib/src/treebuilder.dart | 6 +- test/blame_test.dart | 8 +- test/blob_test.dart | 57 +++-- test/branch_test.dart | 132 +++++++---- test/checkout_test.dart | 18 +- test/commit_test.dart | 102 ++++---- test/describe_test.dart | 37 ++- test/diff_test.dart | 54 ++++- test/helpers/util.dart | 24 +- test/index_test.dart | 35 ++- test/mailmap_test.dart | 8 +- test/merge_test.dart | 109 +++++---- test/note_test.dart | 26 +- test/odb_test.dart | 8 +- test/oid_test.dart | 8 +- test/packbuilder_test.dart | 16 +- test/patch_test.dart | 28 ++- test/rebase_test.dart | 26 +- test/reference_test.dart | 187 +++++++-------- test/reflog_test.dart | 8 +- test/remote_prune_test.dart | 59 +++-- test/remote_test.dart | 98 ++++---- test/repository_clone_test.dart | 23 +- test/repository_init_test.dart | 14 +- test/repository_test.dart | 31 ++- test/reset_test.dart | 8 +- test/revparse_test.dart | 12 +- test/revwalk_test.dart | 8 +- test/stash_test.dart | 8 +- test/submodule_test.dart | 8 +- test/tag_test.dart | 26 +- test/tree_test.dart | 15 +- test/treebuilder_test.dart | 17 +- test/worktree_test.dart | 73 +++--- 51 files changed, 1380 insertions(+), 1062 deletions(-) diff --git a/example/config_example.dart b/example/config_example.dart index 6508320..1fa1618 100644 --- a/example/config_example.dart +++ b/example/config_example.dart @@ -2,9 +2,9 @@ import 'dart:io'; import 'package:libgit2dart/libgit2dart.dart'; import '../test/helpers/util.dart'; -void main() async { +void main() { // Preparing example repository. - final tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + final tmpDir = setupRepo(Directory('test/assets/testrepo/')); // Open system + global config file. final config = Config.open(); @@ -54,5 +54,5 @@ void main() async { } // Removing example repository. - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); } diff --git a/example/reference_example.dart b/example/reference_example.dart index 0bf20ba..63db570 100644 --- a/example/reference_example.dart +++ b/example/reference_example.dart @@ -2,17 +2,17 @@ import 'dart:io'; import 'package:libgit2dart/libgit2dart.dart'; import '../test/helpers/util.dart'; -void main() async { +void main() { // Preparing example repository. - final tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + final tmpDir = setupRepo(Directory('test/assets/testrepo/')); final repo = Repository.open(tmpDir.path); // Get list of repo's references. - print('Repository references: ${repo.references.list}'); + print('Repository references: ${repo.references}'); // Get reference by name. - final ref = repo.references['refs/heads/master']; + final ref = repo.lookupReference('refs/heads/master'); print('Reference SHA hex: ${ref.target.sha}'); print('Is reference a local branch: ${ref.isBranch}'); @@ -20,16 +20,16 @@ void main() async { print('Reference shorthand name: ${ref.shorthand}'); // Create new reference (direct or symbolic). - final newRef = repo.references.create( + final newRef = repo.createReference( name: 'refs/tags/v1', target: 'refs/heads/master', ); // Rename reference. - newRef.rename(newName: 'refs/tags/v1.1'); + repo.renameReference(oldName: 'v1', newName: 'refs/tags/v1.1'); // Delete reference. - newRef.delete(); + repo.deleteReference('v1.1'); // free() should be called on object to free memory when done. ref.free(); @@ -37,5 +37,5 @@ void main() async { repo.free(); // Removing example repository. - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); } diff --git a/lib/src/bindings/branch.dart b/lib/src/bindings/branch.dart index 972209e..6fc21ab 100644 --- a/lib/src/bindings/branch.dart +++ b/lib/src/bindings/branch.dart @@ -9,7 +9,7 @@ import '../util.dart'; /// Return a list of branches. /// /// Throws a [LibGit2Error] if error occured. -List list({ +List> list({ required Pointer repoPointer, required int flags, }) { @@ -24,7 +24,7 @@ List list({ throw LibGit2Error(libgit2.git_error_last()); } - var result = []; + var result = >[]; var error = 0; while (error == 0) { @@ -32,10 +32,8 @@ List list({ final refType = calloc(); error = libgit2.git_branch_next(reference, refType, iterator.value); if (error == 0) { - final refName = reference_bindings.shorthand(reference.value); - result.add(refName); + result.add(reference.value); calloc.free(refType); - calloc.free(reference); } else { break; } @@ -129,11 +127,8 @@ void delete(Pointer branch) { /// /// The new branch name will be checked for validity. /// -/// Note that if the move succeeds, the old reference object will not be valid anymore, -/// and will be freed immediately. -/// /// Throws a [LibGit2Error] if error occured. -Pointer rename({ +void rename({ required Pointer branchPointer, required String newBranchName, required bool force, @@ -149,8 +144,7 @@ Pointer rename({ if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { - reference_bindings.free(branchPointer); - return out.value; + reference_bindings.free(out.value); } } diff --git a/lib/src/bindings/commit.dart b/lib/src/bindings/commit.dart index 9bf2dc3..8261ca0 100644 --- a/lib/src/bindings/commit.dart +++ b/lib/src/bindings/commit.dart @@ -3,7 +3,6 @@ import 'package:ffi/ffi.dart'; import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; -import 'oid.dart' as oid_bindings; /// Lookup a commit object from a repository. /// @@ -92,26 +91,21 @@ Pointer create({ required String message, required Pointer treePointer, required int parentCount, - required List parents, + required List> parents, }) { final out = calloc(); final updateRefC = updateRef?.toNativeUtf8().cast() ?? nullptr; final messageEncodingC = messageEncoding?.toNativeUtf8().cast() ?? nullptr; final messageC = message.toNativeUtf8().cast(); - Pointer> parentsC = - calloc.call>(parentCount); + final parentsC = calloc>(parentCount); if (parents.isNotEmpty) { for (var i = 0; i < parentCount; i++) { - final oid = oid_bindings.fromSHA(parents[i]); - var commit = calloc(); - commit = lookup(repoPointer: repoPointer, oidPointer: oid).cast(); - parentsC[i] = commit.cast(); + parentsC[i] = parents[i]; } } else { - final commit = calloc(); - parentsC[0] = commit.cast(); + parentsC[0] = nullptr; } final error = libgit2.git_commit_create( @@ -130,9 +124,6 @@ Pointer create({ calloc.free(updateRefC); calloc.free(messageEncodingC); calloc.free(messageC); - for (var i = 0; i < parentCount; i++) { - free(parentsC[i]); - } calloc.free(parentsC); if (error < 0) { diff --git a/lib/src/bindings/note.dart b/lib/src/bindings/note.dart index 4dd45ce..da82c74 100644 --- a/lib/src/bindings/note.dart +++ b/lib/src/bindings/note.dart @@ -107,10 +107,10 @@ Pointer create({ } } -/// Remove the note for an object. +/// Delete the note for an object. /// /// Throws a [LibGit2Error] if error occured. -void remove({ +void delete({ required Pointer repoPointer, String notesRef = 'refs/notes/commits', required Pointer authorPointer, diff --git a/lib/src/bindings/treebuilder.dart b/lib/src/bindings/treebuilder.dart index 0363fd4..0c18b47 100644 --- a/lib/src/bindings/treebuilder.dart +++ b/lib/src/bindings/treebuilder.dart @@ -130,5 +130,5 @@ void remove({ } } -/// Free a tree builder to release memory. +/// Free a tree builder and all the entries to release memory. void free(Pointer bld) => libgit2.git_treebuilder_free(bld); diff --git a/lib/src/bindings/worktree.dart b/lib/src/bindings/worktree.dart index 1ed8b47..661817b 100644 --- a/lib/src/bindings/worktree.dart +++ b/lib/src/bindings/worktree.dart @@ -20,13 +20,22 @@ Pointer create({ final out = calloc>(); final nameC = name.toNativeUtf8().cast(); final pathC = path.toNativeUtf8().cast(); - final opts = - calloc(sizeOf()); - opts.ref.version = 1; - opts.ref.lock = 0; + + final opts = calloc(); + final optsError = libgit2.git_worktree_add_options_init( + opts, + GIT_WORKTREE_ADD_OPTIONS_VERSION, + ); + + if (optsError < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } + + opts.ref.ref = nullptr; if (refPointer != null) { opts.ref.ref = refPointer; } + final error = libgit2.git_worktree_add(out, repoPointer, nameC, pathC, opts); calloc.free(nameC); @@ -82,6 +91,7 @@ List list(Pointer repo) { final result = []; if (error < 0) { + calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { for (var i = 0; i < out.ref.count; i++) { diff --git a/lib/src/blob.dart b/lib/src/blob.dart index ac5e943..626b125 100644 --- a/lib/src/blob.dart +++ b/lib/src/blob.dart @@ -8,18 +8,16 @@ class Blob { /// Initializes a new instance of [Blob] class from provided pointer to /// blob object in memory. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Blob(this._blobPointer); - /// Initializes a new instance of [Blob] class from provided - /// [Repository] object and [sha] hex string. + /// Lookups a blob object for provided [id] in a [repo]sitory. /// - /// Should be freed with `free()` to release allocated memory. - Blob.lookup({required Repository repo, required String sha}) { - final oid = Oid.fromSHA(repo: repo, sha: sha); + /// Should be freed to release allocated memory. + Blob.lookup({required Repository repo, required Oid id}) { _blobPointer = bindings.lookup( repoPointer: repo.pointer, - oidPointer: oid.pointer, + oidPointer: id.pointer, ); } diff --git a/lib/src/branch.dart b/lib/src/branch.dart index bd926db..fb55cdb 100644 --- a/lib/src/branch.dart +++ b/lib/src/branch.dart @@ -4,134 +4,131 @@ import 'bindings/libgit2_bindings.dart'; import 'bindings/branch.dart' as bindings; import 'bindings/reference.dart' as reference_bindings; -class Branches { - /// Initializes a new instance of the [Branches] class - /// from provided [Repository] object. - Branches(Repository repo) { - _repoPointer = repo.pointer; - } - - /// Pointer to memory address for allocated repository object. - late final Pointer _repoPointer; - - /// Returns a list of all branches that can be found in a repository. +class Branch { + /// Initializes a new instance of [Branch] class from provided pointer to + /// branch object in memory. /// - /// Throws a [LibGit2Error] if error occured. - List list() { - return bindings.list( - repoPointer: _repoPointer, - flags: GitBranch.all.value, - ); - } - - /// Returns a list of local branches that can be found in a repository. - /// - /// Throws a [LibGit2Error] if error occured. - List get local { - return bindings.list( - repoPointer: _repoPointer, - flags: GitBranch.local.value, - ); - } - - /// Returns a list of remote branches that can be found in a repository. - /// - /// Throws a [LibGit2Error] if error occured. - List get remote { - return bindings.list( - repoPointer: _repoPointer, - flags: GitBranch.remote.value, - ); - } - - /// Lookups a branch by its name in a repository. - /// - /// The generated reference must be freed. The branch name will be checked for validity. - /// - /// Throws a [LibGit2Error] if error occured. - Branch operator [](String branchName) { - final ref = Reference( - reference_bindings.lookupDWIM( - repoPointer: _repoPointer, - name: branchName, - ), - ); - late final GitBranch type; - ref.isBranch ? type = GitBranch.local : GitBranch.remote; - ref.free(); - - return Branch(bindings.lookup( - repoPointer: _repoPointer, - branchName: branchName, - branchType: type.value, - )); - } + /// Should be freed with [free] to release allocated memory when no longer + /// needed. + Branch(this._branchPointer); /// Creates a new branch pointing at a [target] commit. /// /// A new direct reference will be created pointing to this target commit. /// If [force] is true and a reference already exists with the given name, it'll be replaced. /// - /// The returned reference must be freed. + /// Should be freed with [free] to release allocated memory when no longer + /// needed. /// /// The branch name will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. - Reference create({ + Branch.create({ + required Repository repo, required String name, required Commit target, bool force = false, }) { - return Reference(bindings.create( - repoPointer: _repoPointer, + _branchPointer = bindings.create( + repoPointer: repo.pointer, branchName: name, targetPointer: target.pointer, force: force, - )); + ); } -} -class Branch { - /// Initializes a new instance of [Branch] class from provided pointer to - /// branch object in memory. + /// Lookups a branch by its [name] in a [repo]sitory. /// - /// Should be freed with `free()` to release allocated memory. - const Branch(this._branchPointer); + /// The branch name will be checked for validity. + /// + /// Should be freed with [free] to release allocated memory when no longer + /// needed. + /// + /// Throws a [LibGit2Error] if error occured. + Branch.lookup({required Repository repo, required String name}) { + final ref = Reference( + reference_bindings.lookupDWIM( + repoPointer: repo.pointer, + name: name, + ), + ); + late final GitBranch type; + ref.isBranch ? type = GitBranch.local : GitBranch.remote; + ref.free(); + + _branchPointer = bindings.lookup( + repoPointer: repo.pointer, + branchName: name, + branchType: type.value, + ); + } + + late final Pointer _branchPointer; /// Pointer to memory address for allocated branch object. - final Pointer _branchPointer; + Pointer get pointer => _branchPointer; - /// Returns the OID pointed to by a branch. + /// Returns a list of branches that can be found in a [repo]sitory for provided [type]. + /// Default is all branches (local and remote). /// - /// Throws an exception if error occured. - Oid get target => Oid(reference_bindings.target(_branchPointer)); + /// IMPORTANT: Branches must be freed manually when no longer needed to prevent + /// memory leak. + /// + /// Throws a [LibGit2Error] if error occured. + static List list({ + required Repository repo, + GitBranch type = GitBranch.all, + }) { + final pointers = bindings.list( + repoPointer: repo.pointer, + flags: type.value, + ); + + final result = []; + for (var pointer in pointers) { + result.add(Branch(pointer)); + } + + return result; + } /// Deletes an existing branch reference. /// - /// Note that if the deletion succeeds, the reference object will not be valid anymore, - /// and will be freed. - /// /// Throws a [LibGit2Error] if error occured. - void delete() => bindings.delete(_branchPointer); + static void delete({required Repository repo, required String name}) { + final branch = Branch.lookup(repo: repo, name: name); + bindings.delete(branch.pointer); + } /// Renames an existing local branch reference. /// /// The new branch name will be checked for validity. /// - /// Note that if the move succeeds, the old reference object will not be valid anymore, - /// and will be freed immediately. - /// /// If [force] is true, existing branch will be overwritten. /// /// Throws a [LibGit2Error] if error occured. - Branch rename({required String newName, bool force = false}) { - return Branch(bindings.rename( - branchPointer: _branchPointer, + static void rename({ + required Repository repo, + required String oldName, + required String newName, + bool force = false, + }) { + final branch = Branch.lookup(repo: repo, name: oldName); + + bindings.rename( + branchPointer: branch.pointer, newBranchName: newName, force: force, - )); + ); + + branch.free(); } + /// Returns the OID pointed to by a branch. + /// + /// Throws an exception if error occured. + Oid get target => Oid(reference_bindings.target(_branchPointer)); + /// Checks if HEAD points to the given branch. /// /// Throws a [LibGit2Error] if error occured. @@ -155,4 +152,9 @@ class Branch { /// Releases memory allocated for branch object. void free() => bindings.free(_branchPointer); + + @override + String toString() { + return 'Branch{name: $name, target: $target}'; + } } diff --git a/lib/src/commit.dart b/lib/src/commit.dart index bd2ef6c..619351c 100644 --- a/lib/src/commit.dart +++ b/lib/src/commit.dart @@ -8,18 +8,16 @@ class Commit { /// Initializes a new instance of [Commit] class from provided pointer to /// commit object in memory. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Commit(this._commitPointer); - /// Initializes a new instance of [Commit] class from provided [Repository] object - /// and [sha] hex string. + /// Lookups commit object for provided [id] in a [repo]sitory. /// - /// Should be freed with `free()` to release allocated memory. - Commit.lookup({required Repository repo, required String sha}) { - final oid = Oid.fromSHA(repo: repo, sha: sha); + /// Should be freed to release allocated memory. + Commit.lookup({required Repository repo, required Oid id}) { _commitPointer = bindings.lookup( repoPointer: repo.pointer, - oidPointer: oid.pointer, + oidPointer: id.pointer, ); } @@ -42,14 +40,14 @@ class Commit { required String message, required Signature author, required Signature commiter, - required String treeSHA, - required List parents, + required Tree tree, + required List parents, String? updateRef, String? messageEncoding, }) { - final tree = Tree.lookup(repo: repo, sha: treeSHA); + final parentsPointers = parents.map((parent) => parent.pointer).toList(); - final result = Oid(bindings.create( + return Oid(bindings.create( repoPointer: repo.pointer, updateRef: updateRef, authorPointer: author.pointer, @@ -58,12 +56,8 @@ class Commit { message: message, treePointer: tree.pointer, parentCount: parents.length, - parents: parents, + parents: parentsPointers, )); - - tree.free(); - - return result; } /// Returns the encoding for the message of a commit, as a string diff --git a/lib/src/index.dart b/lib/src/index.dart index bdd0bb7..bbeaa9a 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -139,25 +139,9 @@ class Index with IterableMixin { bindings.read(indexPointer: _indexPointer, force: force); /// Updates the contents of an existing index object in memory by reading from the - /// specified tree. - void readTree(Object target) { - late final Tree tree; - - if (target is Oid) { - final repo = Repository(bindings.owner(_indexPointer)); - tree = Tree.lookup(repo: repo, sha: target.sha); - } else if (target is Tree) { - tree = target; - } else if (target is String) { - final repo = Repository(bindings.owner(_indexPointer)); - tree = Tree.lookup(repo: repo, sha: target); - } else { - throw ArgumentError.value( - '$target should be either Oid object, SHA hex string or Tree object'); - } - + /// specified [tree]. + void readTree(Tree tree) { bindings.readTree(indexPointer: _indexPointer, treePointer: tree.pointer); - tree.free(); } /// Writes an existing index object from memory back to disk using an atomic file lock. diff --git a/lib/src/note.dart b/lib/src/note.dart index 092b433..2903b18 100644 --- a/lib/src/note.dart +++ b/lib/src/note.dart @@ -3,43 +3,44 @@ import 'package:libgit2dart/libgit2dart.dart'; import 'bindings/libgit2_bindings.dart'; import 'bindings/note.dart' as bindings; -class Notes { - /// Initializes a new instance of the [Notes] class from - /// provided [Repository] object. - Notes(Repository repo) { - _repoPointer = repo.pointer; - } +class Note { + /// Initializes a new instance of the [Note] class from provided + /// pointer to note and annotatedId objects in memory. + Note(this._notePointer, this._annotatedIdPointer); - /// Pointer to memory address for allocated repository object. - late final Pointer _repoPointer; - - /// Reads the note for an object. + /// Reads the note for an [annotatedId]. /// - /// The note must be freed manually by the user. + /// IMPORTANT: Notes must be freed manually when no longer needed to prevent + /// memory leak. /// /// Throws a [LibGit2Error] if error occured. - static Note lookup({ + Note.lookup({ required Repository repo, required Oid annotatedId, String notesRef = 'refs/notes/commits', }) { - final note = bindings.lookup( + _notePointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: annotatedId.pointer, notesRef: notesRef, ); - - return Note(note, annotatedId.pointer, repo.pointer); + _annotatedIdPointer = annotatedId.pointer; } - /// Adds a note for an [object]. + /// Pointer to memory address for allocated note object. + late final Pointer _notePointer; + + /// Pointer to memory address for allocated annotetedId object. + late final Pointer _annotatedIdPointer; + + /// Adds a note for an [annotatedId]. /// /// Throws a [LibGit2Error] if error occured. static Oid create({ required Repository repo, required Signature author, required Signature committer, - required Oid object, + required Oid annotatedId, required String note, String notesRef = 'refs/notes/commits', bool force = false, @@ -48,62 +49,49 @@ class Notes { repoPointer: repo.pointer, authorPointer: author.pointer, committerPointer: committer.pointer, - oidPointer: object.pointer, + oidPointer: annotatedId.pointer, note: note, notesRef: notesRef, force: force, )); } - /// Returns list of notes for repository. - /// - /// Notes must be freed manually by the user. + /// Deletes the note for an [annotatedId]. /// /// Throws a [LibGit2Error] if error occured. - List get list { - final notesPointers = bindings.list(_repoPointer); + static void delete({ + required Repository repo, + required Oid annotatedId, + required Signature author, + required Signature committer, + String notesRef = 'refs/notes/commits', + }) { + bindings.delete( + repoPointer: repo.pointer, + authorPointer: author.pointer, + committerPointer: committer.pointer, + oidPointer: annotatedId.pointer, + ); + } + + /// Returns list of notes for repository. + /// + /// IMPORTANT: Notes must be freed manually when no longer needed to prevent + /// memory leak. + /// + /// Throws a [LibGit2Error] if error occured. + static List list(Repository repo) { + final notesPointers = bindings.list(repo.pointer); var result = []; for (var note in notesPointers) { result.add(Note( note['note'] as Pointer, note['annotatedId'] as Pointer, - _repoPointer, )); } return result; } -} - -class Note { - /// Initializes a new instance of the [Note] class from provided - /// pointer to note object in memory. - Note(this._notePointer, this._annotatedIdPointer, this._repoPointer); - - /// Pointer to memory address for allocated note object. - final Pointer _notePointer; - - /// Pointer to memory address for allocated annotetedId object. - final Pointer _annotatedIdPointer; - - /// Pointer to memory address for allocated repository object. - final Pointer _repoPointer; - - /// Removes the note for an [object]. - /// - /// Throws a [LibGit2Error] if error occured. - void remove({ - required Signature author, - required Signature committer, - String notesRef = 'refs/notes/commits', - }) { - bindings.remove( - repoPointer: _repoPointer, - authorPointer: author.pointer, - committerPointer: committer.pointer, - oidPointer: annotatedId.pointer, - ); - } /// Returns the note object's [Oid]. Oid get id => Oid(bindings.id(_notePointer)); diff --git a/lib/src/reference.dart b/lib/src/reference.dart index d326651..b08ebdc 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -7,53 +7,31 @@ import 'bindings/refdb.dart' as refdb_bindings; import 'bindings/repository.dart' as repository_bindings; import 'util.dart'; -class References { - /// Initializes a new instance of the [References] class - /// from provided [Repository] object. - References(Repository repo) { - _repoPointer = repo.pointer; - } - - /// Pointer to memory address for allocated repository object. - late final Pointer _repoPointer; - - /// Returns a list of all the references that can be found in a repository. - /// - /// Throws a [LibGit2Error] if error occured. - List get list => bindings.list(_repoPointer); - - /// Returns number of all the references that can be found in a repository. - int get length => list.length; - - /// Returns a [Reference] by lookingup [name] in a repository. - /// - /// Should be freed with `free()` to release allocated memory. - /// - /// The name will be checked for validity. - /// - /// Throws a [LibGit2Error] if error occured. - Reference operator [](String name) { - return Reference(bindings.lookup(repoPointer: _repoPointer, name: name)); - } +class Reference { + /// Initializes a new instance of the [Reference] class. + /// Should be freed to release allocated memory. + Reference(this._refPointer); /// Creates a new reference. /// - /// The reference will be created in the repository and written to the disk. + /// The reference will be created in the [repo]sitory and written to the disk. /// The generated [Reference] object must be freed by the user. /// - /// Valid reference names must follow one of two patterns: + /// Valid reference [name]s must follow one of two patterns: /// /// Top-level names must contain only capital letters and underscores, and must begin and end /// with a letter. (e.g. "HEAD", "ORIG_HEAD"). /// Names prefixed with "refs/" can be almost anything. You must avoid the characters /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have /// special meaning to revparse. - /// Throws a [LibGit2Error] if a reference already exists with the given name - /// unless force is true, in which case it will be overwritten. /// - /// The message for the reflog will be ignored if the reference does not belong in the + /// Throws a [LibGit2Error] if a reference already exists with the given [name] + /// unless [force] is true, in which case it will be overwritten. + /// + /// The [logMessage] message for the reflog will be ignored if the reference does not belong in the /// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog. - Reference create({ + Reference.create({ + required Repository repo, required String name, required Object target, bool force = false, @@ -66,7 +44,6 @@ class References { oid = target; isDirect = true; } else if (isValidShaHex(target as String)) { - final repo = Repository(_repoPointer); oid = Oid.fromSHA(repo: repo, sha: target); isDirect = true; } else { @@ -74,46 +51,97 @@ class References { } if (isDirect) { - return Reference(bindings.createDirect( - repoPointer: _repoPointer, + _refPointer = bindings.createDirect( + repoPointer: repo.pointer, name: name, oidPointer: oid.pointer, force: force, logMessage: logMessage, - )); + ); } else { - return Reference(bindings.createSymbolic( - repoPointer: _repoPointer, + _refPointer = bindings.createSymbolic( + repoPointer: repo.pointer, name: name, target: target as String, force: force, logMessage: logMessage, - )); + ); } } - /// Suggests that the given refdb compress or optimize its references. - /// This mechanism is implementation specific. For on-disk reference databases, - /// for example, this may pack all loose references. + /// Lookups reference [name] in a [repo]sitory. + /// + /// Should be freed to release allocated memory. + /// + /// The [name] will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. - void compress() { - final refdb = repository_bindings.refdb(_repoPointer); - refdb_bindings.compress(refdb); - refdb_bindings.free(refdb); + Reference.lookup({required Repository repo, required String name}) { + _refPointer = bindings.lookup(repoPointer: repo.pointer, name: name); } -} - -class Reference { - /// Initializes a new instance of the [Reference] class. - /// Should be freed with `free()` to release allocated memory. - Reference(this._refPointer); late Pointer _refPointer; /// Pointer to memory address for allocated reference object. Pointer get pointer => _refPointer; + /// Deletes an existing reference with provided [name]. + /// + /// This method works for both direct and symbolic references. + /// + /// Throws a [LibGit2Error] if error occured. + static void delete({required Repository repo, required String name}) { + final ref = Reference.lookup(repo: repo, name: name); + bindings.delete(ref.pointer); + ref.free(); + } + + /// Renames an existing reference. + /// + /// This method works for both direct and symbolic references. + /// + /// The [newName] will be checked for validity. + /// + /// If the [force] flag is set to false, and there's already a reference with the given name, + /// the renaming will fail. + /// + /// IMPORTANT: The user needs to write a proper reflog entry [logMessage] if the reflog is + /// enabled for the repository. We only rename the reflog if it exists. + /// + /// Throws a [LibGit2Error] if error occured. + static void rename({ + required Repository repo, + required String oldName, + required String newName, + bool force = false, + String? logMessage, + }) { + final ref = Reference.lookup(repo: repo, name: oldName); + bindings.rename( + refPointer: ref.pointer, + newName: newName, + force: force, + logMessage: logMessage, + ); + ref.free(); + } + + /// Returns a list of all the references that can be found in a [repo]sitory. + /// + /// Throws a [LibGit2Error] if error occured. + static List list(Repository repo) => bindings.list(repo.pointer); + + /// Suggests that the [repo]sitory's refdb compress or optimize its references. + /// This mechanism is implementation specific. For on-disk reference databases, + /// for example, this may pack all loose references. + /// + /// Throws a [LibGit2Error] if error occured. + static void compress(Repository repo) { + final refdb = repository_bindings.refdb(repo.pointer); + refdb_bindings.compress(refdb); + refdb_bindings.free(refdb); + } + /// Returns the type of the reference. ReferenceType get type { return bindings.referenceType(_refPointer) == 1 @@ -180,6 +208,8 @@ class Reference { /// ```dart /// final commit = ref.peel(GitObject.commit) as Commit; /// final tree = ref.peel(GitObject.tree) as Tree; + /// final blob = ref.peel(GitObject.blob) as Blob; + /// final tag = ref.peel(GitObject.tag) as Tag; /// ``` /// /// Throws a [LibGit2Error] if error occured. @@ -207,32 +237,6 @@ class Reference { /// If no shortname is appropriate, it will return the full name. String get shorthand => bindings.shorthand(_refPointer); - /// Renames an existing reference. - /// - /// This method works for both direct and symbolic references. - /// - /// The new name will be checked for validity. - /// - /// If the force flag is not enabled, and there's already a reference with the given name, - /// the renaming will fail. - /// - /// IMPORTANT: The user needs to write a proper reflog entry if the reflog is enabled for - /// the repository. We only rename the reflog if it exists. - /// - /// Throws a [LibGit2Error] if error occured. - void rename({ - required String newName, - bool force = false, - String? logMessage, - }) { - _refPointer = bindings.rename( - refPointer: _refPointer, - newName: newName, - force: force, - logMessage: logMessage, - ); - } - /// Checks if a reflog exists for the specified reference [name]. /// /// Throws a [LibGit2Error] if error occured. @@ -261,14 +265,6 @@ class Reference { /// Returns the repository where a reference resides. Repository get owner => Repository(bindings.owner(_refPointer)); - /// Delete an existing reference. - /// - /// This method works for both direct and symbolic references. - /// The reference will be immediately removed on disk but the memory will not be freed. - /// - /// Throws a [LibGit2Error] if the reference has changed from the time it was looked up. - void delete() => bindings.delete(_refPointer); - @override bool operator ==(other) { return (other is Reference) && @@ -283,4 +279,9 @@ class Reference { /// Releases memory allocated for reference object. void free() => bindings.free(_refPointer); + + @override + String toString() { + return 'Reference{name: $name, target: $target}'; + } } diff --git a/lib/src/remote.dart b/lib/src/remote.dart index 0503560..02fca03 100644 --- a/lib/src/remote.dart +++ b/lib/src/remote.dart @@ -3,70 +3,63 @@ import 'package:libgit2dart/libgit2dart.dart'; import 'bindings/libgit2_bindings.dart'; import 'bindings/remote.dart' as bindings; -class Remotes { - /// Initializes a new instance of the [Remotes] class from - /// provided [Repository] object. - Remotes(Repository repo) { - _repoPointer = repo.pointer; - } +class Remote { + /// Initializes a new instance of [Remote] class from provided pointer + /// to remote object in memory. + Remote(this._remotePointer); - /// Pointer to memory address for allocated repository object. - late final Pointer _repoPointer; - - /// Returns a list of the configured remotes for a repo. - /// - /// Throws a [LibGit2Error] if error occured. - List get list { - return bindings.list(_repoPointer); - } - - /// Returns number of the configured remotes for a repo. - int get length => list.length; - - /// Returns [Remote] by looking up [name] in a repository. + /// Initializes a new instance of [Remote] class by looking up remote with + /// provided [name] in a [repo]sitory. /// /// The name will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. - Remote operator [](String name) { - return Remote(bindings.lookup(repoPointer: _repoPointer, name: name)); + Remote.lookup({required Repository repo, required String name}) { + _remotePointer = bindings.lookup(repoPointer: repo.pointer, name: name); } - /// Adds a remote to the repository's configuration with the default [fetch] - /// refspec if none provided . + /// Initializes a new instance of [Remote] class by adding a remote with + /// provided [name] and [url] to the [repo]sitory's configuration with the + /// default [fetch] refspec if none provided . /// /// Throws a [LibGit2Error] if error occured. - Remote create({ + Remote.create({ + required Repository repo, required String name, required String url, String? fetch, }) { if (fetch == null) { - return Remote(bindings.create( - repoPointer: _repoPointer, + _remotePointer = bindings.create( + repoPointer: repo.pointer, name: name, url: url, - )); + ); } else { - return Remote(bindings.createWithFetchSpec( - repoPointer: _repoPointer, + _remotePointer = bindings.createWithFetchSpec( + repoPointer: repo.pointer, name: name, url: url, fetch: fetch, - )); + ); } } + late final Pointer _remotePointer; + + /// Pointer to memory address for allocated remote object. + Pointer get pointer => _remotePointer; + /// Deletes an existing persisted remote. /// /// All remote-tracking branches and configuration settings for the remote will be removed. /// /// Throws a [LibGit2Error] if error occured. - void delete(String name) { - bindings.delete(repoPointer: _repoPointer, name: name); + static void delete({required Repository repo, required String name}) { + bindings.delete(repoPointer: repo.pointer, name: name); } - /// Give the remote a new name. + /// Gives the remote a new name. /// /// Returns list of non-default refspecs that cannot be renamed. /// @@ -78,23 +71,38 @@ class Remotes { /// their list of refspecs. /// /// Throws a [LibGit2Error] if error occured. - List rename({required String remote, required String newName}) { + static List rename({ + required Repository repo, + required String oldName, + required String newName, + }) { return bindings.rename( - repoPointer: _repoPointer, - name: remote, + repoPointer: repo.pointer, + name: oldName, newName: newName, ); } + /// Returns a list of the configured remotes for a [repo]sitory. + /// + /// Throws a [LibGit2Error] if error occured. + static List list(Repository repo) { + return bindings.list(repo.pointer); + } + /// Sets the remote's url in the configuration. /// /// Remote objects already in memory will not be affected. This assumes the common /// case of a single-url remote and will otherwise return an error. /// /// Throws a [LibGit2Error] if error occured. - void setUrl({required String remote, required String url}) { + static void setUrl({ + required Repository repo, + required String remote, + required String url, + }) { bindings.setUrl( - repoPointer: _repoPointer, + repoPointer: repo.pointer, remote: remote, url: url, ); @@ -106,9 +114,13 @@ class Remotes { /// case of a single-url remote and will otherwise return an error. /// /// Throws a [LibGit2Error] if error occured. - void setPushUrl({required String remote, required String url}) { + static void setPushUrl({ + required Repository repo, + required String remote, + required String url, + }) { bindings.setPushUrl( - repoPointer: _repoPointer, + repoPointer: repo.pointer, remote: remote, url: url, ); @@ -119,9 +131,13 @@ class Remotes { /// No loaded remote instances will be affected. /// /// Throws a [LibGit2Error] if error occured. - void addFetch({required String remote, required String refspec}) { + static void addFetch({ + required Repository repo, + required String remote, + required String refspec, + }) { bindings.addFetch( - repoPointer: _repoPointer, + repoPointer: repo.pointer, remote: remote, refspec: refspec, ); @@ -132,24 +148,17 @@ class Remotes { /// No loaded remote instances will be affected. /// /// Throws a [LibGit2Error] if error occured. - void addPush({required String remote, required String refspec}) { + static void addPush({ + required Repository repo, + required String remote, + required String refspec, + }) { bindings.addPush( - repoPointer: _repoPointer, + repoPointer: repo.pointer, remote: remote, refspec: refspec, ); } -} - -class Remote { - /// Initializes a new instance of [Remote] class from provided pointer - /// to remote object in memory. - const Remote(this._remotePointer); - - final Pointer _remotePointer; - - /// Pointer to memory address for allocated remote object. - Pointer get pointer => _remotePointer; /// Returns the remote's name. String get name => bindings.name(_remotePointer); diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 42a426d..6310d99 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -130,6 +130,16 @@ class Repository { ); } + /// Returns [Oid] object if it can be found in the ODB of repository with + /// provided hexadecimal [sha] string that is 40 characters long or shorter. + /// + /// Throws [ArgumentError] if provided [sha] hex string is not valid. + /// + /// Throws a [LibGit2Error] if error occured. + Oid operator [](String sha) { + return Oid.fromSHA(repo: this, sha: sha); + } + /// Returns path to the `.git` folder for normal repositories /// or path to the repository itself for bare repositories. String get path => bindings.path(_repoPointer); @@ -322,8 +332,89 @@ class Repository { /// Must be freed once it's no longer being used. Reference get head => Reference(bindings.head(_repoPointer)); - /// Returns [References] object. - References get references => References(this); + /// Returns a list of all the references that can be found in a repository. + /// + /// Throws a [LibGit2Error] if error occured. + List get references => Reference.list(this); + + /// Lookups reference [name] in a [repo]sitory. + /// + /// Should be freed to release allocated memory. + /// + /// The [name] will be checked for validity. + /// + /// Throws a [LibGit2Error] if error occured. + Reference lookupReference(String name) { + return Reference.lookup(repo: this, name: name); + } + + /// Creates a new reference. + /// + /// The reference will be created in the repository and written to the disk. + /// The generated [Reference] object must be freed by the user. + /// + /// Valid reference [name]s must follow one of two patterns: + /// + /// Top-level names must contain only capital letters and underscores, and must begin and end + /// with a letter. (e.g. "HEAD", "ORIG_HEAD"). + /// Names prefixed with "refs/" can be almost anything. You must avoid the characters + /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have + /// special meaning to revparse. + /// + /// Throws a [LibGit2Error] if a reference already exists with the given [name] + /// unless [force] is true, in which case it will be overwritten. + /// + /// The [logMessage] message for the reflog will be ignored if the reference does not belong in the + /// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog. + Reference createReference({ + required String name, + required Object target, + bool force = false, + String? logMessage, + }) { + return Reference.create( + repo: this, + name: name, + target: target, + force: force, + logMessage: logMessage, + ); + } + + /// Deletes an existing reference with provided [name]. + /// + /// This method works for both direct and symbolic references. + /// + /// Throws a [LibGit2Error] if error occured. + void deleteReference(String name) => Reference.delete(repo: this, name: name); + + /// Renames an existing reference. + /// + /// This method works for both direct and symbolic references. + /// + /// The [newName] will be checked for validity. + /// + /// If the [force] flag is set to false, and there's already a reference with the given name, + /// the renaming will fail. + /// + /// IMPORTANT: The user needs to write a proper reflog entry [logMessage] if the reflog is + /// enabled for the repository. We only rename the reflog if it exists. + /// + /// Throws a [LibGit2Error] if error occured. + void renameReference({ + required String oldName, + required String newName, + bool force = false, + String? logMessage, + }) { + Reference.rename( + repo: this, + oldName: oldName, + newName: newName, + force: force, + logMessage: logMessage, + ); + } /// Returns [Index] file for this repository. /// @@ -337,39 +428,11 @@ class Repository { /// Throws a [LibGit2Error] if error occured. Odb get odb => Odb(bindings.odb(_repoPointer)); - /// Looksup git object (commit, tree, blob, tag) for provided [sha] hex string. + /// Lookups a tree object for provided [id]. /// - /// Returned object should be explicitly downcasted to one of four of git object types. - /// - /// ```dart - /// final commit = repo['s0m3sh4'] as Commit; - /// final tree = repo['s0m3sh4'] as Tree; - /// final blob = repo['s0m3sh4'] as Blob; - /// final tag = repo['s0m3sh4'] as Tag; - /// ``` - /// - /// Throws [ArgumentError] if provided [sha] is not pointing to commit, tree, blob or tag. - Object operator [](String sha) { - final oid = Oid.fromSHA(repo: this, sha: sha); - final object = object_bindings.lookup( - repoPointer: _repoPointer, - oidPointer: oid.pointer, - type: GitObject.any.value, - ); - final type = object_bindings.type(object); - - if (type == GitObject.commit.value) { - return Commit(object.cast()); - } else if (type == GitObject.tree.value) { - return Tree(object.cast()); - } else if (type == GitObject.blob.value) { - return Blob(object.cast()); - } else if (type == GitObject.tag.value) { - return Tag(object.cast()); - } else { - throw ArgumentError.value( - '$sha should be pointing to either commit, tree, blob or a tag'); - } + /// Should be freed to release allocated memory. + Tree lookupTree(Oid id) { + return Tree.lookup(repo: this, id: id); } /// Creates a new action signature with default user and now timestamp. @@ -410,6 +473,13 @@ class Repository { return RevParse.single(repo: this, spec: spec); } + /// Lookups commit object for provided [id]. + /// + /// Should be freed to release allocated memory. + Commit lookupCommit(Oid id) { + return Commit.lookup(repo: this, id: id); + } + /// Creates new commit in the repository. /// /// [updateRef] is name of the reference that will be updated to point to this commit. @@ -423,8 +493,8 @@ class Repository { required String message, required Signature author, required Signature commiter, - required String treeSHA, - required List parents, + required Tree tree, + required List parents, String? updateRef, String? messageEncoding, }) { @@ -433,7 +503,7 @@ class Repository { message: message, author: author, commiter: commiter, - treeSHA: treeSHA, + tree: tree, parents: parents, ); } @@ -464,6 +534,13 @@ class Repository { return RevParse.range(repo: this, spec: spec); } + /// Lookups a blob object for provided [id]. + /// + /// Should be freed to release allocated memory. + Blob lookupBlob(Oid id) { + return Blob.lookup(repo: this, id: id); + } + /// Creates a new blob from a [content] string and writes it to ODB. /// /// Throws a [LibGit2Error] if error occured. @@ -487,12 +564,22 @@ class Repository { return Blob.createFromDisk(repo: this, path: path); } - /// Creates a new tag in the repository from provided Oid object. + /// Returns a list with all the tags in the repository. /// - /// A new reference will also be created pointing to this tag object. If force is true + /// Throws a [LibGit2Error] if error occured. + List get tags => Tag.list(this); + + /// Lookups tag object for provided [id]. + /// + /// Should be freed to release allocated memory. + Tag lookupTag(Oid id) => Tag.lookup(repo: this, id: id); + + /// Creates a new tag in the repository for provided [target] object. + /// + /// A new reference will also be created pointing to this tag object. If [force] is true /// and a reference already exists with the given name, it'll be replaced. /// - /// The message will not be cleaned up. + /// The [message] will not be cleaned up. /// /// The tag name will be checked for validity. You must avoid the characters /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have @@ -501,7 +588,7 @@ class Repository { /// Throws a [LibGit2Error] if error occured. Oid createTag({ required String tagName, - required String target, + required Oid target, required GitObject targetType, required Signature tagger, required String message, @@ -517,13 +604,93 @@ class Repository { force: force); } - /// Returns a list with all the tags in the repository. + /// Deletes an existing tag reference with provided [name]. + /// + /// The tag [name] will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. - List get tags => Tag.list(this); + void deleteTag(String name) => Tag.delete(repo: this, name: name); - /// Returns a [Branches] object. - Branches get branches => Branches(this); + /// Returns a list of all branches that can be found in a repository. + /// + /// IMPORTANT: Branches must be freed manually when no longer needed to prevent + /// memory leak. + /// + /// Throws a [LibGit2Error] if error occured. + List get branches => Branch.list(repo: this); + + /// Returns a list of local branches that can be found in a repository. + /// + /// IMPORTANT: Branches must be freed manually when no longer needed to prevent + /// memory leak. + /// + /// Throws a [LibGit2Error] if error occured. + List get branchesLocal => + Branch.list(repo: this, type: GitBranch.local); + + /// Returns a list of remote branches that can be found in a repository. + /// + /// IMPORTANT: Branches must be freed manually when no longer needed to prevent + /// memory leak. + /// + /// Throws a [LibGit2Error] if error occured. + List get branchesRemote => + Branch.list(repo: this, type: GitBranch.remote); + + /// Lookups a branch by its [name] in a repository. + /// + /// The branch name will be checked for validity. + /// + /// Should be freed to release allocated memory when no longer needed. + /// + /// Throws a [LibGit2Error] if error occured. + Branch lookupBranch(String name) { + return Branch.lookup(repo: this, name: name); + } + + /// Creates a new branch pointing at a [target] commit. + /// + /// A new direct reference will be created pointing to this target commit. + /// If [force] is true and a reference already exists with the given name, it'll be replaced. + /// + /// Should be freed with [free] to release allocated memory when no longer + /// needed. + /// + /// The branch name will be checked for validity. + /// + /// Throws a [LibGit2Error] if error occured. + Branch createBranch({ + required String name, + required Commit target, + bool force = false, + }) { + return Branch.create( + repo: this, + name: name, + target: target, + force: force, + ); + } + + /// Deletes an existing branch reference. + /// + /// Throws a [LibGit2Error] if error occured. + void deleteBranch(String name) => Branch.delete(repo: this, name: name); + + /// Renames an existing local branch reference. + /// + /// The new branch name will be checked for validity. + /// + /// If [force] is true, existing branch will be overwritten. + /// + /// Throws a [LibGit2Error] if error occured. + void renameBranch({ + required String oldName, + required String newName, + bool force = false, + }) { + Branch.rename(repo: this, oldName: oldName, newName: newName, force: force); + } /// Checks status of the repository and returns map of file paths and their statuses. /// @@ -619,7 +786,7 @@ class Repository { required Oid theirHead, String ourRef = 'HEAD', }) { - final ref = references[ourRef]; + final ref = lookupReference(ourRef); final head = commit_bindings.annotatedLookup( repoPointer: _repoPointer, oidPointer: theirHead.pointer, @@ -831,7 +998,7 @@ class Repository { paths: paths, ); } else { - final ref = references[refName]; + final ref = lookupReference(refName); final treeish = object_bindings.lookup( repoPointer: _repoPointer, oidPointer: ref.target.pointer, @@ -1062,8 +1229,57 @@ class Repository { return stash_bindings.list(_repoPointer); } - /// Returns [Remotes] object. - Remotes get remotes => Remotes(this); + /// Returns a list of the configured remotes for a repository. + /// + /// Throws a [LibGit2Error] if error occured. + List get remotes => Remote.list(this); + + /// Lookups remote with provided [name]. + /// + /// The name will be checked for validity. + /// + /// Throws a [LibGit2Error] if error occured. + Remote lookupRemote(String name) { + return Remote.lookup(repo: this, name: name); + } + + /// Adds a remote with provided [name] and [url] to the repository's + /// configuration with the default [fetch] refspec if none provided . + /// + /// Throws a [LibGit2Error] if error occured. + Remote createRemote({ + required String name, + required String url, + String? fetch, + }) { + return Remote.create(repo: this, name: name, url: url, fetch: fetch); + } + + /// Deletes an existing persisted remote. + /// + /// All remote-tracking branches and configuration settings for the remote will be removed. + /// + /// Throws a [LibGit2Error] if error occured. + void deleteRemote(String name) => Remote.delete(repo: this, name: name); + + /// Gives the remote a new name. + /// + /// Returns list of non-default refspecs that cannot be renamed. + /// + /// All remote-tracking branches and configuration settings for the remote are updated. + /// + /// The new name will be checked for validity. + /// + /// No loaded instances of a the remote with the old name will change their name or + /// their list of refspecs. + /// + /// Throws a [LibGit2Error] if error occured. + List renameRemote({ + required String oldName, + required String newName, + }) { + return Remote.rename(repo: this, oldName: oldName, newName: newName); + } /// Looks up the value of one git attribute for path. /// @@ -1132,49 +1348,69 @@ class Repository { /// Returns list of notes for repository. /// - /// Notes must be freed manually. + /// IMPORTANT: Notes must be freed manually when no longer needed to prevent + /// memory leak. /// /// Throws a [LibGit2Error] if error occured. - List get notes => Notes(this).list; + List get notes => Note.list(this); - /// Reads the note for an object. + /// Reads the note for an [annotatedId]. /// - /// The note must be freed manually. + /// IMPORTANT: Notes must be freed manually when no longer needed to prevent + /// memory leak. /// /// Throws a [LibGit2Error] if error occured. Note lookupNote({ required Oid annotatedId, String notesRef = 'refs/notes/commits', }) { - return Notes.lookup( + return Note.lookup( repo: this, annotatedId: annotatedId, notesRef: notesRef, ); } - /// Adds a note for an [object]. + /// Adds a note for an [annotatedId]. /// /// Throws a [LibGit2Error] if error occured. Oid createNote({ required Signature author, required Signature committer, - required Oid object, + required Oid annotatedId, required String note, String notesRef = 'refs/notes/commits', bool force = false, }) { - return Notes.create( + return Note.create( repo: this, author: author, committer: committer, - object: object, + annotatedId: annotatedId, note: note, notesRef: notesRef, force: force, ); } + /// Deletes the note for an [annotatedId]. + /// + /// Throws a [LibGit2Error] if error occured. + void deleteNote({ + required Oid annotatedId, + required Signature author, + required Signature committer, + String notesRef = 'refs/notes/commits', + }) { + Note.delete( + repo: this, + annotatedId: annotatedId, + author: author, + committer: committer, + notesRef: notesRef, + ); + } + /// Checks if a commit is the descendant of another commit. /// /// Note that a commit is not considered a descendant of itself, in contrast to @@ -1397,4 +1633,34 @@ class Repository { callbacks: callbacks, ); } + + /// Returns list of names of linked working trees. + /// + /// Throws a [LibGit2Error] if error occured. + List get worktrees => Worktree.list(this); + + /// Lookups up existing worktree for provided [name]. + /// + /// Should be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + Worktree lookupWorktree(String name) { + return Worktree.lookup(repo: this, name: name); + } + + /// Creates new worktree. + /// + /// If [ref] is provided, no new branch will be created but specified [ref] will + /// be used instead. + /// + /// Should be freed with `free()` to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + Worktree createWorktree({ + required String name, + required String path, + Reference? ref, + }) { + return Worktree.create(repo: this, name: name, path: path, ref: ref); + } } diff --git a/lib/src/tag.dart b/lib/src/tag.dart index 0f936e2..7402103 100644 --- a/lib/src/tag.dart +++ b/lib/src/tag.dart @@ -8,18 +8,16 @@ class Tag { /// Initializes a new instance of [Tag] class from provided pointer to /// tag object in memory. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Tag(this._tagPointer); - /// Initializes a new instance of [Tag] class from provided - /// [Repository] object and [sha] hex string. + /// Lookups tag object for provided [id] in a [repo]sitory. /// - /// Should be freed with `free()` to release allocated memory. - Tag.lookup({required Repository repo, required String sha}) { - final oid = Oid.fromSHA(repo: repo, sha: sha); + /// Should be freed to release allocated memory. + Tag.lookup({required Repository repo, required Oid id}) { _tagPointer = bindings.lookup( repoPointer: repo.pointer, - oidPointer: oid.pointer, + oidPointer: id.pointer, ); } @@ -28,12 +26,12 @@ class Tag { /// Pointer to memory address for allocated tag object. Pointer get pointer => _tagPointer; - /// Creates a new tag in the repository from provided Oid object. + /// Creates a new tag in the repository for provided [target] object. /// - /// A new reference will also be created pointing to this tag object. If force is true + /// A new reference will also be created pointing to this tag object. If [force] is true /// and a reference already exists with the given name, it'll be replaced. /// - /// The message will not be cleaned up. + /// The [message] will not be cleaned up. /// /// The tag name will be checked for validity. You must avoid the characters /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have @@ -43,16 +41,15 @@ class Tag { static Oid create({ required Repository repo, required String tagName, - required String target, + required Oid target, required GitObject targetType, required Signature tagger, required String message, bool force = false, }) { - final targetOid = Oid.fromSHA(repo: repo, sha: target); final object = object_bindings.lookup( repoPointer: repo.pointer, - oidPointer: targetOid.pointer, + oidPointer: target.pointer, type: targetType.value, ); final result = bindings.create( @@ -68,6 +65,15 @@ class Tag { return Oid(result); } + /// Deletes an existing tag reference with provided [name] in a [repo]sitory. + /// + /// The tag [name] will be checked for validity. + /// + /// Throws a [LibGit2Error] if error occured. + static void delete({required Repository repo, required String name}) { + bindings.delete(repoPointer: repo.pointer, tagName: name); + } + /// Returns a list with all the tags in the repository. /// /// Throws a [LibGit2Error] if error occured. @@ -123,16 +129,6 @@ class Tag { } } - /// Deletes an existing tag reference. - /// - /// The tag name will be checked for validity. - /// - /// Throws a [LibGit2Error] if error occured. - void delete() { - final owner = bindings.owner(_tagPointer); - bindings.delete(repoPointer: owner, tagName: name); - } - /// Releases memory allocated for tag object. void free() => bindings.free(_tagPointer); } diff --git a/lib/src/tree.dart b/lib/src/tree.dart index 72a064b..a64893a 100644 --- a/lib/src/tree.dart +++ b/lib/src/tree.dart @@ -8,18 +8,16 @@ class Tree { /// Initializes a new instance of [Tree] class from provided pointer to /// tree object in memory. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Tree(this._treePointer); - /// Initializes a new instance of [Tree] class from provided - /// [Repository] object and [sha] hex string. + /// Lookups a tree object for provided [id] in a [repo]sitory. /// - /// Should be freed with `free()` to release allocated memory. - Tree.lookup({required Repository repo, required String sha}) { - final oid = Oid.fromSHA(repo: repo, sha: sha); + /// Should be freed to release allocated memory. + Tree.lookup({required Repository repo, required Oid id}) { _treePointer = bindings.lookup( repoPointer: repo.pointer, - oidPointer: oid.pointer, + oidPointer: id.pointer, ); } diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart index 4f00fe0..850d077 100644 --- a/lib/src/treebuilder.dart +++ b/lib/src/treebuilder.dart @@ -5,9 +5,9 @@ import 'bindings/treebuilder.dart' as bindings; class TreeBuilder { /// Initializes a new instance of [TreeBuilder] class from provided - /// [Repository] and optional [Tree] objects. + /// [repo]sitory and optional [tree] objects. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. /// /// Throws a [LibGit2Error] if error occured. TreeBuilder({required Repository repo, Tree? tree}) { @@ -79,6 +79,6 @@ class TreeBuilder { ); } - /// Releases memory allocated for tree builder object. + /// Releases memory allocated for tree builder object and all the entries. void free() => bindings.free(_treeBuilderPointer); } diff --git a/test/blame_test.dart b/test/blame_test.dart index 0b5fb2d..8363b53 100644 --- a/test/blame_test.dart +++ b/test/blame_test.dart @@ -10,8 +10,8 @@ void main() { late Signature sig2; var hunks = >[]; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/blamerepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/blamerepo/')); repo = Repository.open(tmpDir.path); sig1 = Signature.create( name: 'Aleksey Kulikov', @@ -48,11 +48,11 @@ void main() { ]; }); - tearDown(() async { + tearDown(() { sig1.free(); sig2.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Blame', () { diff --git a/test/blob_test.dart b/test/blob_test.dart index ee37d75..fb5ae55 100644 --- a/test/blob_test.dart +++ b/test/blob_test.dart @@ -11,16 +11,16 @@ void main() { const blobContent = 'Feature edit\n'; const newBlobContent = 'New blob\n'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); - blob = Blob.lookup(repo: repo, sha: blobSHA); + blob = repo.lookupBlob(repo[blobSHA]); }); - tearDown(() async { + tearDown(() { blob.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Blob', () { @@ -36,8 +36,8 @@ void main() { }); test('successfully creates new blob', () { - final oid = Blob.create(repo: repo, content: newBlobContent); - final newBlob = Blob.lookup(repo: repo, sha: oid.sha); + final oid = repo.createBlob(newBlobContent); + final newBlob = repo.lookupBlob(oid); expect(newBlob.id.sha, '18fdaeef018e57a92bcad2d4a35b577f34089af6'); expect(newBlob.isBinary, false); @@ -49,11 +49,8 @@ void main() { test('successfully creates new blob from file at provided relative path', () { - final oid = Blob.createFromWorkdir( - repo: repo, - relativePath: 'feature_file', - ); - final newBlob = Blob.lookup(repo: repo, sha: oid.sha); + final oid = repo.createBlobFromWorkdir('feature_file'); + final newBlob = repo.lookupBlob(oid); expect(newBlob.id.sha, blobSHA); expect(newBlob.isBinary, false); @@ -65,10 +62,7 @@ void main() { test('throws when creating new blob from invalid path', () { expect( - () => Blob.createFromWorkdir( - repo: repo, - relativePath: 'invalid/path.txt', - ), + () => repo.createBlobFromWorkdir('invalid/path.txt'), throwsA(isA()), ); }); @@ -79,10 +73,7 @@ void main() { final outsideFile = File('${Directory.current.absolute.path}/test/blob_test.dart'); expect( - () => Blob.createFromWorkdir( - repo: repo, - relativePath: outsideFile.path, - ), + () => repo.createBlobFromWorkdir(outsideFile.path), throwsA(isA()), ); }); @@ -90,8 +81,8 @@ void main() { test('successfully creates new blob from file at provided path', () { final outsideFile = File('${Directory.current.absolute.path}/test/blob_test.dart'); - final oid = Blob.createFromDisk(repo: repo, path: outsideFile.path); - final newBlob = Blob.lookup(repo: repo, sha: oid.sha); + final oid = repo.createBlobFromDisk(outsideFile.path); + final newBlob = repo.lookupBlob(oid); expect(newBlob, isA()); expect(newBlob.isBinary, false); @@ -101,8 +92,6 @@ void main() { group('diff', () { const path = 'feature_file'; - const oldBlobSha = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'; - const newBlobSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'; const blobPatch = """ diff --git a/feature_file b/feature_file index e69de29..9c78c21 100644 @@ -120,8 +109,12 @@ index e69de29..0000000 +++ /dev/null """; test('successfully creates from blobs', () { - final a = repo[oldBlobSha] as Blob; - final b = repo[newBlobSha] as Blob; + final a = repo.lookupBlob( + repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'], + ); + final b = repo.lookupBlob( + repo['9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'], + ); final patch = repo.diffBlobs( a: a, b: b, @@ -135,7 +128,9 @@ index e69de29..0000000 }); test('successfully creates from one blob (delete)', () { - final a = repo[oldBlobSha] as Blob; + final a = repo.lookupBlob( + repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'], + ); final patch = a.diff( newBlob: null, oldAsPath: path, @@ -148,7 +143,9 @@ index e69de29..0000000 }); test('successfully creates from blob and buffer', () { - final a = repo[oldBlobSha] as Blob; + final a = repo.lookupBlob( + repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'], + ); final patch = Patch.createFrom( a: a, b: 'Feature edit\n', @@ -162,7 +159,9 @@ index e69de29..0000000 }); test('successfully creates from blob and buffer (delete)', () { - final a = repo[oldBlobSha] as Blob; + final a = repo.lookupBlob( + repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'], + ); final patch = Patch.createFrom( a: a, b: null, diff --git a/test/branch_test.dart b/test/branch_test.dart index ce7d144..dd226f5 100644 --- a/test/branch_test.dart +++ b/test/branch_test.dart @@ -6,48 +6,59 @@ import 'helpers/util.dart'; void main() { late Repository repo; late Directory tmpDir; - const lastCommit = '821ed6e80627b8769d170a293862f9fc60825226'; - const featureCommit = '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'; + late Oid lastCommit; + late Oid featureCommit; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); + lastCommit = repo['821ed6e80627b8769d170a293862f9fc60825226']; + featureCommit = repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4']; }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Branch', () { test('returns a list of all branches', () { - final branches = Branches(repo); - expect(branches.list(), ['feature', 'master']); + const branchesExpected = ['feature', 'master']; + final branches = repo.branches; + + for (var i = 0; i < branches.length; i++) { + expect(branches[i].name, branchesExpected[i]); + branches[i].free(); + } }); test('returns a list of local branches', () { - final branches = repo.branches.local; - expect(branches, ['feature', 'master']); + const branchesExpected = ['feature', 'master']; + final branches = repo.branchesLocal; + + for (var i = 0; i < branches.length; i++) { + expect(branches[i].name, branchesExpected[i]); + branches[i].free(); + } }); - test('returns a list of remote branches for provided type', () { - final branches = repo.branches.remote; - expect(branches, []); + test('returns a list of remote branches', () { + expect(repo.branchesRemote, []); }); test('returns a branch with provided name', () { - final branch = repo.branches['master']; - expect(branch.target.sha, lastCommit); + final branch = repo.lookupBranch('master'); + expect(branch.target.sha, lastCommit.sha); branch.free(); }); test('throws when provided name not found', () { - expect(() => repo.branches['invalid'], throwsA(isA())); + expect(() => repo.lookupBranch('invalid'), throwsA(isA())); }); test('checks if branch is current head', () { - final masterBranch = repo.branches['master']; - final featureBranch = repo.branches['feature']; + final masterBranch = repo.lookupBranch('master'); + final featureBranch = repo.lookupBranch('feature'); expect(masterBranch.isHead, true); expect(featureBranch.isHead, false); @@ -57,30 +68,33 @@ void main() { }); test('returns name', () { - final branch = repo.branches['master']; + final branch = repo.lookupBranch('master'); expect(branch.name, 'master'); branch.free(); }); group('create()', () { test('successfully creates', () { - final commit = repo[lastCommit] as Commit; + final commit = repo.lookupCommit(lastCommit); - final ref = repo.branches.create(name: 'testing', target: commit); - final branch = repo.branches['testing']; - expect(repo.branches.list().length, 3); - expect(branch.target.sha, lastCommit); + final branch = repo.createBranch(name: 'testing', target: commit); + final branches = repo.branches; + expect(repo.branches.length, 3); + expect(branch.target, lastCommit); + + for (final branch in branches) { + branch.free(); + } branch.free(); - ref.free(); commit.free(); }); test('throws when name already exists', () { - final commit = repo[lastCommit] as Commit; + final commit = repo.lookupCommit(lastCommit); expect( - () => repo.branches.create(name: 'feature', target: commit), + () => repo.createBranch(name: 'feature', target: commit), throwsA(isA()), ); @@ -88,30 +102,39 @@ void main() { }); test('successfully creates with force flag when name already exists', () { - final commit = repo[lastCommit] as Commit; + final commit = repo.lookupCommit(lastCommit); - final ref = - repo.branches.create(name: 'feature', target: commit, force: true); - final branch = repo.branches['feature']; - expect(repo.branches.local.length, 2); - expect(branch.target.sha, lastCommit); + final branch = repo.createBranch( + name: 'feature', + target: commit, + force: true, + ); + final localBranches = repo.branchesLocal; + expect(localBranches.length, 2); + expect(branch.target, lastCommit); + + for (final branch in localBranches) { + branch.free(); + } branch.free(); - ref.free(); commit.free(); }); }); group('delete()', () { test('successfully deletes', () { - repo.branches['feature'].delete(); - expect(repo.branches.local.length, 1); - expect(() => repo.branches['feature'], throwsA(isA())); + repo.deleteBranch('feature'); + + expect( + () => repo.lookupBranch('feature'), + throwsA(isA()), + ); }); test('throws when trying to delete current HEAD', () { expect( - () => repo.branches['master'].delete(), + () => repo.deleteBranch('master'), throwsA(isA()), ); }); @@ -119,43 +142,48 @@ void main() { group('rename()', () { test('successfully renames', () { - final renamed = repo.branches['feature'].rename(newName: 'renamed'); - final branch = repo.branches['renamed']; + repo.renameBranch(oldName: 'feature', newName: 'renamed'); + final branch = repo.lookupBranch('renamed'); + final branches = repo.branches; - expect(renamed.target.sha, featureCommit); - expect(branch.target.sha, featureCommit); + expect(branches.length, 2); + expect( + () => repo.lookupBranch('feature'), + throwsA(isA()), + ); + expect(branch.target, featureCommit); + for (final branch in branches) { + branch.free(); + } branch.free(); - renamed.free(); }); test('throws when name already exists', () { - final branch = repo.branches['feature']; expect( - () => branch.rename(newName: 'master'), + () => repo.renameBranch(oldName: 'feature', newName: 'master'), throwsA(isA()), ); - branch.free(); }); test('successfully renames with force flag when name already exists', () { - final renamed = repo.branches['master'].rename( + repo.renameBranch( + oldName: 'master', newName: 'feature', force: true, ); + final branch = repo.lookupBranch('feature'); - expect(renamed.target.sha, lastCommit); + expect(branch.target, lastCommit); - renamed.free(); + branch.free(); }); test('throws when name is invalid', () { - final branch = repo.branches['feature']; expect( - () => branch.rename(newName: 'inv@{id'), + () => repo.renameBranch(oldName: 'feature', newName: 'inv@{id'), throwsA(isA()), ); - branch.free(); }); }); }); diff --git a/test/checkout_test.dart b/test/checkout_test.dart index de03fa6..75a3670 100644 --- a/test/checkout_test.dart +++ b/test/checkout_test.dart @@ -8,14 +8,14 @@ void main() { late Repository repo; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Checkout', () { @@ -39,8 +39,9 @@ void main() { }); test('successfully checkouts tree', () { - final masterHead = - repo['821ed6e80627b8769d170a293862f9fc60825226'] as Commit; + final masterHead = repo.lookupCommit( + repo['821ed6e80627b8769d170a293862f9fc60825226'], + ); final masterTree = masterHead.tree; expect( masterTree.entries.any((e) => e.name == 'another_feature_file'), @@ -48,8 +49,9 @@ void main() { ); repo.checkout(refName: 'refs/heads/feature'); - final featureHead = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; + final featureHead = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); final featureTree = featureHead.tree; final repoHead = repo.head; expect(repoHead.target.sha, featureHead.id.sha); diff --git a/test/commit_test.dart b/test/commit_test.dart index 457c972..3415d4e 100644 --- a/test/commit_test.dart +++ b/test/commit_test.dart @@ -6,15 +6,14 @@ import 'helpers/util.dart'; void main() { late Repository repo; late Directory tmpDir; - - const mergeCommit = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'; - const message = "Commit message.\n\nSome description.\n"; - const tree = '7796359a96eb722939c24bafdb1afe9f07f2f628'; late Signature author; late Signature commiter; + late Tree tree; + late Oid mergeCommit; + const message = "Commit message.\n\nSome description.\n"; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); author = Signature.create( name: 'Author Name', @@ -26,31 +25,35 @@ void main() { email: 'commiter@email.com', time: 124, ); + mergeCommit = repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8']; + tree = Tree.lookup( + repo: repo, + id: repo['7796359a96eb722939c24bafdb1afe9f07f2f628'], + ); }); - tearDown(() async { + tearDown(() { author.free(); commiter.free(); + tree.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Commit', () { test('successfully returns when 40 char sha hex is provided', () { - final commit = repo[mergeCommit] as Commit; - expect(commit, isA()); - commit.free(); - }); - - test('successfully returns when sha hex is short', () { - final commit = repo[mergeCommit.substring(0, 5)] as Commit; + final commit = repo.lookupCommit(mergeCommit); expect(commit, isA()); commit.free(); }); test('successfully reverts commit', () { - final to = repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'] as Commit; - final from = repo['821ed6e80627b8769d170a293862f9fc60825226'] as Commit; + final to = repo.lookupCommit( + repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'], + ); + final from = repo.lookupCommit( + repo['821ed6e80627b8769d170a293862f9fc60825226'], + ); final index = repo.index; expect(index.find('dir/dir_file.txt'), true); @@ -64,16 +67,16 @@ void main() { }); test('successfully creates commit', () { - final oid = Commit.create( - repo: repo, + final parent = repo.lookupCommit(mergeCommit); + final oid = repo.createCommit( message: message, author: author, commiter: commiter, - treeSHA: tree, - parents: [mergeCommit], + tree: tree, + parents: [parent], ); - final commit = repo[oid.sha] as Commit; + final commit = repo.lookupCommit(oid); expect(commit.id.sha, oid.sha); expect(commit.message, message); @@ -81,11 +84,12 @@ void main() { expect(commit.author, author); expect(commit.committer, commiter); expect(commit.time, 124); - expect(commit.tree.id.sha, tree); + expect(commit.tree.id, tree.id); expect(commit.parents.length, 1); - expect(commit.parents[0].sha, mergeCommit); + expect(commit.parents[0], mergeCommit); commit.free(); + parent.free(); }); test('successfully creates commit without parents', () { @@ -93,11 +97,11 @@ void main() { message: message, author: author, commiter: commiter, - treeSHA: tree, + tree: tree, parents: [], ); - final commit = repo[oid.sha] as Commit; + final commit = repo.lookupCommit(oid); expect(commit.id.sha, oid.sha); expect(commit.message, message); @@ -105,23 +109,28 @@ void main() { expect(commit.author, author); expect(commit.committer, commiter); expect(commit.time, 124); - expect(commit.tree.id.sha, tree); + expect(commit.tree.id, tree.id); expect(commit.parents.length, 0); commit.free(); }); test('successfully creates commit with 2 parents', () { + final parent1 = repo.lookupCommit(mergeCommit); + final parent2 = repo.lookupCommit( + repo['fc38877b2552ab554752d9a77e1f48f738cca79b'], + ); + final oid = Commit.create( repo: repo, message: message, author: author, commiter: commiter, - treeSHA: tree, - parents: [mergeCommit, 'fc38877b2552ab554752d9a77e1f48f738cca79b'], + tree: tree, + parents: [parent1, parent2], ); - final commit = repo[oid.sha] as Commit; + final commit = repo.lookupCommit(oid); expect(commit.id.sha, oid.sha); expect(commit.message, message); @@ -129,36 +138,13 @@ void main() { expect(commit.author, author); expect(commit.committer, commiter); expect(commit.time, 124); - expect(commit.tree.id.sha, tree); + expect(commit.tree.id, tree.id); expect(commit.parents.length, 2); - expect(commit.parents[0].sha, mergeCommit); - expect(commit.parents[1].sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - - commit.free(); - }); - - test('successfully creates commit with short sha of tree', () { - final oid = Commit.create( - repo: repo, - message: message, - author: author, - commiter: commiter, - treeSHA: tree.substring(0, 5), - parents: [mergeCommit], - ); - - final commit = repo[oid.sha] as Commit; - - expect(commit.id.sha, oid.sha); - expect(commit.message, message); - expect(commit.messageEncoding, 'utf-8'); - expect(commit.author, author); - expect(commit.committer, commiter); - expect(commit.time, 124); - expect(commit.tree.id.sha, tree); - expect(commit.parents.length, 1); - expect(commit.parents[0].sha, mergeCommit); + expect(commit.parents[0], mergeCommit); + expect(commit.parents[1], parent2.id); + parent1.free(); + parent2.free(); commit.free(); }); }); diff --git a/test/describe_test.dart b/test/describe_test.dart index c3f992d..99328cf 100644 --- a/test/describe_test.dart +++ b/test/describe_test.dart @@ -7,14 +7,14 @@ void main() { late Repository repo; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Describe', () { @@ -23,25 +23,22 @@ void main() { }); test('successfully describes commit', () { - final tag = Tag.lookup(repo: repo, sha: 'f0fdbf5'); - tag.delete(); + repo.deleteTag('v0.2'); expect( repo.describe(describeStrategy: GitDescribeStrategy.tags), 'v0.1-1-g821ed6e', ); - - tag.free(); }); test('throws when trying to describe and no reference found', () { - final commit = repo['f17d0d48'] as Commit; + final commit = repo.lookupCommit(repo['f17d0d48']); expect(() => repo.describe(commit: commit), throwsA(isA())); commit.free(); }); test('returns oid when fallback argument is provided', () { - final commit = repo['f17d0d48'] as Commit; + final commit = repo.lookupCommit(repo['f17d0d48']); expect( repo.describe(commit: commit, showCommitOidAsFallback: true), 'f17d0d4', @@ -50,7 +47,7 @@ void main() { }); test('successfully describes with provided strategy', () { - final commit = repo['5aecfa0'] as Commit; + final commit = repo.lookupCommit(repo['5aecfa0']); expect( repo.describe( commit: commit, @@ -63,10 +60,10 @@ void main() { test('successfully describes with provided pattern', () { final signature = repo.defaultSignature; - final commit = repo['fc38877'] as Commit; + final commit = repo.lookupCommit(repo['fc38877']); repo.createTag( tagName: 'test/tag1', - target: 'f17d0d48', + target: repo['f17d0d48'], targetType: GitObject.commit, tagger: signature, message: '', @@ -82,10 +79,9 @@ void main() { }); test('successfully describes and follows first parent only', () { - final tag = Tag.lookup(repo: repo, sha: 'f0fdbf5'); - tag.delete(); + final commit = repo.lookupCommit(repo['821ed6e']); + repo.deleteTag('v0.2'); - final commit = repo['821ed6e'] as Commit; expect( repo.describe( commit: commit, @@ -95,15 +91,13 @@ void main() { 'v0.1-1-g821ed6e', ); - tag.free(); commit.free(); }); - test('successfully describes with abbreviated size provided', () { - final tag = Tag.lookup(repo: repo, sha: 'f0fdbf5'); - tag.delete(); + test('successfully describes with provided abbreviated size', () { + final commit = repo.lookupCommit(repo['821ed6e']); + repo.deleteTag('v0.2'); - final commit = repo['821ed6e'] as Commit; expect( repo.describe( commit: commit, @@ -122,7 +116,6 @@ void main() { 'v0.1', ); - tag.free(); commit.free(); }); diff --git a/test/diff_test.dart b/test/diff_test.dart index 1d8eaf1..2b03e03 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -74,14 +74,14 @@ index e69de29..c217c63 100644 8 files changed, 4 insertions(+), 2 deletions(-) """; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/dirtyrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/dirtyrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Diff', () { @@ -100,7 +100,9 @@ index e69de29..c217c63 100644 test('successfully returns diff between index and tree', () { final index = repo.index; - final tree = (repo[repo.head.target.sha] as Commit).tree; + final head = repo.head; + final commit = repo.lookupCommit(head.target); + final tree = commit.tree; final diff = index.diffToTree(tree: tree); expect(diff.length, 8); @@ -108,13 +110,17 @@ index e69de29..c217c63 100644 expect(diff.deltas[i].newFile.path, indexToTree[i]); } + commit.free(); + head.free(); tree.free(); diff.free(); index.free(); }); test('successfully returns diff between tree and workdir', () { - final tree = (repo[repo.head.target.sha] as Commit).tree; + final head = repo.head; + final commit = repo.lookupCommit(head.target); + final tree = commit.tree; final diff = repo.diff(a: tree); expect(diff.length, 9); @@ -122,13 +128,17 @@ index e69de29..c217c63 100644 expect(diff.deltas[i].newFile.path, treeToWorkdir[i]); } + commit.free(); + head.free(); tree.free(); diff.free(); }); test('successfully returns diff between tree and index', () { final index = repo.index; - final tree = (repo[repo.head.target.sha] as Commit).tree; + final head = repo.head; + final commit = repo.lookupCommit(head.target); + final tree = commit.tree; final diff = repo.diff(a: tree, cached: true); expect(diff.length, 8); @@ -136,14 +146,20 @@ index e69de29..c217c63 100644 expect(diff.deltas[i].newFile.path, indexToTree[i]); } + commit.free(); + head.free(); tree.free(); diff.free(); index.free(); }); test('successfully returns diff between tree and tree', () { - final tree1 = (repo[repo.head.target.sha] as Commit).tree; - final tree2 = repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'] as Tree; + final head = repo.head; + final commit = repo.lookupCommit(head.target); + final tree1 = commit.tree; + final tree2 = repo.lookupTree( + repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'], + ); final diff = repo.diff(a: tree1, b: tree2); expect(diff.length, 10); @@ -151,14 +167,20 @@ index e69de29..c217c63 100644 expect(diff.deltas[i].newFile.path, treeToTree[i]); } + commit.free(); + head.free(); tree1.free(); tree2.free(); diff.free(); }); test('successfully merges diffs', () { - final tree1 = (repo[repo.head.target.sha] as Commit).tree; - final tree2 = repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'] as Tree; + final head = repo.head; + final commit = repo.lookupCommit(head.target); + final tree1 = commit.tree; + final tree2 = repo.lookupTree( + repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'], + ); final diff1 = tree1.diffToTree(tree: tree2); final diff2 = tree1.diffToWorkdir(); @@ -168,6 +190,8 @@ index e69de29..c217c63 100644 diff1.merge(diff2); expect(diff1.length, 11); + commit.free(); + head.free(); tree1.free(); tree2.free(); diff1.free(); @@ -219,8 +243,10 @@ index e69de29..c217c63 100644 test('successfully finds similar entries', () { final index = repo.index; - final oldTree = (repo[repo.head.target.sha] as Commit).tree; - final newTree = repo[index.writeTree().sha] as Tree; + final head = repo.head; + final commit = repo.lookupCommit(head.target); + final oldTree = commit.tree; + final newTree = repo.lookupTree(index.writeTree()); final diff = oldTree.diffToTree(tree: newTree); expect( @@ -234,6 +260,8 @@ index e69de29..c217c63 100644 GitDelta.renamed, ); + commit.free(); + head.free(); diff.free(); index.free(); oldTree.free(); diff --git a/test/helpers/util.dart b/test/helpers/util.dart index 895a195..0d10c3a 100644 --- a/test/helpers/util.dart +++ b/test/helpers/util.dart @@ -3,29 +3,31 @@ import 'package:path/path.dart' as p; final tmpDir = Directory.systemTemp.createTempSync('testrepo'); -Future setupRepo(Directory repoDir) async { - if (await tmpDir.exists()) { - await tmpDir.delete(recursive: true); +Directory setupRepo(Directory repoDir) { + if (tmpDir.existsSync()) { + tmpDir.deleteSync(recursive: true); } - await copyRepo(from: repoDir, to: await tmpDir.create()); + tmpDir.createSync(); + copyRepo(from: repoDir, to: tmpDir); return tmpDir; } -Future copyRepo({required Directory from, required Directory to}) async { - await for (final entity in from.list()) { +void copyRepo({required Directory from, required Directory to}) { + for (final entity in from.listSync()) { if (entity is Directory) { Directory newDir; if (p.basename(entity.path) == '.gitdir') { - newDir = Directory(p.join(to.absolute.path, '.git')); + newDir = Directory(p.join(to.absolute.path, '.git'))..createSync(); } else { - newDir = Directory(p.join(to.absolute.path, p.basename(entity.path))); + newDir = Directory(p.join(to.absolute.path, p.basename(entity.path))) + ..createSync(); } - await copyRepo(from: entity.absolute, to: await newDir.create()); + copyRepo(from: entity.absolute, to: newDir); } else if (entity is File) { if (p.basename(entity.path) == 'gitignore') { - await entity.copy(p.join(to.path, '.gitignore')); + entity.copySync(p.join(to.path, '.gitignore')); } else { - await entity.copy(p.join(to.path, p.basename(entity.path))); + entity.copySync(p.join(to.path, p.basename(entity.path))); } } } diff --git a/test/index_test.dart b/test/index_test.dart index 199bb43..c6d48da 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -8,16 +8,16 @@ void main() { late Index index; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); index = repo.index; }); - tearDown(() async { + tearDown(() { index.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Index', () { @@ -156,25 +156,18 @@ void main() { expect(index.find('feature_file'), false); }); - group('read tree', () { - const treeSha = 'df2b8fc99e1c1d4dbc0a854d9f72157f1d6ea078'; - test('successfully reads with provided SHA hex', () { - expect(index.length, 4); - index.readTree(treeSha); + test('successfully reads tree with provided SHA hex', () { + final tree = repo.lookupTree( + repo['df2b8fc99e1c1d4dbc0a854d9f72157f1d6ea078'], + ); + expect(index.length, 4); + index.readTree(tree); - expect(index.length, 1); + expect(index.length, 1); - // make sure the index is only modified in memory - index.read(); - expect(index.length, 4); - }); - - test('successfully reads with provided short SHA hex', () { - expect(index.length, 4); - index.readTree(treeSha.substring(0, 5)); - - expect(index.length, 1); - }); + // make sure the index is only modified in memory + index.read(); + expect(index.length, 4); }); test('successfully writes tree', () { diff --git a/test/mailmap_test.dart b/test/mailmap_test.dart index 6c2c12e..66b1960 100644 --- a/test/mailmap_test.dart +++ b/test/mailmap_test.dart @@ -126,14 +126,14 @@ Santa Claus late Repository repo; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/mailmaprepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/mailmaprepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Mailmap', () { diff --git a/test/merge_test.dart b/test/merge_test.dart index f5742cd..914ed93 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -9,21 +9,22 @@ void main() { late Repository repo; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/mergerepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/mergerepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Merge', () { group('analysis', () { test('is up to date when no reference is provided', () { - final commit = - repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit; + final commit = repo.lookupCommit( + repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'], + ); final result = repo.mergeAnalysis(theirHead: commit.id); expect(result[0], {GitMergeAnalysis.upToDate}); @@ -34,8 +35,9 @@ void main() { }); test('is up to date for provided ref', () { - final commit = - repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit; + final commit = repo.lookupCommit( + repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'], + ); final result = repo.mergeAnalysis( theirHead: commit.id, @@ -48,18 +50,20 @@ void main() { }); test('is fast forward', () { - final theirHead = - repo['6cbc22e509d72758ab4c8d9f287ea846b90c448b'] as Commit; - final ffCommit = - repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'] as Commit; - final ffBranch = repo.branches.create( + final theirHead = repo.lookupCommit( + repo['6cbc22e509d72758ab4c8d9f287ea846b90c448b'], + ); + final ffCommit = repo.lookupCommit( + repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'], + ); + final ffBranch = repo.createBranch( name: 'ff-branch', target: ffCommit, ); final result = repo.mergeAnalysis( theirHead: theirHead.id, - ourRef: ffBranch.name, + ourRef: 'refs/heads/${ffBranch.name}', ); expect( result[0], @@ -73,8 +77,9 @@ void main() { }); test('is not fast forward and there is no conflicts', () { - final commit = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; + final commit = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); final result = repo.mergeAnalysis(theirHead: commit.id); expect(result[0], {GitMergeAnalysis.normal}); @@ -85,7 +90,7 @@ void main() { }); test('writes conflicts to index', () { - final conflictBranch = repo.branches['conflict-branch']; + final conflictBranch = repo.lookupBranch('conflict-branch'); final index = repo.index; final result = repo.mergeAnalysis(theirHead: conflictBranch.target); @@ -123,7 +128,7 @@ void main() { }); test('successfully removes conflicts', () { - final conflictBranch = repo.branches['conflict-branch']; + final conflictBranch = repo.lookupBranch('conflict-branch'); final index = repo.index; repo.merge(conflictBranch.target); @@ -149,7 +154,7 @@ master conflict edit conflict branch edit >>>>>>> conflict_file """; - final conflictBranch = repo.branches['conflict-branch']; + final conflictBranch = repo.lookupBranch('conflict-branch'); final index = repo.index; repo.merge(conflictBranch.target); @@ -171,10 +176,12 @@ conflict branch edit group('merge commits', () { test('successfully merges with default values', () { - final theirCommit = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; - final ourCommit = - repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit; + final theirCommit = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); + final ourCommit = repo.lookupCommit( + repo['14905459d775f3f56a39ebc2ff081163f7da3529'], + ); final mergeIndex = repo.mergeCommits( ourCommit: ourCommit, @@ -197,10 +204,12 @@ conflict branch edit }); test('successfully merges with provided favor', () { - final theirCommit = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; - final ourCommit = - repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit; + final theirCommit = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); + final ourCommit = repo.lookupCommit( + repo['14905459d775f3f56a39ebc2ff081163f7da3529'], + ); final mergeIndex = repo.mergeCommits( ourCommit: ourCommit, @@ -215,10 +224,12 @@ conflict branch edit }); test('successfully merges with provided merge and file flags', () { - final theirCommit = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; - final ourCommit = - repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit; + final theirCommit = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); + final ourCommit = repo.lookupCommit( + repo['14905459d775f3f56a39ebc2ff081163f7da3529'], + ); final mergeIndex = repo.mergeCommits( ourCommit: ourCommit, @@ -243,13 +254,15 @@ conflict branch edit group('merge trees', () { test('successfully merges with default values', () { - final theirCommit = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; - final ourCommit = - repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit; - final baseCommit = - repo[repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha).sha] - as Commit; + final theirCommit = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); + final ourCommit = repo.lookupCommit( + repo['14905459d775f3f56a39ebc2ff081163f7da3529'], + ); + final baseCommit = repo.lookupCommit( + repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha), + ); final theirTree = theirCommit.tree; final ourTree = ourCommit.tree; final ancestorTree = baseCommit.tree; @@ -281,13 +294,15 @@ conflict branch edit }); test('successfully merges with provided favor', () { - final theirCommit = - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; - final ourCommit = - repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit; - final baseCommit = - repo[repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha).sha] - as Commit; + final theirCommit = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); + final ourCommit = repo.lookupCommit( + repo['14905459d775f3f56a39ebc2ff081163f7da3529'], + ); + final baseCommit = repo.lookupCommit( + repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha), + ); final theirTree = theirCommit.tree; final ourTree = ourCommit.tree; final ancestorTree = baseCommit.tree; @@ -311,7 +326,9 @@ conflict branch edit }); test('successfully cherry-picks commit', () { - final cherry = repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; + final cherry = repo.lookupCommit( + repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], + ); repo.cherryPick(cherry); expect(repo.state, GitRepositoryState.cherrypick); expect(repo.message, 'add another feature file\n'); diff --git a/test/note_test.dart b/test/note_test.dart index b0c5249..e0669be 100644 --- a/test/note_test.dart +++ b/test/note_test.dart @@ -19,14 +19,14 @@ void main() { }, ]; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Note', () { @@ -39,10 +39,7 @@ void main() { expect(notes[i].id.sha, notesExpected[i]['id']); expect(notes[i].message, notesExpected[i]['message']); expect(notes[i].annotatedId.sha, notesExpected[i]['annotatedId']); - } - - for (var note in notes) { - note.free(); + notes[i].free(); } }); @@ -64,11 +61,11 @@ void main() { final noteOid = repo.createNote( author: signature, committer: signature, - object: head.target, + annotatedId: head.target, note: 'New note for HEAD', force: true, ); - final noteBlob = repo[noteOid.sha] as Blob; + final noteBlob = repo.lookupBlob(noteOid); expect(noteOid.sha, 'ffd6e2ceaf91c00ea6d29e2e897f906da720529f'); expect(noteBlob.content, 'New note for HEAD'); @@ -81,15 +78,18 @@ void main() { test('successfully removes note', () { final signature = repo.defaultSignature; final head = repo.head; - final note = repo.lookupNote(annotatedId: head.target); - note.remove(author: signature, committer: signature); + repo.deleteNote( + annotatedId: repo.head.target, + author: signature, + committer: signature, + ); + expect( () => repo.lookupNote(annotatedId: head.target), throwsA(isA()), ); - note.free(); head.free(); signature.free(); }); diff --git a/test/odb_test.dart b/test/odb_test.dart index f0ab320..18f9a2b 100644 --- a/test/odb_test.dart +++ b/test/odb_test.dart @@ -10,14 +10,14 @@ void main() { const blobSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'; const blobContent = 'Feature edit\n'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Odb', () { diff --git a/test/oid_test.dart b/test/oid_test.dart index 16adb66..29f33df 100644 --- a/test/oid_test.dart +++ b/test/oid_test.dart @@ -11,14 +11,14 @@ void main() { const biggerSha = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e9'; const lesserSha = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e7'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Oid', () { diff --git a/test/packbuilder_test.dart b/test/packbuilder_test.dart index 0cd313a..5642e7c 100644 --- a/test/packbuilder_test.dart +++ b/test/packbuilder_test.dart @@ -7,14 +7,14 @@ void main() { late Repository repo; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('PackBuilder', () { @@ -86,12 +86,14 @@ void main() { test('successfully packs with provided packDelegate', () { void packDelegate(PackBuilder packBuilder) { - for (var branchName in repo.branches.list()) { - final branch = repo.references['refs/heads/$branchName']; - for (var commit in repo.log(sha: branch.target.sha)) { + final branches = repo.branches; + for (var branch in branches) { + final ref = repo.lookupReference('refs/heads/${branch.name}'); + for (var commit in repo.log(sha: ref.target.sha)) { packBuilder.addRecursively(commit.id); commit.free(); } + ref.free(); branch.free(); } } diff --git a/test/patch_test.dart b/test/patch_test.dart index 98ae7a0..33671b2 100644 --- a/test/patch_test.dart +++ b/test/patch_test.dart @@ -8,9 +8,9 @@ void main() { late Directory tmpDir; const oldBlob = ''; const newBlob = 'Feature edit\n'; + late Oid oldBlobID; + late Oid newBlobID; const path = 'feature_file'; - const oldBlobSha = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'; - const newBlobSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'; const blobPatch = """ diff --git a/feature_file b/feature_file index e69de29..9c78c21 100644 @@ -38,14 +38,16 @@ index e69de29..0000000 +++ /dev/null """; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); + oldBlobID = repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391']; + newBlobID = repo['9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc']; }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Patch', () { @@ -90,8 +92,8 @@ index e69de29..0000000 }); test('successfully creates from blobs', () { - final a = repo[oldBlobSha] as Blob; - final b = repo[newBlobSha] as Blob; + final a = repo.lookupBlob(oldBlobID); + final b = repo.lookupBlob(newBlobID); final patch = Patch.createFrom( a: a, b: b, @@ -105,7 +107,7 @@ index e69de29..0000000 }); test('successfully creates from one blob (add)', () { - final b = repo[newBlobSha] as Blob; + final b = repo.lookupBlob(newBlobID); final patch = Patch.createFrom( a: null, b: b, @@ -119,7 +121,7 @@ index e69de29..0000000 }); test('successfully creates from one blob (delete)', () { - final a = repo[oldBlobSha] as Blob; + final a = repo.lookupBlob(oldBlobID); final patch = Patch.createFrom( a: a, b: null, @@ -133,7 +135,7 @@ index e69de29..0000000 }); test('successfully creates from blob and buffer', () { - final a = repo[oldBlobSha] as Blob; + final a = repo.lookupBlob(oldBlobID); final patch = Patch.createFrom( a: a, b: newBlob, @@ -147,7 +149,9 @@ index e69de29..0000000 }); test('throws when argument is not Blob or String', () { - final commit = repo['fc38877b2552ab554752d9a77e1f48f738cca79b'] as Commit; + final commit = repo.lookupCommit( + repo['fc38877b2552ab554752d9a77e1f48f738cca79b'], + ); expect( () => Patch.createFrom( a: commit, diff --git a/test/rebase_test.dart b/test/rebase_test.dart index 2e4e191..3b5a358 100644 --- a/test/rebase_test.dart +++ b/test/rebase_test.dart @@ -12,21 +12,21 @@ void main() { '14905459d775f3f56a39ebc2ff081163f7da3529', ]; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/mergerepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/mergerepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Rebase', () { test('successfully performs rebase when there is no conflicts', () { final signature = repo.defaultSignature; - final master = repo.references['refs/heads/master']; - final feature = repo.references['refs/heads/feature']; + final master = repo.lookupReference('refs/heads/master'); + final feature = repo.lookupReference('refs/heads/feature'); repo.checkout(refName: feature.name); expect(() => repo.index['.gitignore'], throwsA(isA())); @@ -60,9 +60,9 @@ void main() { test('successfully performs rebase with provided upstream', () { final signature = repo.defaultSignature; - final master = repo.references['refs/heads/master']; - final feature = repo.references['refs/heads/feature']; - final startCommit = repo[shas[1]] as Commit; + final master = repo.lookupReference('refs/heads/master'); + final feature = repo.lookupReference('refs/heads/feature'); + final startCommit = repo.lookupCommit(repo[shas[1]]); repo.checkout(refName: feature.name); expect( @@ -97,8 +97,8 @@ void main() { test('stops when there is conflicts', () { final signature = repo.defaultSignature; - final master = repo.references['refs/heads/master']; - final conflict = repo.references['refs/heads/conflict-branch']; + final master = repo.lookupReference('refs/heads/master'); + final conflict = repo.lookupReference('refs/heads/conflict-branch'); repo.checkout(refName: conflict.name); @@ -124,8 +124,8 @@ void main() { }); test('successfully aborts rebase in progress', () { - final master = repo.references['refs/heads/master']; - final conflict = repo.references['refs/heads/conflict-branch']; + final master = repo.lookupReference('refs/heads/master'); + final conflict = repo.lookupReference('refs/heads/conflict-branch'); repo.checkout(refName: conflict.name); diff --git a/test/reference_test.dart b/test/reference_test.dart index 238f7d9..1a9933a 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -9,20 +9,20 @@ void main() { const lastCommit = '821ed6e80627b8769d170a293862f9fc60825226'; const newCommit = 'c68ff54aabf660fcdd9a2838d401583fe31249e3'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Reference', () { test('returns a list', () { expect( - repo.references.list, + repo.references, [ 'refs/heads/feature', 'refs/heads/master', @@ -38,7 +38,7 @@ void main() { expect(head.type, ReferenceType.direct); head.free(); - final ref = repo.references['HEAD']; + final ref = repo.lookupReference('HEAD'); expect(ref.type, ReferenceType.symbolic); ref.free(); }); @@ -50,7 +50,7 @@ void main() { }); test('returns SHA hex of symbolic reference', () { - final ref = repo.references['HEAD']; + final ref = repo.lookupReference('HEAD'); expect(ref.target.sha, lastCommit); ref.free(); }); @@ -62,7 +62,7 @@ void main() { }); test('returns the short name', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/remotes/origin/master', target: lastCommit, ); @@ -77,19 +77,19 @@ void main() { }); test('checks if reference is a local branch', () { - final ref = repo.references['refs/heads/feature']; + final ref = repo.lookupReference('refs/heads/feature'); expect(ref.isBranch, true); ref.free(); }); test('checks if reference is a note', () { - final ref = repo.references['refs/heads/master']; + final ref = repo.lookupReference('refs/heads/master'); expect(ref.isNote, false); ref.free(); }); test('checks if reference is a remote branch', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/remotes/origin/master', target: lastCommit, ); @@ -100,16 +100,16 @@ void main() { }); test('checks if reference is a tag', () { - final ref = repo.references['refs/tags/v0.1']; + final ref = repo.lookupReference('refs/tags/v0.1'); expect(ref.isTag, true); ref.free(); }); test('checks if reflog exists for the reference', () { - var ref = repo.references['refs/heads/master']; + var ref = repo.lookupReference('refs/heads/master'); expect(ref.hasLog, true); - ref = repo.references['refs/tags/v0.1']; + ref = repo.lookupReference('refs/tags/v0.1'); expect(ref.hasLog, false); ref.free(); @@ -117,43 +117,43 @@ void main() { group('create direct', () { test('successfully creates with Oid as target', () { - final ref = repo.references['refs/heads/master']; - final refFromOid = repo.references.create( + final ref = repo.lookupReference('refs/heads/master'); + final refFromOid = repo.createReference( name: 'refs/tags/from.oid', target: ref.target, ); - expect(repo.references.list, contains('refs/tags/from.oid')); + expect(repo.references, contains('refs/tags/from.oid')); refFromOid.free(); ref.free(); }); test('successfully creates with SHA hash as target', () { - final refFromHash = repo.references.create( + final refFromHash = repo.createReference( name: 'refs/tags/from.hash', target: lastCommit, ); - expect(repo.references.list, contains('refs/tags/from.hash')); + expect(repo.references, contains('refs/tags/from.hash')); refFromHash.free(); }); test('successfully creates with short SHA hash as target', () { - final refFromHash = repo.references.create( + final refFromHash = repo.createReference( name: 'refs/tags/from.short.hash', target: '78b8bf', ); - expect(repo.references.list, contains('refs/tags/from.short.hash')); + expect(repo.references, contains('refs/tags/from.short.hash')); refFromHash.free(); }); test('successfully creates with log message', () { repo.setIdentity(name: 'name', email: 'email'); - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/heads/log.message', target: lastCommit, logMessage: 'log message', @@ -172,7 +172,7 @@ void main() { test('throws if target is not valid', () { expect( - () => repo.references.create( + () => repo.createReference( name: 'refs/tags/invalid', target: '78b', ), @@ -182,7 +182,7 @@ void main() { test('throws if name is not valid', () { expect( - () => repo.references.create( + () => repo.createReference( name: 'refs/tags/invalid~', target: lastCommit, ), @@ -191,12 +191,12 @@ void main() { }); test('successfully creates with force flag if name already exists', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/tags/test', target: lastCommit, ); - final forceRef = repo.references.create( + final forceRef = repo.createReference( name: 'refs/tags/test', target: lastCommit, force: true, @@ -209,13 +209,13 @@ void main() { }); test('throws if name already exists', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/tags/test', target: lastCommit, ); expect( - () => repo.references.create( + () => repo.createReference( name: 'refs/tags/test', target: lastCommit, ), @@ -228,24 +228,24 @@ void main() { group('create symbolic', () { test('successfully creates with valid target', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/tags/symbolic', target: 'refs/heads/master', ); - expect(repo.references.list, contains('refs/tags/symbolic')); + expect(repo.references, contains('refs/tags/symbolic')); expect(ref.type, ReferenceType.symbolic); ref.free(); }); test('successfully creates with force flag if name already exists', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/tags/test', target: 'refs/heads/master', ); - final forceRef = repo.references.create( + final forceRef = repo.createReference( name: 'refs/tags/test', target: 'refs/heads/master', force: true, @@ -259,13 +259,13 @@ void main() { }); test('throws if name already exists', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/tags/exists', target: 'refs/heads/master', ); expect( - () => repo.references.create( + () => repo.createReference( name: 'refs/tags/exists', target: 'refs/heads/master', ), @@ -277,7 +277,7 @@ void main() { test('throws if name is not valid', () { expect( - () => repo.references.create( + () => repo.createReference( name: 'refs/tags/invalid~', target: 'refs/heads/master', ), @@ -287,7 +287,7 @@ void main() { test('successfully creates with log message', () { repo.setIdentity(name: 'name', email: 'email'); - final ref = repo.references.create( + final ref = repo.createReference( name: 'HEAD', target: 'refs/heads/feature', force: true, @@ -307,34 +307,34 @@ void main() { }); test('successfully deletes reference', () { - final ref = repo.references.create( + final ref = repo.createReference( name: 'refs/tags/test', target: lastCommit, ); - expect(repo.references.list, contains('refs/tags/test')); + expect(repo.references, contains('refs/tags/test')); - ref.delete(); - expect(repo.references.list, isNot(contains('refs/tags/test'))); + repo.deleteReference('refs/tags/test'); + expect(repo.references, isNot(contains('refs/tags/test'))); ref.free(); }); group('finds', () { test('with provided name', () { - final ref = repo.references['refs/heads/master']; + final ref = repo.lookupReference('refs/heads/master'); expect(ref.target.sha, lastCommit); ref.free(); }); test('throws when error occured', () { expect( - () => repo.references['refs/heads/not/there'], + () => repo.lookupReference('refs/heads/not/there'), throwsA(isA()), ); }); }); test('returns log for reference', () { - final ref = repo.references['refs/heads/master']; + final ref = repo.lookupReference('refs/heads/master'); final reflog = ref.log; expect(reflog.last.message, 'commit (initial): init'); @@ -344,7 +344,7 @@ void main() { group('set target', () { test('successfully sets with SHA hex', () { - final ref = repo.references['refs/heads/master']; + final ref = repo.lookupReference('refs/heads/master'); ref.setTarget(target: newCommit); expect(ref.target.sha, newCommit); @@ -352,7 +352,7 @@ void main() { }); test('successfully sets target with short SHA hex', () { - final ref = repo.references['refs/heads/master']; + final ref = repo.lookupReference('refs/heads/master'); ref.setTarget(target: newCommit.substring(0, 5)); expect(ref.target.sha, newCommit); @@ -360,7 +360,7 @@ void main() { }); test('successfully sets symbolic target', () { - final ref = repo.references['HEAD']; + final ref = repo.lookupReference('HEAD'); expect(ref.target.sha, lastCommit); ref.setTarget(target: 'refs/heads/feature'); @@ -370,7 +370,7 @@ void main() { }); test('successfully sets target with log message', () { - final ref = repo.references['HEAD']; + final ref = repo.lookupReference('HEAD'); expect(ref.target.sha, lastCommit); repo.setIdentity(name: 'name', email: 'email'); @@ -386,7 +386,7 @@ void main() { }); test('throws on invalid target', () { - final ref = repo.references['HEAD']; + final ref = repo.lookupReference('HEAD'); expect( () => ref.setTarget(target: 'refs/heads/invalid~'), throwsA(isA()), @@ -398,67 +398,56 @@ void main() { group('rename', () { test('successfully renames reference', () { - final ref = repo.references.create( - name: 'refs/tags/v1', - target: lastCommit, + repo.renameReference( + oldName: 'refs/tags/v0.1', + newName: 'refs/tags/renamed', ); - expect(ref.name, 'refs/tags/v1'); - ref.rename(newName: 'refs/tags/v2'); - expect(ref.name, 'refs/tags/v2'); - - ref.free(); + expect(repo.references, contains('refs/tags/renamed')); + expect(repo.references, isNot(contains('refs/tags/v0.1'))); }); test('throws on invalid name', () { - final ref = repo.references.create( - name: 'refs/tags/v1', - target: lastCommit, - ); - expect( - () => ref.rename(newName: 'refs/tags/invalid~'), + () => repo.renameReference( + oldName: 'refs/tags/v0.1', + newName: 'refs/tags/invalid~', + ), throwsA(isA()), ); - - ref.free(); }); test('throws if name already exists', () { - final ref1 = repo.references.create( - name: 'refs/tags/v1', - target: lastCommit, - ); - - final ref2 = repo.references.create( - name: 'refs/tags/v2', - target: lastCommit, - ); - expect( - () => ref1.rename(newName: 'refs/tags/v2'), + () => repo.renameReference( + oldName: 'refs/tags/v0.1', + newName: 'refs/tags/v0.2', + ), throwsA(isA()), ); - - ref1.free(); - ref2.free(); }); test('successfully renames with force flag set to true', () { - final ref1 = repo.references.create( - name: 'refs/tags/v1', - target: lastCommit, + final ref1 = repo.lookupReference('refs/tags/v0.1'); + final ref2 = repo.lookupReference('refs/tags/v0.2'); + + expect(ref1.target.sha, '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'); + expect(ref2.target.sha, 'f0fdbf506397e9f58c59b88dfdd72778ec06cc0c'); + expect(repo.references.length, 5); + + repo.renameReference( + oldName: 'refs/tags/v0.1', + newName: 'refs/tags/v0.2', + force: true, ); - final ref2 = repo.references.create( - name: 'refs/tags/v2', - target: newCommit, + final updatedRef1 = repo.lookupReference('refs/tags/v0.2'); + expect( + updatedRef1.target.sha, + '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8', ); - - expect(ref2.target.sha, newCommit); - - ref1.rename(newName: 'refs/tags/v2', force: true); - expect(ref1.name, 'refs/tags/v2'); + expect(repo.references, isNot(contains('refs/tags/v0.1'))); + expect(repo.references.length, 4); ref1.free(); ref2.free(); @@ -466,9 +455,9 @@ void main() { }); test('checks equality', () { - final ref1 = repo.references['refs/heads/master']; - final ref2 = repo.references['refs/heads/master']; - final ref3 = repo.references['refs/heads/feature']; + final ref1 = repo.lookupReference('refs/heads/master'); + final ref2 = repo.lookupReference('refs/heads/master'); + final ref3 = repo.lookupReference('refs/heads/feature'); expect(ref1 == ref2, true); expect(ref1 != ref2, false); @@ -481,8 +470,8 @@ void main() { }); test('successfully peels to non-tag object when no type is provided', () { - final ref = repo.references['refs/heads/master']; - final commit = repo[ref.target.sha] as Commit; + final ref = repo.lookupReference('refs/heads/master'); + final commit = repo.lookupCommit(ref.target); final peeled = ref.peel() as Commit; expect(peeled.id, commit.id); @@ -493,8 +482,8 @@ void main() { }); test('successfully peels to object of provided type', () { - final ref = repo.references['refs/heads/master']; - final commit = repo[ref.target.sha] as Commit; + final ref = repo.lookupReference('refs/heads/master'); + final commit = repo.lookupCommit(ref.target); final tree = commit.tree; final peeledCommit = ref.peel(GitObject.commit) as Commit; final peeledTree = ref.peel(GitObject.tree) as Tree; @@ -511,12 +500,12 @@ void main() { test('successfully compresses references', () { final packedRefsFile = File('${tmpDir.path}/.git/packed-refs'); expect(packedRefsFile.existsSync(), false); - final oldRefs = repo.references.list; + final oldRefs = repo.references; - repo.references.compress(); + Reference.compress(repo); expect(packedRefsFile.existsSync(), true); - final newRefs = repo.references.list; + final newRefs = repo.references; expect(newRefs, oldRefs); }); }); diff --git a/test/reflog_test.dart b/test/reflog_test.dart index 3c8efe0..02f106c 100644 --- a/test/reflog_test.dart +++ b/test/reflog_test.dart @@ -9,18 +9,18 @@ void main() { late Reference head; late Directory tmpDir; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); head = repo.head; reflog = RefLog(head); }); - tearDown(() async { + tearDown(() { reflog.free(); head.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('RefLog', () { diff --git a/test/remote_prune_test.dart b/test/remote_prune_test.dart index b157a5c..f12fbf0 100644 --- a/test/remote_prune_test.dart +++ b/test/remote_prune_test.dart @@ -10,43 +10,61 @@ void main() { late Remote remote; final cloneDir = Directory('${Directory.systemTemp.path}/cloned'); - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); - if (await cloneDir.exists()) { - cloneDir.delete(recursive: true); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); + if (cloneDir.existsSync()) { + cloneDir.deleteSync(recursive: true); } originRepo = Repository.open(tmpDir.path); clonedRepo = Repository.clone( url: tmpDir.path, localPath: cloneDir.path, ); - originRepo.branches['feature'].delete(); - remote = clonedRepo.remotes['origin']; + originRepo.deleteBranch('feature'); + remote = clonedRepo.lookupRemote('origin'); }); - tearDown(() async { + tearDown(() { remote.free(); originRepo.free(); clonedRepo.free(); - await tmpDir.delete(recursive: true); - if (await cloneDir.exists()) { - cloneDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); + if (cloneDir.existsSync()) { + cloneDir.deleteSync(recursive: true); } }); group('Remote', () { test('fetch() does not prune branch by default', () { remote.fetch(); - expect(clonedRepo.branches.list(), contains('origin/feature')); + + final branches = clonedRepo.branches; + expect(branches.any((branch) => branch.name == 'origin/feature'), true); + + for (final branch in branches) { + branch.free(); + } }); test('fetch() successfully prunes branch with provided flag', () { remote.fetch(prune: GitFetchPrune.prune); - expect(clonedRepo.branches.list(), isNot(contains('origin/feature'))); + + final branches = clonedRepo.branches; + expect(branches.any((branch) => branch.name == 'origin/feature'), false); + + for (final branch in branches) { + branch.free(); + } }); test('fetch() does not prune branch with provided flag', () { remote.fetch(prune: GitFetchPrune.noPrune); - expect(clonedRepo.branches.list(), contains('origin/feature')); + + final branches = clonedRepo.branches; + expect(branches.any((branch) => branch.name == 'origin/feature'), true); + + for (final branch in branches) { + branch.free(); + } }); test('prune() successfully prunes branches', () { @@ -58,11 +76,22 @@ void main() { final callbacks = Callbacks(updateTips: updateTips); remote.fetch(prune: GitFetchPrune.noPrune); - expect(clonedRepo.branches.list(), contains('origin/feature')); + var branches = clonedRepo.branches; + expect(branches.any((branch) => branch.name == 'origin/feature'), true); + + for (final branch in branches) { + branch.free(); + } remote.prune(callbacks); + + branches = clonedRepo.branches; expect(pruned, contains('refs/remotes/origin/feature')); - expect(clonedRepo.branches.list(), isNot(contains('origin/feature'))); + expect(branches.any((branch) => branch.name == 'origin/feature'), false); + + for (final branch in branches) { + branch.free(); + } }); }); } diff --git a/test/remote_test.dart b/test/remote_test.dart index 285ee86..b83799d 100644 --- a/test/remote_test.dart +++ b/test/remote_test.dart @@ -9,23 +9,23 @@ void main() { const remoteName = 'origin'; const remoteUrl = 'git://github.com/SkinnyMind/libgit2dart.git'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Remote', () { test('returns list of remotes', () { - expect(repo.remotes.list, ['origin']); + expect(repo.remotes, ['origin']); }); test('successfully looks up remote for provided name', () { - final remote = repo.remotes['origin']; + final remote = repo.lookupRemote('origin'); expect(remote.name, remoteName); expect(remote.url, remoteUrl); @@ -35,11 +35,11 @@ void main() { }); test('throws when provided name for lookup is not found', () { - expect(() => repo.remotes['upstream'], throwsA(isA())); + expect(() => repo.lookupRemote('upstream'), throwsA(isA())); }); test('successfully creates without fetchspec', () { - final remote = repo.remotes.create(name: 'upstream', url: remoteUrl); + final remote = repo.createRemote(name: 'upstream', url: remoteUrl); expect(repo.remotes.length, 2); expect(remote.name, 'upstream'); @@ -51,7 +51,7 @@ void main() { test('successfully creates with provided fetchspec', () { const spec = '+refs/*:refs/*'; - final remote = repo.remotes.create( + final remote = repo.createRemote( name: 'upstream', url: remoteUrl, fetch: spec, @@ -67,23 +67,23 @@ void main() { }); test('successfully deletes', () { - final remote = repo.remotes.create(name: 'upstream', url: remoteUrl); + final remote = repo.createRemote(name: 'upstream', url: remoteUrl); expect(repo.remotes.length, 2); - repo.remotes.delete(remote.name); + repo.deleteRemote(remote.name); expect(repo.remotes.length, 1); remote.free(); }); test('successfully renames', () { - final remote = repo.remotes[remoteName]; + final remote = repo.lookupRemote(remoteName); - final problems = repo.remotes.rename(remote: remoteName, newName: 'new'); + final problems = repo.renameRemote(oldName: remoteName, newName: 'new'); expect(problems, isEmpty); expect(remote.name, isNot('new')); - final newRemote = repo.remotes['new']; + final newRemote = repo.lookupRemote('new'); expect(newRemote.name, 'new'); newRemote.free(); @@ -92,19 +92,19 @@ void main() { test('throws when renaming with invalid names', () { expect( - () => repo.remotes.rename(remote: '', newName: ''), + () => repo.renameRemote(oldName: '', newName: ''), throwsA(isA()), ); }); test('successfully sets url', () { - final remote = repo.remotes[remoteName]; + final remote = repo.lookupRemote(remoteName); expect(remote.url, remoteUrl); const newUrl = 'git://new/url.git'; - repo.remotes.setUrl(remote: remoteName, url: newUrl); + Remote.setUrl(repo: repo, remote: remoteName, url: newUrl); - final newRemote = repo.remotes[remoteName]; + final newRemote = repo.lookupRemote(remoteName); expect(newRemote.url, newUrl); newRemote.free(); @@ -113,16 +113,16 @@ void main() { test('throws when trying to set invalid url name', () { expect( - () => repo.remotes.setUrl(remote: 'origin', url: ''), + () => Remote.setUrl(repo: repo, remote: 'origin', url: ''), throwsA(isA()), ); }); test('successfully sets url for pushing', () { const newUrl = 'git://new/url.git'; - repo.remotes.setPushUrl(remote: remoteName, url: newUrl); + Remote.setPushUrl(repo: repo, remote: remoteName, url: newUrl); - final remote = repo.remotes[remoteName]; + final remote = repo.lookupRemote(remoteName); expect(remote.pushUrl, newUrl); remote.free(); @@ -130,13 +130,13 @@ void main() { test('throws when trying to set invalid push url name', () { expect( - () => repo.remotes.setPushUrl(remote: 'origin', url: ''), + () => Remote.setPushUrl(repo: repo, remote: 'origin', url: ''), throwsA(isA()), ); }); test('returns refspec', () { - final remote = repo.remotes['origin']; + final remote = repo.lookupRemote('origin'); expect(remote.refspecCount, 1); final refspec = remote.getRefspec(0); @@ -162,11 +162,12 @@ void main() { }); test('successfully adds fetch refspec', () { - repo.remotes.addFetch( + Remote.addFetch( + repo: repo, remote: 'origin', refspec: '+refs/test/*:refs/test/remotes/*', ); - final remote = repo.remotes['origin']; + final remote = repo.lookupRemote('origin'); expect(remote.fetchRefspecs.length, 2); expect( remote.fetchRefspecs, @@ -180,11 +181,12 @@ void main() { }); test('successfully adds push refspec', () { - repo.remotes.addPush( + Remote.addPush( + repo: repo, remote: 'origin', refspec: '+refs/test/*:refs/test/remotes/*', ); - final remote = repo.remotes['origin']; + final remote = repo.lookupRemote('origin'); expect(remote.pushRefspecs.length, 1); expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']); @@ -192,11 +194,12 @@ void main() { }); test('successfully returns remote repo\'s reference list', () { - repo.remotes.setUrl( + Remote.setUrl( + repo: repo, remote: 'libgit2', url: 'https://github.com/libgit2/TestGitRepository', ); - final remote = repo.remotes['libgit2']; + final remote = repo.lookupRemote('libgit2'); final refs = remote.ls(); expect(refs.first['local'], false); @@ -212,11 +215,12 @@ void main() { }); test('successfully fetches data', () { - repo.remotes.setUrl( + Remote.setUrl( + repo: repo, remote: 'libgit2', url: 'https://github.com/libgit2/TestGitRepository', ); - final remote = repo.remotes['libgit2']; + final remote = repo.lookupRemote('libgit2'); final stats = remote.fetch(); @@ -233,11 +237,12 @@ void main() { test('successfully fetches data with provided transfer progress callback', () { - repo.remotes.setUrl( + Remote.setUrl( + repo: repo, remote: 'libgit2', url: 'https://github.com/libgit2/TestGitRepository', ); - final remote = repo.remotes['libgit2']; + final remote = repo.lookupRemote('libgit2'); TransferProgress? callbackStats; void tp(TransferProgress stats) => callbackStats = stats; @@ -263,11 +268,12 @@ Enumerating objects: 69, done. Counting objects: 100% (1/1)\rCounting objects: 100% (1/1), done. Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 """; - repo.remotes.setUrl( + Remote.setUrl( + repo: repo, remote: 'libgit2', url: 'https://github.com/libgit2/TestGitRepository', ); - final remote = repo.remotes['libgit2']; + final remote = repo.lookupRemote('libgit2'); var sidebandOutput = StringBuffer(); void sideband(String message) { @@ -283,11 +289,12 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 }); test('successfully fetches data with provided update tips callback', () { - repo.remotes.setUrl( + Remote.setUrl( + repo: repo, remote: 'libgit2', url: 'https://github.com/libgit2/TestGitRepository', ); - final remote = repo.remotes['libgit2']; + final remote = repo.lookupRemote('libgit2'); const tipsExpected = [ { 'refname': 'refs/tags/annotated_tag', @@ -323,21 +330,22 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 remote.free(); }); - test('successfully pushes with update reference callback', () async { + test('successfully pushes with update reference callback', () { final originDir = Directory('${Directory.systemTemp.path}/origin_testrepo'); - if (await originDir.exists()) { - await originDir.delete(recursive: true); + if (originDir.existsSync()) { + originDir.deleteSync(recursive: true); } - await copyRepo( + originDir.createSync(); + copyRepo( from: Directory('test/assets/empty_bare.git/'), - to: await originDir.create(), + to: originDir, ); final originRepo = Repository.open(originDir.path); - repo.remotes.create(name: 'local', url: originDir.path); - final remote = repo.remotes['local']; + repo.createRemote(name: 'local', url: originDir.path); + final remote = repo.lookupRemote('local'); var updateRefOutput = {}; void updateRef(String refname, String message) { @@ -348,7 +356,7 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 remote.push(refspecs: ['refs/heads/master'], callbacks: callbacks); expect( - (originRepo[originRepo.head.target.sha] as Commit).id.sha, + originRepo.lookupCommit(originRepo.head.target).id.sha, '821ed6e80627b8769d170a293862f9fc60825226', ); expect(updateRefOutput, {'refs/heads/master': ''}); diff --git a/test/repository_clone_test.dart b/test/repository_clone_test.dart index b9dc713..d529ba8 100644 --- a/test/repository_clone_test.dart +++ b/test/repository_clone_test.dart @@ -8,20 +8,19 @@ void main() { late Directory tmpDir; final cloneDir = Directory('${Directory.systemTemp.path}/cloned'); - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); - - if (await cloneDir.exists()) { + if (cloneDir.existsSync()) { cloneDir.delete(recursive: true); } }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); - if (await cloneDir.exists()) { - cloneDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); + if (cloneDir.existsSync()) { + cloneDir.deleteSync(recursive: true); } }); @@ -69,7 +68,7 @@ void main() { test('successfully clones repository with provided remote callback', () { Remote remote(Repository repo, String name, String url) => - repo.remotes.create(name: 'test', url: tmpDir.path); + repo.createRemote(name: 'test', url: tmpDir.path); final clonedRepo = Repository.clone( url: tmpDir.path, @@ -79,15 +78,15 @@ void main() { expect(clonedRepo.isEmpty, false); expect(clonedRepo.isBare, false); - expect(clonedRepo.remotes.list, ['test']); - expect(clonedRepo.references.list, contains('refs/remotes/test/master')); + expect(clonedRepo.remotes, ['test']); + expect(clonedRepo.references, contains('refs/remotes/test/master')); clonedRepo.free(); }); test('throws when cloning repository with invalid remote callback', () { Remote remote(Repository repo, String name, String url) => - repo.remotes.create(name: '', url: ''); + repo.createRemote(name: '', url: ''); expect( () => Repository.clone( diff --git a/test/repository_init_test.dart b/test/repository_init_test.dart index 7d050ba..a4084f6 100644 --- a/test/repository_init_test.dart +++ b/test/repository_init_test.dart @@ -6,17 +6,17 @@ void main() { late Repository repo; final initDir = Directory('${Directory.systemTemp.path}/init_repo'); - setUp(() async { - if (await initDir.exists()) { - await initDir.delete(recursive: true); + setUp(() { + if (initDir.existsSync()) { + initDir.deleteSync(recursive: true); } else { - await initDir.create(); + initDir.createSync(); } }); - tearDown(() async { + tearDown(() { repo.free(); - await initDir.delete(recursive: true); + initDir.deleteSync(recursive: true); }); group('Repository.init', () { test('successfully creates new bare repo at provided path', () { @@ -49,7 +49,7 @@ void main() { File('${initDir.path}/.git/description').readAsStringSync(), 'test repo', ); - expect(repo.remotes['origin'].url, 'test.url'); + expect(repo.lookupRemote('origin').url, 'test.url'); }); }); } diff --git a/test/repository_test.dart b/test/repository_test.dart index 4645feb..3132d98 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -9,14 +9,14 @@ void main() { const lastCommit = '821ed6e80627b8769d170a293862f9fc60825226'; const featureCommit = '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Repository', () { @@ -125,7 +125,7 @@ void main() { test('successfully creates new blob', () { final oid = repo.createBlob(newBlobContent); - final newBlob = repo[oid.sha] as Blob; + final newBlob = repo.lookupBlob(oid); expect(newBlob, isA()); @@ -135,7 +135,7 @@ void main() { test('successfully creates new blob from file at provided relative path', () { final oid = repo.createBlobFromWorkdir('feature_file'); - final newBlob = repo[oid.sha] as Blob; + final newBlob = repo.lookupBlob(oid); expect(newBlob, isA()); @@ -146,7 +146,7 @@ void main() { final outsideFile = File('${Directory.current.absolute.path}/test/blob_test.dart'); final oid = repo.createBlobFromDisk(outsideFile.path); - final newBlob = repo[oid.sha] as Blob; + final newBlob = repo.lookupBlob(oid); expect(newBlob, isA()); @@ -161,11 +161,10 @@ void main() { time: 1234, ); const tagName = 'tag'; - const target = 'f17d0d48eae3aa08cecf29128a35e310c97b3521'; + final target = repo['f17d0d48eae3aa08cecf29128a35e310c97b3521']; const message = 'init tag\n'; - final oid = Tag.create( - repo: repo, + final oid = repo.createTag( tagName: tagName, target: target, targetType: GitObject.commit, @@ -173,7 +172,7 @@ void main() { message: message, ); - final newTag = repo[oid.sha] as Tag; + final newTag = repo.lookupTag(oid); final tagger = newTag.tagger; final newTagTarget = newTag.target as Commit; @@ -181,7 +180,7 @@ void main() { expect(newTag.name, tagName); expect(newTag.message, message); expect(tagger, signature); - expect(newTagTarget.id.sha, target); + expect(newTagTarget.id, target); newTag.free(); newTagTarget.free(); @@ -252,8 +251,8 @@ void main() { }); test('checks if commit is a descendant of another commit', () { - final commit1 = repo['821ed6e8'] as Commit; - final commit2 = repo['78b8bf12'] as Commit; + final commit1 = repo.lookupCommit(repo['821ed6e8']); + final commit2 = repo.lookupCommit(repo['78b8bf12']); expect( repo.descendantOf( @@ -284,8 +283,8 @@ void main() { }); test('returns number of ahead behind commits', () { - final commit1 = repo['821ed6e8'] as Commit; - final commit2 = repo['c68ff54a'] as Commit; + final commit1 = repo.lookupCommit(repo['821ed6e8']); + final commit2 = repo.lookupCommit(repo['c68ff54a']); expect( repo.aheadBehind(localSHA: commit1.id.sha, upstreamSHA: commit2.id.sha), diff --git a/test/reset_test.dart b/test/reset_test.dart index 9e55ec8..286b695 100644 --- a/test/reset_test.dart +++ b/test/reset_test.dart @@ -9,15 +9,15 @@ void main() { late File file; const sha = '6cbc22e509d72758ab4c8d9f287ea846b90c448b'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); file = File('${tmpDir.path}/feature_file'); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Reset', () { diff --git a/test/revparse_test.dart b/test/revparse_test.dart index d503d9b..ebeb238 100644 --- a/test/revparse_test.dart +++ b/test/revparse_test.dart @@ -9,14 +9,14 @@ void main() { const headSHA = '821ed6e80627b8769d170a293862f9fc60825226'; const parentSHA = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('revParse', () { @@ -36,7 +36,7 @@ void main() { }); test('.ext() returns commit and reference', () { - final masterRef = repo.references['refs/heads/master']; + final masterRef = repo.lookupReference('refs/heads/master'); var headParse = repo.revParseExt('master'); expect(headParse.object.id.sha, headSHA); @@ -46,7 +46,7 @@ void main() { headParse.object.free(); headParse.reference?.free(); - final featureRef = repo.references['refs/heads/feature']; + final featureRef = repo.lookupReference('refs/heads/feature'); headParse = repo.revParseExt('feature'); expect( diff --git a/test/revwalk_test.dart b/test/revwalk_test.dart index 247cb22..9efbbf0 100644 --- a/test/revwalk_test.dart +++ b/test/revwalk_test.dart @@ -14,14 +14,14 @@ void main() { 'f17d0d48eae3aa08cecf29128a35e310c97b3521', ]; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('RevWalk', () { diff --git a/test/stash_test.dart b/test/stash_test.dart index d38df7f..a16094e 100644 --- a/test/stash_test.dart +++ b/test/stash_test.dart @@ -8,8 +8,8 @@ void main() { late Directory tmpDir; late Signature stasher; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); stasher = Signature.create( name: 'Stasher', @@ -17,10 +17,10 @@ void main() { ); }); - tearDown(() async { + tearDown(() { stasher.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Stash', () { diff --git a/test/submodule_test.dart b/test/submodule_test.dart index 83a9882..a21ec2f 100644 --- a/test/submodule_test.dart +++ b/test/submodule_test.dart @@ -10,14 +10,14 @@ void main() { const submoduleUrl = 'https://github.com/libgit2/TestGitRepository'; const submoduleHeadSha = '49322bb17d3acc9146f98c97d078513228bbf3c0'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/submodulerepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/submodulerepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Submodule', () { diff --git a/test/tag_test.dart b/test/tag_test.dart index 65199de..4fd9746 100644 --- a/test/tag_test.dart +++ b/test/tag_test.dart @@ -7,18 +7,19 @@ void main() { late Repository repo; late Tag tag; late Directory tmpDir; - const tagSHA = 'f0fdbf506397e9f58c59b88dfdd72778ec06cc0c'; + late Oid tagID; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); - tag = Tag.lookup(repo: repo, sha: tagSHA); + tagID = repo['f0fdbf506397e9f58c59b88dfdd72778ec06cc0c']; + tag = repo.lookupTag(tagID); }); - tearDown(() async { + tearDown(() { tag.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Tag', () { @@ -36,7 +37,7 @@ void main() { final target = tag.target as Commit; final tagger = tag.tagger; - expect(tag.id.sha, tagSHA); + expect(tag.id, tagID); expect(tag.name, 'v0.2'); expect(tag.message, 'annotated tag\n'); expect(target.message, 'add subdirectory file\n'); @@ -53,11 +54,10 @@ void main() { time: 1234, ); const tagName = 'tag'; - final target = 'f17d0d48eae3aa08cecf29128a35e310c97b3521'; + final target = repo['f17d0d48eae3aa08cecf29128a35e310c97b3521']; const message = 'init tag\n'; - final oid = Tag.create( - repo: repo, + final oid = repo.createTag( tagName: tagName, target: target, targetType: GitObject.commit, @@ -65,7 +65,7 @@ void main() { message: message, ); - final newTag = Tag.lookup(repo: repo, sha: oid.sha); + final newTag = repo.lookupTag(oid); final tagger = newTag.tagger; final newTagTarget = newTag.target as Commit; @@ -73,7 +73,7 @@ void main() { expect(newTag.name, tagName); expect(newTag.message, message); expect(tagger, signature); - expect(newTagTarget.id.sha, target); + expect(newTagTarget.id, target); newTag.free(); newTagTarget.free(); @@ -87,7 +87,7 @@ void main() { test('successfully deletes tag', () { expect(repo.tags, ['v0.1', 'v0.2']); - tag.delete(); + repo.deleteTag('v0.2'); expect(repo.tags, ['v0.1']); }); }); diff --git a/test/tree_test.dart b/test/tree_test.dart index b15d3dd..b8bdfd0 100644 --- a/test/tree_test.dart +++ b/test/tree_test.dart @@ -7,19 +7,20 @@ void main() { late Repository repo; late Tree tree; late Directory tmpDir; - const treeSHA = 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'; const fileSHA = '1377554ebea6f98a2c748183bc5a96852af12ac2'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); - tree = Tree.lookup(repo: repo, sha: treeSHA); + tree = repo.lookupTree( + repo['a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'], + ); }); - tearDown(() async { + tearDown(() { tree.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('Tree', () { @@ -70,7 +71,7 @@ void main() { oid: fileOid, filemode: GitFilemode.blob, ); - final newTree = Tree.lookup(repo: repo, sha: builder.write().sha); + final newTree = repo.lookupTree(builder.write()); final entry = newTree['filename']; expect(newTree.length, 1); diff --git a/test/treebuilder_test.dart b/test/treebuilder_test.dart index 354a6fd..4412415 100644 --- a/test/treebuilder_test.dart +++ b/test/treebuilder_test.dart @@ -7,18 +7,17 @@ void main() { late Repository repo; late Tree tree; late Directory tmpDir; - const treeSHA = 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'; - setUp(() async { - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + setUp(() { + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); - tree = Tree.lookup(repo: repo, sha: treeSHA); + tree = repo.lookupTree(repo['a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f']); }); - tearDown(() async { + tearDown(() { tree.free(); repo.free(); - await tmpDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); }); group('TreeBuilder', () { @@ -56,11 +55,13 @@ void main() { expect(() => builder[entry.name], throwsA(isA())); builder.add( - filename: entry.name, oid: entry.id, filemode: entry.filemode); + filename: entry.name, + oid: entry.id, + filemode: entry.filemode, + ); expect(builder[entry.name].name, entry.name); builder.free(); - entry.free(); }); test('successfully removes an entry', () { diff --git a/test/worktree_test.dart b/test/worktree_test.dart index 8c8e168..1699466 100644 --- a/test/worktree_test.dart +++ b/test/worktree_test.dart @@ -7,91 +7,98 @@ void main() { late Repository repo; late Directory tmpDir; final worktreeDir = Directory('${Directory.systemTemp.path}/worktree'); + const worktreeName = 'worktree'; - setUp(() async { - if (await worktreeDir.exists()) { - await worktreeDir.delete(recursive: true); + setUp(() { + if (worktreeDir.existsSync()) { + worktreeDir.deleteSync(recursive: true); } - tmpDir = await setupRepo(Directory('test/assets/testrepo/')); + tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); }); - tearDown(() async { + tearDown(() { repo.free(); - await tmpDir.delete(recursive: true); - if (await worktreeDir.exists()) { - await worktreeDir.delete(recursive: true); + tmpDir.deleteSync(recursive: true); + if (worktreeDir.existsSync()) { + worktreeDir.deleteSync(recursive: true); } }); group('Worktree', () { test('successfully creates worktree at provided path', () { - const worktreeName = 'worktree'; - expect(Worktree.list(repo), []); + expect(repo.worktrees, []); - final worktree = Worktree.create( - repo: repo, + final worktree = repo.createWorktree( name: worktreeName, path: worktreeDir.path, ); + final lookedupWorktree = repo.lookupWorktree(worktreeName); + final branches = repo.branches; - expect(Worktree.list(repo), [worktreeName]); - expect(repo.branches.list(), contains(worktreeName)); + expect(repo.worktrees, [worktreeName]); + expect(branches.any((branch) => branch.name == worktreeName), true); expect(worktree.name, worktreeName); + expect(lookedupWorktree.name, worktreeName); expect(worktree.path, worktreeDir.path); + expect(lookedupWorktree.path, worktreeDir.path); expect(File('${worktreeDir.path}/.git').existsSync(), true); + for (final branch in branches) { + branch.free(); + } + lookedupWorktree.free(); worktree.free(); }); test( 'successfully creates worktree at provided path from provided reference', () { - const worktreeName = 'worktree'; final head = repo.revParseSingle('HEAD'); - final worktreeRef = repo.branches.create(name: 'v1', target: head); - final worktreeBranch = repo.branches['v1']; - expect(Worktree.list(repo), []); + final worktreeBranch = repo.createBranch(name: 'v1', target: head); + final ref = repo.lookupReference('refs/heads/v1'); + expect(repo.worktrees, []); - final worktree = Worktree.create( - repo: repo, + final worktree = repo.createWorktree( name: worktreeName, path: worktreeDir.path, - ref: worktreeRef, + ref: ref, ); + final branches = repo.branches; - expect(Worktree.list(repo), [worktreeName]); - expect(repo.branches.list(), contains('v1')); - expect(repo.branches.list(), isNot(contains(worktreeName))); + expect(repo.worktrees, [worktreeName]); + expect(branches.any((branch) => branch.name == 'v1'), true); + expect(branches.any((branch) => branch.name == worktreeName), false); expect(worktreeBranch.isCheckedOut, true); worktreeDir.deleteSync(recursive: true); worktree.prune(); - expect(Worktree.list(repo), []); + expect(repo.worktrees, []); expect(worktreeBranch.isCheckedOut, false); - expect(repo.branches.list(), contains('v1')); + expect(branches.any((branch) => branch.name == 'v1'), true); + for (final branch in branches) { + branch.free(); + } worktreeBranch.free(); - worktreeRef.free(); + ref.free(); head.free(); worktree.free(); }); test('successfully prunes worktree', () { - const worktreeName = 'worktree'; - expect(Worktree.list(repo), []); + expect(repo.worktrees, []); - final worktree = Worktree.create( - repo: repo, + final worktree = repo.createWorktree( name: worktreeName, path: worktreeDir.path, ); - expect(Worktree.list(repo), [worktreeName]); + expect(repo.worktrees, [worktreeName]); worktreeDir.deleteSync(recursive: true); worktree.prune(); - expect(Worktree.list(repo), []); + expect(repo.worktrees, []); worktree.free(); });