diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index f7001fa..11b4b57 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -16,10 +16,10 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v2 - - uses: subosito/flutter-action@v1 + - uses: actions/checkout@v3.0.1 + - uses: subosito/flutter-action@v2 with: - channel: stable + channel: master - name: Install dependencies run: flutter pub get @@ -44,10 +44,10 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@v2 - - uses: subosito/flutter-action@v1 + - uses: actions/checkout@v3.0.1 + - uses: subosito/flutter-action@v2 with: - channel: stable + channel: master - name: Install dependencies run: flutter pub get diff --git a/README.md b/README.md index f2f2fa5..4c9d094 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,6 @@ dart run libgit2dart:setup libgit2dart provides you ability to manage Git repository. You can read and write objects (commit, tag, tree and blob), walk a tree, access the staging area, manage config and lots more. -**Important**: Most of the instantiated objects require to call `free()` method on them, when they are no longer needed, in order to release allocated memory and prevent memory leak. - Let's look at some of the classes and methods. ### Repository @@ -79,13 +77,17 @@ Let's look at some of the classes and methods. You can instantiate a `Repository` class with a path to open an existing repository: ```dart -final repo = Repository.open('path/to/repository'); +final repo = Repository.open('path/to/repository'); // => Repository +// Release memory allocated for Repository object when it's no longer needed +repo.free(); ``` You can create new repository with provided path and optional `bare` argument if you want it to be bare: ```dart -final repo = Repository.init(path: 'path/to/folder', bare: true); +final repo = Repository.init(path: 'path/to/folder', bare: true); // => Repository +// Release memory allocated for Repository object when it's no longer needed +repo.free(); ``` You can clone the existing repository at provided url into local path: @@ -94,7 +96,9 @@ You can clone the existing repository at provided url into local path: final repo = Repository.clone( url: 'https://some.url/', localPath: 'path/to/clone/into', -); +); // => Repository +// Release memory allocated for Repository object when it's no longer needed +repo.free(); ``` Also you can discover the path to the '.git' directory of repository if you provide a path to subdirectory: @@ -126,15 +130,11 @@ final ref = repo.head; // => Reference ref.name; // => 'refs/heads/master' ref.target; // => Oid ref.target.sha; // => '821ed6e80627b8769d170a293862f9fc60825226' -// Release memory allocated for Reference object when it's no longer needed -ref.free(); // Looking up object with oid final oid = repo['821ed6e80627b8769d170a293862f9fc60825226']; // => Oid final commit = Commit.lookup(repo: repo, oid: oid); // => Commit commit.message; // => 'initial commit' -// Release memory allocated for Commit object when it's no longer needed -commit.free(); // Release memory allocated for Repository object when it's no longer needed repo.free(); @@ -161,8 +161,7 @@ Commit.create( parents: [], // empty list for initial commit, 1 parent for regular and 2+ for merge commits ); // => Oid -tree.free(); -index.free(); +// Release memory allocated for Repository object when it's no longer needed repo.free(); ``` @@ -195,9 +194,6 @@ commit.message; // => 'initial commit\n' commit.time; // => 1635869993 (seconds since epoch) commit.author; // => Signature commit.tree; // => Tree - -// Release memory allocated for Commit object when it's no longer needed -commit.free(); ``` ### Tree and TreeEntry @@ -223,9 +219,6 @@ final entry = tree['file.txt']; // => TreeEntry entry.oid; // => Oid entry.name // => 'file.txt' entry.filemode // => GitFilemode.blob - -// Release memory allocated for Tree object when it's no longer needed -tree.free(); ``` You can also write trees with TreeBuilder: @@ -239,10 +232,6 @@ builder.add( ); final treeOid = builder.write(); // => Oid -// Release memory allocated for TreeBuilder object and all the entries when -// they are no longer needed -builder.free(); - // Perform commit using that tree in arguments ... ``` @@ -278,9 +267,6 @@ repo.tags; // => ['v0.1', 'v0.2'] tag.oid; // => Oid tag.name; // => 'v0.1' - -// Release memory allocated for Tag object when it's no longer needed -tag.free(); ``` ### Blob @@ -297,9 +283,6 @@ final blob = Blob.lookup(repo: repo, oid: repo['e69de29']); // => Blob blob.oid; // => Oid blob.content; // => 'content of the file' blob.size; // => 19 - -// Release memory allocated for Blob object when it's no longer needed -blob.free(); ``` --- @@ -326,9 +309,6 @@ walker.hide(repo['c68ff54']); // Perform traversal final commits = walker.walk(); // => [Commit, Commit, ...] - -// Release memory allocated for Walker object when it's no longer needed -walker.free(); ``` --- @@ -363,9 +343,6 @@ index.add('new.txt'); // Unstage entry from index index.remove('new.txt'); - -// Release memory allocated for Index object when it's no longer needed -index.free(); ``` --- @@ -405,12 +382,6 @@ final entry = reflog.first; // RefLogEntry entry.message; // => 'commit (initial): init' entry.committer; // => Signature - -// Release memory allocated for RefLog object when it's no longer needed -reflog.free(); - -// Release memory allocated for Reference object when it's no longer needed -ref.free(); ``` --- @@ -434,16 +405,13 @@ branch.isHead; // => true branch.name; // => 'master' // Create branch -final branch = Branch.create(repo: repo, name: 'feature', target: commit); // => Branch +Branch.create(repo: repo, name: 'feature', target: commit); // => Branch // Rename branch Branch.rename(repo: repo, oldName: 'feature', newName: 'feature2'); // Delete branch Branch.delete(repo: repo, name: 'feature2'); - -// Release memory allocated for Branch object when it's no longer needed -branch.free(); ``` --- @@ -473,9 +441,6 @@ final diff = Diff.indexToIndex(repo: repo, oldIndex: repo.index, newIndex: index // Read the contents of a git patch file final diff = Diff.parse(patch.text); // => Diff - -// Release memory allocated for Diff object when it's no longer needed -diff.free(); ``` Some methods for inspecting Diff object: @@ -492,8 +457,6 @@ final stats = diff.stats; // => DiffStats stats.insertions; // => 69 stats.deletions; // => 420 stats.filesChanged; // => 1 -// Release memory allocated for DiffStats object when it's no longer needed -stats.free(); // Get the list of DiffDelta's containing file pairs with and old and new revisions final deltas = diff.deltas; // => [DiffDelta, DiffDelta, ...] @@ -519,9 +482,6 @@ final patch = Patch.fromBlobs( // Patch from entry in the diff list at provided index position final patch = Patch.fromDiff(diff: diff, index: 0); // => Patch - -// Release memory allocated for Patch object when it's no longer needed -patch.free(); ``` Some methods for inspecting Patch object: @@ -535,7 +495,6 @@ patch.size(); // => 1337 // Get the list of hunks in a patch patch.hunks; // => [DiffHunk, DiffHunk, ...] - ``` --- @@ -559,9 +518,6 @@ config['user.name'] = 'Another Name'; // Delete variable from the config config.delete('user.name'); - -// Release memory allocated for Config object when it's no longer needed -config.free(); ``` --- @@ -620,7 +576,7 @@ Merge.cherryPick(repo: repo, commit: commit); ```dart // Get the list of all stashed states (first being the most recent) -repo.stashes; +repo.stashes; // => [Stash, Stash, ...] // Save local modifications to a new stash Stash.create(repo: repo, stasher: signature, message: 'WIP'); // => Oid @@ -648,7 +604,7 @@ Stash.pop(repo: repo); repo.worktrees; // => ['worktree1', 'worktree2']; // Lookup existing worktree -final worktree = Worktree.lookup(repo: repo, name: 'worktree1'); // => Worktree +Worktree.lookup(repo: repo, name: 'worktree1'); // => Worktree // Create new worktree final worktree = Worktree.create( @@ -682,18 +638,14 @@ Some API methods for submodule management: repo.submodules; // => ['Submodule1', 'Submodule2']; // Lookup submodule -final submodule = Submodule.lookup(repo: repo, name: 'Submodule'); // => Submodule +Submodule.lookup(repo: repo, name: 'Submodule'); // => Submodule // Init and update Submodule.init(repo: repo, name: 'Submodule'); Submodule.update(repo: repo, name: 'Submodule'); // Add submodule -final submodule = Submodule.add( - repo: repo, - url: 'https://some.url', - path: 'submodule', -); // => Submodule +Submodule.add(repo: repo, url: 'https://some.url', path: 'submodule'); // => Submodule ``` Some methods for inspecting Submodule object: diff --git a/analysis_options.yaml b/analysis_options.yaml index ee80c33..f9d5ecc 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,8 +4,6 @@ analyzer: language: strict-casts: true strict-raw-types: true - strong-mode: - implicit-casts: false exclude: - lib/src/bindings/libgit2_bindings.dart @@ -35,15 +33,12 @@ linter: - directives_ordering - eol_at_end_of_file - join_return_with_assignment - - library_private_types_in_public_api - lines_longer_than_80_chars - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - no_default_cases - noop_primitive_operations - - null_check_on_nullable_type_parameter - omit_local_variable_types - - one_member_abstracts - parameter_assignments - prefer_asserts_in_initializer_lists - prefer_asserts_with_message @@ -70,3 +65,4 @@ linter: - use_raw_strings - use_setters_to_change_properties - use_string_buffers + - use_super_parameters diff --git a/lib/src/annotated.dart b/lib/src/annotated.dart index b5958f9..68c3817 100644 --- a/lib/src/annotated.dart +++ b/lib/src/annotated.dart @@ -14,20 +14,17 @@ class AnnotatedCommit { /// It is preferable to use [AnnotatedCommit.fromReference] instead of this /// one, for commit to contain more information about how it was looked up. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.lookup({required Repository repo, required Oid oid}) { _annotatedCommitPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } /// Creates an annotated commit from the given [reference]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.fromReference({ required Repository repo, @@ -37,6 +34,7 @@ class AnnotatedCommit { repoPointer: repo.pointer, referencePointer: reference.pointer, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } /// Creates an annotated commit from a revision string. @@ -44,8 +42,6 @@ class AnnotatedCommit { /// See `man gitrevisions`, or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions /// for information on the syntax accepted. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.fromRevSpec({ required Repository repo, @@ -55,6 +51,7 @@ class AnnotatedCommit { repoPointer: repo.pointer, revspec: spec, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } /// Creates an annotated commit from the given fetch head data. @@ -67,8 +64,6 @@ class AnnotatedCommit { /// /// [oid] is the commit object id of the remote branch. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.fromFetchHead({ required Repository repo, @@ -82,6 +77,7 @@ class AnnotatedCommit { remoteUrl: remoteUrl, oid: oid.pointer, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } late final Pointer _annotatedCommitPointer; @@ -98,5 +94,14 @@ class AnnotatedCommit { String get refName => bindings.refName(_annotatedCommitPointer); /// Releases memory allocated for commit object. - void free() => bindings.free(_annotatedCommitPointer); + void free() { + bindings.free(_annotatedCommitPointer); + _finalizer.detach(this); + } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/bindings/annotated.dart b/lib/src/bindings/annotated.dart index 30bc0d3..be021cb 100644 --- a/lib/src/bindings/annotated.dart +++ b/lib/src/bindings/annotated.dart @@ -27,11 +27,14 @@ Pointer lookup({ oidPointer, ); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -49,11 +52,14 @@ Pointer fromRef({ referencePointer, ); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -75,13 +81,15 @@ Pointer fromRevSpec({ revspecC, ); + final result = out.value; + calloc.free(revspecC); + calloc.free(out); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -105,14 +113,16 @@ Pointer fromFetchHead({ oid, ); + final result = out.value; + + calloc.free(out); calloc.free(branchNameC); calloc.free(remoteUrlC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/attr.dart b/lib/src/bindings/attr.dart index 8bdd29e..363c304 100644 --- a/lib/src/bindings/attr.dart +++ b/lib/src/bindings/attr.dart @@ -19,27 +19,25 @@ Object? getAttribute({ final nameC = name.toNativeUtf8().cast(); libgit2.git_attr_get(out, repoPointer, flags, pathC, nameC); + final result = out.value; + + calloc.free(out); calloc.free(pathC); calloc.free(nameC); - final attributeValue = libgit2.git_attr_value(out.value); + final attributeValue = libgit2.git_attr_value(result); if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_UNSPECIFIED) { - calloc.free(out); return null; } if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_TRUE) { - calloc.free(out); return true; } if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_FALSE) { - calloc.free(out); return false; } if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_STRING) { - final result = out.value.cast().toDartString(); - calloc.free(out); - return result; + return result.cast().toDartString(); } return null; } diff --git a/lib/src/bindings/blame.dart b/lib/src/bindings/blame.dart index 2600e40..7bf9941 100644 --- a/lib/src/bindings/blame.dart +++ b/lib/src/bindings/blame.dart @@ -47,14 +47,16 @@ Pointer file({ final error = libgit2.git_blame_file(out, repoPointer, pathC, options); + final result = out.value; + + calloc.free(out); calloc.free(pathC); calloc.free(options); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -80,13 +82,15 @@ Pointer buffer({ buffer.length, ); + final result = out.value; + + calloc.free(out); calloc.free(bufferC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/blob.dart b/lib/src/bindings/blob.dart index b69edc5..dd3dad8 100644 --- a/lib/src/bindings/blob.dart +++ b/lib/src/bindings/blob.dart @@ -16,11 +16,14 @@ Pointer lookup({ final out = calloc>(); final error = libgit2.git_blob_lookup(out, repoPointer, oidPointer); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -128,7 +131,11 @@ Pointer createFromDisk({ Pointer duplicate(Pointer source) { final out = calloc>(); libgit2.git_blob_dup(out, source); - return out.value; + final result = out.value; + + calloc.free(out); + + return result; } /// Get a buffer with the filtered content of a blob. @@ -156,15 +163,19 @@ String filterContent({ final error = libgit2.git_blob_filter(out, blobPointer, asPathC, opts); + late final String result; + if (out.ref.ptr != nullptr) { + result = out.ref.ptr.cast().toDartString(length: out.ref.size); + } + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(asPathC); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } diff --git a/lib/src/bindings/branch.dart b/lib/src/bindings/branch.dart index a634121..72ecddd 100644 --- a/lib/src/bindings/branch.dart +++ b/lib/src/bindings/branch.dart @@ -35,13 +35,15 @@ List> list({ error = libgit2.git_branch_next(reference, refType, iterator.value); if (error == 0) { result.add(reference.value); - calloc.free(refType); } else { break; } + calloc.free(reference); + calloc.free(refType); } libgit2.git_branch_iterator_free(iterator.value); + calloc.free(iterator); return result; } @@ -65,13 +67,15 @@ Pointer lookup({ branchType, ); + final result = out.value; + + calloc.free(out); calloc.free(branchNameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -103,13 +107,15 @@ Pointer create({ forceC, ); + final result = out.value; + + calloc.free(out); calloc.free(branchNameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -192,13 +198,14 @@ String name(Pointer ref) { final out = calloc>(); final error = libgit2.git_branch_name(out, ref); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.value.cast().toDartString(); - calloc.free(out); - return result; + return result.cast().toDartString(); } } @@ -218,14 +225,15 @@ String remoteName({ final branchNameC = branchName.toNativeUtf8().cast(); final error = libgit2.git_branch_remote_name(out, repoPointer, branchNameC); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(branchNameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -240,11 +248,14 @@ Pointer getUpstream(Pointer branch) { final out = calloc>(); final error = libgit2.git_branch_upstream(out, branch); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -286,14 +297,15 @@ String upstreamName({ final branchNameC = branchName.toNativeUtf8().cast(); final error = libgit2.git_branch_upstream_name(out, repoPointer, branchNameC); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(branchNameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -316,14 +328,15 @@ String upstreamRemote({ branchNameC, ); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(branchNameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -346,12 +359,15 @@ String upstreamMerge({ branchNameC, ); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + calloc.free(branchNameC); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } diff --git a/lib/src/bindings/commit.dart b/lib/src/bindings/commit.dart index d1aa89f..0059575 100644 --- a/lib/src/bindings/commit.dart +++ b/lib/src/bindings/commit.dart @@ -17,11 +17,14 @@ Pointer lookup({ final out = calloc>(); final error = libgit2.git_commit_lookup(out, repoPointer, oidPointer); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -124,17 +127,18 @@ String createBuffer({ parentsC, ); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(updateRefC); calloc.free(messageEncodingC); calloc.free(messageC); calloc.free(parentsC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -196,7 +200,12 @@ Pointer amend({ Pointer duplicate(Pointer source) { final out = calloc>(); libgit2.git_commit_dup(out, source); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Get the encoding for the message of a commit, as a string representing a @@ -254,14 +263,15 @@ String headerField({ final fieldC = field.toNativeUtf8().cast(); final error = libgit2.git_commit_header_field(out, commitPointer, fieldC); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(fieldC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -292,11 +302,14 @@ Pointer parent({ final out = calloc>(); final error = libgit2.git_commit_parent(out, commitPointer, position); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -315,11 +328,14 @@ Pointer nthGenAncestor({ final out = calloc>(); final error = libgit2.git_commit_nth_gen_ancestor(out, commitPointer, n); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -350,7 +366,12 @@ Pointer treeOid(Pointer commit) { Pointer tree(Pointer commit) { final out = calloc>(); libgit2.git_commit_tree(out, commit); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Reverts the given commit, producing changes in the index and working @@ -393,13 +414,15 @@ Pointer revertCommit({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/config.dart b/lib/src/bindings/config.dart index e74e304..d3919b5 100644 --- a/lib/src/bindings/config.dart +++ b/lib/src/bindings/config.dart @@ -29,11 +29,14 @@ Pointer openDefault() { final out = calloc>(); final error = libgit2.git_config_open_default(out); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -54,12 +57,14 @@ String findGlobal() { final out = calloc(); final error = libgit2.git_config_find_global(out); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -73,12 +78,14 @@ String findSystem() { final out = calloc(); final error = libgit2.git_config_find_system(out); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -93,12 +100,14 @@ String findXdg() { final out = calloc(); final error = libgit2.git_config_find_xdg(out); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -111,7 +120,12 @@ String findXdg() { Pointer snapshot(Pointer config) { final out = calloc>(); libgit2.git_config_snapshot(out, config); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Get the config entry of a config variable. @@ -122,16 +136,18 @@ Pointer getEntry({ required String variable, }) { final out = calloc>(); - final name = variable.toNativeUtf8().cast(); - final error = libgit2.git_config_get_entry(out, configPointer, name); + final nameC = variable.toNativeUtf8().cast(); + final error = libgit2.git_config_get_entry(out, configPointer, nameC); - calloc.free(name); + final result = out.value; + + calloc.free(out); + calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -142,10 +158,10 @@ void setBool({ required String variable, required bool value, }) { - final name = variable.toNativeUtf8().cast(); + final nameC = variable.toNativeUtf8().cast(); final valueC = value ? 1 : 0; - libgit2.git_config_set_bool(configPointer, name, valueC); - calloc.free(name); + libgit2.git_config_set_bool(configPointer, nameC, valueC); + calloc.free(nameC); } /// Set the value of an integer config variable in the config file with the @@ -155,9 +171,9 @@ void setInt({ required String variable, required int value, }) { - final name = variable.toNativeUtf8().cast(); - libgit2.git_config_set_int64(configPointer, name, value); - calloc.free(name); + final nameC = variable.toNativeUtf8().cast(); + libgit2.git_config_set_int64(configPointer, nameC, value); + calloc.free(nameC); } /// Set the value of a string config variable in the config file with the @@ -167,10 +183,10 @@ void setString({ required String variable, required String value, }) { - final name = variable.toNativeUtf8().cast(); + final nameC = variable.toNativeUtf8().cast(); final valueC = value.toNativeUtf8().cast(); - libgit2.git_config_set_string(configPointer, name, valueC); - calloc.free(name); + libgit2.git_config_set_string(configPointer, nameC, valueC); + calloc.free(nameC); calloc.free(valueC); } @@ -178,7 +194,12 @@ void setString({ Pointer iterator(Pointer cfg) { final out = calloc>(); libgit2.git_config_iterator_new(out, cfg); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Delete a config variable from the config file with the highest level @@ -189,10 +210,10 @@ void delete({ required Pointer configPointer, required String variable, }) { - final name = variable.toNativeUtf8().cast(); - final error = libgit2.git_config_delete_entry(configPointer, name); + final nameC = variable.toNativeUtf8().cast(); + final error = libgit2.git_config_delete_entry(configPointer, nameC); - calloc.free(name); + calloc.free(nameC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); @@ -208,7 +229,7 @@ List multivarValues({ required String variable, String? regexp, }) { - final name = variable.toNativeUtf8().cast(); + final nameC = variable.toNativeUtf8().cast(); final regexpC = regexp?.toNativeUtf8().cast() ?? nullptr; final iterator = calloc>(); final entry = calloc>(); @@ -216,7 +237,7 @@ List multivarValues({ libgit2.git_config_multivar_iterator_new( iterator, configPointer, - name, + nameC, regexpC, ); @@ -232,8 +253,9 @@ List multivarValues({ } } - calloc.free(name); + calloc.free(nameC); calloc.free(regexpC); + libgit2.git_config_iterator_free(iterator.value); calloc.free(iterator); calloc.free(entry); @@ -250,13 +272,13 @@ void setMultivar({ required String regexp, required String value, }) { - final name = variable.toNativeUtf8().cast(); + final nameC = variable.toNativeUtf8().cast(); final regexpC = regexp.toNativeUtf8().cast(); final valueC = value.toNativeUtf8().cast(); - libgit2.git_config_set_multivar(configPointer, name, regexpC, valueC); + libgit2.git_config_set_multivar(configPointer, nameC, regexpC, valueC); - calloc.free(name); + calloc.free(nameC); calloc.free(regexpC); calloc.free(valueC); } @@ -270,14 +292,22 @@ void deleteMultivar({ required String variable, required String regexp, }) { - final name = variable.toNativeUtf8().cast(); + final nameC = variable.toNativeUtf8().cast(); final regexpC = regexp.toNativeUtf8().cast(); - libgit2.git_config_delete_multivar(configPointer, name, regexpC); + libgit2.git_config_delete_multivar(configPointer, nameC, regexpC); - calloc.free(name); + calloc.free(nameC); calloc.free(regexpC); } /// Free the configuration and its associated memory and files. void free(Pointer cfg) => libgit2.git_config_free(cfg); + +/// Free a config entry. +void freeEntry(Pointer entry) => + libgit2.git_config_entry_free(entry); + +/// Free a config iterator. +void freeIterator(Pointer iter) => + libgit2.git_config_iterator_free(iter); diff --git a/lib/src/bindings/credentials.dart b/lib/src/bindings/credentials.dart index 6ead9ed..fffe30d 100644 --- a/lib/src/bindings/credentials.dart +++ b/lib/src/bindings/credentials.dart @@ -15,10 +15,13 @@ Pointer userPass({ libgit2.git_credential_userpass_plaintext_new(out, usernameC, passwordC); + final result = out.value; + + calloc.free(out); calloc.free(usernameC); calloc.free(passwordC); - return out.value; + return result; } /// Create a new passphrase-protected ssh key credential object. @@ -42,12 +45,15 @@ Pointer sshKey({ passPhraseC, ); + final result = out.value; + + calloc.free(out); calloc.free(usernameC); calloc.free(publicKeyC); calloc.free(privateKeyC); calloc.free(passPhraseC); - return out.value; + return result; } /// Create a new ssh key credential object used for querying an ssh-agent. @@ -57,9 +63,12 @@ Pointer sshKeyFromAgent(String username) { libgit2.git_credential_ssh_key_from_agent(out, usernameC); + final result = out.value; + + calloc.free(out); calloc.free(usernameC); - return out.value; + return result; } /// Create a new ssh key credential object reading the keys from memory. @@ -83,10 +92,13 @@ Pointer sshKeyFromMemory({ passPhraseC, ); + final result = out.value; + + calloc.free(out); calloc.free(usernameC); calloc.free(publicKeyC); calloc.free(privateKeyC); calloc.free(passPhraseC); - return out.value; + return result; } diff --git a/lib/src/bindings/describe.dart b/lib/src/bindings/describe.dart index d8237cc..5c2725f 100644 --- a/lib/src/bindings/describe.dart +++ b/lib/src/bindings/describe.dart @@ -32,13 +32,15 @@ Pointer commit({ final error = libgit2.git_describe_commit(out, commitPointer.cast(), opts); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -68,13 +70,15 @@ Pointer workdir({ final error = libgit2.git_describe_workdir(out, repo, opts); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -106,8 +110,9 @@ String format({ final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(opts); + libgit2.git_buf_dispose(out); calloc.free(out); + calloc.free(opts); return result; } diff --git a/lib/src/bindings/diff.dart b/lib/src/bindings/diff.dart index 316baf8..8cdd7c6 100644 --- a/lib/src/bindings/diff.dart +++ b/lib/src/bindings/diff.dart @@ -31,13 +31,15 @@ Pointer indexToIndex({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -58,9 +60,12 @@ Pointer indexToWorkdir({ libgit2.git_diff_index_to_workdir(out, repoPointer, indexPointer, opts); + final result = out.value; + + calloc.free(out); calloc.free(opts); - return out.value; + return result; } /// Create a diff between a tree and repository index. @@ -87,9 +92,12 @@ Pointer treeToIndex({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); - return out.value; + return result; } /// Create a diff between a tree and the working directory. @@ -116,13 +124,15 @@ Pointer treeToWorkdir({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -155,13 +165,15 @@ Pointer treeToWorkdirWithIndex({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -191,13 +203,15 @@ Pointer treeToTree({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -235,9 +249,12 @@ Pointer parse(String content) { final contentC = content.toNativeUtf8().cast(); libgit2.git_diff_from_buffer(out, contentC, content.length); + final result = out.value; + + calloc.free(out); calloc.free(contentC); - return out.value; + return result; } /// Transform a diff marking file renames, copies, etc. @@ -324,11 +341,14 @@ Pointer stats(Pointer diff) { final out = calloc>(); final error = libgit2.git_diff_get_stats(out, diff); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -355,12 +375,14 @@ String statsPrint({ final out = calloc(); final error = libgit2.git_diff_stats_to_buf(out, statsPointer, format, width); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -369,10 +391,14 @@ String statsPrint({ String addToBuf(Pointer diff) { final out = calloc(); libgit2.git_diff_to_buf(out, diff, git_diff_format_t.GIT_DIFF_FORMAT_PATCH); + final result = out.ref.ptr == nullptr ? '' : out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); calloc.free(out); + return result; } @@ -462,14 +488,16 @@ Pointer applyToTree({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(payload); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/index.dart b/lib/src/bindings/index.dart index faa77ce..6a342eb 100644 --- a/lib/src/bindings/index.dart +++ b/lib/src/bindings/index.dart @@ -14,7 +14,12 @@ import 'package:libgit2dart/src/util.dart'; Pointer newInMemory() { final out = calloc>(); libgit2.git_index_new(out); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Read index capabilities flags. @@ -266,6 +271,8 @@ void addFromBuffer({ buffer.length, ); + calloc.free(bufferC); + if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } @@ -452,12 +459,12 @@ List>> conflictList( 'our': ourOut.value, 'their': theirOut.value, }); - calloc.free(ancestorOut); - calloc.free(ourOut); - calloc.free(theirOut); } else { break; } + calloc.free(ancestorOut); + calloc.free(ourOut); + calloc.free(theirOut); } libgit2.git_index_conflict_iterator_free(iterator.value); diff --git a/lib/src/bindings/mailmap.dart b/lib/src/bindings/mailmap.dart index 8c8f361..74704c1 100644 --- a/lib/src/bindings/mailmap.dart +++ b/lib/src/bindings/mailmap.dart @@ -13,7 +13,11 @@ Pointer init() { final out = calloc>(); libgit2.git_mailmap_new(out); - return out.value; + final result = out.value; + + calloc.free(out); + + return result; } /// Create a new mailmap instance containing a single mailmap file. @@ -23,9 +27,12 @@ Pointer fromBuffer(String buffer) { libgit2.git_mailmap_from_buffer(out, bufferC, buffer.length); + final result = out.value; + + calloc.free(out); calloc.free(bufferC); - return out.value; + return result; } /// Create a new mailmap instance from a repository, loading mailmap files based @@ -43,11 +50,14 @@ Pointer fromRepository(Pointer repo) { final out = calloc>(); final error = libgit2.git_mailmap_from_repository(out, repo); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -87,7 +97,11 @@ Pointer resolveSignature({ final out = calloc>(); libgit2.git_mailmap_resolve_signature(out, mailmapPointer, signaturePointer); - return out.value; + final result = out.value; + + calloc.free(out); + + return result; } /// Add a single entry to the given mailmap object. If the entry already exists, diff --git a/lib/src/bindings/merge.dart b/lib/src/bindings/merge.dart index 45d98e9..1a92b00 100644 --- a/lib/src/bindings/merge.dart +++ b/lib/src/bindings/merge.dart @@ -234,12 +234,16 @@ String mergeFileFromIndex({ nullptr, ); + late final String result; + if (out.ref.ptr != nullptr) { + result = out.ref.ptr.cast().toDartString(length: out.ref.len); + } + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.len); - calloc.free(out); return result; } } @@ -275,13 +279,15 @@ Pointer mergeCommits({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -318,13 +324,15 @@ Pointer mergeTrees({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/note.dart b/lib/src/bindings/note.dart index da26a8a..5243957 100644 --- a/lib/src/bindings/note.dart +++ b/lib/src/bindings/note.dart @@ -30,15 +30,21 @@ List> list(Pointer repo) { if (nextError >= 0) { final out = calloc>(); libgit2.git_note_read(out, repo, notesRef, annotatedOid); - calloc.free(noteOid); - result.add({'note': out.value, 'annotatedOid': annotatedOid}); + + final note = out.value; + + calloc.free(out); + + result.add({'note': note, 'annotatedOid': annotatedOid}); } else { break; } + calloc.free(noteOid); } calloc.free(notesRef); libgit2.git_note_iterator_free(iterator.value); + calloc.free(iterator); return result; } @@ -57,13 +63,15 @@ Pointer lookup({ final notesRefC = notesRef.toNativeUtf8().cast(); final error = libgit2.git_note_read(out, repoPointer, notesRefC, oidPointer); + final result = out.value; + + calloc.free(out); calloc.free(notesRefC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/object.dart b/lib/src/bindings/object.dart index 3e691f1..b3dd558 100644 --- a/lib/src/bindings/object.dart +++ b/lib/src/bindings/object.dart @@ -26,11 +26,14 @@ Pointer lookup({ final out = calloc>(); final error = libgit2.git_object_lookup(out, repoPointer, oidPointer, type); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/odb.dart b/lib/src/bindings/odb.dart index 1948b41..20e3e86 100644 --- a/lib/src/bindings/odb.dart +++ b/lib/src/bindings/odb.dart @@ -14,7 +14,12 @@ import 'package:libgit2dart/src/util.dart'; Pointer create() { final out = calloc>(); libgit2.git_odb_new(out); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Add an on-disk alternate to an existing Object DB. @@ -78,8 +83,7 @@ int _forEachCb( Pointer oid, Pointer payload, ) { - final _oid = oid_bindings.copy(oid); - _objects.add(Oid(_oid)); + _objects.add(Oid(oid_bindings.copy(oid))); return 0; } @@ -121,11 +125,14 @@ Pointer read({ final out = calloc>(); final error = libgit2.git_odb_read(out, odbPointer, oidPointer); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -186,14 +193,15 @@ Pointer write({ throw LibGit2Error(libgit2.git_error_last()); } - final buffer = data.toNativeUtf8().cast(); - libgit2.git_odb_stream_write(stream.value, buffer, data.length); + final bufferC = data.toNativeUtf8().cast(); + libgit2.git_odb_stream_write(stream.value, bufferC, data.length); final out = calloc(); libgit2.git_odb_stream_finalize_write(out, stream.value); - calloc.free(buffer); + calloc.free(bufferC); libgit2.git_odb_stream_free(stream.value); + calloc.free(stream); return out; } diff --git a/lib/src/bindings/oid.dart b/lib/src/bindings/oid.dart index 20523b7..c95b49d 100644 --- a/lib/src/bindings/oid.dart +++ b/lib/src/bindings/oid.dart @@ -7,10 +7,10 @@ import 'package:libgit2dart/src/util.dart'; /// Parse N characters of a hex formatted object id into a git_oid. Pointer fromStrN(String hex) { final out = calloc(); - final str = hex.toNativeUtf8().cast(); - libgit2.git_oid_fromstrn(out, str, hex.length); + final hexC = hex.toNativeUtf8().cast(); + libgit2.git_oid_fromstrn(out, hexC, hex.length); - calloc.free(str); + calloc.free(hexC); return out; } @@ -18,10 +18,10 @@ Pointer fromStrN(String hex) { /// Parse a hex formatted object id into a git_oid. Pointer fromSHA(String hex) { final out = calloc(); - final str = hex.toNativeUtf8().cast(); - libgit2.git_oid_fromstr(out, str); + final hexC = hex.toNativeUtf8().cast(); + libgit2.git_oid_fromstr(out, hexC); - calloc.free(str); + calloc.free(hexC); return out; } diff --git a/lib/src/bindings/packbuilder.dart b/lib/src/bindings/packbuilder.dart index 12a4de6..219e32c 100644 --- a/lib/src/bindings/packbuilder.dart +++ b/lib/src/bindings/packbuilder.dart @@ -12,11 +12,14 @@ Pointer init(Pointer repo) { final out = calloc>(); final error = libgit2.git_packbuilder_new(out, repo); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/patch.dart b/lib/src/bindings/patch.dart index f24724c..4037672 100644 --- a/lib/src/bindings/patch.dart +++ b/lib/src/bindings/patch.dart @@ -42,14 +42,17 @@ Pointer fromBuffers({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(oldAsPathC); calloc.free(newAsPathC); calloc.free(opts); - // We are not freeing buffers because patch object does not have reference to - // underlying buffers. So if the buffer is freed the patch text becomes - // corrupted. + // We are not freeing buffers `oldBufferC` and `newBufferC` because patch + // object does not have reference to underlying buffers. So if the buffer is + // freed the patch text becomes corrupted. - return out.value; + return result; } /// Directly generate a patch from the difference between two blobs. @@ -83,11 +86,14 @@ Pointer fromBlobs({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(oldAsPathC); calloc.free(newAsPathC); calloc.free(opts); - return out.value; + return result; } /// Directly generate a patch from the difference between a blob and a buffer. @@ -124,11 +130,17 @@ Pointer fromBlobAndBuffer({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(oldAsPathC); calloc.free(bufferAsPathC); calloc.free(opts); + // We are not freeing buffer `bufferC` because patch object does not have + // reference to underlying buffers. So if the buffer is freed the patch text + // becomes corrupted. - return out.value; + return result; } /// Return a patch for an entry in the diff list. @@ -145,11 +157,14 @@ Pointer fromDiff({ final out = calloc>(); final error = libgit2.git_patch_from_diff(out, diffPointer, index); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -172,9 +187,13 @@ Map hunk({ final linesInHunk = calloc(); libgit2.git_patch_get_hunk(out, linesInHunk.cast(), patchPointer, hunkIndex); + final hunk = out.value; final linesN = linesInHunk.value; + + calloc.free(out); calloc.free(linesInHunk); - return {'hunk': out.value, 'linesN': linesN}; + + return {'hunk': hunk, 'linesN': linesN}; } /// Get line counts of each type in a patch. @@ -211,7 +230,11 @@ Pointer lines({ final out = calloc>(); libgit2.git_patch_get_line_in_hunk(out, patchPointer, hunkIndex, lineOfHunk); - return out.value; + final result = out.value; + + calloc.free(out); + + return result; } /// Get the content of a patch as a single diff text. @@ -221,12 +244,14 @@ String text(Pointer patch) { final out = calloc(); final error = libgit2.git_patch_to_buf(out, patch); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } diff --git a/lib/src/bindings/rebase.dart b/lib/src/bindings/rebase.dart index 853459c..d0b734a 100644 --- a/lib/src/bindings/rebase.dart +++ b/lib/src/bindings/rebase.dart @@ -40,13 +40,15 @@ Pointer init({ opts, ); + final result = out.value; + + calloc.free(out); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -61,11 +63,14 @@ Pointer open(Pointer repo) { final error = libgit2.git_rebase_open(out, repo, opts); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -100,11 +105,14 @@ Pointer next(Pointer rebase) { final out = calloc>(); final error = libgit2.git_rebase_next(out, rebase); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/reference.dart b/lib/src/bindings/reference.dart index c911e45..7bd2589 100644 --- a/lib/src/bindings/reference.dart +++ b/lib/src/bindings/reference.dart @@ -33,11 +33,14 @@ Pointer resolve(Pointer ref) { final out = calloc>(); final error = libgit2.git_reference_resolve(out, ref); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -56,13 +59,15 @@ Pointer lookup({ final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_reference_lookup(out, repoPointer, nameC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -110,14 +115,16 @@ Pointer rename({ logMessageC, ); + final result = out.value; + + calloc.free(out); calloc.free(newNameC); calloc.free(logMessageC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -149,10 +156,10 @@ bool hasLog({ required Pointer repoPointer, required String name, }) { - final refname = name.toNativeUtf8().cast(); - final result = libgit2.git_reference_has_log(repoPointer, refname); + final nameC = name.toNativeUtf8().cast(); + final result = libgit2.git_reference_has_log(repoPointer, nameC); - calloc.free(refname); + calloc.free(nameC); return result == 1 || false; } @@ -244,14 +251,16 @@ Pointer createDirect({ logMessageC, ); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(logMessageC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -303,15 +312,17 @@ Pointer createSymbolic({ logMessageC, ); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(targetC); calloc.free(logMessageC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -348,13 +359,15 @@ Pointer setTarget({ logMessageC, ); + final result = out.value; + + calloc.free(out); calloc.free(logMessageC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -386,14 +399,16 @@ Pointer setTargetSymbolic({ logMessageC, ); + final result = out.value; + + calloc.free(out); calloc.free(targetC); calloc.free(logMessageC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -421,11 +436,14 @@ Pointer peel({ final out = calloc>(); final error = libgit2.git_reference_peel(out, refPointer, type); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -433,7 +451,12 @@ Pointer peel({ Pointer duplicate(Pointer source) { final out = calloc>(); libgit2.git_reference_dup(out, source); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Free the given reference. diff --git a/lib/src/bindings/reflog.dart b/lib/src/bindings/reflog.dart index 5a5977c..8765ac8 100644 --- a/lib/src/bindings/reflog.dart +++ b/lib/src/bindings/reflog.dart @@ -19,9 +19,12 @@ Pointer read({ final nameC = name.toNativeUtf8().cast(); libgit2.git_reflog_read(out, repoPointer, nameC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); - return out.value; + return result; } /// Write an existing in-memory reflog object back to disk using an atomic file diff --git a/lib/src/bindings/refspec.dart b/lib/src/bindings/refspec.dart index 1656f7f..a7a484a 100644 --- a/lib/src/bindings/refspec.dart +++ b/lib/src/bindings/refspec.dart @@ -66,14 +66,15 @@ String transform({ final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_refspec_transform(out, refspecPointer, nameC); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -90,14 +91,15 @@ String rTransform({ final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_refspec_rtransform(out, refspecPointer, nameC); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } diff --git a/lib/src/bindings/remote.dart b/lib/src/bindings/remote.dart index adae26f..1d65cc4 100644 --- a/lib/src/bindings/remote.dart +++ b/lib/src/bindings/remote.dart @@ -9,8 +9,6 @@ import 'package:libgit2dart/src/oid.dart'; import 'package:libgit2dart/src/util.dart'; /// Get a list of the configured remotes for a repo. -/// -/// Throws a [LibGit2Error] if error occured. List list(Pointer repo) { final out = calloc(); libgit2.git_remote_list(out, repo); @@ -38,13 +36,15 @@ Pointer lookup({ final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_remote_lookup(out, repoPointer, nameC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -62,14 +62,16 @@ Pointer create({ final urlC = url.toNativeUtf8().cast(); final error = libgit2.git_remote_create(out, repoPointer, nameC, urlC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(urlC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -95,15 +97,17 @@ Pointer createWithFetchSpec({ fetchC, ); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(urlC); calloc.free(fetchC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/remote_callbacks.dart b/lib/src/bindings/remote_callbacks.dart index 030d849..47cfeb5 100644 --- a/lib/src/bindings/remote_callbacks.dart +++ b/lib/src/bindings/remote_callbacks.dart @@ -5,6 +5,7 @@ import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/credentials.dart' as credentials_bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:libgit2dart/src/bindings/remote.dart' as remote_bindings; import 'package:libgit2dart/src/util.dart'; class RemoteCallbacks { @@ -65,11 +66,9 @@ class RemoteCallbacks { return 0; } - /// A function matching the - /// `Remote Function(Repository repo, String name, String url)` signature to - /// override the remote creation and customization process during a clone - /// operation. - static Remote Function(Repository, String, String)? remoteFunction; + /// Values used to override the remote creation and customization process + /// during a clone operation. + static RemoteCallback? remoteCbData; /// A callback used to create the git remote, prior to its being used to /// perform the clone operation. @@ -80,19 +79,31 @@ class RemoteCallbacks { Pointer url, Pointer payload, ) { - remote[0] = remoteFunction!( - Repository(repo), - name.cast().toDartString(), - url.cast().toDartString(), - ).pointer; + late final Pointer remotePointer; + + if (remoteCbData!.fetch == null) { + remotePointer = remote_bindings.create( + repoPointer: repo, + name: remoteCbData!.name, + url: remoteCbData!.url, + ); + } else { + remotePointer = remote_bindings.createWithFetchSpec( + repoPointer: repo, + name: remoteCbData!.name, + url: remoteCbData!.url, + fetch: remoteCbData!.fetch!, + ); + } + + remote[0] = remotePointer; return 0; } - /// A function matching the `Repository Function(String path, bool bare)` - /// signature to override the repository creation and customization process + /// Values used to override the repository creation and customization process /// during a clone operation. - static Repository Function(String, bool)? repositoryFunction; + static RepositoryCallback? repositoryCbData; /// A callback used to create the new repository into which to clone. static int repositoryCb( @@ -101,11 +112,20 @@ class RemoteCallbacks { int bare, Pointer payload, ) { - repo[0] = repositoryFunction!( - path.cast().toDartString(), - bare == 1 || false, + final repoPointer = Repository.init( + path: repositoryCbData!.path, + bare: repositoryCbData!.bare, + flags: repositoryCbData!.flags, + mode: repositoryCbData!.mode, + workdirPath: repositoryCbData!.workdirPath, + description: repositoryCbData!.description, + templatePath: repositoryCbData!.templatePath, + initialHead: repositoryCbData!.initialHead, + originUrl: repositoryCbData!.originUrl, ).pointer; + repo[0] = repoPointer; + return 0; } @@ -236,8 +256,8 @@ class RemoteCallbacks { sidebandProgress = null; updateTips = null; pushUpdateReference = null; - remoteFunction = null; - repositoryFunction = null; + remoteCbData = null; + repositoryCbData = null; credentials = null; } } diff --git a/lib/src/bindings/repository.dart b/lib/src/bindings/repository.dart index 770f657..cd0bbe3 100644 --- a/lib/src/bindings/repository.dart +++ b/lib/src/bindings/repository.dart @@ -19,13 +19,15 @@ Pointer open(String path) { final pathC = path.toNativeUtf8().cast(); final error = libgit2.git_repository_open(out, pathC); + final result = out.value; + + calloc.free(out); calloc.free(pathC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -50,7 +52,10 @@ String discover({ calloc.free(ceilingDirsC); final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); calloc.free(out); + return result; } @@ -90,6 +95,9 @@ Pointer init({ final error = libgit2.git_repository_init_ext(out, pathC, opts); + final result = out.value; + + calloc.free(out); calloc.free(pathC); calloc.free(workdirPathC); calloc.free(descriptionC); @@ -99,10 +107,9 @@ Pointer init({ calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -113,8 +120,9 @@ Pointer clone({ required String url, required String localPath, required bool bare, - Remote Function(Repository, String, String)? remote, - Repository Function(String, bool)? repository, + RemoteCallback? remoteCallback, + // Repository Function(String, bool)? repository, + RepositoryCallback? repositoryCallback, String? checkoutBranch, required Callbacks callbacks, }) { @@ -138,14 +146,14 @@ Pointer clone({ const except = -1; git_remote_create_cb remoteCb = nullptr; - if (remote != null) { - RemoteCallbacks.remoteFunction = remote; + if (remoteCallback != null) { + RemoteCallbacks.remoteCbData = remoteCallback; remoteCb = Pointer.fromFunction(RemoteCallbacks.remoteCb, except); } git_repository_create_cb repositoryCb = nullptr; - if (repository != null) { - RemoteCallbacks.repositoryFunction = repository; + if (repositoryCallback != null) { + RemoteCallbacks.repositoryCbData = repositoryCallback; repositoryCb = Pointer.fromFunction(RemoteCallbacks.repositoryCb, except); } @@ -157,6 +165,9 @@ Pointer clone({ final error = libgit2.git_clone(out, urlC, localPathC, cloneOptions); + final result = out.value; + + calloc.free(out); calloc.free(urlC); calloc.free(localPathC); calloc.free(checkoutBranchC); @@ -165,10 +176,9 @@ Pointer clone({ RemoteCallbacks.reset(); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -211,9 +221,9 @@ void setNamespace({ required Pointer repoPointer, String? namespace, }) { - final nmspace = namespace?.toNativeUtf8().cast() ?? nullptr; - libgit2.git_repository_set_namespace(repoPointer, nmspace); - calloc.free(nmspace); + final namespaceC = namespace?.toNativeUtf8().cast() ?? nullptr; + libgit2.git_repository_set_namespace(repoPointer, namespaceC); + calloc.free(namespaceC); } /// Check if a repository is bare or not. @@ -247,11 +257,14 @@ Pointer head(Pointer repo) { final out = calloc>(); final error = libgit2.git_repository_head(out, repo); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -337,7 +350,12 @@ Map identity(Pointer repo) { Pointer config(Pointer repo) { final out = calloc>(); libgit2.git_repository_config(out, repo); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Get a snapshot of the repository's configuration. @@ -351,7 +369,12 @@ Pointer config(Pointer repo) { Pointer configSnapshot(Pointer repo) { final out = calloc>(); libgit2.git_repository_config_snapshot(out, repo); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Get the Index file for this repository. @@ -363,7 +386,12 @@ Pointer configSnapshot(Pointer repo) { Pointer index(Pointer repo) { final out = calloc>(); libgit2.git_repository_index(out, repo); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; } /// Determine if the repository was a shallow clone. @@ -392,12 +420,14 @@ String message(Pointer repo) { final out = calloc(); final error = libgit2.git_repository_message(out, repo); + final result = out.ref.ptr.cast().toDartString(length: out.ref.size); + + libgit2.git_buf_dispose(out); + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.ref.ptr.cast().toDartString(length: out.ref.size); - calloc.free(out); return result; } } @@ -419,11 +449,14 @@ Pointer odb(Pointer repo) { final out = calloc>(); final error = libgit2.git_repository_odb(out, repo); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -440,11 +473,14 @@ Pointer refdb(Pointer repo) { final out = calloc>(); final error = libgit2.git_repository_refdb(out, repo); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -516,15 +552,15 @@ void setWorkdir({ required String path, required bool updateGitlink, }) { - final workdir = path.toNativeUtf8().cast(); + final workdirC = path.toNativeUtf8().cast(); final updateGitlinkC = updateGitlink ? 1 : 0; final error = libgit2.git_repository_set_workdir( repoPointer, - workdir, + workdirC, updateGitlinkC, ); - calloc.free(workdir); + calloc.free(workdirC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); diff --git a/lib/src/bindings/revparse.dart b/lib/src/bindings/revparse.dart index 89c5e3f..044d581 100644 --- a/lib/src/bindings/revparse.dart +++ b/lib/src/bindings/revparse.dart @@ -46,13 +46,15 @@ Pointer revParseSingle({ final error = libgit2.git_revparse_single(out, repoPointer, specC); + final result = out.value; + + calloc.free(out); calloc.free(specC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -83,18 +85,19 @@ List revParseExt({ specC, ); + final result = []; + result.add(objectOut.value); + if (referenceOut.value != nullptr) { + result.add(referenceOut.value); + } + + calloc.free(objectOut); + calloc.free(referenceOut); calloc.free(specC); if (error < 0) { - calloc.free(objectOut); - calloc.free(referenceOut); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = []; - result.add(objectOut.value); - if (referenceOut.value != nullptr) { - result.add(referenceOut.value); - } return result; } } diff --git a/lib/src/bindings/revwalk.dart b/lib/src/bindings/revwalk.dart index a60be4c..c914553 100644 --- a/lib/src/bindings/revwalk.dart +++ b/lib/src/bindings/revwalk.dart @@ -23,11 +23,14 @@ Pointer create(Pointer repo) { final out = calloc>(); final error = libgit2.git_revwalk_new(out, repo); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -152,10 +155,10 @@ List> walk({ oidPointer: oid, ); result.add(commit); - calloc.free(oid); } else { break; } + calloc.free(oid); } return result; diff --git a/lib/src/bindings/signature.dart b/lib/src/bindings/signature.dart index f0678df..af7b365 100644 --- a/lib/src/bindings/signature.dart +++ b/lib/src/bindings/signature.dart @@ -22,14 +22,16 @@ Pointer create({ final emailC = email.toNativeUtf8().cast(); final error = libgit2.git_signature_new(out, nameC, emailC, time, offset); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(emailC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -42,14 +44,16 @@ Pointer now({required String name, required String email}) { final emailC = email.toNativeUtf8().cast(); final error = libgit2.git_signature_now(out, nameC, emailC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(emailC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -61,7 +65,24 @@ Pointer now({required String name, required String email}) { Pointer defaultSignature(Pointer repo) { final out = calloc>(); libgit2.git_signature_default(out, repo); - return out.value; + + final result = out.value; + + calloc.free(out); + + return result; +} + +/// Create a copy of an existing signature. +Pointer duplicate(Pointer sig) { + final out = calloc>(); + libgit2.git_signature_dup(out, sig); + + final result = out.value; + + calloc.free(out); + + return result; } /// Free an existing signature. diff --git a/lib/src/bindings/status.dart b/lib/src/bindings/status.dart index 89a12c7..07da663 100644 --- a/lib/src/bindings/status.dart +++ b/lib/src/bindings/status.dart @@ -13,11 +13,14 @@ Pointer listNew(Pointer repo) { final out = calloc>(); final error = libgit2.git_status_list_new(out, repo, nullptr); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -62,14 +65,14 @@ int file({required Pointer repoPointer, required String path}) { final pathC = path.toNativeUtf8().cast(); final error = libgit2.git_status_file(out, repoPointer, pathC); + final result = out.value; + + calloc.free(out); calloc.free(pathC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - final result = out.value; - calloc.free(out); return result; } } diff --git a/lib/src/bindings/submodule.dart b/lib/src/bindings/submodule.dart index 984c62d..baf672c 100644 --- a/lib/src/bindings/submodule.dart +++ b/lib/src/bindings/submodule.dart @@ -55,13 +55,15 @@ Pointer lookup({ final error = libgit2.git_submodule_lookup(out, repoPointer, nameC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -130,11 +132,14 @@ Pointer open(Pointer submodule) { final out = calloc>(); final error = libgit2.git_submodule_open(out, submodule); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -165,14 +170,16 @@ Pointer addSetup({ useGitlinkC, ); + final result = out.value; + + calloc.free(out); calloc.free(urlC); calloc.free(pathC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -229,10 +236,11 @@ int status({ final nameC = name.toNativeUtf8().cast(); libgit2.git_submodule_status(out, repoPointer, nameC, ignore); + final result = out.value; + + calloc.free(out); calloc.free(nameC); - final result = out.value; - calloc.free(out); return result; } diff --git a/lib/src/bindings/tag.dart b/lib/src/bindings/tag.dart index d177a64..f1ed60a 100644 --- a/lib/src/bindings/tag.dart +++ b/lib/src/bindings/tag.dart @@ -36,11 +36,14 @@ Pointer lookup({ final out = calloc>(); final error = libgit2.git_tag_lookup(out, repoPointer, oidPointer); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -54,11 +57,14 @@ Pointer target(Pointer tag) { final out = calloc>(); final error = libgit2.git_tag_target(out, tag); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -77,8 +83,10 @@ String name(Pointer tag) => libgit2.git_tag_name(tag).cast().toDartString(); /// Get the message of a tag. -String message(Pointer tag) => - libgit2.git_tag_message(tag).cast().toDartString(); +String message(Pointer tag) { + final result = libgit2.git_tag_message(tag); + return result == nullptr ? '' : result.cast().toDartString(); +} /// Get the tagger (author) of a tag. Pointer tagger(Pointer tag) => diff --git a/lib/src/bindings/tree.dart b/lib/src/bindings/tree.dart index 33d1f10..489ef9b 100644 --- a/lib/src/bindings/tree.dart +++ b/lib/src/bindings/tree.dart @@ -18,11 +18,14 @@ Pointer lookup({ final out = calloc>(); final error = libgit2.git_tree_lookup(out, repoPointer, oidPointer); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -82,13 +85,15 @@ Pointer getByPath({ final pathC = path.toNativeUtf8().cast(); final error = libgit2.git_tree_entry_bypath(out, rootPointer, pathC); + final result = out.value; + + calloc.free(out); calloc.free(pathC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/treebuilder.dart b/lib/src/bindings/treebuilder.dart index 757be0b..9bee27f 100644 --- a/lib/src/bindings/treebuilder.dart +++ b/lib/src/bindings/treebuilder.dart @@ -24,11 +24,14 @@ Pointer create({ final out = calloc>(); final error = libgit2.git_treebuilder_new(out, repoPointer, sourcePointer); + final result = out.value; + + calloc.free(out); + if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } diff --git a/lib/src/bindings/worktree.dart b/lib/src/bindings/worktree.dart index 979e908..92581c8 100644 --- a/lib/src/bindings/worktree.dart +++ b/lib/src/bindings/worktree.dart @@ -33,15 +33,17 @@ Pointer create({ final error = libgit2.git_worktree_add(out, repoPointer, nameC, pathC, opts); + final result = out.value; + + calloc.free(out); calloc.free(nameC); calloc.free(pathC); calloc.free(opts); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -56,13 +58,15 @@ Pointer lookup({ final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_worktree_lookup(out, repoPointer, nameC); + final result = out.value; + + calloc.free(out); calloc.free(nameC); if (error < 0) { - calloc.free(out); throw LibGit2Error(libgit2.git_error_last()); } else { - return out.value; + return result; } } @@ -80,7 +84,11 @@ bool isPrunable(Pointer wt) { GIT_WORKTREE_PRUNE_OPTIONS_VERSION, ); - return libgit2.git_worktree_is_prunable(wt, opts) > 0 || false; + final result = libgit2.git_worktree_is_prunable(wt, opts); + + calloc.free(opts); + + return result > 0 || false; } /// Prune working tree. diff --git a/lib/src/blame.dart b/lib/src/blame.dart index ad0a60b..3354218 100644 --- a/lib/src/blame.dart +++ b/lib/src/blame.dart @@ -34,8 +34,6 @@ class Blame with IterableMixin { /// [maxLine] is the last line in the file to blame. The default is the last /// line of the file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Blame.file({ required Repository repo, @@ -57,6 +55,7 @@ class Blame with IterableMixin { minLine: minLine, maxLine: maxLine, ); + _finalizer.attach(this, _blamePointer, detach: this); } /// Returns blame data for a file that has been modified in memory. The @@ -74,6 +73,7 @@ class Blame with IterableMixin { reference: reference._blamePointer, buffer: buffer, ); + _finalizer.attach(this, _blamePointer, detach: this); } /// Pointer to memory address for allocated blame object. @@ -83,7 +83,7 @@ class Blame with IterableMixin { /// /// Throws [RangeError] if index out of range. BlameHunk operator [](int index) { - return BlameHunk( + return BlameHunk._( bindings.getHunkByIndex( blamePointer: _blamePointer, index: index, @@ -96,7 +96,7 @@ class Blame with IterableMixin { /// /// Throws [RangeError] if [lineNumber] is out of range. BlameHunk forLine(int lineNumber) { - return BlameHunk( + return BlameHunk._( bindings.getHunkByLine( blamePointer: _blamePointer, lineNumber: lineNumber, @@ -105,16 +105,25 @@ class Blame with IterableMixin { } /// Releases memory allocated for blame object. - void free() => bindings.free(_blamePointer); + void free() { + bindings.free(_blamePointer); + _finalizer.detach(this); + } @override Iterator get iterator => _BlameIterator(_blamePointer); } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class BlameHunk { /// Initializes a new instance of the [BlameHunk] class from /// provided pointer to blame hunk object in memory. - const BlameHunk(this._blameHunkPointer); + const BlameHunk._(this._blameHunkPointer); /// Pointer to memory address for allocated blame hunk object. final Pointer _blameHunkPointer; @@ -194,7 +203,7 @@ class _BlameIterator implements Iterator { if (index == count) { return false; } else { - currentHunk = BlameHunk( + currentHunk = BlameHunk._( bindings.getHunkByIndex( blamePointer: _blamePointer, index: index, diff --git a/lib/src/blob.dart b/lib/src/blob.dart index 0d78ed5..2c1a202 100644 --- a/lib/src/blob.dart +++ b/lib/src/blob.dart @@ -7,18 +7,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; class Blob { /// Initializes a new instance of [Blob] class from provided pointer to /// blob object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Blob(this._blobPointer); + Blob(this._blobPointer) { + _finalizer.attach(this, _blobPointer, detach: this); + } /// Lookups a blob object for provided [oid] in a [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Blob.lookup({required Repository repo, required Oid oid}) { _blobPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _blobPointer, detach: this); } late final Pointer _blobPointer; @@ -80,8 +79,6 @@ class Blob { int get size => bindings.size(_blobPointer); /// Creates an in-memory copy of a blob. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Blob duplicate() => Blob(bindings.duplicate(_blobPointer)); /// Returns filtered content of a blob. @@ -114,10 +111,19 @@ class Blob { } /// Releases memory allocated for blob object. - void free() => bindings.free(_blobPointer); + void free() { + bindings.free(_blobPointer); + _finalizer.detach(this); + } @override String toString() { return 'Blob{oid: $oid, isBinary: $isBinary, size: $size}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/branch.dart b/lib/src/branch.dart index 21657e5..c296c8b 100644 --- a/lib/src/branch.dart +++ b/lib/src/branch.dart @@ -8,9 +8,9 @@ import 'package:libgit2dart/src/bindings/reference.dart' as reference_bindings; class Branch { /// Initializes a new instance of [Branch] class from provided pointer to /// branch object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Branch(this._branchPointer); + Branch(this._branchPointer) { + _finalizer.attach(this, _branchPointer, detach: this); + } /// Creates a new branch pointing at a [target] commit. /// @@ -24,8 +24,6 @@ class Branch { /// [target] is the commit to which this branch should point. This object must /// belong to the given [repo]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Branch.create({ required Repository repo, @@ -39,6 +37,7 @@ class Branch { targetPointer: target.pointer, force: force, ); + _finalizer.attach(this, _branchPointer, detach: this); } /// Lookups a branch by its [name] and [type] in a [repo]sitory. Lookups in @@ -49,8 +48,6 @@ class Branch { /// If branch [type] is [GitBranch.remote] you must include the remote name /// in the [name] (e.g. "origin/master"). /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Branch.lookup({ required Repository repo, @@ -62,6 +59,7 @@ class Branch { branchName: name, branchType: type.value, ); + _finalizer.attach(this, _branchPointer, detach: this); } late final Pointer _branchPointer; @@ -72,9 +70,6 @@ class Branch { /// Returns a list of branches that can be found in a [repo]sitory for /// provided [type]. Default is all branches (local and remote). /// - /// **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, @@ -94,7 +89,6 @@ class Branch { static void delete({required Repository repo, required String name}) { final branch = Branch.lookup(repo: repo, name: name); bindings.delete(branch.pointer); - branch.free(); } /// Renames an existing local branch reference with provided [oldName]. @@ -117,8 +111,6 @@ class Branch { newBranchName: newName, force: force, ); - - branch.free(); } /// [Oid] pointed to by a branch. @@ -164,8 +156,6 @@ class Branch { /// Upstream [Reference] of a local branch. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Reference get upstream => Reference(bindings.getUpstream(_branchPointer)); @@ -222,7 +212,10 @@ class Branch { } /// Releases memory allocated for branch object. - void free() => bindings.free(_branchPointer); + void free() { + bindings.free(_branchPointer); + _finalizer.detach(this); + } @override String toString() { @@ -230,3 +223,9 @@ class Branch { 'isCheckedOut: $isCheckedOut}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/checkout.dart b/lib/src/checkout.dart index e3b8d8f..249a400 100644 --- a/lib/src/checkout.dart +++ b/lib/src/checkout.dart @@ -116,7 +116,6 @@ class Checkout { ); object_bindings.free(treeish); - ref.free(); } /// Updates files in the working tree to match the content of the tree diff --git a/lib/src/commit.dart b/lib/src/commit.dart index 73fcead..3bce907 100644 --- a/lib/src/commit.dart +++ b/lib/src/commit.dart @@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; class Commit { /// Initializes a new instance of [Commit] class from provided pointer to /// commit object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Commit(this._commitPointer); + Commit(this._commitPointer) { + _finalizer.attach(this, _commitPointer, detach: this); + } /// Lookups commit object for provided [oid] in the [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit.lookup({required Repository repo, required Oid oid}) { _commitPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _commitPointer, detach: this); } late final Pointer _commitPointer; @@ -194,8 +193,6 @@ class Commit { /// /// [mainline] is parent of the commit if it is a merge (i.e. 1, 2). /// - /// **IMPORTANT**: produced index should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Index revertTo({ required Commit commit, @@ -221,7 +218,7 @@ class Commit { /// leading newlines. String get message => bindings.message(_commitPointer); - /// Returns the short "summary" of the git commit message. + /// Short "summary" of the git commit message. /// /// The returned message is the summary of the commit, comprising the first /// paragraph of the message with whitespace trimmed and squashed. @@ -229,7 +226,7 @@ class Commit { /// Throws a [LibGit2Error] if error occured. String get summary => bindings.summary(_commitPointer); - /// Returns the long "body" of the commit message. + /// Long "body" of the commit message. /// /// The returned message is the body of the commit, comprising everything but /// the first paragraph of the message. Leading and trailing whitespaces are @@ -272,8 +269,6 @@ class Commit { /// Returns the specified parent of the commit at provided 0-based [position]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Commit parent(int position) { return Commit( @@ -303,8 +298,6 @@ class Commit { /// Passing 0 as the generation number returns another instance of the base /// commit itself. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Commit nthGenAncestor(int n) { return Commit(bindings.nthGenAncestor(commitPointer: _commitPointer, n: n)); @@ -323,12 +316,13 @@ class Commit { } /// Creates an in-memory copy of a commit. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit duplicate() => Commit(bindings.duplicate(_commitPointer)); /// Releases memory allocated for commit object. - void free() => bindings.free(_commitPointer); + void free() { + bindings.free(_commitPointer); + _finalizer.detach(this); + } @override String toString() { @@ -337,3 +331,9 @@ class Commit { ' author: $author}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/config.dart b/lib/src/config.dart index 1b84f4c..4b35c0d 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -11,9 +11,9 @@ import 'package:libgit2dart/src/util.dart'; class Config with IterableMixin { /// Initializes a new instance of [Config] class from provided /// pointer to config object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Config(this._configPointer); + Config(this._configPointer) { + _finalizer.attach(this, _configPointer, detach: this); + } /// Opens config file at provided [path]. /// @@ -23,8 +23,6 @@ class Config with IterableMixin { /// Git config file following the default Git config syntax (see /// `man git-config`). /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws an [Exception] if file not found at provided path. Config.open([String? path]) { libgit2.git_libgit2_init(); @@ -38,39 +36,44 @@ class Config with IterableMixin { throw Exception('File not found'); } } + + _finalizer.attach(this, _configPointer, detach: this); } /// Opens the system configuration file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Config.system() { libgit2.git_libgit2_init(); _configPointer = bindings.open(bindings.findSystem()); + // coverage:ignore-start + _finalizer.attach(this, _configPointer, detach: this); + // coverage:ignore-end } /// Opens the global configuration file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Config.global() { libgit2.git_libgit2_init(); _configPointer = bindings.open(bindings.findGlobal()); + // coverage:ignore-start + _finalizer.attach(this, _configPointer, detach: this); + // coverage:ignore-end } /// Opens the global XDG configuration file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Config.xdg() { libgit2.git_libgit2_init(); _configPointer = bindings.open(bindings.findXdg()); + // coverage:ignore-start + _finalizer.attach(this, _configPointer, detach: this); + // coverage:ignore-end } /// Pointer to memory address for allocated config object. @@ -83,11 +86,24 @@ class Config with IterableMixin { /// Returns the [ConfigEntry] of a [variable]. ConfigEntry operator [](String variable) { - return ConfigEntry( - bindings.getEntry( - configPointer: _configPointer, - variable: variable, - ), + final entryPointer = bindings.getEntry( + configPointer: _configPointer, + variable: variable, + ); + final name = entryPointer.ref.name.cast().toDartString(); + final value = entryPointer.ref.value.cast().toDartString(); + final includeDepth = entryPointer.ref.include_depth; + final level = GitConfigLevel.values.singleWhere( + (e) => entryPointer.ref.level == e.value, + ); + + bindings.freeEntry(entryPointer); + + return ConfigEntry._( + name: name, + value: value, + includeDepth: includeDepth, + level: level, ); } @@ -167,36 +183,41 @@ class Config with IterableMixin { } /// Releases memory allocated for config object. - void free() => bindings.free(_configPointer); + void free() { + bindings.free(_configPointer); + _finalizer.detach(this); + } @override Iterator get iterator => _ConfigIterator(bindings.iterator(_configPointer)); } -class ConfigEntry { - /// Initializes a new instance of [ConfigEntry] class from provided - /// pointer to config entry object in memory. - const ConfigEntry(this._configEntryPointer); +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end - /// Pointer to memory address for allocated config entry object. - final Pointer _configEntryPointer; +class ConfigEntry { + ConfigEntry._({ + required this.name, + required this.value, + required this.includeDepth, + required this.level, + }); /// Name of the entry (normalised). - String get name => _configEntryPointer.ref.name.cast().toDartString(); + final String name; /// Value of the entry. - String get value => _configEntryPointer.ref.value.cast().toDartString(); + final String value; /// Depth of includes where this variable was found - int get includeDepth => _configEntryPointer.ref.include_depth; + final int includeDepth; /// Which config file this was found in. - GitConfigLevel get level { - return GitConfigLevel.values.singleWhere( - (e) => _configEntryPointer.ref.level == e.value, - ); - } + final GitConfigLevel level; @override String toString() { @@ -206,7 +227,9 @@ class ConfigEntry { } class _ConfigIterator implements Iterator { - _ConfigIterator(this._iteratorPointer); + _ConfigIterator(this._iteratorPointer) { + _iteratorFinalizer.attach(this, _iteratorPointer); + } /// Pointer to memory address for allocated config iterator. final Pointer _iteratorPointer; @@ -225,7 +248,20 @@ class _ConfigIterator implements Iterator { } else { error = libgit2.git_config_next(entry, _iteratorPointer); if (error != -31) { - _currentEntry = ConfigEntry(entry.value); + final name = entry.value.ref.name.cast().toDartString(); + final value = entry.value.ref.value.cast().toDartString(); + final includeDepth = entry.value.ref.include_depth; + final level = GitConfigLevel.values.singleWhere( + (e) => entry.value.ref.level == e.value, + ); + + _currentEntry = ConfigEntry._( + name: name, + value: value, + includeDepth: includeDepth, + level: level, + ); + return true; } else { return false; @@ -233,3 +269,9 @@ class _ConfigIterator implements Iterator { } } } + +// coverage:ignore-start +final _iteratorFinalizer = Finalizer>( + (pointer) => bindings.freeIterator(pointer), +); +// coverage:ignore-end diff --git a/lib/src/diff.dart b/lib/src/diff.dart index 848e65a..4af064b 100644 --- a/lib/src/diff.dart +++ b/lib/src/diff.dart @@ -4,15 +4,14 @@ import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/diff.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; -import 'package:libgit2dart/src/bindings/patch.dart' as patch_bindings; import 'package:libgit2dart/src/util.dart'; class Diff { /// Initializes a new instance of [Diff] class from provided /// pointer to diff object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Diff(this._diffPointer); + Diff(this._diffPointer) { + _finalizer.attach(this, _diffPointer, detach: this); + } /// Creates a diff between the [repo]sitory [index] and the workdir directory. /// @@ -30,8 +29,6 @@ class Diff { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Diff.indexToWorkdir({ required Repository repo, @@ -47,6 +44,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff between a [tree] and [repo]sitory [index]. @@ -83,6 +81,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff between a [tree] and the working directory. @@ -124,6 +123,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff between a [tree] and the working directory using index @@ -145,8 +145,6 @@ class Diff { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Diff.treeToWorkdirWithIndex({ required Repository repo, @@ -162,6 +160,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff with the difference between two [Tree] objects. @@ -204,6 +203,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff with the difference between two [Index] objects. @@ -239,6 +239,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Reads the [content]s of a git patch file into a git diff object. @@ -255,6 +256,7 @@ class Diff { Diff.parse(String content) { libgit2.git_libgit2_init(); _diffPointer = bindings.parse(content); + _finalizer.attach(this, _diffPointer, detach: this); } late final Pointer _diffPointer; @@ -441,7 +443,10 @@ class Diff { Oid get patchOid => Oid(bindings.patchOid(_diffPointer)); /// Releases memory allocated for diff object. - void free() => bindings.free(_diffPointer); + void free() { + bindings.free(_diffPointer); + _finalizer.detach(this); + } @override String toString() { @@ -449,6 +454,12 @@ class Diff { } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class DiffDelta { /// Initializes a new instance of [DiffDelta] class from provided /// pointer to diff delta object in memory. @@ -487,10 +498,10 @@ class DiffDelta { int get numberOfFiles => _diffDeltaPointer.ref.nfiles; /// Represents the "from" side of the diff. - DiffFile get oldFile => DiffFile(_diffDeltaPointer.ref.old_file); + DiffFile get oldFile => DiffFile._(_diffDeltaPointer.ref.old_file); /// Represents the "to" side of the diff. - DiffFile get newFile => DiffFile(_diffDeltaPointer.ref.new_file); + DiffFile get newFile => DiffFile._(_diffDeltaPointer.ref.new_file); @override String toString() { @@ -507,7 +518,7 @@ class DiffDelta { class DiffFile { /// Initializes a new instance of [DiffFile] class from provided diff file /// object. - const DiffFile(this._diffFile); + const DiffFile._(this._diffFile); final git_diff_file _diffFile; @@ -543,9 +554,9 @@ class DiffFile { class DiffStats { /// Initializes a new instance of [DiffStats] class from provided /// pointer to diff stats object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - const DiffStats(this._diffStatsPointer); + DiffStats(this._diffStatsPointer) { + _statsFinalizer.attach(this, _diffStatsPointer, detach: this); + } /// Pointer to memory address for allocated diff delta object. final Pointer _diffStatsPointer; @@ -573,7 +584,10 @@ class DiffStats { } /// Releases memory allocated for diff stats object. - void free() => bindings.statsFree(_diffStatsPointer); + void free() { + bindings.statsFree(_diffStatsPointer); + _statsFinalizer.detach(this); + } @override String toString() { @@ -582,111 +596,8 @@ class DiffStats { } } -class DiffHunk { - /// Initializes a new instance of [DiffHunk] class from provided - /// pointers to patch object and diff hunk object in memory and number of - /// lines in hunk. - const DiffHunk( - this._patchPointer, - this._diffHunkPointer, - this.linesCount, - this.index, - ); - - /// Pointer to memory address for allocated diff hunk object. - final Pointer _diffHunkPointer; - - /// Pointer to memory address for allocated patch object. - final Pointer _patchPointer; - - /// Number of total lines in this hunk. - final int linesCount; - - /// Index of this hunk in the patch. - final int index; - - /// Starting line number in 'old file'. - int get oldStart => _diffHunkPointer.ref.old_start; - - /// Number of lines in 'old file'. - int get oldLines => _diffHunkPointer.ref.old_lines; - - /// Starting line number in 'new file'. - int get newStart => _diffHunkPointer.ref.new_start; - - /// Number of lines in 'new file'. - int get newLines => _diffHunkPointer.ref.new_lines; - - /// Header of a hunk. - String get header { - final list = []; - for (var i = 0; i < _diffHunkPointer.ref.header_len; i++) { - list.add(_diffHunkPointer.ref.header[i]); - } - return String.fromCharCodes(list); - } - - /// List of lines in a hunk of a patch. - List get lines { - final lines = []; - for (var i = 0; i < linesCount; i++) { - lines.add( - DiffLine( - patch_bindings.lines( - patchPointer: _patchPointer, - hunkIndex: index, - lineOfHunk: i, - ), - ), - ); - } - return lines; - } - - @override - String toString() { - return 'DiffHunk{linesCount: $linesCount, index: $index, ' - 'oldStart: $oldStart, oldLines: $oldLines, newStart: $newStart, ' - 'newLines: $newLines, header: $header}'; - } -} - -class DiffLine { - /// Initializes a new instance of [DiffLine] class from provided - /// pointer to diff line object in memory. - const DiffLine(this._diffLinePointer); - - /// Pointer to memory address for allocated diff line object. - final Pointer _diffLinePointer; - - /// Type of the line. - GitDiffLine get origin { - return GitDiffLine.values.singleWhere( - (e) => _diffLinePointer.ref.origin == e.value, - ); - } - - /// Line number in old file or -1 for added line. - int get oldLineNumber => _diffLinePointer.ref.old_lineno; - - /// Line number in new file or -1 for deleted line. - int get newLineNumber => _diffLinePointer.ref.new_lineno; - - /// Number of newline characters in content. - int get numLines => _diffLinePointer.ref.num_lines; - - /// Offset in the original file to the content. - int get contentOffset => _diffLinePointer.ref.content_offset; - - /// Content of the diff line. - String get content => _diffLinePointer.ref.content - .cast() - .toDartString(length: _diffLinePointer.ref.content_len); - - @override - String toString() { - return 'DiffLine{oldLineNumber: $oldLineNumber, ' - 'newLineNumber: $newLineNumber, numLines: $numLines, ' - 'contentOffset: $contentOffset, content: $content}'; - } -} +// coverage:ignore-start +final _statsFinalizer = Finalizer>( + (pointer) => bindings.statsFree(pointer), +); +// coverage:ignore-end diff --git a/lib/src/index.dart b/lib/src/index.dart index dd94306..99f60e6 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -9,19 +9,20 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; class Index with IterableMixin { /// Initializes a new instance of [Index] class from provided /// pointer to index object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - const Index(this._indexPointer); + Index(this._indexPointer) { + _finalizer.attach(this, _indexPointer, detach: this); + } /// Creates an in-memory index object. /// /// This index object cannot be read/written to the filesystem, but may be /// used to perform in-memory index operations. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Index.newInMemory() : _indexPointer = bindings.newInMemory(); + Index.newInMemory() { + _indexPointer = bindings.newInMemory(); + _finalizer.attach(this, _indexPointer, detach: this); + } - final Pointer _indexPointer; + late final Pointer _indexPointer; /// Pointer to memory address for allocated index object. Pointer get pointer => _indexPointer; @@ -300,7 +301,10 @@ class Index with IterableMixin { bindings.removeAll(indexPointer: _indexPointer, pathspec: path); /// Releases memory allocated for index object. - void free() => bindings.free(_indexPointer); + void free() { + bindings.free(_indexPointer); + _finalizer.detach(this); + } @override String toString() => 'Index{hasConflicts: $hasConflicts}'; @@ -309,6 +313,12 @@ class Index with IterableMixin { Iterator get iterator => _IndexIterator(_indexPointer); } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class IndexEntry { /// Initializes a new instance of [IndexEntry] class. const IndexEntry(this._indexEntryPointer); diff --git a/lib/src/libgit2.dart b/lib/src/libgit2.dart index cbd611b..f0441f4 100644 --- a/lib/src/libgit2.dart +++ b/lib/src/libgit2.dart @@ -8,7 +8,7 @@ import 'package:libgit2dart/src/util.dart'; class Libgit2 { Libgit2._(); // coverage:ignore-line - /// Returns libgit2 version number. + /// Libgit2 version number. static String get version { libgit2.git_libgit2_init(); @@ -26,7 +26,7 @@ class Libgit2 { return version; } - /// Returns list of options libgit2 was compiled with. + /// Options libgit2 was compiled with. static Set get features { libgit2.git_libgit2_init(); final featuresInt = libgit2.git_libgit2_features(); @@ -35,15 +35,19 @@ class Libgit2 { .toSet(); } - /// Returns owner validation setting for repository directories. + /// Owner validation setting for repository directories. static bool get ownerValidation { libgit2.git_libgit2_init(); + final out = calloc(); libgit2.git_libgit2_opts( git_libgit2_opt_t.GIT_OPT_GET_OWNER_VALIDATION, out, ); - return out.value == 1 || false; + final result = out.value; + calloc.free(out); + + return result == 1 || false; } /// Sets owner validation setting for repository directories. diff --git a/lib/src/mailmap.dart b/lib/src/mailmap.dart index c0e51d9..6abd47a 100644 --- a/lib/src/mailmap.dart +++ b/lib/src/mailmap.dart @@ -9,21 +9,19 @@ class Mailmap { /// /// This object is empty, so you'll have to add a mailmap file before you can /// do anything with it. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Mailmap.empty() { libgit2.git_libgit2_init(); _mailmapPointer = bindings.init(); + _finalizer.attach(this, _mailmapPointer, detach: this); } /// Initializes a new instance of [Mailmap] class from provided buffer. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Mailmap.fromBuffer(String buffer) { libgit2.git_libgit2_init(); _mailmapPointer = bindings.fromBuffer(buffer); + _finalizer.attach(this, _mailmapPointer, detach: this); } /// Initializes a new instance of [Mailmap] class from a [repo]sitory, loading @@ -34,14 +32,13 @@ class Mailmap { /// 1. `.mailmap` in the root of the repository's working directory, if /// present. /// 2. The blob object identified by the `mailmap.blob` config entry, if set. - /// NOTE: `mailmap.blob` defaults to `HEAD:.mailmap` in bare repositories + /// NOTE: `mailmap.blob` defaults to `HEAD:.mailmap` in bare repositories. /// 3. The path in the `mailmap.file` config entry, if set. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Mailmap.fromRepository(Repository repo) { _mailmapPointer = bindings.fromRepository(repo.pointer); + _finalizer.attach(this, _mailmapPointer, detach: this); } /// Pointer to memory address for allocated mailmap object. @@ -94,5 +91,14 @@ class Mailmap { } /// Releases memory allocated for mailmap object. - void free() => bindings.free(_mailmapPointer); + void free() { + bindings.free(_mailmapPointer); + _finalizer.detach(this); + } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/merge.dart b/lib/src/merge.dart index 06d13fb..66d9cda 100644 --- a/lib/src/merge.dart +++ b/lib/src/merge.dart @@ -74,9 +74,6 @@ class Merge { (e) => analysisInt[1] == e.value, ); - head.free(); - ref.free(); - return [analysisSet, mergePreference]; } @@ -140,8 +137,6 @@ class Merge { /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to /// [GitMergeFileFlag.defaults]. /// - /// **IMPORTANT**: returned index should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static Index commits({ required Repository repo, @@ -188,8 +183,6 @@ class Merge { /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to /// [GitMergeFileFlag.defaults]. /// - /// **IMPORTANT**: returned index should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static Index trees({ required Repository repo, diff --git a/lib/src/note.dart b/lib/src/note.dart index a8e0753..0461c96 100644 --- a/lib/src/note.dart +++ b/lib/src/note.dart @@ -6,7 +6,9 @@ import 'package:libgit2dart/src/bindings/note.dart' as bindings; class Note { /// Initializes a new instance of the [Note] class from provided /// pointer to note and annotatedOid objects in memory. - Note(this._notePointer, this._annotatedOidPointer); + Note(this._notePointer, this._annotatedOidPointer) { + _finalizer.attach(this, _notePointer, detach: this); + } /// Lookups the note for an [annotatedOid]. /// @@ -16,8 +18,6 @@ class Note { /// /// [notesRef] is the canonical name of the reference to use. Defaults to "refs/notes/commits". /// - /// **IMPORTANT**: Notes must be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Note.lookup({ required Repository repo, @@ -30,6 +30,7 @@ class Note { notesRef: notesRef, ); _annotatedOidPointer = annotatedOid.pointer; + _finalizer.attach(this, _notePointer, detach: this); } /// Pointer to memory address for allocated note object. @@ -107,8 +108,6 @@ class Note { /// Returns list of notes for [repo]sitory. /// - /// **IMPORTANT**: Notes must be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static List list(Repository repo) { final notesPointers = bindings.list(repo.pointer); @@ -132,10 +131,19 @@ class Note { Oid get annotatedOid => Oid(_annotatedOidPointer); /// Releases memory allocated for note object. - void free() => bindings.free(_notePointer); + void free() { + bindings.free(_notePointer); + _finalizer.detach(this); + } @override String toString() { return 'Note{oid: $oid, message: $message, annotatedOid: $annotatedOid}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/odb.dart b/lib/src/odb.dart index 53b5466..111bb58 100644 --- a/lib/src/odb.dart +++ b/lib/src/odb.dart @@ -7,20 +7,19 @@ import 'package:libgit2dart/src/util.dart'; class Odb { /// Initializes a new instance of [Odb] class from provided /// pointer to Odb object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Odb(this._odbPointer); + Odb(this._odbPointer) { + _finalizer.attach(this, _odbPointer, detach: this); + } /// Creates a new object database with no backends. /// /// Before the ODB can be used for read/writing, a custom database backend must be /// manually added. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Odb.create() { libgit2.git_libgit2_init(); _odbPointer = bindings.create(); + _finalizer.attach(this, _odbPointer, detach: this); } late final Pointer _odbPointer; @@ -59,12 +58,9 @@ class Odb { /// This method queries all available ODB backends trying to read the given /// [oid]. /// - /// **IMPORTANT**: Returned object should be freed to release allocated - /// memory. - /// /// Throws a [LibGit2Error] if error occured. OdbObject read(Oid oid) { - return OdbObject( + return OdbObject._( bindings.read( odbPointer: _odbPointer, oidPointer: oid.pointer, @@ -97,13 +93,24 @@ class Odb { } /// Releases memory allocated for odb object. - void free() => bindings.free(_odbPointer); + void free() { + bindings.free(_odbPointer); + _finalizer.detach(this); + } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class OdbObject { /// Initializes a new instance of the [OdbObject] class from /// provided pointer to odbObject object in memory. - const OdbObject(this._odbObjectPointer); + OdbObject._(this._odbObjectPointer) { + _objectfinalizer.attach(this, _odbObjectPointer, detach: this); + } /// Pointer to memory address for allocated odbObject object. final Pointer _odbObjectPointer; @@ -124,10 +131,19 @@ class OdbObject { int get size => bindings.objectSize(_odbObjectPointer); /// Releases memory allocated for odbObject object. - void free() => bindings.objectFree(_odbObjectPointer); + void free() { + bindings.objectFree(_odbObjectPointer); + _objectfinalizer.detach(this); + } @override String toString() { return 'OdbObject{oid: $oid, type: $type, size: $size}'; } } + +// coverage:ignore-start +final _objectfinalizer = Finalizer>( + (pointer) => bindings.objectFree(pointer), +); +// coverage:ignore-end diff --git a/lib/src/oid.dart b/lib/src/oid.dart index eeb4a62..c9f8efb 100644 --- a/lib/src/oid.dart +++ b/lib/src/oid.dart @@ -25,13 +25,11 @@ class Oid { if (sha.length == 40) { _oidPointer = bindings.fromSHA(sha); } else { - final odb = repo.odb; _oidPointer = odb_bindings.existsPrefix( - odbPointer: odb.pointer, + odbPointer: repo.odb.pointer, shortOidPointer: bindings.fromStrN(sha), length: sha.length, ); - odb.free(); } } else { throw ArgumentError.value('$sha is not a valid sha hex string'); diff --git a/lib/src/packbuilder.dart b/lib/src/packbuilder.dart index 40fdbb7..e585a16 100644 --- a/lib/src/packbuilder.dart +++ b/lib/src/packbuilder.dart @@ -6,11 +6,10 @@ import 'package:libgit2dart/src/bindings/packbuilder.dart' as bindings; class PackBuilder { /// Initializes a new instance of [PackBuilder] class. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. PackBuilder(Repository repo) { _packbuilderPointer = bindings.init(repo.pointer); + _finalizer.attach(this, _packbuilderPointer, detach: this); } /// Pointer to memory address for allocated packbuilder object. @@ -108,10 +107,19 @@ class PackBuilder { } /// Releases memory allocated for packbuilder object. - void free() => bindings.free(_packbuilderPointer); + void free() { + bindings.free(_packbuilderPointer); + _finalizer.detach(this); + } @override String toString() { return 'PackBuilder{length: $length, writtenLength: $writtenLength}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/patch.dart b/lib/src/patch.dart index f5f6005..5c28622 100644 --- a/lib/src/patch.dart +++ b/lib/src/patch.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; +import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/patch.dart' as bindings; @@ -8,9 +9,9 @@ import 'package:libgit2dart/src/util.dart'; class Patch { /// Initializes a new instance of [Patch] class from provided /// pointer to patch object in memory and pointers to old and new blobs/buffers. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Patch(this._patchPointer); + Patch(this._patchPointer) { + _finalizer.attach(this, _patchPointer, detach: this); + } /// Directly generates a [Patch] from the difference between two blobs. /// @@ -30,8 +31,6 @@ class Patch { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromBlobs({ required Blob? oldBlob, @@ -51,6 +50,7 @@ class Patch { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _patchPointer, detach: this); } /// Directly generates a [Patch] from the difference between the blob and a @@ -72,8 +72,6 @@ class Patch { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromBlobAndBuffer({ required Blob? blob, @@ -93,6 +91,7 @@ class Patch { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _patchPointer, detach: this); } /// Directly generates a [Patch] from the difference between two buffers @@ -113,8 +112,6 @@ class Patch { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromBuffers({ required String? oldBuffer, @@ -136,6 +133,7 @@ class Patch { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _patchPointer, detach: this); } /// Creates a patch for an entry in the diff list. @@ -144,11 +142,10 @@ class Patch { /// /// [index] is the position of an entry in diff list. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromDiff({required Diff diff, required int index}) { _patchPointer = bindings.fromDiff(diffPointer: diff.pointer, index: index); + _finalizer.attach(this, _patchPointer, detach: this); } late final Pointer _patchPointer; @@ -157,7 +154,7 @@ class Patch { PatchStats get stats { final result = bindings.lineStats(_patchPointer); - return PatchStats( + return PatchStats._( context: result['context']!, insertions: result['insertions']!, deletions: result['deletions']!, @@ -201,14 +198,49 @@ class Patch { final length = bindings.numHunks(_patchPointer); final hunks = []; - for (var i = 0; i < length; i++) { - final hunk = bindings.hunk(patchPointer: _patchPointer, hunkIndex: i); + for (var index = 0; index < length; index++) { + final hunk = bindings.hunk(patchPointer: _patchPointer, hunkIndex: index); + final hunkPointer = hunk['hunk']! as Pointer; + final linesCount = hunk['linesN']! as int; + + final lines = []; + for (var i = 0; i < linesCount; i++) { + final linePointer = bindings.lines( + patchPointer: _patchPointer, + hunkIndex: index, + lineOfHunk: i, + ); + lines.add( + DiffLine._( + origin: GitDiffLine.values.singleWhere( + (e) => linePointer.ref.origin == e.value, + ), + oldLineNumber: linePointer.ref.old_lineno, + newLineNumber: linePointer.ref.new_lineno, + numLines: linePointer.ref.num_lines, + contentOffset: linePointer.ref.content_offset, + content: linePointer.ref.content + .cast() + .toDartString(length: linePointer.ref.content_len), + ), + ); + } + + final intHeader = []; + for (var i = 0; i < hunkPointer.ref.header_len; i++) { + intHeader.add(hunkPointer.ref.header[i]); + } + hunks.add( - DiffHunk( - _patchPointer, - hunk['hunk']! as Pointer, - hunk['linesN']! as int, - i, + DiffHunk._( + linesCount: linesCount, + index: index, + oldStart: hunkPointer.ref.old_start, + oldLines: hunkPointer.ref.old_lines, + newStart: hunkPointer.ref.new_start, + newLines: hunkPointer.ref.new_lines, + header: String.fromCharCodes(intHeader), + lines: lines, ), ); } @@ -217,15 +249,24 @@ class Patch { } /// Releases memory allocated for patch object. - void free() => bindings.free(_patchPointer); + void free() { + bindings.free(_patchPointer); + _finalizer.detach(this); + } @override String toString() => 'Patch{size: ${size()}, delta: $delta}'; } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + /// Line counts of each type in a patch. class PatchStats { - const PatchStats({ + const PatchStats._({ required this.context, required this.insertions, required this.deletions, @@ -246,3 +287,83 @@ class PatchStats { 'deletions: $deletions}'; } } + +class DiffHunk { + const DiffHunk._({ + required this.linesCount, + required this.index, + required this.oldStart, + required this.oldLines, + required this.newStart, + required this.newLines, + required this.header, + required this.lines, + }); + + /// Number of total lines in this hunk. + final int linesCount; + + /// Index of this hunk in the patch. + final int index; + + /// Starting line number in 'old file'. + final int oldStart; + + /// Number of lines in 'old file'. + final int oldLines; + + /// Starting line number in 'new file'. + final int newStart; + + /// Number of lines in 'new file'. + final int newLines; + + /// Header of a hunk. + final String header; + + /// List of lines in a hunk of a patch. + final List lines; + + @override + String toString() { + return 'DiffHunk{linesCount: $linesCount, index: $index, ' + 'oldStart: $oldStart, oldLines: $oldLines, newStart: $newStart, ' + 'newLines: $newLines, header: $header}'; + } +} + +class DiffLine { + const DiffLine._({ + required this.origin, + required this.oldLineNumber, + required this.newLineNumber, + required this.numLines, + required this.contentOffset, + required this.content, + }); + + /// Type of the line. + final GitDiffLine origin; + + /// Line number in old file or -1 for added line. + final int oldLineNumber; + + /// Line number in new file or -1 for deleted line. + final int newLineNumber; + + /// Number of newline characters in content. + final int numLines; + + /// Offset in the original file to the content. + final int contentOffset; + + /// Content of the diff line. + final String content; + + @override + String toString() { + return 'DiffLine{oldLineNumber: $oldLineNumber, ' + 'newLineNumber: $newLineNumber, numLines: $numLines, ' + 'contentOffset: $contentOffset, content: $content}'; + } +} diff --git a/lib/src/rebase.dart b/lib/src/rebase.dart index f9fda60..93650c4 100644 --- a/lib/src/rebase.dart +++ b/lib/src/rebase.dart @@ -17,8 +17,6 @@ class Rebase { /// [onto] is the branch to rebase onto, default is to rebase onto the given /// [upstream]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Rebase.init({ required Repository repo, @@ -32,16 +30,16 @@ class Rebase { upstreamPointer: upstream?.pointer, ontoPointer: onto?.pointer, ); + _finalizer.attach(this, _rebasePointer, detach: this); } /// Opens an existing rebase that was previously started by either an /// invocation of [Rebase.init] or by another client. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Rebase.open(Repository repo) { _rebasePointer = bindings.open(repo.pointer); + _finalizer.attach(this, _rebasePointer, detach: this); } /// Pointer to memory address for allocated rebase object. @@ -57,7 +55,7 @@ class Rebase { rebase: _rebasePointer, index: i, ); - result.add(RebaseOperation(operation)); + result.add(RebaseOperation._(operation)); } return result; @@ -101,7 +99,7 @@ class Rebase { /// /// Throws a [LibGit2Error] if error occured. RebaseOperation next() { - return RebaseOperation(bindings.next(_rebasePointer)); + return RebaseOperation._(bindings.next(_rebasePointer)); } /// Commits the current patch. You must have resolved any conflicts that were @@ -138,13 +136,22 @@ class Rebase { void abort() => bindings.abort(_rebasePointer); /// Releases memory allocated for rebase object. - void free() => bindings.free(_rebasePointer); + void free() { + bindings.free(_rebasePointer); + _finalizer.detach(this); + } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class RebaseOperation { /// Initializes a new instance of the [RebaseOperation] class from /// provided pointer to rebase operation object in memory. - const RebaseOperation(this._rebaseOperationPointer); + const RebaseOperation._(this._rebaseOperationPointer); /// Pointer to memory address for allocated rebase operation object. final Pointer _rebaseOperationPointer; diff --git a/lib/src/reference.dart b/lib/src/reference.dart index c67b721..2a5cf73 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -10,17 +10,14 @@ import 'package:libgit2dart/src/bindings/repository.dart' class Reference { /// Initializes a new instance of the [Reference] class. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Reference(this._refPointer); + Reference(this._refPointer) { + _finalizer.attach(this, _refPointer, detach: this); + } /// Creates a new reference for provided [target]. /// /// The reference will be created in the [repo]sitory and written to the disk. /// - /// **IMPORTANT**: The generated [Reference] object should be freed to release - /// allocated memory. - /// /// 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"). @@ -65,17 +62,17 @@ class Reference { '$target must be either Oid or String reference name', ); } + _finalizer.attach(this, _refPointer, detach: this); } /// Lookups reference [name] in a [repo]sitory. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// The [name] will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. Reference.lookup({required Repository repo, required String name}) { _refPointer = bindings.lookup(repoPointer: repo.pointer, name: name); + _finalizer.attach(this, _refPointer, detach: this); } late Pointer _refPointer; @@ -89,7 +86,6 @@ class Reference { 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 with provided [oldName]. @@ -120,7 +116,6 @@ class Reference { force: force, logMessage: logMessage, ); - ref.free(); } /// List of all the references names that can be found in a [repo]sitory. @@ -153,8 +148,6 @@ class Reference { } /// Creates a copy of an existing reference. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Reference duplicate() => Reference(bindings.duplicate(_refPointer)); /// Type of the reference. @@ -192,7 +185,6 @@ class Reference { oidPointer: target.pointer, logMessage: logMessage, ); - free(); _refPointer = newPointer; } else if (target is String) { final newPointer = bindings.setTargetSymbolic( @@ -200,7 +192,6 @@ class Reference { target: target, logMessage: logMessage, ); - free(); _refPointer = newPointer; } else { throw ArgumentError.value( @@ -261,8 +252,6 @@ class Reference { } /// [RefLog] object. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. RefLog get log => RefLog(this); /// Whether reference is a local branch. @@ -292,7 +281,10 @@ class Reference { bool notEquals(Reference other) => !equals(other); /// Releases memory allocated for reference object. - void free() => bindings.free(_refPointer); + void free() { + bindings.free(_refPointer); + _finalizer.detach(this); + } @override String toString() { @@ -301,3 +293,9 @@ class Reference { 'isTag: $isTag}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/reflog.dart b/lib/src/reflog.dart index 89a50a2..69a0561 100644 --- a/lib/src/reflog.dart +++ b/lib/src/reflog.dart @@ -6,13 +6,12 @@ import 'package:libgit2dart/src/bindings/reflog.dart' as bindings; class RefLog with IterableMixin { /// Initializes a new instance of [RefLog] class from provided [Reference]. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. RefLog(Reference ref) { _reflogPointer = bindings.read( repoPointer: ref.owner.pointer, name: ref.name, ); + _finalizer.attach(this, _reflogPointer, detach: this); } /// Pointer to memory address for allocated reflog object. @@ -47,7 +46,7 @@ class RefLog with IterableMixin { /// Requesting the reflog entry with an index of 0 will return the most /// recently created entry. RefLogEntry operator [](int index) { - return RefLogEntry( + return RefLogEntry._( bindings.getByIndex( reflogPointer: _reflogPointer, index: index, @@ -89,16 +88,25 @@ class RefLog with IterableMixin { void write() => bindings.write(_reflogPointer); /// Releases memory allocated for reflog object. - void free() => bindings.free(_reflogPointer); + void free() { + bindings.free(_reflogPointer); + _finalizer.detach(this); + } @override Iterator get iterator => _RefLogIterator(_reflogPointer); } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class RefLogEntry { /// Initializes a new instance of [RefLogEntry] class from provided /// pointer to RefLogEntry object in memory. - const RefLogEntry(this._entryPointer); + const RefLogEntry._(this._entryPointer); /// Pointer to memory address for allocated reflog entry object. final Pointer _entryPointer; @@ -141,7 +149,7 @@ class _RefLogIterator implements Iterator { if (_index == _count) { return false; } else { - _currentEntry = RefLogEntry( + _currentEntry = RefLogEntry._( bindings.getByIndex( reflogPointer: _reflogPointer, index: _index, diff --git a/lib/src/remote.dart b/lib/src/remote.dart index 26ec809..70238d1 100644 --- a/lib/src/remote.dart +++ b/lib/src/remote.dart @@ -8,18 +8,15 @@ class Remote { /// /// The [name] will be checked for validity. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Remote.lookup({required Repository repo, required String name}) { _remotePointer = bindings.lookup(repoPointer: repo.pointer, name: name); + _finalizer.attach(this, _remotePointer, detach: this); } /// Adds remote with provided [name] and [url] to the [repo]sitory's /// configuration with the default [fetch] refspec if none provided. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Remote.create({ required Repository repo, @@ -41,12 +38,11 @@ class Remote { fetch: fetch, ); } + _finalizer.attach(this, _remotePointer, detach: this); } - late final Pointer _remotePointer; - /// Pointer to memory address for allocated remote object. - Pointer get pointer => _remotePointer; + late final Pointer _remotePointer; /// Deletes an existing persisted remote with provided [name]. /// @@ -298,7 +294,10 @@ class Remote { } /// Releases memory allocated for remote object. - void free() => bindings.free(_remotePointer); + void free() { + bindings.free(_remotePointer); + _finalizer.detach(this); + } @override String toString() { @@ -307,6 +306,12 @@ class Remote { } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + /// Provides callers information about the progress of indexing a packfile, /// either directly or part of a fetch or clone that downloads a packfile. class TransferProgress { @@ -346,3 +351,21 @@ class TransferProgress { 'indexedDeltas: $indexedDeltas, receivedBytes: $receivedBytes}'; } } + +class RemoteCallback { + /// Values used to override the remote creation and customization process + /// during a repository clone operation. + /// + /// Remote will have provided [name] and [url] with the default [fetch] + /// refspec if none provided. + const RemoteCallback({required this.name, required this.url, this.fetch}); + + /// Remote's name. + final String name; + + /// Remote's url. + final String url; + + /// Remote's fetch refspec. + final String? fetch; +} diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 0a47078..2822bc4 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -111,13 +111,11 @@ class Repository { /// /// [bare] whether cloned repo should be bare. /// - /// [remote] is the callback function with - /// `Remote Function(Repository repo, String name, String url)` signature. - /// The [Remote] it returns will be used instead of default one. + /// [remoteCallback] is the [RemoteCallback] object values that will be used + /// in creation and customization process of remote instead of default ones. /// - /// [repository] is the callback function matching the - /// `Repository Function(String path, bool bare)` signature. The [Repository] - /// it returns will be used instead of creating a new one. + /// [repositoryCallback] is the [RepositoryCallback] object values that will + /// be used in creation and customization process of repository. /// /// [checkoutBranch] is the name of the branch to checkout after the clone. /// Defaults to using the remote's default branch. @@ -132,8 +130,8 @@ class Repository { required String url, required String localPath, bool bare = false, - Remote Function(Repository, String, String)? remote, - Repository Function(String, bool)? repository, + RemoteCallback? remoteCallback, + RepositoryCallback? repositoryCallback, String? checkoutBranch, Callbacks callbacks = const Callbacks(), }) { @@ -143,8 +141,8 @@ class Repository { url: url, localPath: localPath, bare: bare, - remote: remote, - repository: repository, + remoteCallback: remoteCallback, + repositoryCallback: repositoryCallback, checkoutBranch: checkoutBranch, callbacks: callbacks, ); @@ -375,8 +373,6 @@ class Repository { /// If a configuration file has not been set, the default config set for the /// repository will be returned, including global and system configurations /// (if they are available). - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Config get config => Config(bindings.config(_repoPointer)); /// Snapshot of the repository's configuration. @@ -386,24 +382,16 @@ class Repository { /// /// The contents of this snapshot will not change, even if the underlying /// config files are modified. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer)); /// Repository's head. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Reference get head => Reference(bindings.head(_repoPointer)); /// Index file for this repository. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Index get index => Index(bindings.index(_repoPointer)); /// ODB for this repository. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Odb get odb => Odb(bindings.odb(_repoPointer)); @@ -419,23 +407,17 @@ class Repository { /// List of all branches that can be found in a repository. /// - /// **IMPORTANT**: Branches should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. List get branches => Branch.list(repo: this); /// List of local branches that can be found in a repository. /// - /// **IMPORTANT**: Branches should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. List get branchesLocal => Branch.list(repo: this, type: GitBranch.local); /// List of remote branches that can be found in a repository. /// - /// **IMPORTANT**: Branches should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. List get branchesRemote => Branch.list(repo: this, type: GitBranch.remote); @@ -465,8 +447,6 @@ class Repository { /// /// If [sorting] isn't provided default will be used (reverse chronological /// order, like in git). - /// - /// **IMPORTANT**: Commits should be freed to release allocated memory. List log({ required Oid oid, Set sorting = const {GitSort.none}, @@ -477,8 +457,6 @@ class Repository { walker.push(oid); final result = walker.walk(); - walker.free(); - return result; } @@ -737,6 +715,7 @@ class Repository { } } + // ignore: no_leading_underscores_for_local_identifiers final _packDelegate = packDelegate ?? packAll; final packbuilder = PackBuilder(this); @@ -745,10 +724,79 @@ class Repository { } _packDelegate(packbuilder); packbuilder.write(path); - final result = packbuilder.writtenLength; - packbuilder.free(); - - return result; + return packbuilder.writtenLength; } } + +class RepositoryCallback { + /// Values used to override the repository creation and customization process + /// during a clone operation. + /// + /// [path] is the path to the repository. + /// + /// [bare] whether new repository should be bare. + /// + /// [flags] is a combination of [GitRepositoryInit] flags. Defaults to + /// [GitRepositoryInit.mkdir]. + /// + /// [mode] is the permissions for the folder. Default to 0 (permissions + /// configured by umask). + /// + /// [workdirPath] is the path to the working directory. Can be null for + /// default path. + /// + /// [description] if set will be used to initialize the "description" file in + /// the repository, instead of using the template content. + /// + /// [templatePath] is the the path to use for the template directory if + /// [GitRepositoryInit.externalTemplate] is set. Defaults to the config or + /// default directory options. + /// + /// [initialHead] is the name of the head to point HEAD at. If null, then + /// this will be treated as "master" and the HEAD ref will be set to + /// "refs/heads/master". If this begins with "refs/" it will be used + /// verbatim, otherwise "refs/heads/" will be prefixed. + /// + /// [originUrl] if set, then after the rest of the repository initialization + /// is completed, an "origin" remote will be added pointing to this URL. + const RepositoryCallback({ + required this.path, + this.bare = false, + this.flags = const {GitRepositoryInit.mkpath}, + this.mode = 0, + this.workdirPath, + this.description, + this.templatePath, + this.initialHead, + this.originUrl, + }); + + /// Path to the repository. + final String path; + + /// Whether repository is bare. + final bool bare; + + /// Combination of [GitRepositoryInit] flags. + final Set flags; + + /// Permissions for the repository folder. + final int mode; + + /// Path to the working directory. + final String? workdirPath; + + /// Description used to initialize the "description" file in the repository. + final String? description; + + /// Path used for the template directory. + final String? templatePath; + + /// Name of the head HEAD points at. + final String? initialHead; + + /// "origin" remote URL that will be added after the rest of the repository + /// initialization is completed. + final String? originUrl; +} diff --git a/lib/src/revparse.dart b/lib/src/revparse.dart index 0e2d617..5df8a63 100644 --- a/lib/src/revparse.dart +++ b/lib/src/revparse.dart @@ -15,8 +15,6 @@ class RevParse { /// point to an intermediate reference. When such expressions are being /// passed in, reference_out will be valued as well. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. RevParse.ext({required Repository repo, required String spec}) { final pointers = bindings.revParseExt( @@ -51,8 +49,6 @@ class RevParse { /// final tag = RevParse.single(repo: repo, spec: 'v1.0') as Tag; /// ``` /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static Object single({required Repository repo, required String spec}) { final object = bindings.revParseSingle( @@ -79,7 +75,7 @@ class RevParse { /// /// Throws a [LibGit2Error] if error occured. static RevSpec range({required Repository repo, required String spec}) { - return RevSpec( + return RevSpec._( bindings.revParse( repoPointer: repo.pointer, spec: spec, @@ -96,19 +92,15 @@ class RevParse { class RevSpec { /// Initializes a new instance of [RevSpec] class from provided /// pointer to revspec object in memory. - const RevSpec(this._revSpecPointer); + const RevSpec._(this._revSpecPointer); /// Pointer to memory address for allocated revspec object. final Pointer _revSpecPointer; /// Left element of the revspec. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit get from => Commit(_revSpecPointer.ref.from.cast()); /// Right element of the revspec. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit? get to { return _revSpecPointer.ref.to == nullptr ? null diff --git a/lib/src/revwalk.dart b/lib/src/revwalk.dart index 29faf68..2c0f23a 100644 --- a/lib/src/revwalk.dart +++ b/lib/src/revwalk.dart @@ -5,10 +5,9 @@ import 'package:libgit2dart/src/bindings/revwalk.dart' as bindings; class RevWalk { /// Initializes a new instance of the [RevWalk] class. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. RevWalk(Repository repo) { _revWalkPointer = bindings.create(repo.pointer); + _finalizer.attach(this, _revWalkPointer, detach: this); } late final Pointer _revWalkPointer; @@ -150,5 +149,14 @@ class RevWalk { void simplifyFirstParent() => bindings.simplifyFirstParent(_revWalkPointer); /// Releases memory allocated for [RevWalk] object. - void free() => bindings.free(_revWalkPointer); + void free() { + bindings.free(_revWalkPointer); + _finalizer.detach(this); + } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/signature.dart b/lib/src/signature.dart index 4c821a7..2fe12e1 100644 --- a/lib/src/signature.dart +++ b/lib/src/signature.dart @@ -10,17 +10,16 @@ import 'package:meta/meta.dart'; class Signature { /// Initializes a new instance of [Signature] class from provided pointer to /// signature object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Signature(this._signaturePointer); + Signature(Pointer pointer) { + _signaturePointer = bindings.duplicate(pointer); + _finalizer.attach(this, _signaturePointer, detach: this); + } /// Creates new [Signature] from provided [name], [email], and optional [time] /// in seconds from epoch and [offset] in minutes. /// /// If [time] isn't provided [Signature] will be created with a timestamp of /// 'now'. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Signature.create({ required String name, required String email, @@ -39,6 +38,7 @@ class Signature { offset: offset, ); } + _finalizer.attach(this, _signaturePointer, detach: this); } /// Creates a new action signature with default user and now timestamp. @@ -48,6 +48,7 @@ class Signature { /// on that information. Signature.defaultSignature(Repository repo) { _signaturePointer = bindings.defaultSignature(repo.pointer); + _finalizer.attach(this, _signaturePointer, detach: this); } late final Pointer _signaturePointer; @@ -79,7 +80,10 @@ class Signature { } /// Releases memory allocated for signature object. - void free() => bindings.free(_signaturePointer); + void free() { + bindings.free(_signaturePointer); + _finalizer.detach(this); + } @override // coverage:ignore-line int get hashCode => @@ -91,3 +95,9 @@ class Signature { 'offset: $offset}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/submodule.dart b/lib/src/submodule.dart index c470fde..84d0679 100644 --- a/lib/src/submodule.dart +++ b/lib/src/submodule.dart @@ -7,14 +7,13 @@ class Submodule { /// Lookups submodule information by [name] or path (they are usually the /// same). /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Submodule.lookup({required Repository repo, required String name}) { _submodulePointer = bindings.lookup( repoPointer: repo.pointer, name: name, ); + _finalizer.attach(this, _submodulePointer, detach: this); } /// Adds a submodule to the index. @@ -29,8 +28,6 @@ class Submodule { /// [callbacks] is the combination of callback functions from [Callbacks] /// object. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Submodule.add({ required Repository repo, @@ -49,6 +46,8 @@ class Submodule { bindings.clone(submodule: _submodulePointer, callbacks: callbacks); bindings.addFinalize(_submodulePointer); + + _finalizer.attach(this, _submodulePointer, detach: this); } /// Pointer to memory address for allocated submodule object. @@ -264,7 +263,10 @@ class Submodule { } /// Releases memory allocated for submodule object. - void free() => bindings.free(_submodulePointer); + void free() { + bindings.free(_submodulePointer); + _finalizer.detach(this); + } @override String toString() { @@ -273,3 +275,9 @@ class Submodule { 'workdirOid: $workdirOid, ignore: $ignore, updateRule: $updateRule}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/tag.dart b/lib/src/tag.dart index fb1fec3..6b779be 100644 --- a/lib/src/tag.dart +++ b/lib/src/tag.dart @@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/tag.dart' as bindings; class Tag { /// Initializes a new instance of [Tag] class from provided pointer to /// tag object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Tag(this._tagPointer); + Tag(this._tagPointer) { + _finalizer.attach(this, _tagPointer, detach: this); + } /// Lookups tag object for provided [oid] in a [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Tag.lookup({required Repository repo, required Oid oid}) { _tagPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _tagPointer, detach: this); } /// Pointer to memory address for allocated tag object. @@ -160,9 +159,6 @@ class Tag { /// Returned object should be explicitly downcasted to one of four of git /// object types. /// - /// **IMPORTANT**: returned object should be freed to release allocated - /// memory. - /// /// ```dart /// final commit = tag.target as Commit; /// final tree = tag.target as Tree; @@ -215,7 +211,10 @@ class Tag { } /// Releases memory allocated for tag object. - void free() => bindings.free(_tagPointer); + void free() { + bindings.free(_tagPointer); + _finalizer.detach(this); + } @override String toString() { @@ -223,3 +222,9 @@ class Tag { 'tagger: $tagger}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/tree.dart b/lib/src/tree.dart index a8035d8..b5a680e 100644 --- a/lib/src/tree.dart +++ b/lib/src/tree.dart @@ -7,18 +7,17 @@ import 'package:libgit2dart/src/bindings/tree.dart' as bindings; class Tree { /// Initializes a new instance of [Tree] class from provided pointer to /// tree object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Tree(this._treePointer); + Tree(this._treePointer) { + _finalizer.attach(this, _treePointer, detach: this); + } /// Lookups a tree object for provided [oid] in a [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Tree.lookup({required Repository repo, required Oid oid}) { _treePointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _treePointer, detach: this); } late final Pointer _treePointer; @@ -63,7 +62,7 @@ class Tree { ), ); } else if (value is String && value.contains('/')) { - return TreeEntry( + return TreeEntry._byPath( bindings.getByPath( rootPointer: _treePointer, path: value, @@ -90,7 +89,10 @@ class Tree { int get length => bindings.entryCount(_treePointer); /// Releases memory allocated for tree object. - void free() => bindings.free(_treePointer); + void free() { + bindings.free(_treePointer); + _finalizer.detach(this); + } @override String toString() { @@ -98,10 +100,24 @@ class Tree { } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class TreeEntry { /// Initializes a new instance of [TreeEntry] class from provided pointer to /// tree entry object in memory. - const TreeEntry(this._treeEntryPointer); + TreeEntry(this._treeEntryPointer); + + /// Initializes a new instance of [TreeEntry] class from provided pointer to + /// tree entry object in memory. + /// + /// Unlike the other lookup methods, must be freed. + TreeEntry._byPath(this._treeEntryPointer) { + _entryFinalizer.attach(this, _treeEntryPointer, detach: this); + } /// Pointer to memory address for allocated tree entry object. final Pointer _treeEntryPointer; @@ -119,8 +135,19 @@ class TreeEntry { } /// Releases memory allocated for tree entry object. - void free() => bindings.entryFree(_treeEntryPointer); + /// + /// **IMPORTANT**: Only tree entries looked up by path should be freed. + void free() { + bindings.entryFree(_treeEntryPointer); + _entryFinalizer.detach(this); + } @override String toString() => 'TreeEntry{oid: $oid, name: $name, filemode: $filemode}'; } + +// coverage:ignore-start +final _entryFinalizer = Finalizer>( + (pointer) => bindings.entryFree(pointer), +); +// coverage:ignore-end diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart index 21cef7c..89f9491 100644 --- a/lib/src/treebuilder.dart +++ b/lib/src/treebuilder.dart @@ -7,14 +7,13 @@ class TreeBuilder { /// Initializes a new instance of [TreeBuilder] class from provided /// [repo]sitory and optional [tree] objects. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. TreeBuilder({required Repository repo, Tree? tree}) { _treeBuilderPointer = bindings.create( repoPointer: repo.pointer, sourcePointer: tree?.pointer ?? nullptr, ); + _finalizer.attach(this, _treeBuilderPointer, detach: this); } /// Pointer to memory address for allocated tree builder object. @@ -31,9 +30,6 @@ class TreeBuilder { /// Returns an entry from the tree builder with provided [filename]. /// - /// **IMPORTANT**: the returned entry is owned by the tree builder and - /// should not be freed manually. - /// /// Throws [ArgumentError] if nothing found for provided [filename]. TreeEntry operator [](String filename) { return TreeEntry( @@ -83,10 +79,19 @@ class TreeBuilder { } /// Releases memory allocated for tree builder object and all the entries. - void free() => bindings.free(_treeBuilderPointer); + void free() { + bindings.free(_treeBuilderPointer); + _finalizer.detach(this); + } @override String toString() { return 'TreeBuilder{length: $length}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/worktree.dart b/lib/src/worktree.dart index 2855576..bca7e3d 100644 --- a/lib/src/worktree.dart +++ b/lib/src/worktree.dart @@ -15,8 +15,6 @@ class Worktree { /// /// [path] is the path to create working tree at. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Worktree.create({ required Repository repo, @@ -30,15 +28,15 @@ class Worktree { path: path, refPointer: ref?.pointer, ); + _finalizer.attach(this, _worktreePointer, detach: this); } /// Lookups existing worktree in [repo] with provided [name]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Worktree.lookup({required Repository repo, required String name}) { _worktreePointer = bindings.lookup(repoPointer: repo.pointer, name: name); + _finalizer.attach(this, _worktreePointer, detach: this); } /// Pointer to memory address for allocated branch object. @@ -88,10 +86,19 @@ class Worktree { bool get isValid => bindings.isValid(_worktreePointer); /// Releases memory allocated for worktree object. - void free() => bindings.free(_worktreePointer); + void free() { + bindings.free(_worktreePointer); + _finalizer.detach(this); + } @override String toString() { return 'Worktree{name: $name, path: $path}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/pubspec.yaml b/pubspec.yaml index 969e2c3..5f92734 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,19 +3,20 @@ description: Dart bindings to libgit2 version: 0.0.1 environment: - sdk: ">=2.16.0 <3.0.0" - flutter: ">=2.10.0" + sdk: ">=2.17.0 <3.0.0" + flutter: ">=2.13.0-0.0.pre.578" dependencies: args: ^2.3.0 cli_util: ^0.3.5 ffi: ^1.1.2 + meta: ^1.7.0 + path: ^1.8.1 pub_cache: ^0.3.1 dev_dependencies: ffigen: ^4.1.2 - lints: ^1.0.1 - path: ^1.8.1 + lints: ^2.0.0 test: ^1.20.0 flutter: diff --git a/test/annotated_test.dart b/test/annotated_test.dart index c01441a..78a4d8b 100644 --- a/test/annotated_test.dart +++ b/test/annotated_test.dart @@ -29,8 +29,6 @@ void main() { expect(annotated.oid, tip); expect(annotated.refName, ''); - - annotated.free(); }); test('throws when trying to lookup annotated commit with invalid oid', () { @@ -41,33 +39,27 @@ void main() { }); test('creates annotated commit from provided reference', () { - final reference = Reference.lookup(repo: repo, name: 'refs/heads/master'); + const refName = 'refs/heads/master'; + final reference = Reference.lookup(repo: repo, name: refName); final annotated = AnnotatedCommit.fromReference( repo: repo, reference: reference, ); expect(annotated.oid, reference.target); - expect(annotated.refName, 'refs/heads/master'); - - annotated.free(); - reference.free(); + expect(annotated.refName, refName); }); test( 'throws when trying to create annotated commit from provided ' 'reference and error occurs', () { - final reference = Reference.lookup(repo: repo, name: 'refs/heads/master'); - expect( () => AnnotatedCommit.fromReference( repo: Repository(nullptr), - reference: reference, + reference: Reference.lookup(repo: repo, name: 'refs/heads/master'), ), throwsA(isA()), ); - - reference.free(); }); test('creates annotated commit from provided revspec', () { @@ -75,8 +67,6 @@ void main() { expect(annotated.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(annotated.refName, ''); - - annotated.free(); }); test('throws when trying to create annotated commit from invalid revspec', @@ -98,8 +88,6 @@ void main() { expect(annotated.oid, oid); expect(annotated.refName, 'master'); - - annotated.free(); }); test( @@ -115,5 +103,10 @@ void main() { throwsA(isA()), ); }); + + test('manually releases allocated memory', () { + final annotated = AnnotatedCommit.lookup(repo: repo, oid: tip); + expect(() => annotated.free(), returnsNormally); + }); }); } diff --git a/test/blame_test.dart b/test/blame_test.dart index 2aa2362..dea5099 100644 --- a/test/blame_test.dart +++ b/test/blame_test.dart @@ -52,8 +52,6 @@ void main() { }); tearDown(() { - sig1.free(); - sig2.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -82,8 +80,6 @@ void main() { expect(blame[i].isBoundary, hunks[i]['isBoundary']); expect(blame[i].originPath, 'feature_file'); } - - blame.free(); }); test('throws when provided file path is invalid', () { @@ -104,9 +100,6 @@ void main() { expect(blameHunk.originCommitter, null); expect(blameHunk.finalCommitOid.sha, '0' * 40); expect(blameHunk.finalCommitter, null); - - bufferBlame.free(); - blame.free(); }); test('throws when trying to get blame for empty buffer', () { @@ -115,7 +108,6 @@ void main() { () => Blame.buffer(reference: blame, buffer: ''), throwsA(isA()), ); - blame.free(); }); test('returns the blame for provided file with minMatchCharacters set', () { @@ -127,8 +119,6 @@ void main() { ); expect(blame.length, 2); - - blame.free(); }); test('returns the blame for provided line', () { @@ -148,22 +138,16 @@ void main() { expect(hunk.originCommitter, hunks[0]['originCommitter']); expect(hunk.isBoundary, hunks[0]['isBoundary']); expect(hunk.originPath, 'feature_file'); - - blame.free(); }); test('throws when provided index for hunk is invalid', () { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(() => blame[10], throwsA(isA())); - - blame.free(); }); test('throws when provided line number for hunk is invalid', () { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(() => blame.forLine(10), throwsA(isA())); - - blame.free(); }); test('returns the blame for provided file with newestCommit argument', () { @@ -190,8 +174,6 @@ void main() { expect(hunk.originCommitter, hunks[0]['originCommitter']); expect(hunk.isBoundary, hunks[0]['isBoundary']); expect(hunk.originPath, 'feature_file'); - - blame.free(); }); test('returns the blame for provided file with minLine and maxLine set', @@ -219,14 +201,16 @@ void main() { expect(blame[i].isBoundary, hunks[i]['isBoundary']); expect(blame[i].originPath, 'feature_file'); } + }); - blame.free(); + test('manually releases allocated memory', () { + final blame = Blame.file(repo: repo, path: 'feature_file'); + expect(() => blame.free(), returnsNormally); }); test('returns string representation of BlameHunk object', () { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(blame.toString(), contains('BlameHunk{')); - blame.free(); }); }); } diff --git a/test/blob_test.dart b/test/blob_test.dart index 0fdbe4d..c022eeb 100644 --- a/test/blob_test.dart +++ b/test/blob_test.dart @@ -26,9 +26,7 @@ void main() { group('Blob', () { test('lookups blob with provided oid', () { - final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); - expect(blob, isA()); - blob.free(); + expect(Blob.lookup(repo: repo, oid: repo[blobSHA]), isA()); }); test('throws when trying to lookup with invalid oid', () { @@ -45,8 +43,6 @@ void main() { expect(blob.isBinary, false); expect(blob.size, 13); expect(blob.content, blobContent); - - blob.free(); }); test('creates new blob with provided content', () { @@ -57,14 +53,11 @@ void main() { expect(newBlob.isBinary, false); expect(newBlob.size, 9); expect(newBlob.content, newBlobContent); - - newBlob.free(); }); test('throws when trying to create new blob and error occurs', () { - final nullRepo = Repository(nullptr); expect( - () => Blob.create(repo: nullRepo, content: ''), + () => Blob.create(repo: Repository(nullptr), content: ''), throwsA(isA()), ); }); @@ -80,8 +73,6 @@ void main() { expect(newBlob.isBinary, false); expect(newBlob.size, 13); expect(newBlob.content, blobContent); - - newBlob.free(); }); test('throws when creating new blob from invalid path', () { @@ -103,8 +94,6 @@ void main() { expect(newBlob, isA()); expect(newBlob.isBinary, false); - - newBlob.free(); }); test('throws when trying to create from invalid path', () { @@ -119,9 +108,6 @@ void main() { final dupBlob = blob.duplicate(); expect(blob.oid.sha, dupBlob.oid.sha); - - dupBlob.free(); - blob.free(); }); test('filters content of a blob', () { @@ -129,8 +115,6 @@ void main() { final blob = Blob.lookup(repo: repo, oid: blobOid); expect(blob.filterContent(asPath: 'file.crlf'), 'clrf\r\nclrf\r\n'); - - blob.free(); }); test('filters content of a blob with provided commit for attributes', () { @@ -152,9 +136,6 @@ void main() { ), 'clrf\r\nclrf\r\n', ); - - commit.free(); - blob.free(); }); test('throws when trying to filter content of a blob and error occurs', () { @@ -164,10 +145,14 @@ void main() { ); }); + test('manually releases allocated memory', () { + final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); + expect(() => blob.free(), returnsNormally); + }); + test('returns string representation of Blob object', () { final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); expect(blob.toString(), contains('Blob{')); - blob.free(); }); }); } diff --git a/test/branch_test.dart b/test/branch_test.dart index d8bdbd1..5c0aafa 100644 --- a/test/branch_test.dart +++ b/test/branch_test.dart @@ -34,8 +34,6 @@ void main() { for (var i = 0; i < branches.length; i++) { expect(branches[i].name, branchesExpected[i]); expect(aliasBranches[i].name, branchesExpected[i]); - branches[i].free(); - aliasBranches[i].free(); } }); @@ -47,8 +45,6 @@ void main() { for (var i = 0; i < branches.length; i++) { expect(branches[i].name, branchesExpected[i]); expect(aliasBranches[i].name, branchesExpected[i]); - branches[i].free(); - aliasBranches[i].free(); } }); @@ -60,8 +56,6 @@ void main() { for (var i = 0; i < branches.length; i++) { expect(branches[i].name, branchesExpected[i]); expect(aliasBranches[i].name, branchesExpected[i]); - branches[i].free(); - aliasBranches[i].free(); } }); @@ -75,7 +69,6 @@ void main() { test('returns a branch with provided name', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.target.sha, lastCommit.sha); - branch.free(); }); test('throws when provided name not found', () { @@ -95,88 +88,66 @@ void main() { }); test('checks if branch is current head', () { - final masterBranch = Branch.lookup(repo: repo, name: 'master'); - final featureBranch = Branch.lookup(repo: repo, name: 'feature'); - - expect(masterBranch.isHead, true); - expect(featureBranch.isHead, false); - - masterBranch.free(); - featureBranch.free(); + expect(Branch.lookup(repo: repo, name: 'master').isHead, true); + expect(Branch.lookup(repo: repo, name: 'feature').isHead, false); }); test('throws when checking if branch is current head and error occurs', () { - final nullBranch = Branch(nullptr); expect( - () => nullBranch.isHead, + () => Branch(nullptr).isHead, throwsA(isA()), ); }); test('checks if branch is checked out', () { - final masterBranch = Branch.lookup(repo: repo, name: 'master'); - final featureBranch = Branch.lookup(repo: repo, name: 'feature'); - - expect(masterBranch.isCheckedOut, true); - expect(featureBranch.isCheckedOut, false); - - masterBranch.free(); - featureBranch.free(); + expect(Branch.lookup(repo: repo, name: 'master').isCheckedOut, true); + expect(Branch.lookup(repo: repo, name: 'feature').isCheckedOut, false); }); test('throws when checking if branch is checked out and error occurs', () { - final nullBranch = Branch(nullptr); expect( - () => nullBranch.isCheckedOut, + () => Branch(nullptr).isCheckedOut, throwsA(isA()), ); }); test('returns name', () { - final branch = Branch.lookup(repo: repo, name: 'master'); - expect(branch.name, 'master'); - branch.free(); + expect(Branch.lookup(repo: repo, name: 'master').name, 'master'); }); test('throws when getting name and error occurs', () { - final nullBranch = Branch(nullptr); - expect(() => nullBranch.name, throwsA(isA())); + expect(() => Branch(nullptr).name, throwsA(isA())); }); test('returns remote name of a remote-tracking branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(branch.remoteName, 'origin'); - branch.free(); }); test( 'throws when getting remote name of a remote-tracking branch and ' 'error occurs', () { - final branch = Branch.lookup(repo: repo, name: 'master'); - expect(() => branch.remoteName, throwsA(isA())); + expect( + () => Branch.lookup(repo: repo, name: 'master').remoteName, + throwsA(isA()), + ); }); test('returns upstream of a local branch', () { - final branch = Branch.lookup(repo: repo, name: 'master'); - final upstream = branch.upstream; + final upstream = Branch.lookup(repo: repo, name: 'master').upstream; expect(upstream.isRemote, true); expect(upstream.name, 'refs/remotes/origin/master'); - - upstream.free(); - branch.free(); }); test('throws when trying to get upstream of a remote branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(() => branch.upstream, throwsA(isA())); - branch.free(); }); test('sets upstream of a branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); - var upstream = branch.upstream; - expect(upstream.name, 'refs/remotes/origin/master'); + expect(branch.upstream.name, 'refs/remotes/origin/master'); final ref = Reference.create( repo: repo, @@ -185,24 +156,15 @@ void main() { ); branch.setUpstream(ref.shorthand); - upstream = branch.upstream; - expect(upstream.name, 'refs/remotes/origin/new'); - - ref.free(); - upstream.free(); - branch.free(); + expect(branch.upstream.name, 'refs/remotes/origin/new'); }); test('unsets upstream of a branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); - final upstream = branch.upstream; - expect(upstream.name, 'refs/remotes/origin/master'); + expect(branch.upstream.name, 'refs/remotes/origin/master'); branch.setUpstream(null); expect(() => branch.upstream, throwsA(isA())); - - upstream.free(); - branch.free(); }); test('throws when trying to set upstream of a branch and error occurs', () { @@ -211,64 +173,61 @@ void main() { () => branch.setUpstream('some/upstream'), throwsA(isA()), ); - branch.free(); }); test('returns upstream name of a local branch', () { - final branch = Branch.lookup(repo: repo, name: 'master'); - expect(branch.upstreamName, 'refs/remotes/origin/master'); - branch.free(); + expect( + Branch.lookup(repo: repo, name: 'master').upstreamName, + 'refs/remotes/origin/master', + ); }); test('throws when trying to get upstream name of a branch and error occurs', () { - final branch = Branch.lookup(repo: repo, name: 'feature'); - expect(() => branch.upstreamName, throwsA(isA())); - branch.free(); + expect( + () => Branch.lookup(repo: repo, name: 'feature').upstreamName, + throwsA(isA()), + ); }); test('returns upstream remote of a local branch', () { - final branch = Branch.lookup(repo: repo, name: 'master'); - expect(branch.upstreamRemote, 'origin'); - branch.free(); + expect( + Branch.lookup(repo: repo, name: 'master').upstreamRemote, + 'origin', + ); }); test('throws when trying to get upstream remote of a remote branch', () { - final branch = Branch.list(repo: repo, type: GitBranch.remote).first; - expect(() => branch.upstreamRemote, throwsA(isA())); - branch.free(); + expect( + () => Branch.list(repo: repo, type: GitBranch.remote) + .first + .upstreamRemote, + throwsA(isA()), + ); }); test('returns upstream merge of a local branch', () { - final branch = Branch.lookup(repo: repo, name: 'master'); - expect(branch.upstreamMerge, 'refs/heads/master'); - branch.free(); + expect( + Branch.lookup(repo: repo, name: 'master').upstreamMerge, + 'refs/heads/master', + ); }); test('throws when trying to get upstream merge of a remote branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(() => branch.upstreamMerge, throwsA(isA())); - branch.free(); }); group('create()', () { test('creates branch', () { - final commit = Commit.lookup(repo: repo, oid: lastCommit); final branch = Branch.create( repo: repo, name: 'testing', - target: commit, + target: Commit.lookup(repo: repo, oid: lastCommit), ); - final branches = Branch.list(repo: repo); - expect(branches.length, 4); + expect(Branch.list(repo: repo).length, 4); expect(branch.target, lastCommit); - - for (final branch in branches) { - branch.free(); - } - branch.free(); - commit.free(); }); test('throws when name already exists', () { @@ -278,28 +237,18 @@ void main() { () => Branch.create(repo: repo, name: 'feature', target: commit), throwsA(isA()), ); - - commit.free(); }); test('creates branch with force flag when name already exists', () { - final commit = Commit.lookup(repo: repo, oid: lastCommit); final branch = Branch.create( repo: repo, name: 'feature', - target: commit, + target: Commit.lookup(repo: repo, oid: lastCommit), force: true, ); - final localBranches = Branch.list(repo: repo, type: GitBranch.local); - expect(localBranches.length, 2); + expect(Branch.list(repo: repo, type: GitBranch.local).length, 2); expect(branch.target, lastCommit); - - for (final branch in localBranches) { - branch.free(); - } - branch.free(); - commit.free(); }); }); @@ -324,20 +273,16 @@ void main() { group('rename()', () { test('renames branch', () { Branch.rename(repo: repo, oldName: 'feature', newName: 'renamed'); - final branch = Branch.lookup(repo: repo, name: 'renamed'); - final branches = Branch.list(repo: repo); - expect(branches.length, 3); + expect(Branch.list(repo: repo).length, 3); expect( () => Branch.lookup(repo: repo, name: 'feature'), throwsA(isA()), ); - expect(branch.target, featureCommit); - - for (final branch in branches) { - branch.free(); - } - branch.free(); + expect( + Branch.lookup(repo: repo, name: 'renamed').target, + featureCommit, + ); }); test('throws when name already exists', () { @@ -358,11 +303,8 @@ void main() { newName: 'feature', force: true, ); - final branch = Branch.lookup(repo: repo, name: 'feature'); - expect(branch.target, lastCommit); - - branch.free(); + expect(Branch.lookup(repo: repo, name: 'feature').target, lastCommit); }); test('throws when name is invalid', () { @@ -377,10 +319,14 @@ void main() { }); }); + test('manually releases allocated memory', () { + final branch = Branch.lookup(repo: repo, name: 'master'); + expect(() => branch.free(), returnsNormally); + }); + test('returns string representation of Branch object', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.toString(), contains('Branch{')); - branch.free(); }); }); } diff --git a/test/checkout_test.dart b/test/checkout_test.dart index 4c6b318..7029b9f 100644 --- a/test/checkout_test.dart +++ b/test/checkout_test.dart @@ -72,8 +72,7 @@ void main() { }); test('checkouts reference', () { - final masterHead = Commit.lookup(repo: repo, oid: repo['821ed6e']); - final masterTree = masterHead.tree; + final masterTree = Commit.lookup(repo: repo, oid: repo['821ed6e']).tree; expect( masterTree.entries.any((e) => e.name == 'another_feature_file'), false, @@ -82,19 +81,12 @@ void main() { Checkout.reference(repo: repo, name: 'refs/heads/feature'); final featureHead = Commit.lookup(repo: repo, oid: repo['5aecfa0']); final featureTree = featureHead.tree; - final repoHead = repo.head; // does not change HEAD - expect(repoHead.target, isNot(featureHead.oid)); + expect(repo.head.target, isNot(featureHead.oid)); expect( featureTree.entries.any((e) => e.name == 'another_feature_file'), true, ); - - repoHead.free(); - featureTree.free(); - featureHead.free(); - masterTree.free(); - masterHead.free(); }); test( @@ -111,20 +103,14 @@ void main() { }); test('checkouts commit', () { - final index = repo.index; - expect(index.find('another_feature_file'), equals(false)); + expect(repo.index.find('another_feature_file'), equals(false)); final featureHead = Commit.lookup(repo: repo, oid: repo['5aecfa0']); Checkout.commit(repo: repo, commit: featureHead); - final repoHead = repo.head; // does not change HEAD - expect(repoHead.target, isNot(featureHead.oid)); - expect(index.find('another_feature_file'), equals(true)); - - repoHead.free(); - featureHead.free(); - index.free(); + expect(repo.head.target, isNot(featureHead.oid)); + expect(repo.index.find('another_feature_file'), equals(true)); }); test('checkouts commit with provided path', () { @@ -135,35 +121,27 @@ void main() { paths: ['another_feature_file'], ); - final repoHead = repo.head; // does not change HEAD - expect(repoHead.target, isNot(featureHead.oid)); + expect(repo.head.target, isNot(featureHead.oid)); expect( repo.status, { 'another_feature_file': {GitStatus.indexNew} }, ); - - repoHead.free(); - featureHead.free(); }); test( 'throws when trying to checkout commit with invalid alternative ' 'directory', () { - final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - expect( () => Checkout.commit( repo: repo, - commit: commit, + commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), directory: 'not/there', ), throwsA(isA()), ); - - commit.free(); }); test('checkouts with alrenative directory', () { @@ -208,8 +186,7 @@ void main() { }); test('performs dry run checkout', () { - final index = repo.index; - expect(index.length, 4); + expect(repo.index.length, 4); final file = File(p.join(repo.workdir, 'another_feature_file')); expect(file.existsSync(), false); @@ -218,10 +195,8 @@ void main() { name: 'refs/heads/feature', strategy: {GitCheckout.dryRun}, ); - expect(index.length, 4); + expect(repo.index.length, 4); expect(file.existsSync(), false); - - index.free(); }); }); } diff --git a/test/commit_test.dart b/test/commit_test.dart index 4d77b58..7222b68 100644 --- a/test/commit_test.dart +++ b/test/commit_test.dart @@ -34,18 +34,13 @@ void main() { }); tearDown(() { - author.free(); - committer.free(); - tree.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); group('Commit', () { test('lookups commit for provided oid', () { - final commit = Commit.lookup(repo: repo, oid: tip); - expect(commit, isA()); - commit.free(); + expect(Commit.lookup(repo: repo, oid: tip), isA()); }); test('throws when trying to lookup with invalid oid', () { @@ -63,18 +58,14 @@ void main() { test('reverts commit affecting index and workdir', () { final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); - final index = repo.index; final file = File(p.join(repo.workdir, 'dir', 'dir_file.txt')); - expect(index.find('dir/dir_file.txt'), true); + expect(repo.index.find('dir/dir_file.txt'), true); expect(file.existsSync(), true); commit.revert(); - expect(index.find('dir/dir_file.txt'), false); + expect(repo.index.find('dir/dir_file.txt'), false); expect(file.existsSync(), false); - - index.free(); - commit.free(); }); test('throws when trying to revert and error occurs', () { @@ -82,21 +73,16 @@ void main() { }); test('reverts commit to provided commit', () { - final to = Commit.lookup(repo: repo, oid: repo['78b8bf1']); - final from = Commit.lookup(repo: repo, oid: repo['821ed6e']); - final index = repo.index; final file = File(p.join(repo.workdir, 'dir', 'dir_file.txt')); - expect(index.find('dir/dir_file.txt'), true); + expect(repo.index.find('dir/dir_file.txt'), true); expect(file.existsSync(), true); - final revertIndex = from.revertTo(commit: to); + final from = Commit.lookup(repo: repo, oid: repo['821ed6e']); + final revertIndex = from.revertTo( + commit: Commit.lookup(repo: repo, oid: repo['78b8bf1']), + ); expect(revertIndex.find('dir/dir_file.txt'), false); expect(file.existsSync(), true); - - revertIndex.free(); - index.free(); - to.free(); - from.free(); }); test('throws when trying to revert commit and error occurs', () { @@ -114,13 +100,9 @@ void main() { expect(commit1.descendantOf(commit2.oid), true); expect(commit1.descendantOf(commit1.oid), false); expect(commit2.descendantOf(commit1.oid), false); - - commit1.free(); - commit2.free(); }); test('creates commit', () { - final parent = Commit.lookup(repo: repo, oid: tip); final oid = Commit.create( repo: repo, updateRef: 'HEAD', @@ -128,7 +110,7 @@ void main() { author: author, committer: committer, tree: tree, - parents: [parent], + parents: [Commit.lookup(repo: repo, oid: tip)], ); final commit = Commit.lookup(repo: repo, oid: oid); @@ -146,13 +128,9 @@ void main() { expect(commit.treeOid, tree.oid); expect(commit.parents.length, 1); expect(commit.parents[0], tip); - - commit.free(); - parent.free(); }); test('writes commit without parents into the buffer', () { - final parent = Commit.lookup(repo: repo, oid: tip); final commit = Commit.createBuffer( repo: repo, updateRef: 'HEAD', @@ -174,12 +152,9 @@ Some description. """; expect(commit, expected); - - parent.free(); }); test('writes commit into the buffer', () { - final parent = Commit.lookup(repo: repo, oid: tip); final commit = Commit.createBuffer( repo: repo, updateRef: 'HEAD', @@ -187,7 +162,7 @@ Some description. author: author, committer: committer, tree: tree, - parents: [parent], + parents: [Commit.lookup(repo: repo, oid: tip)], ); const expected = """ @@ -202,8 +177,6 @@ Some description. """; expect(commit, expected); - - parent.free(); }); test('creates commit without parents', () { @@ -227,8 +200,6 @@ Some description. expect(commit.time, 124); expect(commit.treeOid, tree.oid); expect(commit.parents.length, 0); - - commit.free(); }); test('creates commit with 2 parents', () { @@ -255,59 +226,44 @@ Some description. expect(commit.time, 124); expect(commit.treeOid, tree.oid); expect(commit.parents.length, 2); - expect(commit.parents[0], tip); + expect(commit.parents[0], parent1.oid); expect(commit.parents[1], parent2.oid); - - parent1.free(); - parent2.free(); - commit.free(); }); test('throws when trying to create commit and error occurs', () { - final parent = Commit.lookup(repo: repo, oid: tip); - final nullRepo = Repository(nullptr); - expect( () => Commit.create( - repo: nullRepo, + repo: Repository(nullptr), updateRef: 'HEAD', message: message, author: author, committer: committer, tree: tree, - parents: [parent], + parents: [Commit.lookup(repo: repo, oid: tip)], ), throwsA(isA()), ); - - parent.free(); }); test('throws when trying to write commit into a buffer and error occurs', () { - final parent = Commit.lookup(repo: repo, oid: tip); - final nullRepo = Repository(nullptr); - expect( () => Commit.createBuffer( - repo: nullRepo, + repo: Repository(nullptr), updateRef: 'HEAD', message: message, author: author, committer: committer, tree: tree, - parents: [parent], + parents: [Commit.lookup(repo: repo, oid: tip)], ), throwsA(isA()), ); - - parent.free(); }); test('amends commit with default arguments', () { - final oldHead = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); - expect(commit.oid, oldHead.target); + expect(commit.oid, repo.head.target); final amendedOid = Commit.amend( repo: repo, @@ -316,25 +272,18 @@ Some description. updateRef: 'HEAD', ); final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid); - final newHead = repo.head; - expect(amendedCommit.oid, newHead.target); + expect(amendedCommit.oid, repo.head.target); expect(amendedCommit.message, 'amended commit\n'); expect(amendedCommit.author, commit.author); expect(amendedCommit.committer, commit.committer); expect(amendedCommit.treeOid, commit.treeOid); expect(amendedCommit.parents, commit.parents); - - amendedCommit.free(); - commit.free(); - newHead.free(); - oldHead.free(); }); test('amends commit with provided arguments', () { - final oldHead = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); - expect(commit.oid, oldHead.target); + expect(commit.oid, repo.head.target); final amendedOid = Commit.amend( repo: repo, @@ -346,25 +295,18 @@ Some description. tree: tree, ); final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid); - final newHead = repo.head; - expect(amendedCommit.oid, newHead.target); + expect(amendedCommit.oid, repo.head.target); expect(amendedCommit.message, 'amended commit\n'); expect(amendedCommit.author, author); expect(amendedCommit.committer, committer); expect(amendedCommit.treeOid, tree.oid); expect(amendedCommit.parents, commit.parents); - - amendedCommit.free(); - commit.free(); - newHead.free(); - oldHead.free(); }); test('amends commit that is not the tip of the branch', () { - final head = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']); - expect(commit.oid, isNot(head.target)); + expect(commit.oid, isNot(repo.head.target)); expect( () => Commit.amend( @@ -375,17 +317,13 @@ Some description. ), returnsNormally, ); - - commit.free(); - head.free(); }); test( 'throws when trying to amend commit that is not the tip of the branch ' 'with HEAD provided as update reference', () { - final head = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']); - expect(commit.oid, isNot(head.target)); + expect(commit.oid, isNot(repo.head.target)); expect( () => Commit.amend( @@ -396,9 +334,6 @@ Some description. ), throwsA(isA()), ); - - commit.free(); - head.free(); }); test('creates an in-memory copy of a commit', () { @@ -406,9 +341,6 @@ Some description. final dupCommit = commit.duplicate(); expect(dupCommit.oid, commit.oid); - - dupCommit.free(); - commit.free(); }); test('returns header field', () { @@ -417,7 +349,6 @@ Some description. commit.headerField('parent'), '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8', ); - commit.free(); }); test('throws when header field not found', () { @@ -426,24 +357,19 @@ Some description. () => commit.headerField('not-there'), throwsA(isA()), ); - commit.free(); }); test('returns nth generation ancestor commit', () { - final commit = Commit.lookup(repo: repo, oid: tip); - final ancestor = commit.nthGenAncestor(3); - + final ancestor = Commit.lookup(repo: repo, oid: tip).nthGenAncestor(3); expect(ancestor.oid.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); - - ancestor.free(); - commit.free(); }); test('throws when trying to get nth generation ancestor and none exists', () { - final commit = Commit.lookup(repo: repo, oid: tip); - expect(() => commit.nthGenAncestor(10), throwsA(isA())); - commit.free(); + expect( + () => Commit.lookup(repo: repo, oid: tip).nthGenAncestor(10), + throwsA(isA()), + ); }); test('returns parent at specified position', () { @@ -453,22 +379,23 @@ Some description. expect(firstParent.oid.sha, 'c68ff54aabf660fcdd9a2838d401583fe31249e3'); expect(secondParent.oid.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - - secondParent.free(); - firstParent.free(); - commit.free(); }); test('throws when trying to get the parent at invalid position', () { + expect( + () => Commit.lookup(repo: repo, oid: tip).parent(10), + throwsA(isA()), + ); + }); + + test('manually releases allocated memory', () { final commit = Commit.lookup(repo: repo, oid: tip); - expect(() => commit.parent(10), throwsA(isA())); - commit.free(); + expect(() => commit.free(), returnsNormally); }); test('returns string representation of Commit object', () { final commit = Commit.lookup(repo: repo, oid: tip); expect(commit.toString(), contains('Commit{')); - commit.free(); }); }); } diff --git a/test/config_test.dart b/test/config_test.dart index 77544c2..b14a8b7 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -32,7 +32,6 @@ void main() { }); tearDown(() { - config.free(); File(filePath).deleteSync(); }); @@ -45,9 +44,7 @@ void main() { 'opens the global, XDG and system configuration files ' '(if they are present) if no path provided', () { try { - final config = Config.open(); - expect(config, isA()); - config.free(); + expect(Config.open(), isA()); } catch (e) { expect(() => Config.open(), throwsA(isA())); } @@ -59,9 +56,7 @@ void main() { test('opens system file or throws is there is none', () { try { - final config = Config.system(); - expect(config, isA()); - config.free(); + expect(Config.system(), isA()); } catch (e) { expect(() => Config.system(), throwsA(isA())); } @@ -69,9 +64,7 @@ void main() { test('opens global file or throws is there is none', () { try { - final config = Config.global(); - expect(config, isA()); - config.free(); + expect(Config.global(), isA()); } catch (e) { expect(() => Config.global(), throwsA(isA())); } @@ -79,18 +72,14 @@ void main() { test('opens xdg file or throws is there is none', () { try { - final config = Config.xdg(); - expect(config, isA()); - config.free(); + expect(Config.xdg(), isA()); } catch (e) { expect(() => Config.xdg(), throwsA(isA())); } }); test('returns config snapshot', () { - final snapshot = config.snapshot; - expect(snapshot, isA()); - snapshot.free(); + expect(config.snapshot, isA()); }); test('returns config entries and their values', () { @@ -219,6 +208,11 @@ void main() { }); }); + test('manually releases allocated memory', () { + final config = Config.open(filePath); + expect(() => config.free(), returnsNormally); + }); + test('returns string representation of ConfigEntry object', () { final entry = config.first; expect(entry.toString(), contains('ConfigEntry{')); diff --git a/test/describe_test.dart b/test/describe_test.dart index 16d4697..6a6f267 100644 --- a/test/describe_test.dart +++ b/test/describe_test.dart @@ -27,8 +27,10 @@ void main() { }); test('throws when trying to describe and error occurs', () { - final nullRepo = Repository(nullptr); - expect(() => nullRepo.describe(), throwsA(isA())); + expect( + () => Repository(nullptr).describe(), + throwsA(isA()), + ); }); test('describes commit', () { @@ -41,30 +43,32 @@ void main() { }); test('throws when trying to describe and no reference found', () { - final commit = Commit.lookup(repo: repo, oid: repo['f17d0d4']); - expect(() => repo.describe(commit: commit), throwsA(isA())); - commit.free(); + expect( + () => repo.describe( + commit: Commit.lookup(repo: repo, oid: repo['f17d0d4']), + ), + throwsA(isA()), + ); }); test('returns oid when fallback argument is provided', () { - final commit = Commit.lookup(repo: repo, oid: repo['f17d0d4']); expect( - repo.describe(commit: commit, showCommitOidAsFallback: true), + repo.describe( + commit: Commit.lookup(repo: repo, oid: repo['f17d0d4']), + showCommitOidAsFallback: true, + ), 'f17d0d4', ); - commit.free(); }); test('describes with provided strategy', () { - final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); expect( repo.describe( - commit: commit, + commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), describeStrategy: GitDescribeStrategy.all, ), 'heads/feature', ); - commit.free(); }); test('describes with provided pattern', () { @@ -73,7 +77,6 @@ void main() { email: 'author@email.com', time: 1234, ); - final commit = Commit.lookup(repo: repo, oid: repo['fc38877']); Tag.createAnnotated( repo: repo, tagName: 'test/tag1', @@ -84,28 +87,25 @@ void main() { ); expect( - repo.describe(commit: commit, pattern: 'test/*'), + repo.describe( + commit: Commit.lookup(repo: repo, oid: repo['fc38877']), + pattern: 'test/*', + ), 'test/tag1-2-gfc38877', ); - - commit.free(); - signature.free(); }); test('describes and follows first parent only', () { - final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); Tag.delete(repo: repo, name: 'v0.2'); expect( repo.describe( - commit: commit, + commit: Commit.lookup(repo: repo, oid: repo['821ed6e']), onlyFollowFirstParent: true, describeStrategy: GitDescribeStrategy.tags, ), 'v0.1-1-g821ed6e', ); - - commit.free(); }); test('describes with provided abbreviated size', () { @@ -129,8 +129,6 @@ void main() { ), 'v0.1', ); - - commit.free(); }); test('describes with long format', () { @@ -138,21 +136,13 @@ void main() { }); test('describes and appends dirty suffix', () { - final index = repo.index; - index.clear(); - + repo.index.clear(); expect(repo.describe(dirtySuffix: '-dirty'), 'v0.2-dirty'); - - index.free(); }); test('describes with max candidates tags flag set', () { - final index = repo.index; - index.clear(); - + repo.index.clear(); expect(repo.describe(maxCandidatesTags: 0), 'v0.2'); - - index.free(); }); }); } diff --git a/test/diff_test.dart b/test/diff_test.dart index 0c7b30f..3ee7afc 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -128,76 +128,53 @@ index e69de29..c217c63 100644 tearDown(() { repo.free(); - tmpDir.deleteSync(recursive: true); + if (Platform.isLinux || Platform.isMacOS) { + tmpDir.deleteSync(recursive: true); + } }); group('Diff', () { test('returns diff between index and workdir', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.length, 8); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToWorkdir[i]); } - - diff.free(); - index.free(); }); test('returns diff between index and tree', () { - final index = repo.index; - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final diff = Diff.treeToIndex(repo: repo, tree: tree, index: index); + final diff = Diff.treeToIndex( + repo: repo, + tree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + index: repo.index, + ); expect(diff.length, 8); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToTree[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); - index.free(); }); test('returns diff between index and empty tree', () { - final index = repo.index; - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final diff = Diff.treeToIndex(repo: repo, tree: null, index: index); + final diff = Diff.treeToIndex(repo: repo, tree: null, index: repo.index); expect(diff.length, 12); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToIndex[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); - index.free(); }); test('returns diff between tree and workdir', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final diff = Diff.treeToWorkdir(repo: repo, tree: tree); + final diff = Diff.treeToWorkdir( + repo: repo, + tree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + ); expect(diff.length, 9); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToWorkdir[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); }); test('throws when trying to diff between tree and workdir and error occurs', @@ -209,20 +186,15 @@ index e69de29..c217c63 100644 }); test('returns diff between tree and workdir with index', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; + final diff = Diff.treeToWorkdirWithIndex( + repo: repo, + tree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + ); - final diff = Diff.treeToWorkdirWithIndex(repo: repo, tree: tree); expect(diff.length, 11); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToWorkdirWithIndex[i]); } - - diff.free(); - tree.free(); - commit.free(); - head.free(); }); test( @@ -238,68 +210,51 @@ index e69de29..c217c63 100644 }); test('returns diff between tree and tree', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree1 = commit.tree; - final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']); - final diff = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2); + final diff = Diff.treeToTree( + repo: repo, + oldTree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + newTree: Tree.lookup(repo: repo, oid: repo['b85d53c']), + ); expect(diff.length, 10); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToTree[i]); } - - commit.free(); - head.free(); - tree1.free(); - tree2.free(); - diff.free(); }); test('returns diff between tree and empty tree', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - - final diff = Diff.treeToTree(repo: repo, oldTree: tree, newTree: null); + final diff = Diff.treeToTree( + repo: repo, + oldTree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + newTree: null, + ); expect(diff.length, 11); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToEmptyTree[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); }); test('returns diff between empty tree and tree', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - - final diff = Diff.treeToTree(repo: repo, oldTree: null, newTree: tree); + final diff = Diff.treeToTree( + repo: repo, + oldTree: null, + newTree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + ); expect(diff.length, 11); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToEmptyTree[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); }); test('throws when trying to diff between tree and tree and error occurs', () { - final nullTree = Tree(nullptr); expect( () => Diff.treeToTree( repo: Repository(nullptr), - oldTree: nullTree, - newTree: nullTree, + oldTree: Tree(nullptr), + newTree: Tree(nullptr), ), throwsA(isA()), ); @@ -313,60 +268,44 @@ index e69de29..c217c63 100644 }); test('returns diff between index and index', () { - final index = repo.index; - final emptyIndex = Index.newInMemory(); - final diff = Diff.indexToIndex( repo: repo, - oldIndex: index, - newIndex: emptyIndex, + oldIndex: repo.index, + newIndex: Index.newInMemory(), ); expect(diff.length, 12); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToIndex[i]); } - - index.free(); - emptyIndex.free(); }); test('throws when trying to diff between index and index and error occurs', () { - final index = repo.index; - expect( () => Diff.indexToIndex( repo: repo, - oldIndex: index, + oldIndex: repo.index, newIndex: Index(nullptr), ), throwsA(isA()), ); - - index.free(); }); test('merges diffs', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree1 = commit.tree; - final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']); - final diff1 = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2); - final diff2 = Diff.treeToWorkdir(repo: repo, tree: tree1); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final diff1 = Diff.treeToTree( + repo: repo, + oldTree: commit.tree, + newTree: Tree.lookup(repo: repo, oid: repo['b85d53c']), + ); + final diff2 = Diff.treeToWorkdir(repo: repo, tree: commit.tree); expect(diff1.length, 10); expect(diff2.length, 9); diff1.merge(diff2); expect(diff1.length, 11); - - commit.free(); - head.free(); - tree1.free(); - tree2.free(); - diff1.free(); - diff2.free(); }); test('parses provided diff', () { @@ -377,15 +316,11 @@ index e69de29..c217c63 100644 expect(stats.filesChanged, 1); expect(stats.insertions, 1); expect(diff.patchOid.sha, '699556913185bc38632ae20a49d5c18b9233335e'); - - stats.free(); - diff.free(); }); group('apply', () { test('checks if diff can be applied to repository', () { - final index = repo.index; - final diff1 = Diff.indexToWorkdir(repo: repo, index: index); + final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index); expect( diff1.applies(repo: repo, location: GitApplyLocation.both), false, @@ -397,16 +332,11 @@ index e69de29..c217c63 100644 diff2.applies(repo: repo, location: GitApplyLocation.both), true, ); - - diff1.free(); - diff2.free(); - index.free(); }); test('checks if hunk with provided index can be applied to repository', () { - final index = repo.index; - final diff1 = Diff.indexToWorkdir(repo: repo, index: index); + final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index); expect( diff1.applies(repo: repo, location: GitApplyLocation.both), false, @@ -423,23 +353,16 @@ index e69de29..c217c63 100644 ), true, ); - - diff1.free(); - diff2.free(); - index.free(); }); test('applies diff to repository', () { - final diff = Diff.parse(patchText); final file = File(p.join(tmpDir.path, 'subdir', 'modified_file')); Checkout.head(repo: repo, strategy: {GitCheckout.force}); expect(file.readAsStringSync(), ''); - diff.apply(repo: repo); + Diff.parse(patchText).apply(repo: repo); expect(file.readAsStringSync(), 'Modified content\n'); - - diff.free(); }); test('throws when trying to apply diff and error occurs', () { @@ -455,9 +378,6 @@ index e69de29..c217c63 100644 expect(diff.length, 1); expect(patch.text, patchText); - - patch.free(); - diff.free(); }); test('applies hunk with provided index to repository', () { @@ -470,112 +390,86 @@ index e69de29..c217c63 100644 diff.apply(repo: repo, hunkIndex: hunk.index); expect(file.readAsStringSync(), 'Modified content\n'); - - diff.free(); }); test('does not apply hunk with non existing index', () { - final diff = Diff.parse(patchText); final file = File(p.join(tmpDir.path, 'subdir', 'modified_file')); Checkout.head(repo: repo, strategy: {GitCheckout.force}); expect(file.readAsStringSync(), ''); - diff.apply(repo: repo, hunkIndex: 10); + Diff.parse(patchText).apply(repo: repo, hunkIndex: 10); expect(file.readAsStringSync(), ''); - - diff.free(); }); test('applies diff to tree', () { - final diff = Diff.parse(patchText); - Checkout.head(repo: repo, strategy: {GitCheckout.force}); - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final oldIndex = repo.index; - final oldBlob = Blob.lookup( - repo: repo, - oid: oldIndex['subdir/modified_file'].oid, + expect( + Blob.lookup( + repo: repo, + oid: repo.index['subdir/modified_file'].oid, + ).content, + '', ); - expect(oldBlob.content, ''); - final newIndex = diff.applyToTree(repo: repo, tree: tree); - final newBlob = Blob.lookup( + final newIndex = Diff.parse(patchText).applyToTree( repo: repo, - oid: newIndex['subdir/modified_file'].oid, + tree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + ); + expect( + Blob.lookup( + repo: repo, + oid: newIndex['subdir/modified_file'].oid, + ).content, + 'Modified content\n', ); - expect(newBlob.content, 'Modified content\n'); - - oldBlob.free(); - newBlob.free(); - oldIndex.free(); - newIndex.free(); - tree.free(); - commit.free(); - head.free(); - diff.free(); }); test('applies hunk with provided index to tree', () { + Checkout.head(repo: repo, strategy: {GitCheckout.force}); + + expect( + Blob.lookup( + repo: repo, + oid: repo.index['subdir/modified_file'].oid, + ).content, + '', + ); + final diff = Diff.parse(patchText); final hunk = diff.patches.first.hunks.first; - Checkout.head(repo: repo, strategy: {GitCheckout.force}); - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - - final oldIndex = repo.index; - final oldBlob = Blob.lookup( - repo: repo, - oid: oldIndex['subdir/modified_file'].oid, - ); - expect(oldBlob.content, ''); - final newIndex = diff.applyToTree( repo: repo, - tree: tree, + tree: Commit.lookup(repo: repo, oid: repo.head.target).tree, hunkIndex: hunk.index, ); - final newBlob = Blob.lookup( - repo: repo, - oid: newIndex['subdir/modified_file'].oid, + expect( + Blob.lookup( + repo: repo, + oid: newIndex['subdir/modified_file'].oid, + ).content, + 'Modified content\n', ); - expect(newBlob.content, 'Modified content\n'); - - oldBlob.free(); - newBlob.free(); - oldIndex.free(); - newIndex.free(); - tree.free(); - commit.free(); - head.free(); - diff.free(); }); test('throws when trying to apply diff to tree and error occurs', () { - final diff = Diff.parse(patchText); expect( - () => diff.applyToTree(repo: repo, tree: Tree(nullptr)), + () => Diff.parse(patchText).applyToTree( + repo: repo, + tree: Tree(nullptr), + ), throwsA(isA()), ); }); }); test('finds similar entries', () { - final index = repo.index; - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final oldTree = commit.tree; - final newTree = Tree.lookup(repo: repo, oid: index.writeTree()); - final diff = Diff.treeToTree( repo: repo, - oldTree: oldTree, - newTree: newTree, + oldTree: Commit.lookup(repo: repo, oid: repo.head.target).tree, + newTree: Tree.lookup(repo: repo, oid: repo.index.writeTree()), ); expect( diff.deltas.singleWhere((e) => e.newFile.path == 'staged_new').status, @@ -587,28 +481,18 @@ index e69de29..c217c63 100644 diff.deltas.singleWhere((e) => e.newFile.path == 'staged_new').status, GitDelta.renamed, ); - - commit.free(); - head.free(); - diff.free(); - index.free(); - oldTree.free(); - newTree.free(); }); test('throws when trying to find similar entries and error occurs', () { - final nullDiff = Diff(nullptr); - expect(() => nullDiff.findSimilar(), throwsA(isA())); + expect(() => Diff(nullptr).findSimilar(), throwsA(isA())); }); test('throws when trying to get patch Oid and error occurs', () { - final nullDiff = Diff(nullptr); - expect(() => nullDiff.patchOid, throwsA(isA())); + expect(() => Diff(nullptr).patchOid, throwsA(isA())); }); test('returns deltas', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.deltas[0].numberOfFiles, 1); expect(diff.deltas[0].status, GitDelta.deleted); @@ -635,110 +519,59 @@ index e69de29..c217c63 100644 ); expect(diff.deltas[0].oldFile.mode, GitFilemode.blob); - - diff.free(); - index.free(); }); test('throws when trying to get delta with invalid index', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); - + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(() => diff.deltas[-1], throwsA(isA())); - - diff.free(); - index.free(); }); test('returns patches', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); final patches = diff.patches; expect(patches.length, 8); expect(patches.first.delta.status, GitDelta.deleted); - - for (final p in patches) { - p.free(); - } - diff.free(); - index.free(); }); test('returns stats', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); final stats = diff.stats; expect(stats.insertions, 4); expect(stats.deletions, 2); expect(stats.filesChanged, 8); expect(stats.print(format: {GitDiffStats.full}, width: 80), statsPrint); - - stats.free(); - diff.free(); - index.free(); }); test('throws when trying to get stats and error occurs', () { - final nullDiff = Diff(nullptr); - expect(() => nullDiff.stats, throwsA(isA())); + expect(() => Diff(nullptr).stats, throwsA(isA())); }); test('throws when trying to print stats and error occurs', () { - final nullStats = DiffStats(nullptr); expect( - () => nullStats.print(format: {GitDiffStats.full}, width: 80), + () => DiffStats(nullptr).print(format: {GitDiffStats.full}, width: 80), throwsA(isA()), ); }); test('returns patch diff string', () { - final diff = Diff.parse(patchText); - - expect(diff.patch, patchText); - - diff.free(); + expect(Diff.parse(patchText).patch, patchText); }); - test('returns hunks in a patch', () { + test('manually releases allocated memory', () { final diff = Diff.parse(patchText); - final patch = Patch.fromDiff(diff: diff, index: 0); - final hunk = patch.hunks[0]; - - expect(patch.hunks.length, 1); - expect(hunk.linesCount, 1); - expect(hunk.oldStart, 0); - expect(hunk.oldLines, 0); - expect(hunk.newStart, 1); - expect(hunk.newLines, 1); - expect(hunk.header, '@@ -0,0 +1 @@\n'); - - patch.free(); - diff.free(); + expect(() => diff.free(), returnsNormally); }); - test('returns lines in a hunk', () { - final diff = Diff.parse(patchText); - final patch = Patch.fromDiff(diff: diff, index: 0); - final hunk = patch.hunks[0]; - final line = hunk.lines[0]; - - expect(hunk.lines.length, 1); - expect(line.origin, GitDiffLine.addition); - expect(line.oldLineNumber, -1); - expect(line.newLineNumber, 1); - expect(line.numLines, 1); - expect(line.contentOffset, 155); - expect(line.content, 'Modified content\n'); - - patch.free(); - diff.free(); + test('manually releases allocated memory for DiffStats object', () { + final stats = Diff.parse(patchText).stats; + expect(() => stats.free(), returnsNormally); }); test( - 'returns string representation of Diff, DiffDelta, DiffFile, ' - 'DiffHunk, DiffLine and DiffStats objects', () { + 'returns string representation of Diff, DiffDelta, DiffFile ' + ' and DiffStats objects', () { final diff = Diff.parse(patchText); final patch = Patch.fromDiff(diff: diff, index: 0); final stats = diff.stats; @@ -746,13 +579,7 @@ index e69de29..c217c63 100644 expect(diff.toString(), contains('Diff{')); expect(patch.delta.toString(), contains('DiffDelta{')); expect(patch.delta.oldFile.toString(), contains('DiffFile{')); - expect(patch.hunks[0].toString(), contains('DiffHunk{')); - expect(patch.hunks[0].lines[0].toString(), contains('DiffLine{')); expect(stats.toString(), contains('DiffStats{')); - - stats.free(); - patch.free(); - diff.free(); }); }); } diff --git a/test/index_test.dart b/test/index_test.dart index 1834251..d6605cc 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -20,7 +20,6 @@ void main() { }); tearDown(() { - index.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -152,30 +151,25 @@ void main() { final bare = Repository.open( p.join('test', 'assets', 'empty_bare.git'), ); - final bareIndex = bare.index; + expect(() => bare.index.add('config'), throwsA(isA())); - expect(() => bareIndex.add('config'), throwsA(isA())); - - bareIndex.free(); bare.free(); }); }); group('addFromBuffer()', () { test('updates index entry from a buffer', () { - final entry = index['file']; expect(repo.status, isEmpty); - index.addFromBuffer(entry: entry, buffer: 'updated'); + index.addFromBuffer(entry: index['file'], buffer: 'updated'); expect(repo.status, { 'file': {GitStatus.indexModified, GitStatus.wtModified} }); }); test('throws when trying to update entry and error occurs', () { - final nullEntry = IndexEntry(nullptr); expect( - () => index.addFromBuffer(entry: nullEntry, buffer: ''), + () => index.addFromBuffer(entry: IndexEntry(nullptr), buffer: ''), throwsA(isA()), ); }); @@ -208,11 +202,8 @@ void main() { final bare = Repository.open( p.join('test', 'assets', 'empty_bare.git'), ); - final bareIndex = bare.index; + expect(() => bare.index.addAll([]), throwsA(isA())); - expect(() => bareIndex.addAll([]), throwsA(isA())); - - bareIndex.free(); bare.free(); }); }); @@ -234,14 +225,11 @@ void main() { final bare = Repository.open( p.join('test', 'assets', 'empty_bare.git'), ); - final bareIndex = bare.index; - expect( - () => bareIndex.updateAll(['not_there']), + () => bare.index.updateAll(['not_there']), throwsA(isA()), ); - bareIndex.free(); bare.free(); }); }); @@ -293,9 +281,8 @@ void main() { }); test('reads tree with provided SHA hex', () { - final tree = Tree.lookup(repo: repo, oid: repo['df2b8fc']); expect(index.length, 4); - index.readTree(tree); + index.readTree(Tree.lookup(repo: repo, oid: repo['df2b8fc'])); expect(index.length, 1); @@ -305,8 +292,7 @@ void main() { }); test('writes tree', () { - final oid = index.writeTree(); - expect(oid.sha, 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'); + expect(index.writeTree().sha, 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'); }); test('throws when trying to write tree to invalid repository', () { @@ -320,20 +306,16 @@ void main() { final tmpDir = setupRepo(Directory(mergeRepoPath)); final repo = Repository.open(tmpDir.path); - final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch'); - final index = repo.index; - final commit = AnnotatedCommit.lookup( + Merge.commit( repo: repo, - oid: conflictBranch.target, + commit: AnnotatedCommit.lookup( + repo: repo, + oid: Branch.lookup(repo: repo, name: 'conflict-branch').target, + ), ); - Merge.commit(repo: repo, commit: commit); + expect(() => repo.index.writeTree(), throwsA(isA())); - expect(() => index.writeTree(), throwsA(isA())); - - commit.free(); - conflictBranch.free(); - index.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -356,30 +338,24 @@ void main() { final repoDir = setupRepo(Directory(mergeRepoPath)); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = Branch.lookup( - repo: conflictRepo, - name: 'ancestor-conflict', - ); - final commit = AnnotatedCommit.lookup( - repo: conflictRepo, - oid: conflictBranch.target, - ); - Checkout.reference(repo: conflictRepo, name: 'refs/heads/feature'); conflictRepo.setHead('refs/heads/feature'); - Merge.commit(repo: conflictRepo, commit: commit); + Merge.commit( + repo: conflictRepo, + commit: AnnotatedCommit.lookup( + repo: conflictRepo, + oid: Branch.lookup(repo: conflictRepo, name: 'ancestor-conflict') + .target, + ), + ); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = conflictRepo.index.conflicts['feature_file']!; expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.our?.path, 'feature_file'); expect(conflictedFile.their?.path, 'feature_file'); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -388,27 +364,23 @@ void main() { final repoDir = setupRepo(Directory(mergeRepoPath)); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = Branch.lookup( + Merge.commit( repo: conflictRepo, - name: 'conflict-branch', - ); - final commit = AnnotatedCommit.lookup( - repo: conflictRepo, - oid: conflictBranch.target, + commit: AnnotatedCommit.lookup( + repo: conflictRepo, + oid: Branch.lookup( + repo: conflictRepo, + name: 'conflict-branch', + ).target, + ), ); - Merge.commit(repo: conflictRepo, commit: commit); - - final index = conflictRepo.index; - final conflictedFile = index.conflicts['conflict_file']!; + final conflictedFile = conflictRepo.index.conflicts['conflict_file']!; expect(conflictedFile.ancestor?.path, null); expect(conflictedFile.our?.path, 'conflict_file'); expect(conflictedFile.their?.path, 'conflict_file'); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -417,30 +389,24 @@ void main() { final repoDir = setupRepo(Directory(mergeRepoPath)); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = Branch.lookup( - repo: conflictRepo, - name: 'ancestor-conflict', - ); - final commit = AnnotatedCommit.lookup( - repo: conflictRepo, - oid: conflictBranch.target, - ); - Checkout.reference(repo: conflictRepo, name: 'refs/heads/our-conflict'); conflictRepo.setHead('refs/heads/our-conflict'); - Merge.commit(repo: conflictRepo, commit: commit); + Merge.commit( + repo: conflictRepo, + commit: AnnotatedCommit.lookup( + repo: conflictRepo, + oid: Branch.lookup(repo: conflictRepo, name: 'ancestor-conflict') + .target, + ), + ); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = conflictRepo.index.conflicts['feature_file']!; expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.our?.path, null); expect(conflictedFile.their?.path, 'feature_file'); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -449,30 +415,23 @@ void main() { final repoDir = setupRepo(Directory(mergeRepoPath)); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = Branch.lookup( - repo: conflictRepo, - name: 'their-conflict', - ); - final commit = AnnotatedCommit.lookup( - repo: conflictRepo, - oid: conflictBranch.target, - ); - Checkout.reference(repo: conflictRepo, name: 'refs/heads/feature'); conflictRepo.setHead('refs/heads/feature'); - Merge.commit(repo: conflictRepo, commit: commit); + Merge.commit( + repo: conflictRepo, + commit: AnnotatedCommit.lookup( + repo: conflictRepo, + oid: Branch.lookup(repo: conflictRepo, name: 'their-conflict').target, + ), + ); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = conflictRepo.index.conflicts['feature_file']!; expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.our?.path, 'feature_file'); expect(conflictedFile.their?.path, null); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -481,17 +440,19 @@ void main() { final repoDir = setupRepo(Directory(mergeRepoPath)); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = Branch.lookup( - repo: conflictRepo, - name: 'conflict-branch', - ); - final commit = AnnotatedCommit.lookup( - repo: conflictRepo, - oid: conflictBranch.target, - ); final index = conflictRepo.index; - Merge.commit(repo: conflictRepo, commit: commit); + Merge.commit( + repo: conflictRepo, + commit: AnnotatedCommit.lookup( + repo: conflictRepo, + oid: Branch.lookup( + repo: conflictRepo, + name: 'conflict-branch', + ).target, + ), + ); + expect(index.hasConflicts, true); expect(index['.gitignore'].isConflict, false); expect(index.conflicts['conflict_file']!.our!.isConflict, true); @@ -499,13 +460,11 @@ void main() { final conflictedFile = index.conflicts['conflict_file']!; conflictedFile.remove(); + expect(index.hasConflicts, false); expect(index.conflicts, isEmpty); expect(index.conflicts['conflict_file'], null); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -522,27 +481,27 @@ void main() { final repoDir = setupRepo(Directory(mergeRepoPath)); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = Branch.lookup( - repo: conflictRepo, - name: 'conflict-branch', - ); - final commit = AnnotatedCommit.lookup( - repo: conflictRepo, - oid: conflictBranch.target, - ); final index = conflictRepo.index; - Merge.commit(repo: conflictRepo, commit: commit); + Merge.commit( + repo: conflictRepo, + commit: AnnotatedCommit.lookup( + repo: conflictRepo, + oid: Branch.lookup( + repo: conflictRepo, + name: 'conflict-branch', + ).target, + ), + ); + expect(index.hasConflicts, true); expect(index.conflicts.length, 1); index.cleanupConflict(); + expect(index.hasConflicts, false); expect(index.conflicts, isEmpty); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -554,13 +513,15 @@ void main() { ); }); + test('manually releases allocated memory', () { + expect(() => repo.index.free(), returnsNormally); + }); + test('returns string representation of Index and IndexEntry objects', () { final index = repo.index; expect(index.toString(), contains('Index{')); expect(index['file'].toString(), contains('IndexEntry{')); - - index.free(); }); }); } diff --git a/test/mailmap_test.dart b/test/mailmap_test.dart index 4a0830f..51ed458 100644 --- a/test/mailmap_test.dart +++ b/test/mailmap_test.dart @@ -142,10 +142,7 @@ Santa Claus group('Mailmap', () { test('initializes empty mailmap object', () { - final empty = Mailmap.empty(); - expect(empty, isA()); - - empty.free(); + expect(Mailmap.empty(), isA()); }); test('initializes from provided buffer', () { @@ -158,8 +155,6 @@ Santa Claus [entry['realName'], entry['realEmail']], ); } - - mailmap.free(); }); test('initializes from repository', () { @@ -172,8 +167,6 @@ Santa Claus [entry['realName'], entry['realEmail']], ); } - - mailmap.free(); }); test('throws when initializing from repository and error occurs', () { @@ -192,8 +185,6 @@ Santa Claus [entry['name'], entry['email']], ); } - - mailmap.free(); }); test('adds entries and resolves them', () { @@ -214,21 +205,15 @@ Santa Claus [entry['realName'], entry['realEmail']], ); } - - mailmap.free(); }); test('throws when trying to add entry with empty replace email', () { - final mailmap = Mailmap.empty(); - expect( - () => mailmap.addEntry( + () => Mailmap.empty().addEntry( replaceEmail: ' ', ), throwsA(isA()), ); - - mailmap.free(); }); test('resolves signature', () { @@ -249,8 +234,10 @@ Santa Claus ); expect(mailmap.resolveSignature(signature), realSignature); + }); - mailmap.free(); + test('manually releases allocated memory', () { + expect(() => Mailmap.empty().free(), returnsNormally); }); }); } diff --git a/test/merge_test.dart b/test/merge_test.dart index 7de6a58..8102d9d 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -26,11 +26,13 @@ void main() { group('Merge', () { group('analysis', () { test('is up to date when no reference is provided', () { - final result = Merge.analysis(repo: repo, theirHead: repo['c68ff54']); - expect(result, [ - {GitMergeAnalysis.upToDate}, - GitMergePreference.none, - ]); + expect( + Merge.analysis(repo: repo, theirHead: repo['c68ff54']), + [ + {GitMergeAnalysis.upToDate}, + GitMergePreference.none, + ], + ); expect(repo.status, isEmpty); }); @@ -45,11 +47,10 @@ void main() { }); test('is fast forward', () { - final ffCommit = Commit.lookup(repo: repo, oid: repo['f17d0d4']); final ffBranch = Branch.create( repo: repo, name: 'ff-branch', - target: ffCommit, + target: Commit.lookup(repo: repo, oid: repo['f17d0d4']), ); final result = Merge.analysis( @@ -62,9 +63,6 @@ void main() { {GitMergeAnalysis.fastForward, GitMergeAnalysis.normal}, ); expect(repo.status, isEmpty); - - ffBranch.free(); - ffCommit.free(); }); test('is not fast forward and there is no conflicts', () { @@ -76,10 +74,6 @@ void main() { test('writes conflicts to index', () { final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch'); - final commit = AnnotatedCommit.lookup( - repo: repo, - oid: conflictBranch.target, - ); final index = repo.index; final result = Merge.analysis( @@ -88,7 +82,10 @@ void main() { ); expect(result[0], {GitMergeAnalysis.normal}); - Merge.commit(repo: repo, commit: commit); + Merge.commit( + repo: repo, + commit: AnnotatedCommit.lookup(repo: repo, oid: conflictBranch.target), + ); expect(index.hasConflicts, true); expect(index.conflicts.length, 1); expect(repo.state, GitRepositoryState.merge); @@ -114,10 +111,6 @@ void main() { 'conflict_file': {GitStatus.indexModified} }, ); - - index.free(); - commit.free(); - conflictBranch.free(); }); group('merge file from index', () { @@ -129,19 +122,16 @@ master conflict edit conflict branch edit >>>>>>> conflict_file """; - final conflictBranch = Branch.lookup( - repo: repo, - name: 'conflict-branch', - ); - final commit = AnnotatedCommit.lookup( - repo: repo, - oid: conflictBranch.target, - ); - final index = repo.index; - Merge.commit(repo: repo, commit: commit); + Merge.commit( + repo: repo, + commit: AnnotatedCommit.lookup( + repo: repo, + oid: Branch.lookup(repo: repo, name: 'conflict-branch').target, + ), + ); - final conflictedFile = index.conflicts['conflict_file']!; + final conflictedFile = repo.index.conflicts['conflict_file']!; final diff = Merge.fileFromIndex( repo: repo, ancestor: null, @@ -150,10 +140,6 @@ conflict branch edit ); expect(diff, diffExpected); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('merges with ancestor', () { @@ -164,21 +150,19 @@ Feature edit on feature branch Another feature edit >>>>>>> feature_file """; - final conflictBranch = Branch.lookup( - repo: repo, - name: 'ancestor-conflict', - ); - final commit = AnnotatedCommit.lookup( - repo: repo, - oid: conflictBranch.target, - ); + Checkout.reference(repo: repo, name: 'refs/heads/feature'); repo.setHead('refs/heads/feature'); - final index = repo.index; - Merge.commit(repo: repo, commit: commit); + Merge.commit( + repo: repo, + commit: AnnotatedCommit.lookup( + repo: repo, + oid: Branch.lookup(repo: repo, name: 'ancestor-conflict').target, + ), + ); - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = repo.index.conflicts['feature_file']!; final diff = Merge.fileFromIndex( repo: repo, ancestor: conflictedFile.ancestor, @@ -187,10 +171,6 @@ Another feature edit ); expect(diff, diffExpected); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('merges with provided merge flags and file flags', () { @@ -201,24 +181,18 @@ master conflict edit conflict branch edit >>>>>>> conflict_file """; - final conflictBranch = Branch.lookup( - repo: repo, - name: 'conflict-branch', - ); - final commit = AnnotatedCommit.lookup( - repo: repo, - oid: conflictBranch.target, - ); - final index = repo.index; Merge.commit( repo: repo, - commit: commit, + commit: AnnotatedCommit.lookup( + repo: repo, + oid: Branch.lookup(repo: repo, name: 'conflict-branch').target, + ), mergeFlags: {GitMergeFlag.noRecursive}, fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL}, ); - final conflictedFile = index.conflicts['conflict_file']!; + final conflictedFile = repo.index.conflicts['conflict_file']!; final diff = Merge.fileFromIndex( repo: repo, ancestor: null, @@ -227,34 +201,23 @@ conflict branch edit ); expect(diff, diffExpected); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('merges with provided merge favor', () { - final conflictBranch = Branch.lookup( + Merge.commit( repo: repo, - name: 'conflict-branch', + commit: AnnotatedCommit.lookup( + repo: repo, + oid: Branch.lookup(repo: repo, name: 'conflict-branch').target, + ), + favor: GitMergeFileFavor.ours, ); - final commit = AnnotatedCommit.lookup( - repo: repo, - oid: conflictBranch.target, - ); - final index = repo.index; - Merge.commit(repo: repo, commit: commit, favor: GitMergeFileFavor.ours); - - expect(index.conflicts, isEmpty); + expect(repo.index.conflicts, isEmpty); expect( File(p.join(repo.workdir, 'conflict_file')).readAsStringSync(), 'master conflict edit\n', ); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('throws when error occurs', () { @@ -328,59 +291,40 @@ theirs content group('merge commits', () { test('merges with default values', () { final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - final theirCommitAnnotated = AnnotatedCommit.lookup( - repo: repo, - oid: theirCommit.oid, - ); - final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); final mergeIndex = Merge.commits( repo: repo, - ourCommit: ourCommit, + ourCommit: Commit.lookup(repo: repo, oid: repo['1490545']), theirCommit: theirCommit, ); expect(mergeIndex.conflicts, isEmpty); final mergeCommitsTree = mergeIndex.writeTree(repo); - Merge.commit(repo: repo, commit: theirCommitAnnotated); - final index = repo.index; - expect(index.conflicts, isEmpty); - final mergeTree = index.writeTree(); + Merge.commit( + repo: repo, + commit: AnnotatedCommit.lookup(repo: repo, oid: theirCommit.oid), + ); + expect(repo.index.conflicts, isEmpty); + final mergeTree = repo.index.writeTree(); expect(mergeCommitsTree == mergeTree, true); - - index.free(); - mergeIndex.free(); - ourCommit.free(); - theirCommitAnnotated.free(); - theirCommit.free(); }); test('merges with provided favor', () { - final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); - final mergeIndex = Merge.commits( repo: repo, - ourCommit: ourCommit, - theirCommit: theirCommit, + ourCommit: Commit.lookup(repo: repo, oid: repo['1490545']), + theirCommit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), favor: GitMergeFileFavor.ours, ); expect(mergeIndex.conflicts, isEmpty); - - mergeIndex.free(); - ourCommit.free(); - theirCommit.free(); }); test('merges with provided merge and file flags', () { - final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); - final mergeIndex = Merge.commits( repo: repo, - ourCommit: ourCommit, - theirCommit: theirCommit, + ourCommit: Commit.lookup(repo: repo, oid: repo['1490545']), + theirCommit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), mergeFlags: { GitMergeFlag.findRenames, GitMergeFlag.noRecursive, @@ -392,10 +336,6 @@ theirs content }, ); expect(mergeIndex.conflicts, isEmpty); - - mergeIndex.free(); - ourCommit.free(); - theirCommit.free(); }); test('throws when error occurs', () { @@ -411,31 +351,33 @@ theirs content }); test('finds merge base for two commits', () { - var base = Merge.base( - repo: repo, - commits: [repo['1490545'], repo['5aecfa0']], + expect( + Merge.base(repo: repo, commits: [repo['1490545'], repo['5aecfa0']]).sha, + 'fc38877b2552ab554752d9a77e1f48f738cca79b', ); - expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - base = Merge.base( - repo: repo, - commits: [repo['f17d0d4'], repo['5aecfa0']], + expect( + Merge.base(repo: repo, commits: [repo['f17d0d4'], repo['5aecfa0']]).sha, + 'f17d0d48eae3aa08cecf29128a35e310c97b3521', ); - expect(base.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); }); test('finds merge base for many commits', () { - var base = Merge.base( - repo: repo, - commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], + expect( + Merge.base( + repo: repo, + commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], + ).sha, + 'fc38877b2552ab554752d9a77e1f48f738cca79b', ); - expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - base = Merge.base( - repo: repo, - commits: [repo['f17d0d4'], repo['5aecfa0'], repo['0e409d6']], + expect( + Merge.base( + repo: repo, + commits: [repo['f17d0d4'], repo['5aecfa0'], repo['0e409d6']], + ).sha, + 'f17d0d48eae3aa08cecf29128a35e310c97b3521', ); - expect(base.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); }); test('throws when trying to find merge base for invalid oid', () { @@ -457,11 +399,13 @@ theirs content }); test('finds octopus merge base', () { - final base = Merge.octopusBase( - repo: repo, - commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], + expect( + Merge.octopusBase( + repo: repo, + commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], + ).sha, + 'fc38877b2552ab554752d9a77e1f48f738cca79b', ); - expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); }); test('throws when trying to find octopus merge base for invalid oid', () { @@ -477,10 +421,6 @@ theirs content group('merge trees', () { test('merges with default values', () { final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - final theirCommitAnnotated = AnnotatedCommit.lookup( - repo: repo, - oid: theirCommit.oid, - ); final ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']); final baseCommit = Commit.lookup( repo: repo, @@ -489,36 +429,25 @@ theirs content commits: [ourCommit.oid, theirCommit.oid], ), ); - final theirTree = theirCommit.tree; - final ourTree = ourCommit.tree; - final ancestorTree = baseCommit.tree; final mergeIndex = Merge.trees( repo: repo, - ancestorTree: ancestorTree, - ourTree: ourTree, - theirTree: theirTree, + ancestorTree: baseCommit.tree, + ourTree: ourCommit.tree, + theirTree: theirCommit.tree, ); expect(mergeIndex.conflicts, isEmpty); final mergeTreesTree = mergeIndex.writeTree(repo); repo.setHead(ourCommit.oid); - Merge.commit(repo: repo, commit: theirCommitAnnotated); - final index = repo.index; - expect(index.conflicts, isEmpty); - final mergeTree = index.writeTree(); + Merge.commit( + repo: repo, + commit: AnnotatedCommit.lookup(repo: repo, oid: theirCommit.oid), + ); + expect(repo.index.conflicts, isEmpty); + final mergeTree = repo.index.writeTree(); expect(mergeTreesTree == mergeTree, true); - - index.free(); - mergeIndex.free(); - ancestorTree.free(); - ourTree.free(); - theirTree.free(); - baseCommit.free(); - ourCommit.free(); - theirCommitAnnotated.free(); - theirCommit.free(); }); test('merges with provided favor', () { @@ -531,26 +460,15 @@ theirs content commits: [ourCommit.oid, theirCommit.oid], ), ); - final theirTree = theirCommit.tree; - final ourTree = ourCommit.tree; - final ancestorTree = baseCommit.tree; final mergeIndex = Merge.trees( repo: repo, - ancestorTree: ancestorTree, - ourTree: ourTree, - theirTree: theirTree, + ancestorTree: baseCommit.tree, + ourTree: ourCommit.tree, + theirTree: theirCommit.tree, favor: GitMergeFileFavor.ours, ); expect(mergeIndex.conflicts, isEmpty); - - mergeIndex.free(); - ancestorTree.free(); - ourTree.free(); - theirTree.free(); - baseCommit.free(); - ourCommit.free(); - theirCommit.free(); }); test('throws when error occurs', () { @@ -567,21 +485,17 @@ theirs content }); test('cherry-picks commit', () { - final cherry = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - Merge.cherryPick(repo: repo, commit: cherry); + Merge.cherryPick( + repo: repo, + commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), + ); expect(repo.state, GitRepositoryState.cherrypick); expect(repo.message, 'add another feature file\n'); - final index = repo.index; - expect(index.conflicts, isEmpty); + expect(repo.index.conflicts, isEmpty); // pretend we've done commit repo.removeMessage(); - expect( - () => repo.message, - throwsA(isA()), - ); - - index.free(); + expect(() => repo.message, throwsA(isA())); }); test('throws when error occurs', () { diff --git a/test/note_test.dart b/test/note_test.dart index 7ad268a..6be3a08 100644 --- a/test/note_test.dart +++ b/test/note_test.dart @@ -43,7 +43,6 @@ void main() { expect(notes[i].oid.sha, notesExpected[i]['oid']); expect(notes[i].message, notesExpected[i]['message']); expect(notes[i].annotatedOid.sha, notesExpected[i]['annotatedOid']); - notes[i].free(); } }); @@ -53,15 +52,11 @@ void main() { }); test('lookups note', () { - final head = repo.head; - final note = Note.lookup(repo: repo, annotatedOid: head.target); + final note = Note.lookup(repo: repo, annotatedOid: repo.head.target); expect(note.oid.sha, notesExpected[1]['oid']); expect(note.message, notesExpected[1]['message']); expect(note.annotatedOid.sha, notesExpected[1]['annotatedOid']); - - note.free(); - head.free(); }); test('creates note', () { @@ -70,23 +65,20 @@ void main() { email: 'author@email.com', time: 1234, ); - final head = repo.head; final noteOid = Note.create( repo: repo, author: signature, committer: signature, - annotatedOid: head.target, + annotatedOid: repo.head.target, note: 'New note for HEAD', force: true, ); - final noteBlob = Blob.lookup(repo: repo, oid: noteOid); expect(noteOid.sha, 'ffd6e2ceaf91c00ea6d29e2e897f906da720529f'); - expect(noteBlob.content, 'New note for HEAD'); - - noteBlob.free(); - head.free(); - signature.free(); + expect( + Blob.lookup(repo: repo, oid: noteOid).content, + 'New note for HEAD', + ); }); test('throws when trying to create note and error occurs', () { @@ -108,7 +100,6 @@ void main() { email: 'author@email.com', time: 1234, ); - final head = repo.head; Note.delete( repo: repo, @@ -118,12 +109,9 @@ void main() { ); expect( - () => Note.lookup(repo: repo, annotatedOid: head.target), + () => Note.lookup(repo: repo, annotatedOid: repo.head.target), throwsA(isA()), ); - - head.free(); - signature.free(); }); test('throws when trying to delete note and error occurs', () { @@ -138,10 +126,14 @@ void main() { ); }); + test('manually releases allocated memory', () { + final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']); + expect(() => note.free(), returnsNormally); + }); + test('returns string representation of Note object', () { final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']); expect(note.toString(), contains('Note{')); - note.free(); }); }); } diff --git a/test/odb_test.dart b/test/odb_test.dart index 51f989a..6a722a9 100644 --- a/test/odb_test.dart +++ b/test/odb_test.dart @@ -26,9 +26,7 @@ void main() { group('Odb', () { test('successfully initializes', () { - final odb = repo.odb; - expect(odb, isA()); - odb.free(); + expect(repo.odb, isA()); }); test('throws when trying to get odb and error occurs', () { @@ -36,9 +34,7 @@ void main() { }); test('creates new odb with no backends', () { - final odb = Odb.create(); - expect(odb, isA()); - odb.free(); + expect(Odb.create(), isA()); }); test('adds disk alternate', () { @@ -46,33 +42,22 @@ void main() { odb.addDiskAlternate(p.join(repo.path, 'objects')); expect(odb.contains(repo[blobSha]), true); - - odb.free(); }); test('reads object', () { - final oid = repo[blobSha]; - final odb = repo.odb; - final object = odb.read(oid); + final object = repo.odb.read(repo[blobSha]); - expect(object.oid, oid); + expect(object.oid, repo[blobSha]); expect(object.type, GitObject.blob); expect(object.data, blobContent); expect(object.size, 13); - - object.free(); - odb.free(); }); test('throws when trying to read object and error occurs', () { - final odb = repo.odb; - expect( - () => odb.read(repo['0' * 40]), + () => repo.odb.read(repo['0' * 40]), throwsA(isA()), ); - - odb.free(); }); test("returns list of all objects oid's in database", () { @@ -80,8 +65,6 @@ void main() { expect(odb.objects, isNot(isEmpty)); expect(odb.objects.contains(repo[commitSha]), true); - - odb.free(); }); test('throws when trying to get list of all objects and error occurs', () { @@ -92,8 +75,6 @@ void main() { () => odb.objects, throwsA(isA()), ); - - odb.free(); }); test('writes data', () { @@ -103,19 +84,13 @@ void main() { expect(odb.contains(oid), true); expect(object.data, 'testing'); - - object.free(); - odb.free(); }); test('throws when trying to write with invalid object type', () { - final odb = repo.odb; expect( - () => odb.write(type: GitObject.any, data: 'testing'), + () => repo.odb.write(type: GitObject.any, data: 'testing'), throwsA(isA()), ); - - odb.free(); }); test('throws when trying to write alternate odb to disk', () { @@ -126,15 +101,20 @@ void main() { () => odb.write(type: GitObject.blob, data: ''), throwsA(isA()), ); + }); - odb.free(); + test('manually releases allocated memory', () { + expect(() => repo.odb.free(), returnsNormally); + }); + + test('manually releases allocated memory for odbObject object', () { + final object = repo.odb.read(repo[blobSha]); + expect(() => object.free(), returnsNormally); }); test('returns string representation of OdbObject object', () { - final odb = repo.odb; - final object = odb.read(repo[blobSha]); + final object = repo.odb.read(repo[blobSha]); expect(object.toString(), contains('OdbObject{')); - odb.free(); }); }); } diff --git a/test/packbuilder_test.dart b/test/packbuilder_test.dart index 4a3d60d..ff63c8a 100644 --- a/test/packbuilder_test.dart +++ b/test/packbuilder_test.dart @@ -29,8 +29,6 @@ void main() { expect(packbuilder, isA()); expect(packbuilder.length, 0); - - packbuilder.free(); }); test('throws when trying to initialize and error occurs', () { @@ -49,20 +47,13 @@ void main() { packbuilder.add(odb.objects[1]); expect(packbuilder.length, 2); - - odb.free(); - packbuilder.free(); }); test('throws when trying to add object and error occurs', () { - final packbuilder = PackBuilder(repo); - expect( - () => packbuilder.add(Oid(nullptr)), + () => PackBuilder(repo).add(Oid(nullptr)), throwsA(isA()), ); - - packbuilder.free(); }); test('adds object recursively', () { @@ -71,119 +62,86 @@ void main() { packbuilder.addRecursively(oid); expect(packbuilder.length, 3); - - packbuilder.free(); }); test('throws when trying to add object recursively and error occurs', () { - final packbuilder = PackBuilder(repo); - expect( - () => packbuilder.addRecursively(Oid(nullptr)), + () => PackBuilder(repo).addRecursively(Oid(nullptr)), throwsA(isA()), ); - - packbuilder.free(); }); test('adds commit', () { final packbuilder = PackBuilder(repo); - final oid = repo['f17d0d4']; - packbuilder.addCommit(oid); + packbuilder.addCommit(repo['f17d0d4']); expect(packbuilder.length, 3); - - packbuilder.free(); }); test('throws when trying to add commit with invalid oid', () { - final packbuilder = PackBuilder(repo); final oid = Oid.fromSHA(repo: repo, sha: '0' * 40); - expect(() => packbuilder.addCommit(oid), throwsA(isA())); - - packbuilder.free(); + expect( + () => PackBuilder(repo).addCommit(oid), + throwsA(isA()), + ); }); test('adds tree', () { final packbuilder = PackBuilder(repo); - final oid = repo['df2b8fc']; - packbuilder.addTree(oid); + packbuilder.addTree(repo['df2b8fc']); expect(packbuilder.length, 2); - - packbuilder.free(); }); test('throws when trying to add tree with invalid oid', () { - final packbuilder = PackBuilder(repo); final oid = Oid.fromSHA(repo: repo, sha: '0' * 40); - expect(() => packbuilder.addTree(oid), throwsA(isA())); - - packbuilder.free(); + expect( + () => PackBuilder(repo).addTree(oid), + throwsA(isA()), + ); }); test('adds objects with walker', () { - final oid = repo['f17d0d4']; final packbuilder = PackBuilder(repo); final walker = RevWalk(repo); walker.sorting({GitSort.none}); - walker.push(oid); + walker.push(repo['f17d0d4']); packbuilder.addWalk(walker); expect(packbuilder.length, 3); - - walker.free(); - packbuilder.free(); }); test('sets number of threads', () { - final packbuilder = PackBuilder(repo); - - expect(packbuilder.setThreads(1), 1); - - packbuilder.free(); + expect(PackBuilder(repo).setThreads(1), 1); }); test('returns name of packfile', () { final packbuilder = PackBuilder(repo); - final odb = repo.odb; - packbuilder.add(odb.objects[0]); + packbuilder.add(repo.odb.objects[0]); Directory(packDirPath).createSync(); expect(packbuilder.name, isEmpty); packbuilder.write(null); expect(packbuilder.name, isNotEmpty); - - packbuilder.free(); }); test('packs with default arguments', () { - final odb = repo.odb; - final objectsCount = odb.objects.length; + final objectsCount = repo.odb.objects.length; Directory(packDirPath).createSync(); - - final writtenCount = repo.pack(); - - expect(writtenCount, objectsCount); - - odb.free(); + expect(repo.pack(), objectsCount); }); test('packs into provided path with threads set', () { - final odb = repo.odb; - final objectsCount = odb.objects.length; final testPackPath = p.join(repo.workdir, 'test-pack'); Directory(testPackPath).createSync(); final writtenCount = repo.pack(path: testPackPath, threads: 1); - expect(writtenCount, objectsCount); + expect(writtenCount, repo.odb.objects.length); expect(Directory(testPackPath).listSync().isNotEmpty, true); - - odb.free(); }); test('packs with provided packDelegate', () { @@ -198,32 +156,26 @@ void main() { ); for (final commit in repo.log(oid: ref.target)) { packBuilder.addRecursively(commit.oid); - commit.free(); } - ref.free(); - branch.free(); } } - final writtenCount = repo.pack(packDelegate: packDelegate); - expect(writtenCount, 18); + expect(repo.pack(packDelegate: packDelegate), 18); }); test('throws when trying to write pack into invalid path', () { - final packbuilder = PackBuilder(repo); - expect( - () => packbuilder.write('invalid/path'), + () => PackBuilder(repo).write('invalid/path'), throwsA(isA()), ); + }); - packbuilder.free(); + test('manually releases allocated memory', () { + expect(() => PackBuilder(repo).free(), returnsNormally); }); test('returns string representation of PackBuilder object', () { - final packbuilder = PackBuilder(repo); - expect(packbuilder.toString(), contains('PackBuilder{')); - packbuilder.free(); + expect(PackBuilder(repo).toString(), contains('PackBuilder{')); }); }); } diff --git a/test/patch_test.dart b/test/patch_test.dart index 81f8e38..7e222e4 100644 --- a/test/patch_test.dart +++ b/test/patch_test.dart @@ -65,8 +65,6 @@ index e69de29..0000000 expect(patch.size(), 14); expect(patch.text, blobPatch); - - patch.free(); }); test('creates from one buffer (add)', () { @@ -78,8 +76,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchAdd); - - patch.free(); }); test('creates from one buffer (delete)', () { @@ -91,65 +87,50 @@ index e69de29..0000000 ); expect(patch.text, blobPatchDelete); - - patch.free(); }); test('creates from blobs', () { - final oldBlob = Blob.lookup(repo: repo, oid: oldBlobOid); - final newBlob = Blob.lookup(repo: repo, oid: newBlobOid); final patch = Patch.fromBlobs( - oldBlob: oldBlob, - newBlob: newBlob, + oldBlob: Blob.lookup(repo: repo, oid: oldBlobOid), + newBlob: Blob.lookup(repo: repo, oid: newBlobOid), oldBlobPath: path, newBlobPath: path, ); expect(patch.text, blobPatch); - - patch.free(); }); test('creates from one blob (add)', () { - final newBlob = Blob.lookup(repo: repo, oid: newBlobOid); final patch = Patch.fromBlobs( oldBlob: null, - newBlob: newBlob, + newBlob: Blob.lookup(repo: repo, oid: newBlobOid), oldBlobPath: path, newBlobPath: path, ); expect(patch.text, blobPatchAdd); - - patch.free(); }); test('creates from one blob (delete)', () { - final oldBlob = Blob.lookup(repo: repo, oid: oldBlobOid); final patch = Patch.fromBlobs( - oldBlob: oldBlob, + oldBlob: Blob.lookup(repo: repo, oid: oldBlobOid), newBlob: null, oldBlobPath: path, newBlobPath: path, ); expect(patch.text, blobPatchDelete); - - patch.free(); }); test('creates from blob and buffer', () { - final blob = Blob.lookup(repo: repo, oid: oldBlobOid); final patch = Patch.fromBlobAndBuffer( - blob: blob, + blob: Blob.lookup(repo: repo, oid: oldBlobOid), buffer: newBuffer, blobPath: path, bufferPath: path, ); expect(patch.text, blobPatch); - - patch.free(); }); test('creates from empty blob and buffer', () { @@ -161,8 +142,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchAdd); - - patch.free(); }); test('throws when trying to create from diff and error occurs', () { @@ -176,6 +155,43 @@ index e69de29..0000000 expect(() => Patch(nullptr).text, throwsA(isA())); }); + test('returns hunks in a patch', () { + final patch = Patch.fromBuffers( + oldBuffer: oldBuffer, + newBuffer: newBuffer, + oldBufferPath: path, + newBufferPath: path, + ); + final hunk = patch.hunks[0]; + + expect(patch.hunks.length, 1); + expect(hunk.linesCount, 1); + expect(hunk.oldStart, 0); + expect(hunk.oldLines, 0); + expect(hunk.newStart, 1); + expect(hunk.newLines, 1); + expect(hunk.header, '@@ -0,0 +1 @@\n'); + }); + + test('returns lines in a hunk', () { + final patch = Patch.fromBuffers( + oldBuffer: oldBuffer, + newBuffer: newBuffer, + oldBufferPath: path, + newBufferPath: path, + ); + final hunk = patch.hunks[0]; + final line = hunk.lines[0]; + + expect(hunk.lines.length, 1); + expect(line.origin, GitDiffLine.addition); + expect(line.oldLineNumber, -1); + expect(line.newLineNumber, 1); + expect(line.numLines, 1); + expect(line.contentOffset, 0); + expect(line.content, 'Feature edit\n'); + }); + test('returns line counts of each type in a patch', () { final patch = Patch.fromBuffers( oldBuffer: oldBuffer, @@ -189,11 +205,21 @@ index e69de29..0000000 expect(stats.insertions, equals(1)); expect(stats.deletions, equals(0)); expect(stats.toString(), contains('PatchStats{')); - - patch.free(); }); - test('returns string representation of Patch object', () { + test('manually releases allocated memory', () { + final patch = Patch.fromBuffers( + oldBuffer: oldBuffer, + newBuffer: newBuffer, + oldBufferPath: path, + newBufferPath: path, + ); + expect(() => patch.free(), returnsNormally); + }); + + test( + 'returns string representation of Patch, DiffHunk and DiffLine objects', + () { final patch = Patch.fromBuffers( oldBuffer: oldBuffer, newBuffer: newBuffer, @@ -202,8 +228,8 @@ index e69de29..0000000 ); expect(patch.toString(), contains('Patch{')); - - patch.free(); + expect(patch.hunks[0].toString(), contains('DiffHunk{')); + expect(patch.hunks[0].lines[0].toString(), contains('DiffLine{')); }); }); } diff --git a/test/rebase_test.dart b/test/rebase_test.dart index f63bb78..353f5ef 100644 --- a/test/rebase_test.dart +++ b/test/rebase_test.dart @@ -33,15 +33,7 @@ void main() { time: 1234, ); final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final branchHead = AnnotatedCommit.fromReference( - repo: repo, - reference: master, - ); final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature'); - final ontoHead = AnnotatedCommit.fromReference( - repo: repo, - reference: feature, - ); Checkout.reference(repo: repo, name: feature.name); repo.setHead(feature.name); @@ -49,8 +41,8 @@ void main() { final rebase = Rebase.init( repo: repo, - branch: branchHead, - onto: ontoHead, + branch: AnnotatedCommit.fromReference(repo: repo, reference: master), + onto: AnnotatedCommit.fromReference(repo: repo, reference: feature), ); expect(rebase.origHeadOid, master.target); @@ -76,13 +68,6 @@ void main() { rebase.finish(); expect(repo.index['.gitignore'], isA()); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - feature.free(); - master.free(); - signature.free(); }); test('performs rebase without branch provided', () { @@ -92,11 +77,10 @@ void main() { time: 1234, ); final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature'); - final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target); final rebase = Rebase.init( repo: repo, - onto: ontoHead, + onto: AnnotatedCommit.lookup(repo: repo, oid: feature.target), ); expect( @@ -124,11 +108,6 @@ void main() { } rebase.finish(); - - rebase.free(); - ontoHead.free(); - feature.free(); - signature.free(); }); test('performs rebase with provided upstream', () { @@ -138,9 +117,7 @@ void main() { time: 1234, ); final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature'); - final upstream = AnnotatedCommit.lookup(repo: repo, oid: repo[shas[1]]); Checkout.reference(repo: repo, name: feature.name); repo.setHead(feature.name); @@ -148,8 +125,8 @@ void main() { final rebase = Rebase.init( repo: repo, - branch: branchHead, - upstream: upstream, + branch: AnnotatedCommit.lookup(repo: repo, oid: master.target), + upstream: AnnotatedCommit.lookup(repo: repo, oid: repo[shas[1]]), ); expect(rebase.origHeadOid, master.target); @@ -170,13 +147,6 @@ void main() { rebase.finish(); expect(repo.index['conflict_file'], isA()); - - rebase.free(); - upstream.free(); - branchHead.free(); - feature.free(); - master.free(); - signature.free(); }); test( @@ -191,21 +161,21 @@ void main() { email: 'author@email.com', time: 1234, ); - final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = Reference.lookup( repo: repo, name: 'refs/heads/conflict-branch', ); - final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target); Checkout.reference(repo: repo, name: conflict.name); repo.setHead(conflict.name); final rebase = Rebase.init( repo: repo, - branch: branchHead, - onto: ontoHead, + branch: AnnotatedCommit.lookup( + repo: repo, + oid: Reference.lookup(repo: repo, name: 'refs/heads/master').target, + ), + onto: AnnotatedCommit.lookup(repo: repo, oid: conflict.target), ); expect(rebase.operations.length, 1); @@ -216,67 +186,48 @@ void main() { () => rebase.commit(committer: signature), throwsA(isA()), ); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - conflict.free(); - master.free(); - signature.free(); }); test('throws when trying to perfrom next rebase operation and error occurs', () { - final signature = Signature.create( - name: 'Author', - email: 'author@email.com', - time: 1234, - ); - final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = Reference.lookup( repo: repo, name: 'refs/heads/conflict-branch', ); - final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target); Checkout.reference(repo: repo, name: conflict.name); repo.setHead(conflict.name); final rebase = Rebase.init( repo: repo, - branch: branchHead, - onto: ontoHead, + branch: AnnotatedCommit.lookup( + repo: repo, + oid: Reference.lookup(repo: repo, name: 'refs/heads/master').target, + ), + onto: AnnotatedCommit.lookup(repo: repo, oid: conflict.target), ); expect(rebase.operations.length, 1); rebase.next(); // repo now have conflicts expect(() => rebase.next(), throwsA(isA())); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - conflict.free(); - master.free(); - signature.free(); }); test('aborts rebase in progress', () { - final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = Reference.lookup( repo: repo, name: 'refs/heads/conflict-branch', ); - final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target); Checkout.reference(repo: repo, name: conflict.name); repo.setHead(conflict.name); final rebase = Rebase.init( repo: repo, - branch: branchHead, - onto: ontoHead, + branch: AnnotatedCommit.lookup( + repo: repo, + oid: Reference.lookup(repo: repo, name: 'refs/heads/master').target, + ), + onto: AnnotatedCommit.lookup(repo: repo, oid: conflict.target), ); expect(rebase.operations.length, 1); @@ -287,35 +238,36 @@ void main() { rebase.abort(); expect(repo.status, isEmpty); expect(repo.state, GitRepositoryState.none); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - conflict.free(); - master.free(); }); test('opens an existing rebase', () { - final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature'); - final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target); - final rebase = Rebase.init( repo: repo, - onto: ontoHead, + onto: AnnotatedCommit.lookup( + repo: repo, + oid: Reference.lookup(repo: repo, name: 'refs/heads/feature').target, + ), ); expect(rebase.operations.length, 3); final openRebase = Rebase.open(repo); expect(openRebase.operations.length, 3); - - openRebase.free(); - rebase.free(); - ontoHead.free(); - feature.free(); }); test('throws when trying to open an existing rebase but there is none', () { expect(() => Rebase.open(repo), throwsA(isA())); }); + + test('manually releases allocated memory', () { + final rebase = Rebase.init( + repo: repo, + onto: AnnotatedCommit.lookup( + repo: repo, + oid: Reference.lookup(repo: repo, name: 'refs/heads/feature').target, + ), + ); + + expect(() => rebase.free(), returnsNormally); + }); }); } diff --git a/test/reference_test.dart b/test/reference_test.dart index 3c1f436..4345317 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -46,25 +46,22 @@ void main() { }); test('returns correct type of reference', () { - final head = repo.head; - expect(head.type, ReferenceType.direct); - head.free(); - - final ref = Reference.lookup(repo: repo, name: 'HEAD'); - expect(ref.type, ReferenceType.symbolic); - ref.free(); + expect(repo.head.type, ReferenceType.direct); + expect( + Reference.lookup(repo: repo, name: 'HEAD').type, + ReferenceType.symbolic, + ); }); test('returns SHA hex of direct reference', () { - final head = repo.head; - expect(head.target.sha, lastCommit); - head.free(); + expect(repo.head.target.sha, lastCommit); }); test('returns SHA hex of symbolic reference', () { - final ref = Reference.lookup(repo: repo, name: 'HEAD'); - expect(ref.target.sha, lastCommit); - ref.free(); + expect( + Reference.lookup(repo: repo, name: 'HEAD').target.sha, + lastCommit, + ); }); test('throws when trying to resolve invalid reference', () { @@ -75,9 +72,7 @@ void main() { }); test('returns the full name', () { - final head = repo.head; - expect(head.name, 'refs/heads/master'); - head.free(); + expect(repo.head.name, 'refs/heads/master'); }); test('returns the short name', () { @@ -87,25 +82,22 @@ void main() { target: repo[lastCommit], ); - final head = repo.head; - - expect(head.shorthand, 'master'); + expect(repo.head.shorthand, 'master'); expect(ref.shorthand, 'origin/upstream'); - - head.free(); - ref.free(); }); test('checks if reference is a local branch', () { - final ref = Reference.lookup(repo: repo, name: 'refs/heads/feature'); - expect(ref.isBranch, true); - ref.free(); + expect( + Reference.lookup(repo: repo, name: 'refs/heads/feature').isBranch, + true, + ); }); test('checks if reference is a note', () { - final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - expect(ref.isNote, false); - ref.free(); + expect( + Reference.lookup(repo: repo, name: 'refs/heads/master').isNote, + false, + ); }); test('checks if reference is a remote branch', () { @@ -114,23 +106,25 @@ void main() { name: 'refs/remotes/origin/master', ); expect(ref.isRemote, true); - ref.free(); }); test('checks if reference is a tag', () { - final ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1'); - expect(ref.isTag, true); - ref.free(); + expect( + Reference.lookup(repo: repo, name: 'refs/tags/v0.1').isTag, + true, + ); }); test('checks if reflog exists for the reference', () { - var ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - expect(ref.hasLog, true); + expect( + Reference.lookup(repo: repo, name: 'refs/heads/master').hasLog, + true, + ); - ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1'); - expect(ref.hasLog, false); - - ref.free(); + expect( + Reference.lookup(repo: repo, name: 'refs/tags/v0.1').hasLog, + false, + ); }); test('ensures updates to the reference will append to its log', () { @@ -141,12 +135,8 @@ void main() { name: 'refs/tags/tag', target: repo[lastCommit], ); - final reflog = ref.log; - expect(reflog.length, 1); - - reflog.free(); - ref.free(); + expect(ref.log.length, 1); }); test('throws when trying to ensure there is a reflog and error occurs', () { @@ -167,24 +157,17 @@ void main() { expect(repo.references.length, 6); expect(duplicate.equals(ref), true); - - duplicate.free(); - ref.free(); }); group('create direct', () { test('creates with oid as target', () { - final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final refFromOid = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/from.oid', - target: ref.target, + target: repo.head.target, ); expect(repo.references, contains('refs/tags/from.oid')); - - refFromOid.free(); - ref.free(); }); test('creates with log message', () { @@ -196,15 +179,11 @@ void main() { logMessage: 'log message', ); - final reflog = ref.log; - final reflogEntry = reflog[0]; + final reflogEntry = ref.log[0]; expect(reflogEntry.message, 'log message'); expect(reflogEntry.committer.name, 'name'); expect(reflogEntry.committer.email, 'email'); - - reflog.free(); - ref.free(); }); test('throws if target is not valid', () { @@ -239,7 +218,7 @@ void main() { }); test('creates with force flag if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/test', target: repo[lastCommit], @@ -253,13 +232,10 @@ void main() { ); expect(forceRef.target.sha, lastCommit); - - ref.free(); - forceRef.free(); }); test('throws if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/test', target: repo[lastCommit], @@ -273,8 +249,6 @@ void main() { ), throwsA(isA()), ); - - ref.free(); }); }); @@ -288,12 +262,10 @@ void main() { expect(repo.references, contains('refs/tags/symbolic')); expect(ref.type, ReferenceType.symbolic); - - ref.free(); }); test('creates with force flag if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/test', target: 'refs/heads/master', @@ -308,13 +280,10 @@ void main() { expect(forceRef.target.sha, lastCommit); expect(forceRef.type, ReferenceType.symbolic); - - ref.free(); - forceRef.free(); }); test('throws if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/exists', target: 'refs/heads/master', @@ -328,8 +297,6 @@ void main() { ), throwsA(isA()), ); - - ref.free(); }); test('throws if name is not valid', () { @@ -353,21 +320,16 @@ void main() { logMessage: 'log message', ); - final reflog = ref.log; - final reflogEntry = reflog[0]; + final reflogEntry = ref.log[0]; expect(reflogEntry.message, 'log message'); expect(reflogEntry.committer.name, 'name'); expect(reflogEntry.committer.email, 'email'); - - reflog.free(); - ref.free(); }); }); test('deletes reference', () { expect(repo.references, contains('refs/tags/v0.1')); - Reference.delete(repo: repo, name: 'refs/tags/v0.1'); expect(repo.references, isNot(contains('refs/tags/v0.1'))); }); @@ -376,7 +338,6 @@ void main() { test('with provided name', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(ref.target.sha, lastCommit); - ref.free(); }); test('throws when error occured', () { @@ -389,11 +350,7 @@ void main() { test('returns log for reference', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final reflog = ref.log; - expect(reflog.last.message, 'commit (initial): init'); - - reflog.free(); - ref.free(); + expect(ref.log.last.message, 'commit (initial): init'); }); group('set target', () { @@ -401,8 +358,6 @@ void main() { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); ref.setTarget(target: repo[newCommit]); expect(ref.target.sha, newCommit); - - ref.free(); }); test('sets symbolic target with provided reference name', () { @@ -411,8 +366,6 @@ void main() { ref.setTarget(target: 'refs/heads/feature'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); - - ref.free(); }); test('sets target with log message', () { @@ -422,13 +375,10 @@ void main() { repo.setIdentity(name: 'name', email: 'email'); ref.setTarget(target: 'refs/heads/feature', logMessage: 'log message'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); - final reflog = ref.log; - expect(reflog.first.message, 'log message'); - expect(reflog.first.committer.name, 'name'); - expect(reflog.first.committer.email, 'email'); - - reflog.free(); - ref.free(); + final logEntry = ref.log.first; + expect(logEntry.message, 'log message'); + expect(logEntry.committer.name, 'name'); + expect(logEntry.committer.email, 'email'); }); test('throws on invalid target', () { @@ -444,8 +394,6 @@ void main() { ); expect(() => ref.setTarget(target: 0), throwsA(isA())); - - ref.free(); }); }); @@ -508,9 +456,6 @@ void main() { ); expect(repo.references, isNot(contains('refs/tags/v0.1'))); expect(repo.references.length, 5); - - ref1.free(); - ref2.free(); }); }); @@ -523,10 +468,6 @@ void main() { expect(ref1.notEquals(ref2), false); expect(ref1.equals(ref3), false); expect(ref1.notEquals(ref3), true); - - ref1.free(); - ref2.free(); - ref3.free(); }); test('peels to non-tag object when no type is provided', () { @@ -535,23 +476,17 @@ void main() { final peeled = ref.peel() as Commit; expect(peeled.oid, commit.oid); - - peeled.free(); - commit.free(); - ref.free(); }); test('peels to object of provided type', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final blob = Blob.lookup(repo: repo, oid: repo['9c78c21']); final blobRef = Reference.create( repo: repo, name: 'refs/tags/blob', - target: blob.oid, + target: Blob.lookup(repo: repo, oid: repo['9c78c21']).oid, ); final tagRef = Reference.lookup(repo: repo, name: 'refs/tags/v0.2'); final commit = Commit.lookup(repo: repo, oid: ref.target); - final tree = commit.tree; final peeledCommit = ref.peel(GitObject.commit) as Commit; final peeledTree = ref.peel(GitObject.tree) as Tree; @@ -559,20 +494,9 @@ void main() { final peeledTag = tagRef.peel(GitObject.tag) as Tag; expect(peeledCommit.oid, commit.oid); - expect(peeledTree.oid, tree.oid); + expect(peeledTree.oid, commit.tree.oid); expect(peeledBlob.content, 'Feature edit\n'); expect(peeledTag.name, 'v0.2'); - - peeledTag.free(); - peeledBlob.free(); - peeledTree.free(); - peeledCommit.free(); - tagRef.free(); - blobRef.free(); - blob.free(); - commit.free(); - tree.free(); - ref.free(); }); test('throws when trying to peel and error occurs', () { @@ -598,10 +522,14 @@ void main() { ); }); + test('manually releases allocated memory', () { + final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); + expect(() => ref.free(), returnsNormally); + }); + test('returns string representation of Reference object', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(ref.toString(), contains('Reference{')); - ref.free(); }); }); } diff --git a/test/reflog_test.dart b/test/reflog_test.dart index d7dad43..021d1f8 100644 --- a/test/reflog_test.dart +++ b/test/reflog_test.dart @@ -10,19 +10,15 @@ import 'helpers/util.dart'; void main() { late Repository repo; late RefLog reflog; - late Reference head; late Directory tmpDir; setUp(() { tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo'))); repo = Repository.open(tmpDir.path); - head = repo.head; - reflog = RefLog(head); + reflog = RefLog(repo.head); }); tearDown(() { - reflog.free(); - head.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -52,9 +48,9 @@ void main() { }); test('deletes the reflog of provided reference', () { - expect(head.hasLog, true); - RefLog.delete(head); - expect(head.hasLog, false); + expect(repo.head.hasLog, true); + RefLog.delete(repo.head); + expect(repo.head.hasLog, false); }); test('renames existing reflog', () { @@ -94,19 +90,21 @@ void main() { ); expect(reflog.length, 4); - reflog.add(oid: head.target, committer: committer); + reflog.add(oid: repo.head.target, committer: committer); expect(reflog.length, 5); - reflog.add(oid: head.target, committer: committer, message: 'new entry'); + reflog.add( + oid: repo.head.target, + committer: committer, + message: 'new entry', + ); expect(reflog.length, 6); expect(reflog[0].message, 'new entry'); - - committer.free(); }); test('throws when trying to add new entry', () { expect( - () => reflog.add(oid: head.target, committer: Signature(nullptr)), + () => reflog.add(oid: repo.head.target, committer: Signature(nullptr)), throwsA(isA()), ); }); @@ -132,12 +130,12 @@ void main() { reflog.remove(0); // making sure change is only in memory - final oldReflog = RefLog(head); + final oldReflog = RefLog(repo.head); expect(oldReflog.length, 4); reflog.write(); - final newReflog = RefLog(head); + final newReflog = RefLog(repo.head); expect(newReflog.length, 3); }); @@ -147,9 +145,10 @@ void main() { Reference.delete(repo: repo, name: ref.name); expect(() => reflog.write(), throwsA(isA())); + }); - reflog.free(); - ref.free(); + test('manually releases allocated memory', () { + expect(() => RefLog(repo.head).free(), returnsNormally); }); test('returns string representation of RefLogEntry object', () { diff --git a/test/remote_prune_test.dart b/test/remote_prune_test.dart index 7b435fa..9ac3ee7 100644 --- a/test/remote_prune_test.dart +++ b/test/remote_prune_test.dart @@ -30,7 +30,6 @@ void main() { }); tearDown(() { - remote.free(); originRepo.free(); clonedRepo.free(); tmpDir.deleteSync(recursive: true); @@ -41,35 +40,26 @@ void main() { group('Remote', () { test('fetch() does not prune branch by default', () { remote.fetch(); - - final branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), true); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + true, + ); }); test('fetch() prunes branch with provided flag', () { remote.fetch(prune: GitFetchPrune.prune); - - final branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), false); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + false, + ); }); test('fetch() does not prune branch with provided flag', () { remote.fetch(prune: GitFetchPrune.noPrune); - - final branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), true); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + true, + ); }); test('prune() prunes branches', () { @@ -81,22 +71,18 @@ void main() { final callbacks = Callbacks(updateTips: updateTips); remote.fetch(prune: GitFetchPrune.noPrune); - var branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), true); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + true, + ); remote.prune(callbacks); - branches = clonedRepo.branches; expect(pruned, contains('refs/remotes/origin/feature')); - expect(branches.any((branch) => branch.name == 'origin/feature'), false); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + false, + ); }); test( diff --git a/test/remote_test.dart b/test/remote_test.dart index 0b865f6..90d8956 100644 --- a/test/remote_test.dart +++ b/test/remote_test.dart @@ -34,8 +34,6 @@ void main() { expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); expect(remote.toString(), contains('Remote{')); - - remote.free(); }); test('throws when provided name for lookup is not found', () { @@ -56,8 +54,6 @@ void main() { expect(remote.name, 'upstream'); expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); - - remote.free(); }); test('creates with provided fetchspec', () { @@ -74,19 +70,12 @@ void main() { expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); expect(remote.fetchRefspecs, [spec]); - - remote.free(); }); test('throws when trying to create with fetchspec with invalid remote name', () { expect( - () => Remote.create( - repo: repo, - name: '', - url: '', - fetch: '', - ), + () => Remote.create(repo: repo, name: '', url: '', fetch: ''), throwsA(isA()), ); }); @@ -101,8 +90,6 @@ void main() { Remote.delete(repo: repo, name: remote.name); expect(repo.remotes.length, 1); - - remote.free(); }); test('throws when trying to delete non existing remote', () { @@ -118,16 +105,13 @@ void main() { final problems = Remote.rename( repo: repo, oldName: remoteName, - newName: 'new', + newName: 'renamed', ); expect(problems, isEmpty); - expect(remote.name, isNot('new')); + expect(remote.name, isNot('renamed')); - final newRemote = Remote.lookup(repo: repo, name: 'new'); - expect(newRemote.name, 'new'); - - newRemote.free(); - remote.free(); + final renamedRemote = Remote.lookup(repo: repo, name: 'renamed'); + expect(renamedRemote.name, 'renamed'); }); test('returns list of non-default refspecs that cannot be renamed', () { @@ -142,8 +126,6 @@ void main() { Remote.rename(repo: repo, oldName: remote.name, newName: 'renamed'), ['+refs/*:refs/*'], ); - - remote.free(); }); test('throws when renaming with invalid names', () { @@ -154,17 +136,12 @@ void main() { }); test('sets url', () { - final remote = Remote.lookup(repo: repo, name: remoteName); - expect(remote.url, remoteUrl); + expect(Remote.lookup(repo: repo, name: remoteName).url, remoteUrl); const newUrl = 'git://new/url.git'; Remote.setUrl(repo: repo, remote: remoteName, url: newUrl); - final newRemote = Remote.lookup(repo: repo, name: remoteName); - expect(newRemote.url, newUrl); - - newRemote.free(); - remote.free(); + expect(Remote.lookup(repo: repo, name: remoteName).url, newUrl); }); test('throws when trying to set invalid url name', () { @@ -178,10 +155,7 @@ void main() { const newUrl = 'git://new/url.git'; Remote.setPushUrl(repo: repo, remote: remoteName, url: newUrl); - final remote = Remote.lookup(repo: repo, name: remoteName); - expect(remote.pushUrl, newUrl); - - remote.free(); + expect(Remote.lookup(repo: repo, name: remoteName).pushUrl, newUrl); }); test('throws when trying to set invalid push url name', () { @@ -215,14 +189,11 @@ void main() { refspec.rTransform('refs/remotes/origin/master'), 'refs/heads/master', ); - - remote.free(); }); test('throws when trying to transform refspec with invalid reference name', () { - final remote = Remote.lookup(repo: repo, name: 'origin'); - final refspec = remote.getRefspec(0); + final refspec = Remote.lookup(repo: repo, name: 'origin').getRefspec(0); expect( () => refspec.transform('invalid/name'), @@ -233,8 +204,6 @@ void main() { () => refspec.rTransform('invalid/name'), throwsA(isA()), ); - - remote.free(); }); test('adds fetch refspec', () { @@ -252,8 +221,6 @@ void main() { '+refs/test/*:refs/test/remotes/*', ], ); - - remote.free(); }); test('throws when trying to add fetch refspec for invalid remote name', () { @@ -276,8 +243,6 @@ void main() { final remote = Remote.lookup(repo: repo, name: 'origin'); expect(remote.pushRefspecs.length, 1); expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']); - - remote.free(); }); test('throws when trying to add push refspec for invalid remote name', () { @@ -308,8 +273,6 @@ void main() { (refs.first['oid']! as Oid).sha, '49322bb17d3acc9146f98c97d078513228bbf3c0', ); - - remote.free(); }); test( @@ -319,8 +282,6 @@ void main() { final remote = Remote.lookup(repo: repo, name: 'libgit2'); expect(() => remote.ls(), throwsA(isA())); - - remote.free(); }); test( @@ -350,8 +311,6 @@ void main() { expect(stats.indexedDeltas, 3); expect(stats.receivedBytes, 0); expect(stats.toString(), contains('TransferProgress{')); - - remote.free(); }, tags: 'remote_fetch', ); @@ -384,8 +343,6 @@ void main() { expect(stats.indexedDeltas, 3); expect(stats.receivedBytes, 0); expect(stats.toString(), contains('TransferProgress{')); - - remote.free(); }, tags: 'remote_fetch', ); @@ -412,8 +369,6 @@ void main() { ), throwsA(isA()), ); - - remote.free(); }, tags: 'remote_fetch', ); @@ -426,8 +381,6 @@ void main() { () => remote.fetch(), throwsA(isA()), ); - - remote.free(); }); test( @@ -442,9 +395,8 @@ void main() { TransferProgress? callbackStats; void tp(TransferProgress stats) => callbackStats = stats; - final callbacks = Callbacks(transferProgress: tp); - final stats = remote.fetch(callbacks: callbacks); + final stats = remote.fetch(callbacks: Callbacks(transferProgress: tp)); expect(stats.totalObjects == callbackStats?.totalObjects, true); expect(stats.indexedObjects == callbackStats?.indexedObjects, true); @@ -453,8 +405,6 @@ void main() { expect(stats.totalDeltas == callbackStats?.totalDeltas, true); expect(stats.indexedDeltas == callbackStats?.indexedDeltas, true); expect(stats.receivedBytes == callbackStats?.receivedBytes, true); - - remote.free(); }, tags: 'remote_fetch', ); @@ -475,16 +425,10 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 final remote = Remote.lookup(repo: repo, name: 'libgit2'); final sidebandOutput = StringBuffer(); - void sideband(String message) { - sidebandOutput.write(message); - } + void sideband(String message) => sidebandOutput.write(message); - final callbacks = Callbacks(sidebandProgress: sideband); - - remote.fetch(callbacks: callbacks); + remote.fetch(callbacks: Callbacks(sidebandProgress: sideband)); expect(sidebandOutput.toString(), sidebandMessage); - - remote.free(); }, tags: 'remote_fetch', ); @@ -525,12 +469,8 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 }); } - final callbacks = Callbacks(updateTips: updateTips); - - remote.fetch(callbacks: callbacks); + remote.fetch(callbacks: Callbacks(updateTips: updateTips)); expect(updateTipsOutput, tipsExpected); - - remote.free(); }, tags: 'remote_fetch', ); @@ -557,16 +497,16 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 updateRefOutput[refname] = message; } - final callbacks = Callbacks(pushUpdateReference: updateRef); - - remote.push(refspecs: ['refs/heads/master'], callbacks: callbacks); + remote.push( + refspecs: ['refs/heads/master'], + callbacks: Callbacks(pushUpdateReference: updateRef), + ); expect( Commit.lookup(repo: originRepo, oid: originRepo.head.target).oid.sha, '821ed6e80627b8769d170a293862f9fc60825226', ); expect(updateRefOutput, {'refs/heads/master': ''}); - remote.free(); originRepo.free(); originDir.delete(recursive: true); }); @@ -579,8 +519,26 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 () => remote.push(refspecs: ['refs/heads/master']), throwsA(isA()), ); + }); - remote.free(); + test('manually releases allocated memory', () { + final remote = Remote.lookup(repo: repo, name: 'origin'); + expect(() => remote.free(), returnsNormally); + }); + }); + + group('RemoteCallback', () { + test('initializes and returns values', () { + const remoteCallback = RemoteCallback( + name: 'name', + url: 'url', + fetch: 'fetchRefspec', + ); + + expect(remoteCallback, isA()); + expect(remoteCallback.name, 'name'); + expect(remoteCallback.url, 'url'); + expect(remoteCallback.fetch, 'fetchRefspec'); }); }); } diff --git a/test/repository_clone_test.dart b/test/repository_clone_test.dart index db1cee4..0dbf1f4 100644 --- a/test/repository_clone_test.dart +++ b/test/repository_clone_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'helpers/util.dart'; void main() { - late Repository repo; late Directory tmpDir; final cloneDir = Directory( p.join(Directory.systemTemp.path, 'repository_cloned'), @@ -17,14 +16,12 @@ void main() { setUp(() { tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo'))); - repo = Repository.open(tmpDir.path); if (cloneDir.existsSync()) { cloneDir.delete(recursive: true); } }); tearDown(() { - repo.free(); tmpDir.deleteSync(recursive: true); if (cloneDir.existsSync()) { cloneDir.deleteSync(recursive: true); @@ -72,14 +69,13 @@ void main() { clonedRepo.free(); }); - test('clones repository with provided remote callback', () { - Remote remote(Repository repo, String name, String url) => - Remote.create(repo: repo, name: 'test', url: tmpDir.path); - + test( + 'clones repository with provided remote callback having default fetch ' + 'refspec value', () { final clonedRepo = Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - remote: remote, + remoteCallback: RemoteCallback(name: 'test', url: tmpDir.path), ); expect(clonedRepo.isEmpty, false); @@ -90,15 +86,35 @@ void main() { clonedRepo.free(); }); - test('throws when cloning repository with invalid remote callback', () { - Remote remote(Repository repo, String name, String url) => - Remote.create(repo: repo, name: '', url: ''); + test('clones repository with provided remote callback ', () { + const fetchRefspec = '+refs/heads/*:refs/remotes/spec/*'; + final clonedRepo = Repository.clone( + url: tmpDir.path, + localPath: cloneDir.path, + remoteCallback: RemoteCallback( + name: 'test', + url: tmpDir.path, + fetch: fetchRefspec, + ), + ); + final remote = Remote.lookup(repo: clonedRepo, name: 'test'); + + expect(clonedRepo.isEmpty, false); + expect(clonedRepo.isBare, false); + expect(clonedRepo.remotes, ['test']); + expect(clonedRepo.references, contains('refs/remotes/spec/master')); + expect(remote.fetchRefspecs, [fetchRefspec]); + + clonedRepo.free(); + }); + + test('throws when cloning repository with invalid remote callback', () { expect( () => Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - remote: remote, + remoteCallback: const RemoteCallback(name: '', url: ''), ), throwsA(isA()), ); @@ -113,13 +129,10 @@ void main() { } callbackPath.createSync(); - Repository repository(String path, bool bare) => - Repository.init(path: callbackPath.path); - final clonedRepo = Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - repository: repository, + repositoryCallback: RepositoryCallback(path: callbackPath.path), ); expect(clonedRepo.isEmpty, false); @@ -131,17 +144,41 @@ void main() { }); test('throws when cloning repository with invalid repository callback', () { - Repository repository(String path, bool bare) => - Repository.init(path: ''); - expect( () => Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - repository: repository, + repositoryCallback: const RepositoryCallback(path: ''), ), throwsA(isA()), ); }); }); + + group('RepositoryCallback', () { + test('initializes and returns values', () { + const repositoryCallback = RepositoryCallback( + path: 'some/path', + bare: true, + flags: {GitRepositoryInit.noReinit}, + mode: 1, + workdirPath: 'some/path', + description: 'description', + templatePath: 'some/path', + initialHead: 'feature', + originUrl: 'some.url', + ); + + expect(repositoryCallback, isA()); + expect(repositoryCallback.path, 'some/path'); + expect(repositoryCallback.bare, true); + expect(repositoryCallback.flags, {GitRepositoryInit.noReinit}); + expect(repositoryCallback.mode, 1); + expect(repositoryCallback.workdirPath, 'some/path'); + expect(repositoryCallback.description, 'description'); + expect(repositoryCallback.templatePath, 'some/path'); + expect(repositoryCallback.initialHead, 'feature'); + expect(repositoryCallback.originUrl, 'some.url'); + }); + }); } diff --git a/test/repository_init_test.dart b/test/repository_init_test.dart index 1eda752..2a64a92 100644 --- a/test/repository_init_test.dart +++ b/test/repository_init_test.dart @@ -5,7 +5,6 @@ import 'package:path/path.dart' as p; import 'package:test/test.dart'; void main() { - late Repository repo; final initDir = Directory(p.join(Directory.systemTemp.path, 'init_repo')); setUp(() { @@ -17,27 +16,30 @@ void main() { }); tearDown(() { - repo.free(); initDir.deleteSync(recursive: true); }); group('Repository.init', () { test('creates new bare repo at provided path', () { - repo = Repository.init(path: initDir.path, bare: true); + final repo = Repository.init(path: initDir.path, bare: true); expect(repo.path, contains('init_repo')); expect(repo.isBare, true); + + repo.free(); }); test('creates new standard repo at provided path', () { - repo = Repository.init(path: initDir.path); + final repo = Repository.init(path: initDir.path); expect(repo.path, contains('init_repo/.git/')); expect(repo.isBare, false); expect(repo.isEmpty, true); + + repo.free(); }); test('creates new standard repo with provided options', () { - repo = Repository.init( + final repo = Repository.init( path: initDir.path, description: 'test repo', originUrl: 'test.url', @@ -52,6 +54,8 @@ void main() { 'test repo', ); expect(Remote.lookup(repo: repo, name: 'origin').url, 'test.url'); + + repo.free(); }); }); } diff --git a/test/repository_test.dart b/test/repository_test.dart index 49f6e66..0d7320c 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -25,22 +25,17 @@ void main() { group('Repository', () { test('returns config for repository', () { - final config = repo.config; expect( - config['remote.origin.url'].value, + repo.config['remote.origin.url'].value, 'git://github.com/SkinnyMind/libgit2dart.git', ); - - config.free(); }); test('returns snapshot of repository config', () { - final snapshot = repo.configSnapshot; expect( - snapshot['remote.origin.url'].value, + repo.configSnapshot['remote.origin.url'].value, 'git://github.com/SkinnyMind/libgit2dart.git', ); - snapshot.free(); }); test('returns list of commits by walking from provided starting oid', () { @@ -57,10 +52,6 @@ void main() { for (var i = 0; i < commits.length; i++) { expect(commits[i].oid.sha, log[i]); } - - for (final c in commits) { - c.free(); - } }); group('.discover()', () { @@ -119,11 +110,6 @@ void main() { }); group('setHead', () { - late Reference head; - - setUp(() => head = repo.head); - tearDown(() => head.free()); - test('sets head when target is reference', () { expect(repo.head.name, 'refs/heads/master'); expect(repo.head.target.sha, lastCommit); @@ -164,9 +150,8 @@ void main() { test('returns status of a repository', () { File(p.join(tmpDir.path, 'new_file.txt')).createSync(); - final index = repo.index; - index.remove('file'); - index.add('new_file.txt'); + repo.index.remove('file'); + repo.index.add('new_file.txt'); expect( repo.status, { @@ -174,8 +159,6 @@ void main() { 'new_file.txt': {GitStatus.indexNew} }, ); - - index.free(); }); test('throws when trying to get status of bare repository', () { @@ -188,14 +171,14 @@ void main() { test('cleans up state', () { expect(repo.state, GitRepositoryState.none); - final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - Merge.cherryPick(repo: repo, commit: commit); + Merge.cherryPick( + repo: repo, + commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), + ); expect(repo.state, GitRepositoryState.cherrypick); repo.stateCleanup(); expect(repo.state, GitRepositoryState.none); - - commit.free(); }); test('throws when trying to clean up state and error occurs', () { @@ -206,15 +189,12 @@ void main() { }); test('returns status of a single file for provided path', () { - final index = repo.index; - index.remove('file'); + repo.index.remove('file'); expect( repo.statusFile('file'), {GitStatus.indexDeleted, GitStatus.wtNew}, ); expect(repo.statusFile('.gitignore'), {GitStatus.current}); - - index.free(); }); test('throws when checking status of a single file for invalid path', () { @@ -229,9 +209,6 @@ void main() { final signature = repo.defaultSignature; expect(signature.name, 'Some Name'); expect(signature.email, 'some@email.com'); - - signature.free(); - config.free(); }); test('returns attribute value', () { @@ -265,9 +242,11 @@ void main() { repo.aheadBehind(local: commit1.oid, upstream: commit1.oid), [0, 0], ); + }); - commit1.free(); - commit2.free(); + test('manually releases allocated memory', () { + final repo = Repository.open(tmpDir.path); + expect(() => repo.free(), returnsNormally); }); test('returns string representation of Repository object', () { diff --git a/test/reset_test.dart b/test/reset_test.dart index 06e5745..54e3725 100644 --- a/test/reset_test.dart +++ b/test/reset_test.dart @@ -25,68 +25,48 @@ void main() { group('Reset', () { test('resets with hard', () { - var contents = file.readAsStringSync(); - expect(contents, 'Feature edit\n'); + expect(file.readAsStringSync(), 'Feature edit\n'); repo.reset(oid: repo[sha], resetType: GitReset.hard); - contents = file.readAsStringSync(); - expect(contents, isEmpty); + expect(file.readAsStringSync(), isEmpty); }); test('resets with soft', () { - var contents = file.readAsStringSync(); - expect(contents, 'Feature edit\n'); + expect(file.readAsStringSync(), 'Feature edit\n'); repo.reset(oid: repo[sha], resetType: GitReset.soft); - contents = file.readAsStringSync(); - expect(contents, 'Feature edit\n'); + expect(file.readAsStringSync(), 'Feature edit\n'); - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.deltas, isEmpty); - - index.free(); }); test('resets with mixed', () { - var contents = file.readAsStringSync(); - expect(contents, 'Feature edit\n'); + expect(file.readAsStringSync(), 'Feature edit\n'); repo.reset(oid: repo[sha], resetType: GitReset.mixed); - contents = file.readAsStringSync(); - expect(contents, 'Feature edit\n'); + expect(file.readAsStringSync(), 'Feature edit\n'); - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.deltas.length, 1); - - index.free(); }); group('resetDefault', () { test('updates entry in the index', () { file.writeAsStringSync('new edit'); - final index = repo.index; - index.add('feature_file'); + repo.index.add('feature_file'); expect(repo.status['feature_file'], {GitStatus.indexModified}); - final head = repo.head; - repo.resetDefault(oid: head.target, pathspec: ['feature_file']); + repo.resetDefault(oid: repo.head.target, pathspec: ['feature_file']); expect(repo.status['feature_file'], {GitStatus.wtModified}); - - head.free(); - index.free(); }); test('throws when pathspec list is empty', () { - final head = repo.head; expect( - () => repo.resetDefault(oid: head.target, pathspec: []), + () => repo.resetDefault(oid: repo.head.target, pathspec: []), throwsA(isA()), ); - - head.free(); }); }); }); diff --git a/test/revparse_test.dart b/test/revparse_test.dart index 8892d0d..e4bf64c 100644 --- a/test/revparse_test.dart +++ b/test/revparse_test.dart @@ -42,11 +42,6 @@ void main() { final tag = RevParse.single(repo: repo, spec: 'v0.2') as Tag; expect(tag, isA()); expect(tag.message, 'annotated tag\n'); - - commit.free(); - tree.free(); - blob.free(); - tag.free(); }); test('.single() throws when spec string not found or invalid', () { @@ -68,10 +63,6 @@ void main() { expect(headParse.reference?.equals(masterRef), true); expect(headParse.toString(), contains('RevParse{')); - masterRef.free(); - headParse.object.free(); - headParse.reference?.free(); - final featureRef = Reference.lookup( repo: repo, name: 'refs/heads/feature', @@ -83,10 +74,6 @@ void main() { '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4', ); expect(headParse.reference?.equals(featureRef), true); - - featureRef.free(); - headParse.object.free(); - headParse.reference?.free(); }); test('.ext() returns only commit when no intermidiate reference found', () { @@ -94,8 +81,6 @@ void main() { expect(headParse.object.oid.sha, parentSHA); expect(headParse.reference, isNull); - - headParse.object.free(); }); test('.ext() throws when spec string not found or invalid', () { @@ -119,17 +104,12 @@ void main() { expect(revspec.flags, {GitRevSpec.single}); expect(revspec.toString(), contains('RevSpec{')); - revspec.from.free(); - revspec = RevParse.range(repo: repo, spec: 'HEAD^1..5aecfa'); expect(revspec.from.oid.sha, parentSHA); expect(revspec.to?.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(revspec.flags, {GitRevSpec.range}); - revspec.from.free(); - revspec.to?.free(); - revspec = RevParse.range(repo: repo, spec: 'HEAD...feature'); expect(revspec.from.oid.sha, headSHA); @@ -139,9 +119,6 @@ void main() { Merge.base(repo: repo, commits: [revspec.from.oid, revspec.to!.oid]), isA(), ); - - revspec.from.free(); - revspec.to?.free(); }); test('throws on invalid range spec', () { diff --git a/test/revwalk_test.dart b/test/revwalk_test.dart index cfe4279..a960f71 100644 --- a/test/revwalk_test.dart +++ b/test/revwalk_test.dart @@ -30,9 +30,7 @@ void main() { group('RevWalk', () { test('initializes walker', () { - final walker = RevWalk(repo); - expect(walker, isA()); - walker.free(); + expect(RevWalk(repo), isA()); }); test('throws when trying to initialize and error occurs', () { @@ -44,55 +42,42 @@ void main() { test('returns list of commits with default sorting', () { final walker = RevWalk(repo); - final start = Oid.fromSHA(repo: repo, sha: log.first); - walker.push(start); + walker.push(repo[log.first]); final commits = walker.walk(); for (var i = 0; i < commits.length; i++) { expect(commits[i].oid.sha, log[i]); - commits[i].free(); } - - walker.free(); }); test('returns list of commits with reverse sorting', () { final walker = RevWalk(repo); - final start = Oid.fromSHA(repo: repo, sha: log.first); - walker.push(start); + walker.push(repo[log.first]); walker.sorting({GitSort.reverse}); final commits = walker.walk(); for (var i = 0; i < commits.length; i++) { expect(commits[i].oid.sha, log.reversed.toList()[i]); - commits[i].free(); } - - walker.free(); }); test('changes sorting', () { final walker = RevWalk(repo); - final start = Oid.fromSHA(repo: repo, sha: log.first); - walker.push(start); + walker.push(repo[log.first]); final timeSortedCommits = walker.walk(); for (var i = 0; i < timeSortedCommits.length; i++) { expect(timeSortedCommits[i].oid.sha, log[i]); - timeSortedCommits[i].free(); } walker.sorting({GitSort.time, GitSort.reverse}); final reverseSortedCommits = walker.walk(); for (var i = 0; i < reverseSortedCommits.length; i++) { expect(reverseSortedCommits[i].oid.sha, log.reversed.toList()[i]); - reverseSortedCommits[i].free(); } - - walker.free(); }); test('adds matching references for traversal with provided glob', () { @@ -101,11 +86,6 @@ void main() { walker.pushGlob('heads'); final commits = walker.walk(); expect(commits.length, 7); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test("adds repository's head for traversal", () { @@ -114,11 +94,6 @@ void main() { walker.pushHead(); final commits = walker.walk(); expect(commits.length, 6); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('adds reference for traversal with provided name', () { @@ -127,23 +102,14 @@ void main() { walker.pushReference('refs/heads/master'); final commits = walker.walk(); expect(commits.length, 6); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('throws when trying to add reference for traversal with invalid name', () { - final walker = RevWalk(repo); - expect( - () => walker.pushReference('invalid'), + () => RevWalk(repo).pushReference('invalid'), throwsA(isA()), ); - - walker.free(); }); test('adds range for traversal', () { @@ -152,22 +118,13 @@ void main() { walker.pushRange('HEAD..@{-1}'); final commits = walker.walk(); expect(commits.length, 1); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('throws when trying to add invalid range for traversal', () { - final walker = RevWalk(repo); - expect( - () => walker.pushRange('HEAD..invalid'), + () => RevWalk(repo).pushRange('HEAD..invalid'), throwsA(isA()), ); - - walker.free(); }); test('hides commit and its ancestors', () { @@ -178,22 +135,13 @@ void main() { final commits = walker.walk(); expect(commits.length, 2); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('throws when trying to hide commit oid and error occurs', () { - final walker = RevWalk(repo); - expect( - () => walker.hide(repo['0' * 40]), + () => RevWalk(repo).hide(repo['0' * 40]), throwsA(isA()), ); - - walker.free(); }); test('hides oids of references for provided glob pattern', () { @@ -207,105 +155,72 @@ void main() { walker.hideGlob('*master'); final hiddenCommits = walker.walk(); expect(hiddenCommits.length, 1); - - hiddenCommits[0].free(); - for (final c in commits) { - c.free(); - } - walker.free(); }); test('hides head', () { - final head = repo.head; final walker = RevWalk(repo); - walker.push(head.target); + walker.push(repo.head.target); final commits = walker.walk(); expect(commits.length, 6); - walker.push(head.target); + walker.push(repo.head.target); walker.hideHead(); final hiddenCommits = walker.walk(); expect(hiddenCommits.length, 0); - - for (final c in commits) { - c.free(); - } - head.free(); - walker.free(); }); test('hides oids of reference with provided name', () { - final head = repo.head; final walker = RevWalk(repo); - walker.push(head.target); + walker.push(repo.head.target); final commits = walker.walk(); expect(commits.length, 6); - walker.push(head.target); + walker.push(repo.head.target); walker.hideReference('refs/heads/master'); final hiddenCommits = walker.walk(); expect(hiddenCommits.length, 0); - - for (final c in commits) { - c.free(); - } - head.free(); - walker.free(); }); test('throws when trying to hide oids of reference with invalid name', () { - final walker = RevWalk(repo); - expect( - () => walker.hideReference('invalid'), + () => RevWalk(repo).hideReference('invalid'), throwsA(isA()), ); - - walker.free(); }); test('resets walker', () { final walker = RevWalk(repo); - final start = Oid.fromSHA(repo: repo, sha: log.first); - walker.push(start); + walker.push(repo[log.first]); walker.reset(); final commits = walker.walk(); expect(commits, []); - - walker.free(); }); test('simplifies walker by enqueuing only first parent for each commit', () { final walker = RevWalk(repo); - final start = Oid.fromSHA(repo: repo, sha: log.first); - walker.push(start); + walker.push(repo[log.first]); walker.simplifyFirstParent(); final commits = walker.walk(); - for (var i = 0; i < commits.length; i++) { - expect(commits.length, 3); - commits[i].free(); - } - - walker.free(); + expect(commits.length, 3); }); test('throws when trying to add new root for traversal and error occurs', () { - final walker = RevWalk(repo); - expect( - () => walker.push(repo['0' * 40]), + () => RevWalk(repo).push(repo['0' * 40]), throwsA(isA()), ); + }); - walker.free(); + test('manually releases allocated memory', () { + expect(() => RevWalk(repo).free(), returnsNormally); }); }); } diff --git a/test/signature_test.dart b/test/signature_test.dart index ff147ee..303a607 100644 --- a/test/signature_test.dart +++ b/test/signature_test.dart @@ -16,9 +16,6 @@ void main() { ); }); - tearDown(() { - signature.free(); - }); group('Signature', () { test('creates with provided time and offset', () { expect(signature, isA()); @@ -50,7 +47,6 @@ void main() { lessThan(5), ); expect(sig.offset, isA()); - sig.free(); }); test('returns correct values', () { @@ -66,9 +62,16 @@ void main() { email: email, time: time, ); - expect(signature == otherSignature, true); + expect(signature, equals(otherSignature)); + }); - otherSignature.free(); + test('manually releases allocated memory', () { + final signature = Signature.create( + name: name, + email: email, + time: time, + ); + expect(() => signature.free(), returnsNormally); }); test('returns string representation of Signature object', () { diff --git a/test/stash_test.dart b/test/stash_test.dart index adf09bf..ca064fa 100644 --- a/test/stash_test.dart +++ b/test/stash_test.dart @@ -24,7 +24,6 @@ void main() { }); tearDown(() { - stasher.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -62,14 +61,11 @@ void main() { test('leaves changes added to index intact', () { File(filePath).writeAsStringSync('edit', mode: FileMode.append); - final index = repo.index; - index.add('file'); + repo.index.add('file'); Stash.create(repo: repo, stasher: stasher, flags: {GitStash.keepIndex}); expect(repo.status.isEmpty, false); expect(repo.stashes.length, 1); - - index.free(); }); test('applies changes from stash', () { @@ -109,8 +105,6 @@ void main() { Stash.apply(repo: repo, reinstateIndex: true); expect(repo.status, contains('stash.this')); expect(index.find('stash.this'), true); - - index.free(); }); test('throws when trying to apply with wrong index', () { @@ -182,8 +176,6 @@ void main() { Stash.pop(repo: repo, reinstateIndex: true); expect(repo.status, contains('stash.this')); expect(index.find('stash.this'), true); - - index.free(); }); test('throws when trying to pop with wrong index', () { diff --git a/test/submodule_test.dart b/test/submodule_test.dart index 77566e8..3438b5d 100644 --- a/test/submodule_test.dart +++ b/test/submodule_test.dart @@ -47,8 +47,6 @@ void main() { expect(submodule.ignore, GitSubmoduleIgnore.none); expect(submodule.updateRule, GitSubmoduleUpdate.checkout); expect(submodule.toString(), contains('Submodule{')); - - submodule.free(); }); test('throws when trying to lookup and submodule not found', () { @@ -70,8 +68,11 @@ void main() { }); test('updates with provided init flag', () { - final submoduleFilePath = - p.join(repo.workdir, testSubmodule, 'master.txt'); + final submoduleFilePath = p.join( + repo.workdir, + testSubmodule, + 'master.txt', + ); expect(File(submoduleFilePath).existsSync(), false); Submodule.update(repo: repo, name: testSubmodule, init: true); @@ -92,24 +93,20 @@ void main() { Submodule.update(repo: repo, name: testSubmodule); final submoduleRepo = submodule.open(); - final subHead = submoduleRepo.head; expect(submoduleRepo, isA()); - expect(subHead.target.sha, submoduleHeadSha); + expect(submoduleRepo.head.target.sha, submoduleHeadSha); expect( submodule.workdirOid?.sha, '49322bb17d3acc9146f98c97d078513228bbf3c0', ); - subHead.free(); submoduleRepo.free(); - submodule.free(); }); test('throws when trying to open repository for not initialized submodule', () { final submodule = Submodule.lookup(repo: repo, name: testSubmodule); expect(() => submodule.open(), throwsA(isA())); - submodule.free(); }); test('adds submodule', () { @@ -125,7 +122,6 @@ void main() { expect(submoduleRepo.isEmpty, false); submoduleRepo.free(); - submodule.free(); }); test('throws when trying to add submodule with wrong url', () { @@ -170,9 +166,6 @@ void main() { expect(updatedSubmodule.branch, 'updated'); expect(updatedSubmodule.ignore, GitSubmoduleIgnore.all); expect(updatedSubmodule.updateRule, GitSubmoduleUpdate.rebase); - - updatedSubmodule.free(); - submodule.free(); }); test('syncs', () { @@ -205,13 +198,8 @@ void main() { 'https://updated.com/', ); - updatedSubmRepoConfig.free(); submRepo.free(); updatedSubmRepo.free(); - updatedSubmodule.free(); - submRepoConfig.free(); - repoConfig.free(); - submodule.free(); }); test('reloads info', () { @@ -222,8 +210,6 @@ void main() { submodule.reload(); expect(submodule.url, 'updated'); - - submodule.free(); }); test('returns status for a submodule', () { @@ -259,8 +245,11 @@ void main() { GitSubmoduleStatus.inWorkdir, }, ); + }); - submodule.free(); + test('manually releases allocated memory', () { + final submodule = Submodule.lookup(repo: repo, name: testSubmodule); + expect(() => submodule.free(), returnsNormally); }); }); } diff --git a/test/tag_test.dart b/test/tag_test.dart index 48e6760..49b3cf0 100644 --- a/test/tag_test.dart +++ b/test/tag_test.dart @@ -21,7 +21,6 @@ void main() { }); tearDown(() { - tag.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -50,18 +49,14 @@ void main() { offset: 180, ); final target = tag.target as Commit; - final tagger = tag.tagger; expect(tag.oid, tagOid); expect(tag.name, 'v0.2'); expect(tag.message, 'annotated tag\n'); expect(tag.targetType, GitObject.commit); expect(target.message, 'add subdirectory file\n'); - expect(tagger, signature); + expect(tag.tagger, signature); expect(tag.toString(), contains('Tag{')); - - signature.free(); - target.free(); }); test('creates new annotated tag with commit as target', () { @@ -85,19 +80,14 @@ void main() { ); final newTag = Tag.lookup(repo: repo, oid: oid); - final tagger = newTag.tagger; final newTagTarget = newTag.target as Commit; expect(newTag.oid, oid); expect(newTag.name, tagName); expect(newTag.message, message); expect(newTag.targetOid.sha, targetSHA); - expect(tagger, signature); + expect(newTag.tagger, signature); expect(newTagTarget.oid, target); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with commit as target', () { @@ -115,8 +105,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); - - newTag.free(); }); test('creates new annotated tag with tree as target', () { @@ -139,18 +127,13 @@ void main() { ); final newTag = Tag.lookup(repo: repo, oid: oid); - final tagger = newTag.tagger; final newTagTarget = newTag.target as Tree; expect(newTag.oid, oid); expect(newTag.name, tagName); expect(newTag.message, message); - expect(tagger, signature); + expect(newTag.tagger, signature); expect(newTagTarget.oid, target); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with tree as target', () { @@ -168,8 +151,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); - - newTag.free(); }); test('creates new annotated tag with blob as target', () { @@ -192,18 +173,13 @@ void main() { ); final newTag = Tag.lookup(repo: repo, oid: oid); - final tagger = newTag.tagger; final newTagTarget = newTag.target as Blob; expect(newTag.oid, oid); expect(newTag.name, tagName); expect(newTag.message, message); - expect(tagger, signature); + expect(newTag.tagger, signature); expect(newTagTarget.oid, target); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with blob as target', () { @@ -221,8 +197,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); - - newTag.free(); }); test('creates new annotated tag with tag as target', () { @@ -244,18 +218,13 @@ void main() { ); final newTag = Tag.lookup(repo: repo, oid: oid); - final tagger = newTag.tagger; final newTagTarget = newTag.target as Tag; expect(newTag.oid, oid); expect(newTag.name, tagName); expect(newTag.message, message); - expect(tagger, signature); + expect(newTag.tagger, signature); expect(newTagTarget.oid, tag.oid); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with tag as target', () { @@ -272,8 +241,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, tag.oid); - - newTag.free(); }); test( @@ -303,20 +270,15 @@ void main() { ); final newTag = Tag.lookup(repo: repo, oid: oid); - final tagger = newTag.tagger; final newTagTarget = newTag.target as Commit; expect(newTag.oid, oid); expect(newTag.name, tagName); expect(newTag.message, message); expect(newTag.targetOid.sha, targetSHA); - expect(tagger, signature); + expect(newTag.tagger, signature); expect(newTagTarget.oid, target); expect(repo.tags.length, equals(2)); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test( @@ -342,8 +304,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); expect(repo.tags.length, equals(2)); - - newTag.free(); }); test('throws when trying to create annotated tag with invalid name', () { @@ -420,5 +380,10 @@ void main() { throwsA(isA()), ); }); + + test('manually releases allocated memory', () { + tag = Tag.lookup(repo: repo, oid: tagOid); + expect(() => tag.free(), returnsNormally); + }); }); } diff --git a/test/tree_test.dart b/test/tree_test.dart index 05cbdbb..e35b6f1 100644 --- a/test/tree_test.dart +++ b/test/tree_test.dart @@ -19,7 +19,6 @@ void main() { }); tearDown(() { - tree.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -65,7 +64,6 @@ void main() { final entry = tree['dir/dir_file.txt']; expect(entry.oid.sha, 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'); expect(entry.toString(), contains('TreeEntry{')); - entry.free(); }); test('throws when nothing found for provided path', () { @@ -92,9 +90,17 @@ void main() { expect(entry.name, 'filename'); expect(entry.filemode, GitFilemode.blob); expect(entry.oid, fileOid); + }); - builder.free(); - newTree.free(); + test('manually releases allocated memory', () { + final tree = Tree.lookup(repo: repo, oid: repo['a8ae3dd']); + expect(() => tree.free(), returnsNormally); + }); + + test( + 'manually releases allocated memory for tree entry ' + 'looked up by path', () { + expect(() => tree['dir/dir_file.txt'].free(), returnsNormally); }); }); } diff --git a/test/treebuilder_test.dart b/test/treebuilder_test.dart index 9b8ad07..e83ac15 100644 --- a/test/treebuilder_test.dart +++ b/test/treebuilder_test.dart @@ -19,7 +19,6 @@ void main() { }); tearDown(() { - tree.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -29,18 +28,14 @@ void main() { final builder = TreeBuilder(repo: repo); expect(builder, isA()); expect(builder.toString(), contains('TreeBuilder{')); - builder.free(); }); test('initializes tree builder with provided tree', () { final builder = TreeBuilder(repo: repo, tree: tree); - final oid = builder.write(); expect(builder, isA()); expect(builder.length, tree.length); - expect(oid, tree.oid); - - builder.free(); + expect(builder.write(), tree.oid); }); test('throws when trying to initialize and error occurs', () { @@ -56,8 +51,6 @@ void main() { expect(builder.length, 4); builder.clear(); expect(builder.length, 0); - - builder.free(); }); test('builds the tree builder from entry of tree', () { @@ -72,8 +65,6 @@ void main() { filemode: entry.filemode, ); expect(builder[entry.name].name, entry.name); - - builder.free(); }); test('throws when trying to add entry with invalid name or invalid oid', @@ -96,8 +87,6 @@ void main() { ), throwsA(isA()), ); - - builder.free(); }); test('removes an entry', () { @@ -108,14 +97,17 @@ void main() { builder.remove('.gitignore'); expect(() => builder['.gitignore'], throwsA(isA())); expect(builder.length, tree.length - 1); - - builder.free(); }); test('throws when trying to remove entry that is not in the tree', () { - final builder = TreeBuilder(repo: repo); - expect(() => builder.remove('not.there'), throwsA(isA())); - builder.free(); + expect( + () => TreeBuilder(repo: repo).remove('not.there'), + throwsA(isA()), + ); + }); + + test('manually releases allocated memory', () { + expect(() => TreeBuilder(repo: repo).free(), returnsNormally); }); }); } diff --git a/test/worktree_test.dart b/test/worktree_test.dart index d8b5c24..9dad2a1 100644 --- a/test/worktree_test.dart +++ b/test/worktree_test.dart @@ -38,28 +38,21 @@ void main() { name: worktreeName, path: worktreeDir.path, ); - final branches = repo.branches; expect(repo.worktrees, [worktreeName]); - expect(branches.any((branch) => branch.name == worktreeName), true); + expect(repo.branches.any((branch) => branch.name == worktreeName), true); expect(worktree.name, worktreeName); expect(worktree.path, contains('worktree')); expect(worktree.isLocked, false); expect(worktree.toString(), contains('Worktree{')); expect(File(p.join(worktreeDir.path, '.git')).existsSync(), true); - - for (final branch in branches) { - branch.free(); - } - worktree.free(); }); test('creates worktree at provided path from provided reference', () { - final head = RevParse.single(repo: repo, spec: 'HEAD') as Commit; final worktreeBranch = Branch.create( repo: repo, name: 'v1', - target: head, + target: RevParse.single(repo: repo, spec: 'HEAD') as Commit, ); final ref = Reference.lookup(repo: repo, name: 'refs/heads/v1'); expect(repo.worktrees, []); @@ -83,14 +76,6 @@ void main() { expect(repo.worktrees, []); expect(worktreeBranch.isCheckedOut, false); expect(branches.any((branch) => branch.name == 'v1'), true); - - for (final branch in branches) { - branch.free(); - } - worktreeBranch.free(); - ref.free(); - head.free(); - worktree.free(); }); test('throws when trying to create worktree with invalid name or path', () { @@ -113,19 +98,16 @@ void main() { }); test('lookups worktree', () { - final worktree = Worktree.create( + Worktree.create( repo: repo, name: worktreeName, path: worktreeDir.path, ); - final lookedupWorktree = Worktree.lookup(repo: repo, name: worktreeName); + final worktree = Worktree.lookup(repo: repo, name: worktreeName); - expect(lookedupWorktree.name, worktreeName); - expect(lookedupWorktree.path, contains('worktree')); - expect(lookedupWorktree.isLocked, false); - - lookedupWorktree.free(); - worktree.free(); + expect(worktree.name, worktreeName); + expect(worktree.path, contains('worktree')); + expect(worktree.isLocked, false); }); test('throws when trying to lookup and error occurs', () { @@ -148,8 +130,6 @@ void main() { worktree.unlock(); expect(worktree.isLocked, false); - - worktree.free(); }); test('prunes worktree', () { @@ -170,8 +150,6 @@ void main() { worktree.prune(); expect(repo.worktrees, []); - - worktree.free(); }); test('throws when trying get list of worktrees and error occurs', () { @@ -180,5 +158,14 @@ void main() { throwsA(isA()), ); }); + + test('manually releases allocated memory', () { + final worktree = Worktree.create( + repo: repo, + name: worktreeName, + path: worktreeDir.path, + ); + expect(() => worktree.free(), returnsNormally); + }); }); }