refactor!: use Finalizer to automatically free allocated memory for objects (#48)

BREAKING CHANGE: signature change for remote and repository callbacks during repository clone operation.
This commit is contained in:
Aleksey Kulikov 2022-04-28 11:04:48 +03:00 committed by GitHub
parent 94c40f9a94
commit a3213a88a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
103 changed files with 2278 additions and 2595 deletions

View file

@ -16,10 +16,10 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3.0.1
- uses: subosito/flutter-action@v1 - uses: subosito/flutter-action@v2
with: with:
channel: stable channel: master
- name: Install dependencies - name: Install dependencies
run: flutter pub get run: flutter pub get
@ -44,10 +44,10 @@ jobs:
git config --global core.autocrlf false git config --global core.autocrlf false
git config --global core.eol lf git config --global core.eol lf
- uses: actions/checkout@v2 - uses: actions/checkout@v3.0.1
- uses: subosito/flutter-action@v1 - uses: subosito/flutter-action@v2
with: with:
channel: stable channel: master
- name: Install dependencies - name: Install dependencies
run: flutter pub get run: flutter pub get

View file

@ -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. 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. Let's look at some of the classes and methods.
### Repository ### 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: You can instantiate a `Repository` class with a path to open an existing repository:
```dart ```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: You can create new repository with provided path and optional `bare` argument if you want it to be bare:
```dart ```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: 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( final repo = Repository.clone(
url: 'https://some.url/', url: 'https://some.url/',
localPath: 'path/to/clone/into', 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: 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.name; // => 'refs/heads/master'
ref.target; // => Oid ref.target; // => Oid
ref.target.sha; // => '821ed6e80627b8769d170a293862f9fc60825226' ref.target.sha; // => '821ed6e80627b8769d170a293862f9fc60825226'
// Release memory allocated for Reference object when it's no longer needed
ref.free();
// Looking up object with oid // Looking up object with oid
final oid = repo['821ed6e80627b8769d170a293862f9fc60825226']; // => Oid final oid = repo['821ed6e80627b8769d170a293862f9fc60825226']; // => Oid
final commit = Commit.lookup(repo: repo, oid: oid); // => Commit final commit = Commit.lookup(repo: repo, oid: oid); // => Commit
commit.message; // => 'initial 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 // Release memory allocated for Repository object when it's no longer needed
repo.free(); repo.free();
@ -161,8 +161,7 @@ Commit.create(
parents: [], // empty list for initial commit, 1 parent for regular and 2+ for merge commits parents: [], // empty list for initial commit, 1 parent for regular and 2+ for merge commits
); // => Oid ); // => Oid
tree.free(); // Release memory allocated for Repository object when it's no longer needed
index.free();
repo.free(); repo.free();
``` ```
@ -195,9 +194,6 @@ commit.message; // => 'initial commit\n'
commit.time; // => 1635869993 (seconds since epoch) commit.time; // => 1635869993 (seconds since epoch)
commit.author; // => Signature commit.author; // => Signature
commit.tree; // => Tree commit.tree; // => Tree
// Release memory allocated for Commit object when it's no longer needed
commit.free();
``` ```
### Tree and TreeEntry ### Tree and TreeEntry
@ -223,9 +219,6 @@ final entry = tree['file.txt']; // => TreeEntry
entry.oid; // => Oid entry.oid; // => Oid
entry.name // => 'file.txt' entry.name // => 'file.txt'
entry.filemode // => GitFilemode.blob 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: You can also write trees with TreeBuilder:
@ -239,10 +232,6 @@ builder.add(
); );
final treeOid = builder.write(); // => Oid 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 // Perform commit using that tree in arguments
... ...
``` ```
@ -278,9 +267,6 @@ repo.tags; // => ['v0.1', 'v0.2']
tag.oid; // => Oid tag.oid; // => Oid
tag.name; // => 'v0.1' tag.name; // => 'v0.1'
// Release memory allocated for Tag object when it's no longer needed
tag.free();
``` ```
### Blob ### Blob
@ -297,9 +283,6 @@ final blob = Blob.lookup(repo: repo, oid: repo['e69de29']); // => Blob
blob.oid; // => Oid blob.oid; // => Oid
blob.content; // => 'content of the file' blob.content; // => 'content of the file'
blob.size; // => 19 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 // Perform traversal
final commits = walker.walk(); // => [Commit, Commit, ...] 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 // Unstage entry from index
index.remove('new.txt'); 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.message; // => 'commit (initial): init'
entry.committer; // => Signature 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' branch.name; // => 'master'
// Create branch // Create branch
final branch = Branch.create(repo: repo, name: 'feature', target: commit); // => Branch Branch.create(repo: repo, name: 'feature', target: commit); // => Branch
// Rename branch // Rename branch
Branch.rename(repo: repo, oldName: 'feature', newName: 'feature2'); Branch.rename(repo: repo, oldName: 'feature', newName: 'feature2');
// Delete branch // Delete branch
Branch.delete(repo: repo, name: 'feature2'); 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 // Read the contents of a git patch file
final diff = Diff.parse(patch.text); // => Diff 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: Some methods for inspecting Diff object:
@ -492,8 +457,6 @@ final stats = diff.stats; // => DiffStats
stats.insertions; // => 69 stats.insertions; // => 69
stats.deletions; // => 420 stats.deletions; // => 420
stats.filesChanged; // => 1 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 // Get the list of DiffDelta's containing file pairs with and old and new revisions
final deltas = diff.deltas; // => [DiffDelta, DiffDelta, ...] 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 // Patch from entry in the diff list at provided index position
final patch = Patch.fromDiff(diff: diff, index: 0); // => Patch 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: Some methods for inspecting Patch object:
@ -535,7 +495,6 @@ patch.size(); // => 1337
// Get the list of hunks in a patch // Get the list of hunks in a patch
patch.hunks; // => [DiffHunk, DiffHunk, ...] patch.hunks; // => [DiffHunk, DiffHunk, ...]
``` ```
--- ---
@ -559,9 +518,6 @@ config['user.name'] = 'Another Name';
// Delete variable from the config // Delete variable from the config
config.delete('user.name'); 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 ```dart
// Get the list of all stashed states (first being the most recent) // 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 // Save local modifications to a new stash
Stash.create(repo: repo, stasher: signature, message: 'WIP'); // => Oid Stash.create(repo: repo, stasher: signature, message: 'WIP'); // => Oid
@ -648,7 +604,7 @@ Stash.pop(repo: repo);
repo.worktrees; // => ['worktree1', 'worktree2']; repo.worktrees; // => ['worktree1', 'worktree2'];
// Lookup existing worktree // Lookup existing worktree
final worktree = Worktree.lookup(repo: repo, name: 'worktree1'); // => Worktree Worktree.lookup(repo: repo, name: 'worktree1'); // => Worktree
// Create new worktree // Create new worktree
final worktree = Worktree.create( final worktree = Worktree.create(
@ -682,18 +638,14 @@ Some API methods for submodule management:
repo.submodules; // => ['Submodule1', 'Submodule2']; repo.submodules; // => ['Submodule1', 'Submodule2'];
// Lookup submodule // Lookup submodule
final submodule = Submodule.lookup(repo: repo, name: 'Submodule'); // => Submodule Submodule.lookup(repo: repo, name: 'Submodule'); // => Submodule
// Init and update // Init and update
Submodule.init(repo: repo, name: 'Submodule'); Submodule.init(repo: repo, name: 'Submodule');
Submodule.update(repo: repo, name: 'Submodule'); Submodule.update(repo: repo, name: 'Submodule');
// Add submodule // Add submodule
final submodule = Submodule.add( Submodule.add(repo: repo, url: 'https://some.url', path: 'submodule'); // => Submodule
repo: repo,
url: 'https://some.url',
path: 'submodule',
); // => Submodule
``` ```
Some methods for inspecting Submodule object: Some methods for inspecting Submodule object:

View file

@ -4,8 +4,6 @@ analyzer:
language: language:
strict-casts: true strict-casts: true
strict-raw-types: true strict-raw-types: true
strong-mode:
implicit-casts: false
exclude: exclude:
- lib/src/bindings/libgit2_bindings.dart - lib/src/bindings/libgit2_bindings.dart
@ -35,15 +33,12 @@ linter:
- directives_ordering - directives_ordering
- eol_at_end_of_file - eol_at_end_of_file
- join_return_with_assignment - join_return_with_assignment
- library_private_types_in_public_api
- lines_longer_than_80_chars - lines_longer_than_80_chars
- literal_only_boolean_expressions - literal_only_boolean_expressions
- missing_whitespace_between_adjacent_strings - missing_whitespace_between_adjacent_strings
- no_default_cases - no_default_cases
- noop_primitive_operations - noop_primitive_operations
- null_check_on_nullable_type_parameter
- omit_local_variable_types - omit_local_variable_types
- one_member_abstracts
- parameter_assignments - parameter_assignments
- prefer_asserts_in_initializer_lists - prefer_asserts_in_initializer_lists
- prefer_asserts_with_message - prefer_asserts_with_message
@ -70,3 +65,4 @@ linter:
- use_raw_strings - use_raw_strings
- use_setters_to_change_properties - use_setters_to_change_properties
- use_string_buffers - use_string_buffers
- use_super_parameters

View file

@ -14,20 +14,17 @@ class AnnotatedCommit {
/// It is preferable to use [AnnotatedCommit.fromReference] instead of this /// It is preferable to use [AnnotatedCommit.fromReference] instead of this
/// one, for commit to contain more information about how it was looked up. /// 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. /// Throws a [LibGit2Error] if error occured.
AnnotatedCommit.lookup({required Repository repo, required Oid oid}) { AnnotatedCommit.lookup({required Repository repo, required Oid oid}) {
_annotatedCommitPointer = bindings.lookup( _annotatedCommitPointer = bindings.lookup(
repoPointer: repo.pointer, repoPointer: repo.pointer,
oidPointer: oid.pointer, oidPointer: oid.pointer,
); );
_finalizer.attach(this, _annotatedCommitPointer, detach: this);
} }
/// Creates an annotated commit from the given [reference]. /// Creates an annotated commit from the given [reference].
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
AnnotatedCommit.fromReference({ AnnotatedCommit.fromReference({
required Repository repo, required Repository repo,
@ -37,6 +34,7 @@ class AnnotatedCommit {
repoPointer: repo.pointer, repoPointer: repo.pointer,
referencePointer: reference.pointer, referencePointer: reference.pointer,
); );
_finalizer.attach(this, _annotatedCommitPointer, detach: this);
} }
/// Creates an annotated commit from a revision string. /// 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 /// See `man gitrevisions`, or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions
/// for information on the syntax accepted. /// for information on the syntax accepted.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
AnnotatedCommit.fromRevSpec({ AnnotatedCommit.fromRevSpec({
required Repository repo, required Repository repo,
@ -55,6 +51,7 @@ class AnnotatedCommit {
repoPointer: repo.pointer, repoPointer: repo.pointer,
revspec: spec, revspec: spec,
); );
_finalizer.attach(this, _annotatedCommitPointer, detach: this);
} }
/// Creates an annotated commit from the given fetch head data. /// 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. /// [oid] is the commit object id of the remote branch.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
AnnotatedCommit.fromFetchHead({ AnnotatedCommit.fromFetchHead({
required Repository repo, required Repository repo,
@ -82,6 +77,7 @@ class AnnotatedCommit {
remoteUrl: remoteUrl, remoteUrl: remoteUrl,
oid: oid.pointer, oid: oid.pointer,
); );
_finalizer.attach(this, _annotatedCommitPointer, detach: this);
} }
late final Pointer<git_annotated_commit> _annotatedCommitPointer; late final Pointer<git_annotated_commit> _annotatedCommitPointer;
@ -98,5 +94,14 @@ class AnnotatedCommit {
String get refName => bindings.refName(_annotatedCommitPointer); String get refName => bindings.refName(_annotatedCommitPointer);
/// Releases memory allocated for commit object. /// 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<git_annotated_commit>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -27,11 +27,14 @@ Pointer<git_annotated_commit> lookup({
oidPointer, oidPointer,
); );
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -49,11 +52,14 @@ Pointer<git_annotated_commit> fromRef({
referencePointer, referencePointer,
); );
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -75,13 +81,15 @@ Pointer<git_annotated_commit> fromRevSpec({
revspecC, revspecC,
); );
final result = out.value;
calloc.free(revspecC); calloc.free(revspecC);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -105,14 +113,16 @@ Pointer<git_annotated_commit> fromFetchHead({
oid, oid,
); );
final result = out.value;
calloc.free(out);
calloc.free(branchNameC); calloc.free(branchNameC);
calloc.free(remoteUrlC); calloc.free(remoteUrlC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -19,27 +19,25 @@ Object? getAttribute({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
libgit2.git_attr_get(out, repoPointer, flags, pathC, nameC); libgit2.git_attr_get(out, repoPointer, flags, pathC, nameC);
final result = out.value;
calloc.free(out);
calloc.free(pathC); calloc.free(pathC);
calloc.free(nameC); 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) { if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_UNSPECIFIED) {
calloc.free(out);
return null; return null;
} }
if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_TRUE) { if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_TRUE) {
calloc.free(out);
return true; return true;
} }
if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_FALSE) { if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_FALSE) {
calloc.free(out);
return false; return false;
} }
if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_STRING) { if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_STRING) {
final result = out.value.cast<Utf8>().toDartString(); return result.cast<Utf8>().toDartString();
calloc.free(out);
return result;
} }
return null; return null;
} }

View file

@ -47,14 +47,16 @@ Pointer<git_blame> file({
final error = libgit2.git_blame_file(out, repoPointer, pathC, options); final error = libgit2.git_blame_file(out, repoPointer, pathC, options);
final result = out.value;
calloc.free(out);
calloc.free(pathC); calloc.free(pathC);
calloc.free(options); calloc.free(options);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -80,13 +82,15 @@ Pointer<git_blame> buffer({
buffer.length, buffer.length,
); );
final result = out.value;
calloc.free(out);
calloc.free(bufferC); calloc.free(bufferC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -16,11 +16,14 @@ Pointer<git_blob> lookup({
final out = calloc<Pointer<git_blob>>(); final out = calloc<Pointer<git_blob>>();
final error = libgit2.git_blob_lookup(out, repoPointer, oidPointer); final error = libgit2.git_blob_lookup(out, repoPointer, oidPointer);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -128,7 +131,11 @@ Pointer<git_oid> createFromDisk({
Pointer<git_blob> duplicate(Pointer<git_blob> source) { Pointer<git_blob> duplicate(Pointer<git_blob> source) {
final out = calloc<Pointer<git_blob>>(); final out = calloc<Pointer<git_blob>>();
libgit2.git_blob_dup(out, source); 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. /// 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); final error = libgit2.git_blob_filter(out, blobPointer, asPathC, opts);
late final String result;
if (out.ref.ptr != nullptr) {
result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
}
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(asPathC); calloc.free(asPathC);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }

View file

@ -35,13 +35,15 @@ List<Pointer<git_reference>> list({
error = libgit2.git_branch_next(reference, refType, iterator.value); error = libgit2.git_branch_next(reference, refType, iterator.value);
if (error == 0) { if (error == 0) {
result.add(reference.value); result.add(reference.value);
calloc.free(refType);
} else { } else {
break; break;
} }
calloc.free(reference);
calloc.free(refType);
} }
libgit2.git_branch_iterator_free(iterator.value); libgit2.git_branch_iterator_free(iterator.value);
calloc.free(iterator);
return result; return result;
} }
@ -65,13 +67,15 @@ Pointer<git_reference> lookup({
branchType, branchType,
); );
final result = out.value;
calloc.free(out);
calloc.free(branchNameC); calloc.free(branchNameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -103,13 +107,15 @@ Pointer<git_reference> create({
forceC, forceC,
); );
final result = out.value;
calloc.free(out);
calloc.free(branchNameC); calloc.free(branchNameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -192,13 +198,14 @@ String name(Pointer<git_reference> ref) {
final out = calloc<Pointer<Int8>>(); final out = calloc<Pointer<Int8>>();
final error = libgit2.git_branch_name(out, ref); final error = libgit2.git_branch_name(out, ref);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.value.cast<Utf8>().toDartString(); return result.cast<Utf8>().toDartString();
calloc.free(out);
return result;
} }
} }
@ -218,14 +225,15 @@ String remoteName({
final branchNameC = branchName.toNativeUtf8().cast<Int8>(); final branchNameC = branchName.toNativeUtf8().cast<Int8>();
final error = libgit2.git_branch_remote_name(out, repoPointer, branchNameC); final error = libgit2.git_branch_remote_name(out, repoPointer, branchNameC);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(branchNameC); calloc.free(branchNameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -240,11 +248,14 @@ Pointer<git_reference> getUpstream(Pointer<git_reference> branch) {
final out = calloc<Pointer<git_reference>>(); final out = calloc<Pointer<git_reference>>();
final error = libgit2.git_branch_upstream(out, branch); final error = libgit2.git_branch_upstream(out, branch);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -286,14 +297,15 @@ String upstreamName({
final branchNameC = branchName.toNativeUtf8().cast<Int8>(); final branchNameC = branchName.toNativeUtf8().cast<Int8>();
final error = libgit2.git_branch_upstream_name(out, repoPointer, branchNameC); final error = libgit2.git_branch_upstream_name(out, repoPointer, branchNameC);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(branchNameC); calloc.free(branchNameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -316,14 +328,15 @@ String upstreamRemote({
branchNameC, branchNameC,
); );
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(branchNameC); calloc.free(branchNameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -346,12 +359,15 @@ String upstreamMerge({
branchNameC, branchNameC,
); );
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(branchNameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }

View file

@ -17,11 +17,14 @@ Pointer<git_commit> lookup({
final out = calloc<Pointer<git_commit>>(); final out = calloc<Pointer<git_commit>>();
final error = libgit2.git_commit_lookup(out, repoPointer, oidPointer); final error = libgit2.git_commit_lookup(out, repoPointer, oidPointer);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -124,17 +127,18 @@ String createBuffer({
parentsC, parentsC,
); );
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(updateRefC); calloc.free(updateRefC);
calloc.free(messageEncodingC); calloc.free(messageEncodingC);
calloc.free(messageC); calloc.free(messageC);
calloc.free(parentsC); calloc.free(parentsC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -196,7 +200,12 @@ Pointer<git_oid> amend({
Pointer<git_commit> duplicate(Pointer<git_commit> source) { Pointer<git_commit> duplicate(Pointer<git_commit> source) {
final out = calloc<Pointer<git_commit>>(); final out = calloc<Pointer<git_commit>>();
libgit2.git_commit_dup(out, source); 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 /// 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<Int8>(); final fieldC = field.toNativeUtf8().cast<Int8>();
final error = libgit2.git_commit_header_field(out, commitPointer, fieldC); final error = libgit2.git_commit_header_field(out, commitPointer, fieldC);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(fieldC); calloc.free(fieldC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -292,11 +302,14 @@ Pointer<git_commit> parent({
final out = calloc<Pointer<git_commit>>(); final out = calloc<Pointer<git_commit>>();
final error = libgit2.git_commit_parent(out, commitPointer, position); final error = libgit2.git_commit_parent(out, commitPointer, position);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -315,11 +328,14 @@ Pointer<git_commit> nthGenAncestor({
final out = calloc<Pointer<git_commit>>(); final out = calloc<Pointer<git_commit>>();
final error = libgit2.git_commit_nth_gen_ancestor(out, commitPointer, n); final error = libgit2.git_commit_nth_gen_ancestor(out, commitPointer, n);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -350,7 +366,12 @@ Pointer<git_oid> treeOid(Pointer<git_commit> commit) {
Pointer<git_tree> tree(Pointer<git_commit> commit) { Pointer<git_tree> tree(Pointer<git_commit> commit) {
final out = calloc<Pointer<git_tree>>(); final out = calloc<Pointer<git_tree>>();
libgit2.git_commit_tree(out, commit); 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 /// Reverts the given commit, producing changes in the index and working
@ -393,13 +414,15 @@ Pointer<git_index> revertCommit({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -29,11 +29,14 @@ Pointer<git_config> openDefault() {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
final error = libgit2.git_config_open_default(out); final error = libgit2.git_config_open_default(out);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -54,12 +57,14 @@ String findGlobal() {
final out = calloc<git_buf>(); final out = calloc<git_buf>();
final error = libgit2.git_config_find_global(out); final error = libgit2.git_config_find_global(out);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -73,12 +78,14 @@ String findSystem() {
final out = calloc<git_buf>(); final out = calloc<git_buf>();
final error = libgit2.git_config_find_system(out); final error = libgit2.git_config_find_system(out);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -93,12 +100,14 @@ String findXdg() {
final out = calloc<git_buf>(); final out = calloc<git_buf>();
final error = libgit2.git_config_find_xdg(out); final error = libgit2.git_config_find_xdg(out);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -111,7 +120,12 @@ String findXdg() {
Pointer<git_config> snapshot(Pointer<git_config> config) { Pointer<git_config> snapshot(Pointer<git_config> config) {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
libgit2.git_config_snapshot(out, config); 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. /// Get the config entry of a config variable.
@ -122,16 +136,18 @@ Pointer<git_config_entry> getEntry({
required String variable, required String variable,
}) { }) {
final out = calloc<Pointer<git_config_entry>>(); final out = calloc<Pointer<git_config_entry>>();
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final error = libgit2.git_config_get_entry(out, configPointer, name); 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) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -142,10 +158,10 @@ void setBool({
required String variable, required String variable,
required bool value, required bool value,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final valueC = value ? 1 : 0; final valueC = value ? 1 : 0;
libgit2.git_config_set_bool(configPointer, name, valueC); libgit2.git_config_set_bool(configPointer, nameC, valueC);
calloc.free(name); calloc.free(nameC);
} }
/// Set the value of an integer config variable in the config file with the /// Set the value of an integer config variable in the config file with the
@ -155,9 +171,9 @@ void setInt({
required String variable, required String variable,
required int value, required int value,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
libgit2.git_config_set_int64(configPointer, name, value); libgit2.git_config_set_int64(configPointer, nameC, value);
calloc.free(name); calloc.free(nameC);
} }
/// Set the value of a string config variable in the config file with the /// 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 variable,
required String value, required String value,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final valueC = value.toNativeUtf8().cast<Int8>(); final valueC = value.toNativeUtf8().cast<Int8>();
libgit2.git_config_set_string(configPointer, name, valueC); libgit2.git_config_set_string(configPointer, nameC, valueC);
calloc.free(name); calloc.free(nameC);
calloc.free(valueC); calloc.free(valueC);
} }
@ -178,7 +194,12 @@ void setString({
Pointer<git_config_iterator> iterator(Pointer<git_config> cfg) { Pointer<git_config_iterator> iterator(Pointer<git_config> cfg) {
final out = calloc<Pointer<git_config_iterator>>(); final out = calloc<Pointer<git_config_iterator>>();
libgit2.git_config_iterator_new(out, cfg); 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 /// Delete a config variable from the config file with the highest level
@ -189,10 +210,10 @@ void delete({
required Pointer<git_config> configPointer, required Pointer<git_config> configPointer,
required String variable, required String variable,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final error = libgit2.git_config_delete_entry(configPointer, name); final error = libgit2.git_config_delete_entry(configPointer, nameC);
calloc.free(name); calloc.free(nameC);
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
@ -208,7 +229,7 @@ List<String> multivarValues({
required String variable, required String variable,
String? regexp, String? regexp,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final regexpC = regexp?.toNativeUtf8().cast<Int8>() ?? nullptr; final regexpC = regexp?.toNativeUtf8().cast<Int8>() ?? nullptr;
final iterator = calloc<Pointer<git_config_iterator>>(); final iterator = calloc<Pointer<git_config_iterator>>();
final entry = calloc<Pointer<git_config_entry>>(); final entry = calloc<Pointer<git_config_entry>>();
@ -216,7 +237,7 @@ List<String> multivarValues({
libgit2.git_config_multivar_iterator_new( libgit2.git_config_multivar_iterator_new(
iterator, iterator,
configPointer, configPointer,
name, nameC,
regexpC, regexpC,
); );
@ -232,8 +253,9 @@ List<String> multivarValues({
} }
} }
calloc.free(name); calloc.free(nameC);
calloc.free(regexpC); calloc.free(regexpC);
libgit2.git_config_iterator_free(iterator.value);
calloc.free(iterator); calloc.free(iterator);
calloc.free(entry); calloc.free(entry);
@ -250,13 +272,13 @@ void setMultivar({
required String regexp, required String regexp,
required String value, required String value,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final regexpC = regexp.toNativeUtf8().cast<Int8>(); final regexpC = regexp.toNativeUtf8().cast<Int8>();
final valueC = value.toNativeUtf8().cast<Int8>(); final valueC = value.toNativeUtf8().cast<Int8>();
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(regexpC);
calloc.free(valueC); calloc.free(valueC);
} }
@ -270,14 +292,22 @@ void deleteMultivar({
required String variable, required String variable,
required String regexp, required String regexp,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final nameC = variable.toNativeUtf8().cast<Int8>();
final regexpC = regexp.toNativeUtf8().cast<Int8>(); final regexpC = regexp.toNativeUtf8().cast<Int8>();
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); calloc.free(regexpC);
} }
/// Free the configuration and its associated memory and files. /// Free the configuration and its associated memory and files.
void free(Pointer<git_config> cfg) => libgit2.git_config_free(cfg); void free(Pointer<git_config> cfg) => libgit2.git_config_free(cfg);
/// Free a config entry.
void freeEntry(Pointer<git_config_entry> entry) =>
libgit2.git_config_entry_free(entry);
/// Free a config iterator.
void freeIterator(Pointer<git_config_iterator> iter) =>
libgit2.git_config_iterator_free(iter);

View file

@ -15,10 +15,13 @@ Pointer<git_credential> userPass({
libgit2.git_credential_userpass_plaintext_new(out, usernameC, passwordC); libgit2.git_credential_userpass_plaintext_new(out, usernameC, passwordC);
final result = out.value;
calloc.free(out);
calloc.free(usernameC); calloc.free(usernameC);
calloc.free(passwordC); calloc.free(passwordC);
return out.value; return result;
} }
/// Create a new passphrase-protected ssh key credential object. /// Create a new passphrase-protected ssh key credential object.
@ -42,12 +45,15 @@ Pointer<git_credential> sshKey({
passPhraseC, passPhraseC,
); );
final result = out.value;
calloc.free(out);
calloc.free(usernameC); calloc.free(usernameC);
calloc.free(publicKeyC); calloc.free(publicKeyC);
calloc.free(privateKeyC); calloc.free(privateKeyC);
calloc.free(passPhraseC); calloc.free(passPhraseC);
return out.value; return result;
} }
/// Create a new ssh key credential object used for querying an ssh-agent. /// Create a new ssh key credential object used for querying an ssh-agent.
@ -57,9 +63,12 @@ Pointer<git_credential> sshKeyFromAgent(String username) {
libgit2.git_credential_ssh_key_from_agent(out, usernameC); libgit2.git_credential_ssh_key_from_agent(out, usernameC);
final result = out.value;
calloc.free(out);
calloc.free(usernameC); calloc.free(usernameC);
return out.value; return result;
} }
/// Create a new ssh key credential object reading the keys from memory. /// Create a new ssh key credential object reading the keys from memory.
@ -83,10 +92,13 @@ Pointer<git_credential> sshKeyFromMemory({
passPhraseC, passPhraseC,
); );
final result = out.value;
calloc.free(out);
calloc.free(usernameC); calloc.free(usernameC);
calloc.free(publicKeyC); calloc.free(publicKeyC);
calloc.free(privateKeyC); calloc.free(privateKeyC);
calloc.free(passPhraseC); calloc.free(passPhraseC);
return out.value; return result;
} }

View file

@ -32,13 +32,15 @@ Pointer<git_describe_result> commit({
final error = libgit2.git_describe_commit(out, commitPointer.cast(), opts); final error = libgit2.git_describe_commit(out, commitPointer.cast(), opts);
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -68,13 +70,15 @@ Pointer<git_describe_result> workdir({
final error = libgit2.git_describe_workdir(out, repo, opts); final error = libgit2.git_describe_workdir(out, repo, opts);
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -106,8 +110,9 @@ String format({
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size); final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(opts); libgit2.git_buf_dispose(out);
calloc.free(out); calloc.free(out);
calloc.free(opts);
return result; return result;
} }

View file

@ -31,13 +31,15 @@ Pointer<git_diff> indexToIndex({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -58,9 +60,12 @@ Pointer<git_diff> indexToWorkdir({
libgit2.git_diff_index_to_workdir(out, repoPointer, indexPointer, opts); libgit2.git_diff_index_to_workdir(out, repoPointer, indexPointer, opts);
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
return out.value; return result;
} }
/// Create a diff between a tree and repository index. /// Create a diff between a tree and repository index.
@ -87,9 +92,12 @@ Pointer<git_diff> treeToIndex({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
return out.value; return result;
} }
/// Create a diff between a tree and the working directory. /// Create a diff between a tree and the working directory.
@ -116,13 +124,15 @@ Pointer<git_diff> treeToWorkdir({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -155,13 +165,15 @@ Pointer<git_diff> treeToWorkdirWithIndex({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -191,13 +203,15 @@ Pointer<git_diff> treeToTree({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -235,9 +249,12 @@ Pointer<git_diff> parse(String content) {
final contentC = content.toNativeUtf8().cast<Int8>(); final contentC = content.toNativeUtf8().cast<Int8>();
libgit2.git_diff_from_buffer(out, contentC, content.length); libgit2.git_diff_from_buffer(out, contentC, content.length);
final result = out.value;
calloc.free(out);
calloc.free(contentC); calloc.free(contentC);
return out.value; return result;
} }
/// Transform a diff marking file renames, copies, etc. /// Transform a diff marking file renames, copies, etc.
@ -324,11 +341,14 @@ Pointer<git_diff_stats> stats(Pointer<git_diff> diff) {
final out = calloc<Pointer<git_diff_stats>>(); final out = calloc<Pointer<git_diff_stats>>();
final error = libgit2.git_diff_get_stats(out, diff); final error = libgit2.git_diff_get_stats(out, diff);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -355,12 +375,14 @@ String statsPrint({
final out = calloc<git_buf>(); final out = calloc<git_buf>();
final error = libgit2.git_diff_stats_to_buf(out, statsPointer, format, width); final error = libgit2.git_diff_stats_to_buf(out, statsPointer, format, width);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -369,10 +391,14 @@ String statsPrint({
String addToBuf(Pointer<git_diff> diff) { String addToBuf(Pointer<git_diff> diff) {
final out = calloc<git_buf>(); final out = calloc<git_buf>();
libgit2.git_diff_to_buf(out, diff, git_diff_format_t.GIT_DIFF_FORMAT_PATCH); libgit2.git_diff_to_buf(out, diff, git_diff_format_t.GIT_DIFF_FORMAT_PATCH);
final result = out.ref.ptr == nullptr final result = out.ref.ptr == nullptr
? '' ? ''
: out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size); : out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out); calloc.free(out);
return result; return result;
} }
@ -462,14 +488,16 @@ Pointer<git_index> applyToTree({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(payload); calloc.free(payload);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -14,7 +14,12 @@ import 'package:libgit2dart/src/util.dart';
Pointer<git_index> newInMemory() { Pointer<git_index> newInMemory() {
final out = calloc<Pointer<git_index>>(); final out = calloc<Pointer<git_index>>();
libgit2.git_index_new(out); libgit2.git_index_new(out);
return out.value;
final result = out.value;
calloc.free(out);
return result;
} }
/// Read index capabilities flags. /// Read index capabilities flags.
@ -266,6 +271,8 @@ void addFromBuffer({
buffer.length, buffer.length,
); );
calloc.free(bufferC);
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} }
@ -452,12 +459,12 @@ List<Map<String, Pointer<git_index_entry>>> conflictList(
'our': ourOut.value, 'our': ourOut.value,
'their': theirOut.value, 'their': theirOut.value,
}); });
calloc.free(ancestorOut);
calloc.free(ourOut);
calloc.free(theirOut);
} else { } else {
break; break;
} }
calloc.free(ancestorOut);
calloc.free(ourOut);
calloc.free(theirOut);
} }
libgit2.git_index_conflict_iterator_free(iterator.value); libgit2.git_index_conflict_iterator_free(iterator.value);

View file

@ -13,7 +13,11 @@ Pointer<git_mailmap> init() {
final out = calloc<Pointer<git_mailmap>>(); final out = calloc<Pointer<git_mailmap>>();
libgit2.git_mailmap_new(out); 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. /// Create a new mailmap instance containing a single mailmap file.
@ -23,9 +27,12 @@ Pointer<git_mailmap> fromBuffer(String buffer) {
libgit2.git_mailmap_from_buffer(out, bufferC, buffer.length); libgit2.git_mailmap_from_buffer(out, bufferC, buffer.length);
final result = out.value;
calloc.free(out);
calloc.free(bufferC); calloc.free(bufferC);
return out.value; return result;
} }
/// Create a new mailmap instance from a repository, loading mailmap files based /// Create a new mailmap instance from a repository, loading mailmap files based
@ -43,11 +50,14 @@ Pointer<git_mailmap> fromRepository(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_mailmap>>(); final out = calloc<Pointer<git_mailmap>>();
final error = libgit2.git_mailmap_from_repository(out, repo); final error = libgit2.git_mailmap_from_repository(out, repo);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -87,7 +97,11 @@ Pointer<git_signature> resolveSignature({
final out = calloc<Pointer<git_signature>>(); final out = calloc<Pointer<git_signature>>();
libgit2.git_mailmap_resolve_signature(out, mailmapPointer, signaturePointer); 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, /// Add a single entry to the given mailmap object. If the entry already exists,

View file

@ -234,12 +234,16 @@ String mergeFileFromIndex({
nullptr, nullptr,
); );
late final String result;
if (out.ref.ptr != nullptr) {
result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.len);
}
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.len);
calloc.free(out);
return result; return result;
} }
} }
@ -275,13 +279,15 @@ Pointer<git_index> mergeCommits({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -318,13 +324,15 @@ Pointer<git_index> mergeTrees({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -30,15 +30,21 @@ List<Map<String, Pointer>> list(Pointer<git_repository> repo) {
if (nextError >= 0) { if (nextError >= 0) {
final out = calloc<Pointer<git_note>>(); final out = calloc<Pointer<git_note>>();
libgit2.git_note_read(out, repo, notesRef, annotatedOid); 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 { } else {
break; break;
} }
calloc.free(noteOid);
} }
calloc.free(notesRef); calloc.free(notesRef);
libgit2.git_note_iterator_free(iterator.value); libgit2.git_note_iterator_free(iterator.value);
calloc.free(iterator);
return result; return result;
} }
@ -57,13 +63,15 @@ Pointer<git_note> lookup({
final notesRefC = notesRef.toNativeUtf8().cast<Int8>(); final notesRefC = notesRef.toNativeUtf8().cast<Int8>();
final error = libgit2.git_note_read(out, repoPointer, notesRefC, oidPointer); final error = libgit2.git_note_read(out, repoPointer, notesRefC, oidPointer);
final result = out.value;
calloc.free(out);
calloc.free(notesRefC); calloc.free(notesRefC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -26,11 +26,14 @@ Pointer<git_object> lookup({
final out = calloc<Pointer<git_object>>(); final out = calloc<Pointer<git_object>>();
final error = libgit2.git_object_lookup(out, repoPointer, oidPointer, type); final error = libgit2.git_object_lookup(out, repoPointer, oidPointer, type);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -14,7 +14,12 @@ import 'package:libgit2dart/src/util.dart';
Pointer<git_odb> create() { Pointer<git_odb> create() {
final out = calloc<Pointer<git_odb>>(); final out = calloc<Pointer<git_odb>>();
libgit2.git_odb_new(out); 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. /// Add an on-disk alternate to an existing Object DB.
@ -78,8 +83,7 @@ int _forEachCb(
Pointer<git_oid> oid, Pointer<git_oid> oid,
Pointer<Void> payload, Pointer<Void> payload,
) { ) {
final _oid = oid_bindings.copy(oid); _objects.add(Oid(oid_bindings.copy(oid)));
_objects.add(Oid(_oid));
return 0; return 0;
} }
@ -121,11 +125,14 @@ Pointer<git_odb_object> read({
final out = calloc<Pointer<git_odb_object>>(); final out = calloc<Pointer<git_odb_object>>();
final error = libgit2.git_odb_read(out, odbPointer, oidPointer); final error = libgit2.git_odb_read(out, odbPointer, oidPointer);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -186,14 +193,15 @@ Pointer<git_oid> write({
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} }
final buffer = data.toNativeUtf8().cast<Int8>(); final bufferC = data.toNativeUtf8().cast<Int8>();
libgit2.git_odb_stream_write(stream.value, buffer, data.length); libgit2.git_odb_stream_write(stream.value, bufferC, data.length);
final out = calloc<git_oid>(); final out = calloc<git_oid>();
libgit2.git_odb_stream_finalize_write(out, stream.value); libgit2.git_odb_stream_finalize_write(out, stream.value);
calloc.free(buffer); calloc.free(bufferC);
libgit2.git_odb_stream_free(stream.value); libgit2.git_odb_stream_free(stream.value);
calloc.free(stream);
return out; return out;
} }

View file

@ -7,10 +7,10 @@ import 'package:libgit2dart/src/util.dart';
/// Parse N characters of a hex formatted object id into a git_oid. /// Parse N characters of a hex formatted object id into a git_oid.
Pointer<git_oid> fromStrN(String hex) { Pointer<git_oid> fromStrN(String hex) {
final out = calloc<git_oid>(); final out = calloc<git_oid>();
final str = hex.toNativeUtf8().cast<Int8>(); final hexC = hex.toNativeUtf8().cast<Int8>();
libgit2.git_oid_fromstrn(out, str, hex.length); libgit2.git_oid_fromstrn(out, hexC, hex.length);
calloc.free(str); calloc.free(hexC);
return out; return out;
} }
@ -18,10 +18,10 @@ Pointer<git_oid> fromStrN(String hex) {
/// Parse a hex formatted object id into a git_oid. /// Parse a hex formatted object id into a git_oid.
Pointer<git_oid> fromSHA(String hex) { Pointer<git_oid> fromSHA(String hex) {
final out = calloc<git_oid>(); final out = calloc<git_oid>();
final str = hex.toNativeUtf8().cast<Int8>(); final hexC = hex.toNativeUtf8().cast<Int8>();
libgit2.git_oid_fromstr(out, str); libgit2.git_oid_fromstr(out, hexC);
calloc.free(str); calloc.free(hexC);
return out; return out;
} }

View file

@ -12,11 +12,14 @@ Pointer<git_packbuilder> init(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_packbuilder>>(); final out = calloc<Pointer<git_packbuilder>>();
final error = libgit2.git_packbuilder_new(out, repo); final error = libgit2.git_packbuilder_new(out, repo);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -42,14 +42,17 @@ Pointer<git_patch> fromBuffers({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(oldAsPathC); calloc.free(oldAsPathC);
calloc.free(newAsPathC); calloc.free(newAsPathC);
calloc.free(opts); calloc.free(opts);
// We are not freeing buffers because patch object does not have reference to // We are not freeing buffers `oldBufferC` and `newBufferC` because patch
// underlying buffers. So if the buffer is freed the patch text becomes // object does not have reference to underlying buffers. So if the buffer is
// corrupted. // freed the patch text becomes corrupted.
return out.value; return result;
} }
/// Directly generate a patch from the difference between two blobs. /// Directly generate a patch from the difference between two blobs.
@ -83,11 +86,14 @@ Pointer<git_patch> fromBlobs({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(oldAsPathC); calloc.free(oldAsPathC);
calloc.free(newAsPathC); calloc.free(newAsPathC);
calloc.free(opts); calloc.free(opts);
return out.value; return result;
} }
/// Directly generate a patch from the difference between a blob and a buffer. /// Directly generate a patch from the difference between a blob and a buffer.
@ -124,11 +130,17 @@ Pointer<git_patch> fromBlobAndBuffer({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(oldAsPathC); calloc.free(oldAsPathC);
calloc.free(bufferAsPathC); calloc.free(bufferAsPathC);
calloc.free(opts); 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. /// Return a patch for an entry in the diff list.
@ -145,11 +157,14 @@ Pointer<git_patch> fromDiff({
final out = calloc<Pointer<git_patch>>(); final out = calloc<Pointer<git_patch>>();
final error = libgit2.git_patch_from_diff(out, diffPointer, index); final error = libgit2.git_patch_from_diff(out, diffPointer, index);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -172,9 +187,13 @@ Map<String, Object> hunk({
final linesInHunk = calloc<Int64>(); final linesInHunk = calloc<Int64>();
libgit2.git_patch_get_hunk(out, linesInHunk.cast(), patchPointer, hunkIndex); libgit2.git_patch_get_hunk(out, linesInHunk.cast(), patchPointer, hunkIndex);
final hunk = out.value;
final linesN = linesInHunk.value; final linesN = linesInHunk.value;
calloc.free(out);
calloc.free(linesInHunk); calloc.free(linesInHunk);
return {'hunk': out.value, 'linesN': linesN};
return {'hunk': hunk, 'linesN': linesN};
} }
/// Get line counts of each type in a patch. /// Get line counts of each type in a patch.
@ -211,7 +230,11 @@ Pointer<git_diff_line> lines({
final out = calloc<Pointer<git_diff_line>>(); final out = calloc<Pointer<git_diff_line>>();
libgit2.git_patch_get_line_in_hunk(out, patchPointer, hunkIndex, lineOfHunk); 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. /// Get the content of a patch as a single diff text.
@ -221,12 +244,14 @@ String text(Pointer<git_patch> patch) {
final out = calloc<git_buf>(); final out = calloc<git_buf>();
final error = libgit2.git_patch_to_buf(out, patch); final error = libgit2.git_patch_to_buf(out, patch);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }

View file

@ -40,13 +40,15 @@ Pointer<git_rebase> init({
opts, opts,
); );
final result = out.value;
calloc.free(out);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -61,11 +63,14 @@ Pointer<git_rebase> open(Pointer<git_repository> repo) {
final error = libgit2.git_rebase_open(out, repo, opts); final error = libgit2.git_rebase_open(out, repo, opts);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -100,11 +105,14 @@ Pointer<git_rebase_operation> next(Pointer<git_rebase> rebase) {
final out = calloc<Pointer<git_rebase_operation>>(); final out = calloc<Pointer<git_rebase_operation>>();
final error = libgit2.git_rebase_next(out, rebase); final error = libgit2.git_rebase_next(out, rebase);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -33,11 +33,14 @@ Pointer<git_reference> resolve(Pointer<git_reference> ref) {
final out = calloc<Pointer<git_reference>>(); final out = calloc<Pointer<git_reference>>();
final error = libgit2.git_reference_resolve(out, ref); final error = libgit2.git_reference_resolve(out, ref);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -56,13 +59,15 @@ Pointer<git_reference> lookup({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final error = libgit2.git_reference_lookup(out, repoPointer, nameC); final error = libgit2.git_reference_lookup(out, repoPointer, nameC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -110,14 +115,16 @@ Pointer<git_reference> rename({
logMessageC, logMessageC,
); );
final result = out.value;
calloc.free(out);
calloc.free(newNameC); calloc.free(newNameC);
calloc.free(logMessageC); calloc.free(logMessageC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -149,10 +156,10 @@ bool hasLog({
required Pointer<git_repository> repoPointer, required Pointer<git_repository> repoPointer,
required String name, required String name,
}) { }) {
final refname = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final result = libgit2.git_reference_has_log(repoPointer, refname); final result = libgit2.git_reference_has_log(repoPointer, nameC);
calloc.free(refname); calloc.free(nameC);
return result == 1 || false; return result == 1 || false;
} }
@ -244,14 +251,16 @@ Pointer<git_reference> createDirect({
logMessageC, logMessageC,
); );
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(logMessageC); calloc.free(logMessageC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -303,15 +312,17 @@ Pointer<git_reference> createSymbolic({
logMessageC, logMessageC,
); );
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(targetC); calloc.free(targetC);
calloc.free(logMessageC); calloc.free(logMessageC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -348,13 +359,15 @@ Pointer<git_reference> setTarget({
logMessageC, logMessageC,
); );
final result = out.value;
calloc.free(out);
calloc.free(logMessageC); calloc.free(logMessageC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -386,14 +399,16 @@ Pointer<git_reference> setTargetSymbolic({
logMessageC, logMessageC,
); );
final result = out.value;
calloc.free(out);
calloc.free(targetC); calloc.free(targetC);
calloc.free(logMessageC); calloc.free(logMessageC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -421,11 +436,14 @@ Pointer<git_object> peel({
final out = calloc<Pointer<git_object>>(); final out = calloc<Pointer<git_object>>();
final error = libgit2.git_reference_peel(out, refPointer, type); final error = libgit2.git_reference_peel(out, refPointer, type);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -433,7 +451,12 @@ Pointer<git_object> peel({
Pointer<git_reference> duplicate(Pointer<git_reference> source) { Pointer<git_reference> duplicate(Pointer<git_reference> source) {
final out = calloc<Pointer<git_reference>>(); final out = calloc<Pointer<git_reference>>();
libgit2.git_reference_dup(out, source); libgit2.git_reference_dup(out, source);
return out.value;
final result = out.value;
calloc.free(out);
return result;
} }
/// Free the given reference. /// Free the given reference.

View file

@ -19,9 +19,12 @@ Pointer<git_reflog> read({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
libgit2.git_reflog_read(out, repoPointer, nameC); libgit2.git_reflog_read(out, repoPointer, nameC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
return out.value; return result;
} }
/// Write an existing in-memory reflog object back to disk using an atomic file /// Write an existing in-memory reflog object back to disk using an atomic file

View file

@ -66,14 +66,15 @@ String transform({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final error = libgit2.git_refspec_transform(out, refspecPointer, nameC); final error = libgit2.git_refspec_transform(out, refspecPointer, nameC);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -90,14 +91,15 @@ String rTransform({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final error = libgit2.git_refspec_rtransform(out, refspecPointer, nameC); final error = libgit2.git_refspec_rtransform(out, refspecPointer, nameC);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }

View file

@ -9,8 +9,6 @@ import 'package:libgit2dart/src/oid.dart';
import 'package:libgit2dart/src/util.dart'; import 'package:libgit2dart/src/util.dart';
/// Get a list of the configured remotes for a repo. /// Get a list of the configured remotes for a repo.
///
/// Throws a [LibGit2Error] if error occured.
List<String> list(Pointer<git_repository> repo) { List<String> list(Pointer<git_repository> repo) {
final out = calloc<git_strarray>(); final out = calloc<git_strarray>();
libgit2.git_remote_list(out, repo); libgit2.git_remote_list(out, repo);
@ -38,13 +36,15 @@ Pointer<git_remote> lookup({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final error = libgit2.git_remote_lookup(out, repoPointer, nameC); final error = libgit2.git_remote_lookup(out, repoPointer, nameC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -62,14 +62,16 @@ Pointer<git_remote> create({
final urlC = url.toNativeUtf8().cast<Int8>(); final urlC = url.toNativeUtf8().cast<Int8>();
final error = libgit2.git_remote_create(out, repoPointer, nameC, urlC); final error = libgit2.git_remote_create(out, repoPointer, nameC, urlC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(urlC); calloc.free(urlC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -95,15 +97,17 @@ Pointer<git_remote> createWithFetchSpec({
fetchC, fetchC,
); );
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(urlC); calloc.free(urlC);
calloc.free(fetchC); calloc.free(fetchC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -5,6 +5,7 @@ import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/bindings/credentials.dart' import 'package:libgit2dart/src/bindings/credentials.dart'
as credentials_bindings; as credentials_bindings;
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/bindings/remote.dart' as remote_bindings;
import 'package:libgit2dart/src/util.dart'; import 'package:libgit2dart/src/util.dart';
class RemoteCallbacks { class RemoteCallbacks {
@ -65,11 +66,9 @@ class RemoteCallbacks {
return 0; return 0;
} }
/// A function matching the /// Values used to override the remote creation and customization process
/// `Remote Function(Repository repo, String name, String url)` signature to /// during a clone operation.
/// override the remote creation and customization process during a clone static RemoteCallback? remoteCbData;
/// operation.
static Remote Function(Repository, String, String)? remoteFunction;
/// A callback used to create the git remote, prior to its being used to /// A callback used to create the git remote, prior to its being used to
/// perform the clone operation. /// perform the clone operation.
@ -80,19 +79,31 @@ class RemoteCallbacks {
Pointer<Int8> url, Pointer<Int8> url,
Pointer<Void> payload, Pointer<Void> payload,
) { ) {
remote[0] = remoteFunction!( late final Pointer<git_remote> remotePointer;
Repository(repo),
name.cast<Utf8>().toDartString(), if (remoteCbData!.fetch == null) {
url.cast<Utf8>().toDartString(), remotePointer = remote_bindings.create(
).pointer; 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; return 0;
} }
/// A function matching the `Repository Function(String path, bool bare)` /// Values used to override the repository creation and customization process
/// signature to override the repository creation and customization process
/// during a clone operation. /// 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. /// A callback used to create the new repository into which to clone.
static int repositoryCb( static int repositoryCb(
@ -101,11 +112,20 @@ class RemoteCallbacks {
int bare, int bare,
Pointer<Void> payload, Pointer<Void> payload,
) { ) {
repo[0] = repositoryFunction!( final repoPointer = Repository.init(
path.cast<Utf8>().toDartString(), path: repositoryCbData!.path,
bare == 1 || false, 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; ).pointer;
repo[0] = repoPointer;
return 0; return 0;
} }
@ -236,8 +256,8 @@ class RemoteCallbacks {
sidebandProgress = null; sidebandProgress = null;
updateTips = null; updateTips = null;
pushUpdateReference = null; pushUpdateReference = null;
remoteFunction = null; remoteCbData = null;
repositoryFunction = null; repositoryCbData = null;
credentials = null; credentials = null;
} }
} }

View file

@ -19,13 +19,15 @@ Pointer<git_repository> open(String path) {
final pathC = path.toNativeUtf8().cast<Int8>(); final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_repository_open(out, pathC); final error = libgit2.git_repository_open(out, pathC);
final result = out.value;
calloc.free(out);
calloc.free(pathC); calloc.free(pathC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -50,7 +52,10 @@ String discover({
calloc.free(ceilingDirsC); calloc.free(ceilingDirsC);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size); final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out); calloc.free(out);
return result; return result;
} }
@ -90,6 +95,9 @@ Pointer<git_repository> init({
final error = libgit2.git_repository_init_ext(out, pathC, opts); final error = libgit2.git_repository_init_ext(out, pathC, opts);
final result = out.value;
calloc.free(out);
calloc.free(pathC); calloc.free(pathC);
calloc.free(workdirPathC); calloc.free(workdirPathC);
calloc.free(descriptionC); calloc.free(descriptionC);
@ -99,10 +107,9 @@ Pointer<git_repository> init({
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -113,8 +120,9 @@ Pointer<git_repository> clone({
required String url, required String url,
required String localPath, required String localPath,
required bool bare, required bool bare,
Remote Function(Repository, String, String)? remote, RemoteCallback? remoteCallback,
Repository Function(String, bool)? repository, // Repository Function(String, bool)? repository,
RepositoryCallback? repositoryCallback,
String? checkoutBranch, String? checkoutBranch,
required Callbacks callbacks, required Callbacks callbacks,
}) { }) {
@ -138,14 +146,14 @@ Pointer<git_repository> clone({
const except = -1; const except = -1;
git_remote_create_cb remoteCb = nullptr; git_remote_create_cb remoteCb = nullptr;
if (remote != null) { if (remoteCallback != null) {
RemoteCallbacks.remoteFunction = remote; RemoteCallbacks.remoteCbData = remoteCallback;
remoteCb = Pointer.fromFunction(RemoteCallbacks.remoteCb, except); remoteCb = Pointer.fromFunction(RemoteCallbacks.remoteCb, except);
} }
git_repository_create_cb repositoryCb = nullptr; git_repository_create_cb repositoryCb = nullptr;
if (repository != null) { if (repositoryCallback != null) {
RemoteCallbacks.repositoryFunction = repository; RemoteCallbacks.repositoryCbData = repositoryCallback;
repositoryCb = Pointer.fromFunction(RemoteCallbacks.repositoryCb, except); repositoryCb = Pointer.fromFunction(RemoteCallbacks.repositoryCb, except);
} }
@ -157,6 +165,9 @@ Pointer<git_repository> clone({
final error = libgit2.git_clone(out, urlC, localPathC, cloneOptions); final error = libgit2.git_clone(out, urlC, localPathC, cloneOptions);
final result = out.value;
calloc.free(out);
calloc.free(urlC); calloc.free(urlC);
calloc.free(localPathC); calloc.free(localPathC);
calloc.free(checkoutBranchC); calloc.free(checkoutBranchC);
@ -165,10 +176,9 @@ Pointer<git_repository> clone({
RemoteCallbacks.reset(); RemoteCallbacks.reset();
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -211,9 +221,9 @@ void setNamespace({
required Pointer<git_repository> repoPointer, required Pointer<git_repository> repoPointer,
String? namespace, String? namespace,
}) { }) {
final nmspace = namespace?.toNativeUtf8().cast<Int8>() ?? nullptr; final namespaceC = namespace?.toNativeUtf8().cast<Int8>() ?? nullptr;
libgit2.git_repository_set_namespace(repoPointer, nmspace); libgit2.git_repository_set_namespace(repoPointer, namespaceC);
calloc.free(nmspace); calloc.free(namespaceC);
} }
/// Check if a repository is bare or not. /// Check if a repository is bare or not.
@ -247,11 +257,14 @@ Pointer<git_reference> head(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_reference>>(); final out = calloc<Pointer<git_reference>>();
final error = libgit2.git_repository_head(out, repo); final error = libgit2.git_repository_head(out, repo);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -337,7 +350,12 @@ Map<String, String> identity(Pointer<git_repository> repo) {
Pointer<git_config> config(Pointer<git_repository> repo) { Pointer<git_config> config(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
libgit2.git_repository_config(out, repo); 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. /// Get a snapshot of the repository's configuration.
@ -351,7 +369,12 @@ Pointer<git_config> config(Pointer<git_repository> repo) {
Pointer<git_config> configSnapshot(Pointer<git_repository> repo) { Pointer<git_config> configSnapshot(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
libgit2.git_repository_config_snapshot(out, repo); 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. /// Get the Index file for this repository.
@ -363,7 +386,12 @@ Pointer<git_config> configSnapshot(Pointer<git_repository> repo) {
Pointer<git_index> index(Pointer<git_repository> repo) { Pointer<git_index> index(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_index>>(); final out = calloc<Pointer<git_index>>();
libgit2.git_repository_index(out, repo); 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. /// Determine if the repository was a shallow clone.
@ -392,12 +420,14 @@ String message(Pointer<git_repository> repo) {
final out = calloc<git_buf>(); final out = calloc<git_buf>();
final error = libgit2.git_repository_message(out, repo); final error = libgit2.git_repository_message(out, repo);
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
libgit2.git_buf_dispose(out);
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(length: out.ref.size);
calloc.free(out);
return result; return result;
} }
} }
@ -419,11 +449,14 @@ Pointer<git_odb> odb(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_odb>>(); final out = calloc<Pointer<git_odb>>();
final error = libgit2.git_repository_odb(out, repo); final error = libgit2.git_repository_odb(out, repo);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -440,11 +473,14 @@ Pointer<git_refdb> refdb(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_refdb>>(); final out = calloc<Pointer<git_refdb>>();
final error = libgit2.git_repository_refdb(out, repo); final error = libgit2.git_repository_refdb(out, repo);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -516,15 +552,15 @@ void setWorkdir({
required String path, required String path,
required bool updateGitlink, required bool updateGitlink,
}) { }) {
final workdir = path.toNativeUtf8().cast<Int8>(); final workdirC = path.toNativeUtf8().cast<Int8>();
final updateGitlinkC = updateGitlink ? 1 : 0; final updateGitlinkC = updateGitlink ? 1 : 0;
final error = libgit2.git_repository_set_workdir( final error = libgit2.git_repository_set_workdir(
repoPointer, repoPointer,
workdir, workdirC,
updateGitlinkC, updateGitlinkC,
); );
calloc.free(workdir); calloc.free(workdirC);
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());

View file

@ -46,13 +46,15 @@ Pointer<git_object> revParseSingle({
final error = libgit2.git_revparse_single(out, repoPointer, specC); final error = libgit2.git_revparse_single(out, repoPointer, specC);
final result = out.value;
calloc.free(out);
calloc.free(specC); calloc.free(specC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -83,18 +85,19 @@ List<Pointer> revParseExt({
specC, specC,
); );
final result = <Pointer>[];
result.add(objectOut.value);
if (referenceOut.value != nullptr) {
result.add(referenceOut.value);
}
calloc.free(objectOut);
calloc.free(referenceOut);
calloc.free(specC); calloc.free(specC);
if (error < 0) { if (error < 0) {
calloc.free(objectOut);
calloc.free(referenceOut);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = <Pointer>[];
result.add(objectOut.value);
if (referenceOut.value != nullptr) {
result.add(referenceOut.value);
}
return result; return result;
} }
} }

View file

@ -23,11 +23,14 @@ Pointer<git_revwalk> create(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_revwalk>>(); final out = calloc<Pointer<git_revwalk>>();
final error = libgit2.git_revwalk_new(out, repo); final error = libgit2.git_revwalk_new(out, repo);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -152,10 +155,10 @@ List<Pointer<git_commit>> walk({
oidPointer: oid, oidPointer: oid,
); );
result.add(commit); result.add(commit);
calloc.free(oid);
} else { } else {
break; break;
} }
calloc.free(oid);
} }
return result; return result;

View file

@ -22,14 +22,16 @@ Pointer<git_signature> create({
final emailC = email.toNativeUtf8().cast<Int8>(); final emailC = email.toNativeUtf8().cast<Int8>();
final error = libgit2.git_signature_new(out, nameC, emailC, time, offset); final error = libgit2.git_signature_new(out, nameC, emailC, time, offset);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(emailC); calloc.free(emailC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -42,14 +44,16 @@ Pointer<git_signature> now({required String name, required String email}) {
final emailC = email.toNativeUtf8().cast<Int8>(); final emailC = email.toNativeUtf8().cast<Int8>();
final error = libgit2.git_signature_now(out, nameC, emailC); final error = libgit2.git_signature_now(out, nameC, emailC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(emailC); calloc.free(emailC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -61,7 +65,24 @@ Pointer<git_signature> now({required String name, required String email}) {
Pointer<git_signature> defaultSignature(Pointer<git_repository> repo) { Pointer<git_signature> defaultSignature(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_signature>>(); final out = calloc<Pointer<git_signature>>();
libgit2.git_signature_default(out, repo); 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<git_signature> duplicate(Pointer<git_signature> sig) {
final out = calloc<Pointer<git_signature>>();
libgit2.git_signature_dup(out, sig);
final result = out.value;
calloc.free(out);
return result;
} }
/// Free an existing signature. /// Free an existing signature.

View file

@ -13,11 +13,14 @@ Pointer<git_status_list> listNew(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_status_list>>(); final out = calloc<Pointer<git_status_list>>();
final error = libgit2.git_status_list_new(out, repo, nullptr); final error = libgit2.git_status_list_new(out, repo, nullptr);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -62,14 +65,14 @@ int file({required Pointer<git_repository> repoPointer, required String path}) {
final pathC = path.toNativeUtf8().cast<Int8>(); final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_status_file(out, repoPointer, pathC); final error = libgit2.git_status_file(out, repoPointer, pathC);
final result = out.value;
calloc.free(out);
calloc.free(pathC); calloc.free(pathC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.value;
calloc.free(out);
return result; return result;
} }
} }

View file

@ -55,13 +55,15 @@ Pointer<git_submodule> lookup({
final error = libgit2.git_submodule_lookup(out, repoPointer, nameC); final error = libgit2.git_submodule_lookup(out, repoPointer, nameC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -130,11 +132,14 @@ Pointer<git_repository> open(Pointer<git_submodule> submodule) {
final out = calloc<Pointer<git_repository>>(); final out = calloc<Pointer<git_repository>>();
final error = libgit2.git_submodule_open(out, submodule); final error = libgit2.git_submodule_open(out, submodule);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -165,14 +170,16 @@ Pointer<git_submodule> addSetup({
useGitlinkC, useGitlinkC,
); );
final result = out.value;
calloc.free(out);
calloc.free(urlC); calloc.free(urlC);
calloc.free(pathC); calloc.free(pathC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -229,10 +236,11 @@ int status({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
libgit2.git_submodule_status(out, repoPointer, nameC, ignore); libgit2.git_submodule_status(out, repoPointer, nameC, ignore);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
final result = out.value;
calloc.free(out);
return result; return result;
} }

View file

@ -36,11 +36,14 @@ Pointer<git_tag> lookup({
final out = calloc<Pointer<git_tag>>(); final out = calloc<Pointer<git_tag>>();
final error = libgit2.git_tag_lookup(out, repoPointer, oidPointer); final error = libgit2.git_tag_lookup(out, repoPointer, oidPointer);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -54,11 +57,14 @@ Pointer<git_object> target(Pointer<git_tag> tag) {
final out = calloc<Pointer<git_object>>(); final out = calloc<Pointer<git_object>>();
final error = libgit2.git_tag_target(out, tag); final error = libgit2.git_tag_target(out, tag);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -77,8 +83,10 @@ String name(Pointer<git_tag> tag) =>
libgit2.git_tag_name(tag).cast<Utf8>().toDartString(); libgit2.git_tag_name(tag).cast<Utf8>().toDartString();
/// Get the message of a tag. /// Get the message of a tag.
String message(Pointer<git_tag> tag) => String message(Pointer<git_tag> tag) {
libgit2.git_tag_message(tag).cast<Utf8>().toDartString(); final result = libgit2.git_tag_message(tag);
return result == nullptr ? '' : result.cast<Utf8>().toDartString();
}
/// Get the tagger (author) of a tag. /// Get the tagger (author) of a tag.
Pointer<git_signature> tagger(Pointer<git_tag> tag) => Pointer<git_signature> tagger(Pointer<git_tag> tag) =>

View file

@ -18,11 +18,14 @@ Pointer<git_tree> lookup({
final out = calloc<Pointer<git_tree>>(); final out = calloc<Pointer<git_tree>>();
final error = libgit2.git_tree_lookup(out, repoPointer, oidPointer); final error = libgit2.git_tree_lookup(out, repoPointer, oidPointer);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -82,13 +85,15 @@ Pointer<git_tree_entry> getByPath({
final pathC = path.toNativeUtf8().cast<Int8>(); final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_tree_entry_bypath(out, rootPointer, pathC); final error = libgit2.git_tree_entry_bypath(out, rootPointer, pathC);
final result = out.value;
calloc.free(out);
calloc.free(pathC); calloc.free(pathC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -24,11 +24,14 @@ Pointer<git_treebuilder> create({
final out = calloc<Pointer<git_treebuilder>>(); final out = calloc<Pointer<git_treebuilder>>();
final error = libgit2.git_treebuilder_new(out, repoPointer, sourcePointer); final error = libgit2.git_treebuilder_new(out, repoPointer, sourcePointer);
final result = out.value;
calloc.free(out);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }

View file

@ -33,15 +33,17 @@ Pointer<git_worktree> create({
final error = libgit2.git_worktree_add(out, repoPointer, nameC, pathC, opts); final error = libgit2.git_worktree_add(out, repoPointer, nameC, pathC, opts);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
calloc.free(pathC); calloc.free(pathC);
calloc.free(opts); calloc.free(opts);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -56,13 +58,15 @@ Pointer<git_worktree> lookup({
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final error = libgit2.git_worktree_lookup(out, repoPointer, nameC); final error = libgit2.git_worktree_lookup(out, repoPointer, nameC);
final result = out.value;
calloc.free(out);
calloc.free(nameC); calloc.free(nameC);
if (error < 0) { if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
return out.value; return result;
} }
} }
@ -80,7 +84,11 @@ bool isPrunable(Pointer<git_worktree> wt) {
GIT_WORKTREE_PRUNE_OPTIONS_VERSION, 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. /// Prune working tree.

View file

@ -34,8 +34,6 @@ class Blame with IterableMixin<BlameHunk> {
/// [maxLine] is the last line in the file to blame. The default is the last /// [maxLine] is the last line in the file to blame. The default is the last
/// line of the file. /// line of the file.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Blame.file({ Blame.file({
required Repository repo, required Repository repo,
@ -57,6 +55,7 @@ class Blame with IterableMixin<BlameHunk> {
minLine: minLine, minLine: minLine,
maxLine: maxLine, maxLine: maxLine,
); );
_finalizer.attach(this, _blamePointer, detach: this);
} }
/// Returns blame data for a file that has been modified in memory. The /// Returns blame data for a file that has been modified in memory. The
@ -74,6 +73,7 @@ class Blame with IterableMixin<BlameHunk> {
reference: reference._blamePointer, reference: reference._blamePointer,
buffer: buffer, buffer: buffer,
); );
_finalizer.attach(this, _blamePointer, detach: this);
} }
/// Pointer to memory address for allocated blame object. /// Pointer to memory address for allocated blame object.
@ -83,7 +83,7 @@ class Blame with IterableMixin<BlameHunk> {
/// ///
/// Throws [RangeError] if index out of range. /// Throws [RangeError] if index out of range.
BlameHunk operator [](int index) { BlameHunk operator [](int index) {
return BlameHunk( return BlameHunk._(
bindings.getHunkByIndex( bindings.getHunkByIndex(
blamePointer: _blamePointer, blamePointer: _blamePointer,
index: index, index: index,
@ -96,7 +96,7 @@ class Blame with IterableMixin<BlameHunk> {
/// ///
/// Throws [RangeError] if [lineNumber] is out of range. /// Throws [RangeError] if [lineNumber] is out of range.
BlameHunk forLine(int lineNumber) { BlameHunk forLine(int lineNumber) {
return BlameHunk( return BlameHunk._(
bindings.getHunkByLine( bindings.getHunkByLine(
blamePointer: _blamePointer, blamePointer: _blamePointer,
lineNumber: lineNumber, lineNumber: lineNumber,
@ -105,16 +105,25 @@ class Blame with IterableMixin<BlameHunk> {
} }
/// Releases memory allocated for blame object. /// Releases memory allocated for blame object.
void free() => bindings.free(_blamePointer); void free() {
bindings.free(_blamePointer);
_finalizer.detach(this);
}
@override @override
Iterator<BlameHunk> get iterator => _BlameIterator(_blamePointer); Iterator<BlameHunk> get iterator => _BlameIterator(_blamePointer);
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_blame>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class BlameHunk { class BlameHunk {
/// Initializes a new instance of the [BlameHunk] class from /// Initializes a new instance of the [BlameHunk] class from
/// provided pointer to blame hunk object in memory. /// 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. /// Pointer to memory address for allocated blame hunk object.
final Pointer<git_blame_hunk> _blameHunkPointer; final Pointer<git_blame_hunk> _blameHunkPointer;
@ -194,7 +203,7 @@ class _BlameIterator implements Iterator<BlameHunk> {
if (index == count) { if (index == count) {
return false; return false;
} else { } else {
currentHunk = BlameHunk( currentHunk = BlameHunk._(
bindings.getHunkByIndex( bindings.getHunkByIndex(
blamePointer: _blamePointer, blamePointer: _blamePointer,
index: index, index: index,

View file

@ -7,18 +7,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
class Blob { class Blob {
/// Initializes a new instance of [Blob] class from provided pointer to /// Initializes a new instance of [Blob] class from provided pointer to
/// blob object in memory. /// blob object in memory.
/// Blob(this._blobPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _blobPointer, detach: this);
Blob(this._blobPointer); }
/// Lookups a blob object for provided [oid] in a [repo]sitory. /// 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}) { Blob.lookup({required Repository repo, required Oid oid}) {
_blobPointer = bindings.lookup( _blobPointer = bindings.lookup(
repoPointer: repo.pointer, repoPointer: repo.pointer,
oidPointer: oid.pointer, oidPointer: oid.pointer,
); );
_finalizer.attach(this, _blobPointer, detach: this);
} }
late final Pointer<git_blob> _blobPointer; late final Pointer<git_blob> _blobPointer;
@ -80,8 +79,6 @@ class Blob {
int get size => bindings.size(_blobPointer); int get size => bindings.size(_blobPointer);
/// Creates an in-memory copy of a blob. /// Creates an in-memory copy of a blob.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Blob duplicate() => Blob(bindings.duplicate(_blobPointer)); Blob duplicate() => Blob(bindings.duplicate(_blobPointer));
/// Returns filtered content of a blob. /// Returns filtered content of a blob.
@ -114,10 +111,19 @@ class Blob {
} }
/// Releases memory allocated for blob object. /// Releases memory allocated for blob object.
void free() => bindings.free(_blobPointer); void free() {
bindings.free(_blobPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
return 'Blob{oid: $oid, isBinary: $isBinary, size: $size}'; return 'Blob{oid: $oid, isBinary: $isBinary, size: $size}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_blob>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -8,9 +8,9 @@ import 'package:libgit2dart/src/bindings/reference.dart' as reference_bindings;
class Branch { class Branch {
/// Initializes a new instance of [Branch] class from provided pointer to /// Initializes a new instance of [Branch] class from provided pointer to
/// branch object in memory. /// branch object in memory.
/// Branch(this._branchPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _branchPointer, detach: this);
Branch(this._branchPointer); }
/// Creates a new branch pointing at a [target] commit. /// 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 /// [target] is the commit to which this branch should point. This object must
/// belong to the given [repo]. /// belong to the given [repo].
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Branch.create({ Branch.create({
required Repository repo, required Repository repo,
@ -39,6 +37,7 @@ class Branch {
targetPointer: target.pointer, targetPointer: target.pointer,
force: force, force: force,
); );
_finalizer.attach(this, _branchPointer, detach: this);
} }
/// Lookups a branch by its [name] and [type] in a [repo]sitory. Lookups in /// 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 /// If branch [type] is [GitBranch.remote] you must include the remote name
/// in the [name] (e.g. "origin/master"). /// in the [name] (e.g. "origin/master").
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Branch.lookup({ Branch.lookup({
required Repository repo, required Repository repo,
@ -62,6 +59,7 @@ class Branch {
branchName: name, branchName: name,
branchType: type.value, branchType: type.value,
); );
_finalizer.attach(this, _branchPointer, detach: this);
} }
late final Pointer<git_reference> _branchPointer; late final Pointer<git_reference> _branchPointer;
@ -72,9 +70,6 @@ class Branch {
/// Returns a list of branches that can be found in a [repo]sitory for /// Returns a list of branches that can be found in a [repo]sitory for
/// provided [type]. Default is all branches (local and remote). /// 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. /// Throws a [LibGit2Error] if error occured.
static List<Branch> list({ static List<Branch> list({
required Repository repo, required Repository repo,
@ -94,7 +89,6 @@ class Branch {
static void delete({required Repository repo, required String name}) { static void delete({required Repository repo, required String name}) {
final branch = Branch.lookup(repo: repo, name: name); final branch = Branch.lookup(repo: repo, name: name);
bindings.delete(branch.pointer); bindings.delete(branch.pointer);
branch.free();
} }
/// Renames an existing local branch reference with provided [oldName]. /// Renames an existing local branch reference with provided [oldName].
@ -117,8 +111,6 @@ class Branch {
newBranchName: newName, newBranchName: newName,
force: force, force: force,
); );
branch.free();
} }
/// [Oid] pointed to by a branch. /// [Oid] pointed to by a branch.
@ -164,8 +156,6 @@ class Branch {
/// Upstream [Reference] of a local branch. /// Upstream [Reference] of a local branch.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Reference get upstream => Reference(bindings.getUpstream(_branchPointer)); Reference get upstream => Reference(bindings.getUpstream(_branchPointer));
@ -222,7 +212,10 @@ class Branch {
} }
/// Releases memory allocated for branch object. /// Releases memory allocated for branch object.
void free() => bindings.free(_branchPointer); void free() {
bindings.free(_branchPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -230,3 +223,9 @@ class Branch {
'isCheckedOut: $isCheckedOut}'; 'isCheckedOut: $isCheckedOut}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_reference>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -116,7 +116,6 @@ class Checkout {
); );
object_bindings.free(treeish); object_bindings.free(treeish);
ref.free();
} }
/// Updates files in the working tree to match the content of the tree /// Updates files in the working tree to match the content of the tree

View file

@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
class Commit { class Commit {
/// Initializes a new instance of [Commit] class from provided pointer to /// Initializes a new instance of [Commit] class from provided pointer to
/// commit object in memory. /// commit object in memory.
/// Commit(this._commitPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _commitPointer, detach: this);
Commit(this._commitPointer); }
/// Lookups commit object for provided [oid] in the [repo]sitory. /// 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}) { Commit.lookup({required Repository repo, required Oid oid}) {
_commitPointer = bindings.lookup( _commitPointer = bindings.lookup(
repoPointer: repo.pointer, repoPointer: repo.pointer,
oidPointer: oid.pointer, oidPointer: oid.pointer,
); );
_finalizer.attach(this, _commitPointer, detach: this);
} }
late final Pointer<git_commit> _commitPointer; late final Pointer<git_commit> _commitPointer;
@ -194,8 +193,6 @@ class Commit {
/// ///
/// [mainline] is parent of the commit if it is a merge (i.e. 1, 2). /// [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. /// Throws a [LibGit2Error] if error occured.
Index revertTo({ Index revertTo({
required Commit commit, required Commit commit,
@ -221,7 +218,7 @@ class Commit {
/// leading newlines. /// leading newlines.
String get message => bindings.message(_commitPointer); 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 /// The returned message is the summary of the commit, comprising the first
/// paragraph of the message with whitespace trimmed and squashed. /// paragraph of the message with whitespace trimmed and squashed.
@ -229,7 +226,7 @@ class Commit {
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
String get summary => bindings.summary(_commitPointer); 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 returned message is the body of the commit, comprising everything but
/// the first paragraph of the message. Leading and trailing whitespaces are /// 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]. /// 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. /// Throws a [LibGit2Error] if error occured.
Commit parent(int position) { Commit parent(int position) {
return Commit( return Commit(
@ -303,8 +298,6 @@ class Commit {
/// Passing 0 as the generation number returns another instance of the base /// Passing 0 as the generation number returns another instance of the base
/// commit itself. /// commit itself.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Commit nthGenAncestor(int n) { Commit nthGenAncestor(int n) {
return Commit(bindings.nthGenAncestor(commitPointer: _commitPointer, n: n)); return Commit(bindings.nthGenAncestor(commitPointer: _commitPointer, n: n));
@ -323,12 +316,13 @@ class Commit {
} }
/// Creates an in-memory copy of a commit. /// Creates an in-memory copy of a commit.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit duplicate() => Commit(bindings.duplicate(_commitPointer)); Commit duplicate() => Commit(bindings.duplicate(_commitPointer));
/// Releases memory allocated for commit object. /// Releases memory allocated for commit object.
void free() => bindings.free(_commitPointer); void free() {
bindings.free(_commitPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -337,3 +331,9 @@ class Commit {
' author: $author}'; ' author: $author}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_commit>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -11,9 +11,9 @@ import 'package:libgit2dart/src/util.dart';
class Config with IterableMixin<ConfigEntry> { class Config with IterableMixin<ConfigEntry> {
/// Initializes a new instance of [Config] class from provided /// Initializes a new instance of [Config] class from provided
/// pointer to config object in memory. /// pointer to config object in memory.
/// Config(this._configPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _configPointer, detach: this);
Config(this._configPointer); }
/// Opens config file at provided [path]. /// Opens config file at provided [path].
/// ///
@ -23,8 +23,6 @@ class Config with IterableMixin<ConfigEntry> {
/// Git config file following the default Git config syntax (see /// Git config file following the default Git config syntax (see
/// `man git-config`). /// `man git-config`).
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws an [Exception] if file not found at provided path. /// Throws an [Exception] if file not found at provided path.
Config.open([String? path]) { Config.open([String? path]) {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
@ -38,39 +36,44 @@ class Config with IterableMixin<ConfigEntry> {
throw Exception('File not found'); throw Exception('File not found');
} }
} }
_finalizer.attach(this, _configPointer, detach: this);
} }
/// Opens the system configuration file. /// Opens the system configuration file.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Config.system() { Config.system() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_configPointer = bindings.open(bindings.findSystem()); _configPointer = bindings.open(bindings.findSystem());
// coverage:ignore-start
_finalizer.attach(this, _configPointer, detach: this);
// coverage:ignore-end
} }
/// Opens the global configuration file. /// Opens the global configuration file.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Config.global() { Config.global() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_configPointer = bindings.open(bindings.findGlobal()); _configPointer = bindings.open(bindings.findGlobal());
// coverage:ignore-start
_finalizer.attach(this, _configPointer, detach: this);
// coverage:ignore-end
} }
/// Opens the global XDG configuration file. /// Opens the global XDG configuration file.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Config.xdg() { Config.xdg() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_configPointer = bindings.open(bindings.findXdg()); _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. /// Pointer to memory address for allocated config object.
@ -83,11 +86,24 @@ class Config with IterableMixin<ConfigEntry> {
/// Returns the [ConfigEntry] of a [variable]. /// Returns the [ConfigEntry] of a [variable].
ConfigEntry operator [](String variable) { ConfigEntry operator [](String variable) {
return ConfigEntry( final entryPointer = bindings.getEntry(
bindings.getEntry( configPointer: _configPointer,
configPointer: _configPointer, variable: variable,
variable: variable, );
), final name = entryPointer.ref.name.cast<Utf8>().toDartString();
final value = entryPointer.ref.value.cast<Utf8>().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<ConfigEntry> {
} }
/// Releases memory allocated for config object. /// Releases memory allocated for config object.
void free() => bindings.free(_configPointer); void free() {
bindings.free(_configPointer);
_finalizer.detach(this);
}
@override @override
Iterator<ConfigEntry> get iterator => Iterator<ConfigEntry> get iterator =>
_ConfigIterator(bindings.iterator(_configPointer)); _ConfigIterator(bindings.iterator(_configPointer));
} }
class ConfigEntry { // coverage:ignore-start
/// Initializes a new instance of [ConfigEntry] class from provided final _finalizer = Finalizer<Pointer<git_config>>(
/// pointer to config entry object in memory. (pointer) => bindings.free(pointer),
const ConfigEntry(this._configEntryPointer); );
// coverage:ignore-end
/// Pointer to memory address for allocated config entry object. class ConfigEntry {
final Pointer<git_config_entry> _configEntryPointer; ConfigEntry._({
required this.name,
required this.value,
required this.includeDepth,
required this.level,
});
/// Name of the entry (normalised). /// Name of the entry (normalised).
String get name => _configEntryPointer.ref.name.cast<Utf8>().toDartString(); final String name;
/// Value of the entry. /// Value of the entry.
String get value => _configEntryPointer.ref.value.cast<Utf8>().toDartString(); final String value;
/// Depth of includes where this variable was found /// 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. /// Which config file this was found in.
GitConfigLevel get level { final GitConfigLevel level;
return GitConfigLevel.values.singleWhere(
(e) => _configEntryPointer.ref.level == e.value,
);
}
@override @override
String toString() { String toString() {
@ -206,7 +227,9 @@ class ConfigEntry {
} }
class _ConfigIterator implements Iterator<ConfigEntry> { class _ConfigIterator implements Iterator<ConfigEntry> {
_ConfigIterator(this._iteratorPointer); _ConfigIterator(this._iteratorPointer) {
_iteratorFinalizer.attach(this, _iteratorPointer);
}
/// Pointer to memory address for allocated config iterator. /// Pointer to memory address for allocated config iterator.
final Pointer<git_config_iterator> _iteratorPointer; final Pointer<git_config_iterator> _iteratorPointer;
@ -225,7 +248,20 @@ class _ConfigIterator implements Iterator<ConfigEntry> {
} else { } else {
error = libgit2.git_config_next(entry, _iteratorPointer); error = libgit2.git_config_next(entry, _iteratorPointer);
if (error != -31) { if (error != -31) {
_currentEntry = ConfigEntry(entry.value); final name = entry.value.ref.name.cast<Utf8>().toDartString();
final value = entry.value.ref.value.cast<Utf8>().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; return true;
} else { } else {
return false; return false;
@ -233,3 +269,9 @@ class _ConfigIterator implements Iterator<ConfigEntry> {
} }
} }
} }
// coverage:ignore-start
final _iteratorFinalizer = Finalizer<Pointer<git_config_iterator>>(
(pointer) => bindings.freeIterator(pointer),
);
// coverage:ignore-end

View file

@ -4,15 +4,14 @@ import 'package:ffi/ffi.dart';
import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/bindings/diff.dart' as bindings; import 'package:libgit2dart/src/bindings/diff.dart' as bindings;
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/bindings/patch.dart' as patch_bindings;
import 'package:libgit2dart/src/util.dart'; import 'package:libgit2dart/src/util.dart';
class Diff { class Diff {
/// Initializes a new instance of [Diff] class from provided /// Initializes a new instance of [Diff] class from provided
/// pointer to diff object in memory. /// pointer to diff object in memory.
/// Diff(this._diffPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _diffPointer, detach: this);
Diff(this._diffPointer); }
/// Creates a diff between the [repo]sitory [index] and the workdir directory. /// 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 /// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0. /// 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. /// Throws a [LibGit2Error] if error occured.
Diff.indexToWorkdir({ Diff.indexToWorkdir({
required Repository repo, required Repository repo,
@ -47,6 +44,7 @@ class Diff {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _diffPointer, detach: this);
} }
/// Creates a diff between a [tree] and [repo]sitory [index]. /// Creates a diff between a [tree] and [repo]sitory [index].
@ -83,6 +81,7 @@ class Diff {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _diffPointer, detach: this);
} }
/// Creates a diff between a [tree] and the working directory. /// Creates a diff between a [tree] and the working directory.
@ -124,6 +123,7 @@ class Diff {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _diffPointer, detach: this);
} }
/// Creates a diff between a [tree] and the working directory using index /// 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 /// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0. /// 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. /// Throws a [LibGit2Error] if error occured.
Diff.treeToWorkdirWithIndex({ Diff.treeToWorkdirWithIndex({
required Repository repo, required Repository repo,
@ -162,6 +160,7 @@ class Diff {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _diffPointer, detach: this);
} }
/// Creates a diff with the difference between two [Tree] objects. /// Creates a diff with the difference between two [Tree] objects.
@ -204,6 +203,7 @@ class Diff {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _diffPointer, detach: this);
} }
/// Creates a diff with the difference between two [Index] objects. /// Creates a diff with the difference between two [Index] objects.
@ -239,6 +239,7 @@ class Diff {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _diffPointer, detach: this);
} }
/// Reads the [content]s of a git patch file into a git diff object. /// Reads the [content]s of a git patch file into a git diff object.
@ -255,6 +256,7 @@ class Diff {
Diff.parse(String content) { Diff.parse(String content) {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_diffPointer = bindings.parse(content); _diffPointer = bindings.parse(content);
_finalizer.attach(this, _diffPointer, detach: this);
} }
late final Pointer<git_diff> _diffPointer; late final Pointer<git_diff> _diffPointer;
@ -441,7 +443,10 @@ class Diff {
Oid get patchOid => Oid(bindings.patchOid(_diffPointer)); Oid get patchOid => Oid(bindings.patchOid(_diffPointer));
/// Releases memory allocated for diff object. /// Releases memory allocated for diff object.
void free() => bindings.free(_diffPointer); void free() {
bindings.free(_diffPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -449,6 +454,12 @@ class Diff {
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_diff>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class DiffDelta { class DiffDelta {
/// Initializes a new instance of [DiffDelta] class from provided /// Initializes a new instance of [DiffDelta] class from provided
/// pointer to diff delta object in memory. /// pointer to diff delta object in memory.
@ -487,10 +498,10 @@ class DiffDelta {
int get numberOfFiles => _diffDeltaPointer.ref.nfiles; int get numberOfFiles => _diffDeltaPointer.ref.nfiles;
/// Represents the "from" side of the diff. /// 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. /// Represents the "to" side of the diff.
DiffFile get newFile => DiffFile(_diffDeltaPointer.ref.new_file); DiffFile get newFile => DiffFile._(_diffDeltaPointer.ref.new_file);
@override @override
String toString() { String toString() {
@ -507,7 +518,7 @@ class DiffDelta {
class DiffFile { class DiffFile {
/// Initializes a new instance of [DiffFile] class from provided diff file /// Initializes a new instance of [DiffFile] class from provided diff file
/// object. /// object.
const DiffFile(this._diffFile); const DiffFile._(this._diffFile);
final git_diff_file _diffFile; final git_diff_file _diffFile;
@ -543,9 +554,9 @@ class DiffFile {
class DiffStats { class DiffStats {
/// Initializes a new instance of [DiffStats] class from provided /// Initializes a new instance of [DiffStats] class from provided
/// pointer to diff stats object in memory. /// pointer to diff stats object in memory.
/// DiffStats(this._diffStatsPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _statsFinalizer.attach(this, _diffStatsPointer, detach: this);
const DiffStats(this._diffStatsPointer); }
/// Pointer to memory address for allocated diff delta object. /// Pointer to memory address for allocated diff delta object.
final Pointer<git_diff_stats> _diffStatsPointer; final Pointer<git_diff_stats> _diffStatsPointer;
@ -573,7 +584,10 @@ class DiffStats {
} }
/// Releases memory allocated for diff stats object. /// Releases memory allocated for diff stats object.
void free() => bindings.statsFree(_diffStatsPointer); void free() {
bindings.statsFree(_diffStatsPointer);
_statsFinalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -582,111 +596,8 @@ class DiffStats {
} }
} }
class DiffHunk { // coverage:ignore-start
/// Initializes a new instance of [DiffHunk] class from provided final _statsFinalizer = Finalizer<Pointer<git_diff_stats>>(
/// pointers to patch object and diff hunk object in memory and number of (pointer) => bindings.statsFree(pointer),
/// lines in hunk. );
const DiffHunk( // coverage:ignore-end
this._patchPointer,
this._diffHunkPointer,
this.linesCount,
this.index,
);
/// Pointer to memory address for allocated diff hunk object.
final Pointer<git_diff_hunk> _diffHunkPointer;
/// Pointer to memory address for allocated patch object.
final Pointer<git_patch> _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 = <int>[];
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<DiffLine> get lines {
final lines = <DiffLine>[];
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<git_diff_line> _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<Utf8>()
.toDartString(length: _diffLinePointer.ref.content_len);
@override
String toString() {
return 'DiffLine{oldLineNumber: $oldLineNumber, '
'newLineNumber: $newLineNumber, numLines: $numLines, '
'contentOffset: $contentOffset, content: $content}';
}
}

View file

@ -9,19 +9,20 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
class Index with IterableMixin<IndexEntry> { class Index with IterableMixin<IndexEntry> {
/// Initializes a new instance of [Index] class from provided /// Initializes a new instance of [Index] class from provided
/// pointer to index object in memory. /// pointer to index object in memory.
/// Index(this._indexPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _indexPointer, detach: this);
const Index(this._indexPointer); }
/// Creates an in-memory index object. /// Creates an in-memory index object.
/// ///
/// This index object cannot be read/written to the filesystem, but may be /// This index object cannot be read/written to the filesystem, but may be
/// used to perform in-memory index operations. /// used to perform in-memory index operations.
/// Index.newInMemory() {
/// **IMPORTANT**: Should be freed to release allocated memory. _indexPointer = bindings.newInMemory();
Index.newInMemory() : _indexPointer = bindings.newInMemory(); _finalizer.attach(this, _indexPointer, detach: this);
}
final Pointer<git_index> _indexPointer; late final Pointer<git_index> _indexPointer;
/// Pointer to memory address for allocated index object. /// Pointer to memory address for allocated index object.
Pointer<git_index> get pointer => _indexPointer; Pointer<git_index> get pointer => _indexPointer;
@ -300,7 +301,10 @@ class Index with IterableMixin<IndexEntry> {
bindings.removeAll(indexPointer: _indexPointer, pathspec: path); bindings.removeAll(indexPointer: _indexPointer, pathspec: path);
/// Releases memory allocated for index object. /// Releases memory allocated for index object.
void free() => bindings.free(_indexPointer); void free() {
bindings.free(_indexPointer);
_finalizer.detach(this);
}
@override @override
String toString() => 'Index{hasConflicts: $hasConflicts}'; String toString() => 'Index{hasConflicts: $hasConflicts}';
@ -309,6 +313,12 @@ class Index with IterableMixin<IndexEntry> {
Iterator<IndexEntry> get iterator => _IndexIterator(_indexPointer); Iterator<IndexEntry> get iterator => _IndexIterator(_indexPointer);
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_index>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class IndexEntry { class IndexEntry {
/// Initializes a new instance of [IndexEntry] class. /// Initializes a new instance of [IndexEntry] class.
const IndexEntry(this._indexEntryPointer); const IndexEntry(this._indexEntryPointer);

View file

@ -8,7 +8,7 @@ import 'package:libgit2dart/src/util.dart';
class Libgit2 { class Libgit2 {
Libgit2._(); // coverage:ignore-line Libgit2._(); // coverage:ignore-line
/// Returns libgit2 version number. /// Libgit2 version number.
static String get version { static String get version {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
@ -26,7 +26,7 @@ class Libgit2 {
return version; return version;
} }
/// Returns list of options libgit2 was compiled with. /// Options libgit2 was compiled with.
static Set<GitFeature> get features { static Set<GitFeature> get features {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
final featuresInt = libgit2.git_libgit2_features(); final featuresInt = libgit2.git_libgit2_features();
@ -35,15 +35,19 @@ class Libgit2 {
.toSet(); .toSet();
} }
/// Returns owner validation setting for repository directories. /// Owner validation setting for repository directories.
static bool get ownerValidation { static bool get ownerValidation {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
final out = calloc<Int8>(); final out = calloc<Int8>();
libgit2.git_libgit2_opts( libgit2.git_libgit2_opts(
git_libgit2_opt_t.GIT_OPT_GET_OWNER_VALIDATION, git_libgit2_opt_t.GIT_OPT_GET_OWNER_VALIDATION,
out, out,
); );
return out.value == 1 || false; final result = out.value;
calloc.free(out);
return result == 1 || false;
} }
/// Sets owner validation setting for repository directories. /// Sets owner validation setting for repository directories.

View file

@ -9,21 +9,19 @@ class Mailmap {
/// ///
/// This object is empty, so you'll have to add a mailmap file before you can /// This object is empty, so you'll have to add a mailmap file before you can
/// do anything with it. /// do anything with it.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Mailmap.empty() { Mailmap.empty() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_mailmapPointer = bindings.init(); _mailmapPointer = bindings.init();
_finalizer.attach(this, _mailmapPointer, detach: this);
} }
/// Initializes a new instance of [Mailmap] class from provided buffer. /// Initializes a new instance of [Mailmap] class from provided buffer.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Mailmap.fromBuffer(String buffer) { Mailmap.fromBuffer(String buffer) {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_mailmapPointer = bindings.fromBuffer(buffer); _mailmapPointer = bindings.fromBuffer(buffer);
_finalizer.attach(this, _mailmapPointer, detach: this);
} }
/// Initializes a new instance of [Mailmap] class from a [repo]sitory, loading /// 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 /// 1. `.mailmap` in the root of the repository's working directory, if
/// present. /// present.
/// 2. The blob object identified by the `mailmap.blob` config entry, if set. /// 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. /// 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. /// Throws a [LibGit2Error] if error occured.
Mailmap.fromRepository(Repository repo) { Mailmap.fromRepository(Repository repo) {
_mailmapPointer = bindings.fromRepository(repo.pointer); _mailmapPointer = bindings.fromRepository(repo.pointer);
_finalizer.attach(this, _mailmapPointer, detach: this);
} }
/// Pointer to memory address for allocated mailmap object. /// Pointer to memory address for allocated mailmap object.
@ -94,5 +91,14 @@ class Mailmap {
} }
/// Releases memory allocated for mailmap object. /// 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<git_mailmap>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -74,9 +74,6 @@ class Merge {
(e) => analysisInt[1] == e.value, (e) => analysisInt[1] == e.value,
); );
head.free();
ref.free();
return <Object>[analysisSet, mergePreference]; return <Object>[analysisSet, mergePreference];
} }
@ -140,8 +137,6 @@ class Merge {
/// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to
/// [GitMergeFileFlag.defaults]. /// [GitMergeFileFlag.defaults].
/// ///
/// **IMPORTANT**: returned index should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
static Index commits({ static Index commits({
required Repository repo, required Repository repo,
@ -188,8 +183,6 @@ class Merge {
/// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to
/// [GitMergeFileFlag.defaults]. /// [GitMergeFileFlag.defaults].
/// ///
/// **IMPORTANT**: returned index should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
static Index trees({ static Index trees({
required Repository repo, required Repository repo,

View file

@ -6,7 +6,9 @@ import 'package:libgit2dart/src/bindings/note.dart' as bindings;
class Note { class Note {
/// Initializes a new instance of the [Note] class from provided /// Initializes a new instance of the [Note] class from provided
/// pointer to note and annotatedOid objects in memory. /// 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]. /// 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". /// [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. /// Throws a [LibGit2Error] if error occured.
Note.lookup({ Note.lookup({
required Repository repo, required Repository repo,
@ -30,6 +30,7 @@ class Note {
notesRef: notesRef, notesRef: notesRef,
); );
_annotatedOidPointer = annotatedOid.pointer; _annotatedOidPointer = annotatedOid.pointer;
_finalizer.attach(this, _notePointer, detach: this);
} }
/// Pointer to memory address for allocated note object. /// Pointer to memory address for allocated note object.
@ -107,8 +108,6 @@ class Note {
/// Returns list of notes for [repo]sitory. /// Returns list of notes for [repo]sitory.
/// ///
/// **IMPORTANT**: Notes must be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
static List<Note> list(Repository repo) { static List<Note> list(Repository repo) {
final notesPointers = bindings.list(repo.pointer); final notesPointers = bindings.list(repo.pointer);
@ -132,10 +131,19 @@ class Note {
Oid get annotatedOid => Oid(_annotatedOidPointer); Oid get annotatedOid => Oid(_annotatedOidPointer);
/// Releases memory allocated for note object. /// Releases memory allocated for note object.
void free() => bindings.free(_notePointer); void free() {
bindings.free(_notePointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
return 'Note{oid: $oid, message: $message, annotatedOid: $annotatedOid}'; return 'Note{oid: $oid, message: $message, annotatedOid: $annotatedOid}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_note>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -7,20 +7,19 @@ import 'package:libgit2dart/src/util.dart';
class Odb { class Odb {
/// Initializes a new instance of [Odb] class from provided /// Initializes a new instance of [Odb] class from provided
/// pointer to Odb object in memory. /// pointer to Odb object in memory.
/// Odb(this._odbPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _odbPointer, detach: this);
Odb(this._odbPointer); }
/// Creates a new object database with no backends. /// Creates a new object database with no backends.
/// ///
/// Before the ODB can be used for read/writing, a custom database backend must be /// Before the ODB can be used for read/writing, a custom database backend must be
/// manually added. /// manually added.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Odb.create() { Odb.create() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
_odbPointer = bindings.create(); _odbPointer = bindings.create();
_finalizer.attach(this, _odbPointer, detach: this);
} }
late final Pointer<git_odb> _odbPointer; late final Pointer<git_odb> _odbPointer;
@ -59,12 +58,9 @@ class Odb {
/// This method queries all available ODB backends trying to read the given /// This method queries all available ODB backends trying to read the given
/// [oid]. /// [oid].
/// ///
/// **IMPORTANT**: Returned object should be freed to release allocated
/// memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
OdbObject read(Oid oid) { OdbObject read(Oid oid) {
return OdbObject( return OdbObject._(
bindings.read( bindings.read(
odbPointer: _odbPointer, odbPointer: _odbPointer,
oidPointer: oid.pointer, oidPointer: oid.pointer,
@ -97,13 +93,24 @@ class Odb {
} }
/// Releases memory allocated for odb object. /// 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<git_odb>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class OdbObject { class OdbObject {
/// Initializes a new instance of the [OdbObject] class from /// Initializes a new instance of the [OdbObject] class from
/// provided pointer to odbObject object in memory. /// 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. /// Pointer to memory address for allocated odbObject object.
final Pointer<git_odb_object> _odbObjectPointer; final Pointer<git_odb_object> _odbObjectPointer;
@ -124,10 +131,19 @@ class OdbObject {
int get size => bindings.objectSize(_odbObjectPointer); int get size => bindings.objectSize(_odbObjectPointer);
/// Releases memory allocated for odbObject object. /// Releases memory allocated for odbObject object.
void free() => bindings.objectFree(_odbObjectPointer); void free() {
bindings.objectFree(_odbObjectPointer);
_objectfinalizer.detach(this);
}
@override @override
String toString() { String toString() {
return 'OdbObject{oid: $oid, type: $type, size: $size}'; return 'OdbObject{oid: $oid, type: $type, size: $size}';
} }
} }
// coverage:ignore-start
final _objectfinalizer = Finalizer<Pointer<git_odb_object>>(
(pointer) => bindings.objectFree(pointer),
);
// coverage:ignore-end

View file

@ -25,13 +25,11 @@ class Oid {
if (sha.length == 40) { if (sha.length == 40) {
_oidPointer = bindings.fromSHA(sha); _oidPointer = bindings.fromSHA(sha);
} else { } else {
final odb = repo.odb;
_oidPointer = odb_bindings.existsPrefix( _oidPointer = odb_bindings.existsPrefix(
odbPointer: odb.pointer, odbPointer: repo.odb.pointer,
shortOidPointer: bindings.fromStrN(sha), shortOidPointer: bindings.fromStrN(sha),
length: sha.length, length: sha.length,
); );
odb.free();
} }
} else { } else {
throw ArgumentError.value('$sha is not a valid sha hex string'); throw ArgumentError.value('$sha is not a valid sha hex string');

View file

@ -6,11 +6,10 @@ import 'package:libgit2dart/src/bindings/packbuilder.dart' as bindings;
class PackBuilder { class PackBuilder {
/// Initializes a new instance of [PackBuilder] class. /// Initializes a new instance of [PackBuilder] class.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
PackBuilder(Repository repo) { PackBuilder(Repository repo) {
_packbuilderPointer = bindings.init(repo.pointer); _packbuilderPointer = bindings.init(repo.pointer);
_finalizer.attach(this, _packbuilderPointer, detach: this);
} }
/// Pointer to memory address for allocated packbuilder object. /// Pointer to memory address for allocated packbuilder object.
@ -108,10 +107,19 @@ class PackBuilder {
} }
/// Releases memory allocated for packbuilder object. /// Releases memory allocated for packbuilder object.
void free() => bindings.free(_packbuilderPointer); void free() {
bindings.free(_packbuilderPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
return 'PackBuilder{length: $length, writtenLength: $writtenLength}'; return 'PackBuilder{length: $length, writtenLength: $writtenLength}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_packbuilder>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -1,5 +1,6 @@
import 'dart:ffi'; import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/bindings/patch.dart' as bindings; import 'package:libgit2dart/src/bindings/patch.dart' as bindings;
@ -8,9 +9,9 @@ import 'package:libgit2dart/src/util.dart';
class Patch { class Patch {
/// Initializes a new instance of [Patch] class from provided /// Initializes a new instance of [Patch] class from provided
/// pointer to patch object in memory and pointers to old and new blobs/buffers. /// pointer to patch object in memory and pointers to old and new blobs/buffers.
/// Patch(this._patchPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _patchPointer, detach: this);
Patch(this._patchPointer); }
/// Directly generates a [Patch] from the difference between two blobs. /// 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 /// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0. /// 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. /// Throws a [LibGit2Error] if error occured.
Patch.fromBlobs({ Patch.fromBlobs({
required Blob? oldBlob, required Blob? oldBlob,
@ -51,6 +50,7 @@ class Patch {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _patchPointer, detach: this);
} }
/// Directly generates a [Patch] from the difference between the blob and a /// 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 /// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0. /// 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. /// Throws a [LibGit2Error] if error occured.
Patch.fromBlobAndBuffer({ Patch.fromBlobAndBuffer({
required Blob? blob, required Blob? blob,
@ -93,6 +91,7 @@ class Patch {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _patchPointer, detach: this);
} }
/// Directly generates a [Patch] from the difference between two buffers /// 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 /// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0. /// 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. /// Throws a [LibGit2Error] if error occured.
Patch.fromBuffers({ Patch.fromBuffers({
required String? oldBuffer, required String? oldBuffer,
@ -136,6 +133,7 @@ class Patch {
contextLines: contextLines, contextLines: contextLines,
interhunkLines: interhunkLines, interhunkLines: interhunkLines,
); );
_finalizer.attach(this, _patchPointer, detach: this);
} }
/// Creates a patch for an entry in the diff list. /// 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. /// [index] is the position of an entry in diff list.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Patch.fromDiff({required Diff diff, required int index}) { Patch.fromDiff({required Diff diff, required int index}) {
_patchPointer = bindings.fromDiff(diffPointer: diff.pointer, index: index); _patchPointer = bindings.fromDiff(diffPointer: diff.pointer, index: index);
_finalizer.attach(this, _patchPointer, detach: this);
} }
late final Pointer<git_patch> _patchPointer; late final Pointer<git_patch> _patchPointer;
@ -157,7 +154,7 @@ class Patch {
PatchStats get stats { PatchStats get stats {
final result = bindings.lineStats(_patchPointer); final result = bindings.lineStats(_patchPointer);
return PatchStats( return PatchStats._(
context: result['context']!, context: result['context']!,
insertions: result['insertions']!, insertions: result['insertions']!,
deletions: result['deletions']!, deletions: result['deletions']!,
@ -201,14 +198,49 @@ class Patch {
final length = bindings.numHunks(_patchPointer); final length = bindings.numHunks(_patchPointer);
final hunks = <DiffHunk>[]; final hunks = <DiffHunk>[];
for (var i = 0; i < length; i++) { for (var index = 0; index < length; index++) {
final hunk = bindings.hunk(patchPointer: _patchPointer, hunkIndex: i); final hunk = bindings.hunk(patchPointer: _patchPointer, hunkIndex: index);
final hunkPointer = hunk['hunk']! as Pointer<git_diff_hunk>;
final linesCount = hunk['linesN']! as int;
final lines = <DiffLine>[];
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<Utf8>()
.toDartString(length: linePointer.ref.content_len),
),
);
}
final intHeader = <int>[];
for (var i = 0; i < hunkPointer.ref.header_len; i++) {
intHeader.add(hunkPointer.ref.header[i]);
}
hunks.add( hunks.add(
DiffHunk( DiffHunk._(
_patchPointer, linesCount: linesCount,
hunk['hunk']! as Pointer<git_diff_hunk>, index: index,
hunk['linesN']! as int, oldStart: hunkPointer.ref.old_start,
i, 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. /// Releases memory allocated for patch object.
void free() => bindings.free(_patchPointer); void free() {
bindings.free(_patchPointer);
_finalizer.detach(this);
}
@override @override
String toString() => 'Patch{size: ${size()}, delta: $delta}'; String toString() => 'Patch{size: ${size()}, delta: $delta}';
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_patch>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
/// Line counts of each type in a patch. /// Line counts of each type in a patch.
class PatchStats { class PatchStats {
const PatchStats({ const PatchStats._({
required this.context, required this.context,
required this.insertions, required this.insertions,
required this.deletions, required this.deletions,
@ -246,3 +287,83 @@ class PatchStats {
'deletions: $deletions}'; '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<DiffLine> 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}';
}
}

View file

@ -17,8 +17,6 @@ class Rebase {
/// [onto] is the branch to rebase onto, default is to rebase onto the given /// [onto] is the branch to rebase onto, default is to rebase onto the given
/// [upstream]. /// [upstream].
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Rebase.init({ Rebase.init({
required Repository repo, required Repository repo,
@ -32,16 +30,16 @@ class Rebase {
upstreamPointer: upstream?.pointer, upstreamPointer: upstream?.pointer,
ontoPointer: onto?.pointer, ontoPointer: onto?.pointer,
); );
_finalizer.attach(this, _rebasePointer, detach: this);
} }
/// Opens an existing rebase that was previously started by either an /// Opens an existing rebase that was previously started by either an
/// invocation of [Rebase.init] or by another client. /// invocation of [Rebase.init] or by another client.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Rebase.open(Repository repo) { Rebase.open(Repository repo) {
_rebasePointer = bindings.open(repo.pointer); _rebasePointer = bindings.open(repo.pointer);
_finalizer.attach(this, _rebasePointer, detach: this);
} }
/// Pointer to memory address for allocated rebase object. /// Pointer to memory address for allocated rebase object.
@ -57,7 +55,7 @@ class Rebase {
rebase: _rebasePointer, rebase: _rebasePointer,
index: i, index: i,
); );
result.add(RebaseOperation(operation)); result.add(RebaseOperation._(operation));
} }
return result; return result;
@ -101,7 +99,7 @@ class Rebase {
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
RebaseOperation next() { RebaseOperation next() {
return RebaseOperation(bindings.next(_rebasePointer)); return RebaseOperation._(bindings.next(_rebasePointer));
} }
/// Commits the current patch. You must have resolved any conflicts that were /// Commits the current patch. You must have resolved any conflicts that were
@ -138,13 +136,22 @@ class Rebase {
void abort() => bindings.abort(_rebasePointer); void abort() => bindings.abort(_rebasePointer);
/// Releases memory allocated for rebase object. /// 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<git_rebase>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class RebaseOperation { class RebaseOperation {
/// Initializes a new instance of the [RebaseOperation] class from /// Initializes a new instance of the [RebaseOperation] class from
/// provided pointer to rebase operation object in memory. /// 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. /// Pointer to memory address for allocated rebase operation object.
final Pointer<git_rebase_operation> _rebaseOperationPointer; final Pointer<git_rebase_operation> _rebaseOperationPointer;

View file

@ -10,17 +10,14 @@ import 'package:libgit2dart/src/bindings/repository.dart'
class Reference { class Reference {
/// Initializes a new instance of the [Reference] class. /// Initializes a new instance of the [Reference] class.
/// Reference(this._refPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _refPointer, detach: this);
Reference(this._refPointer); }
/// Creates a new reference for provided [target]. /// Creates a new reference for provided [target].
/// ///
/// The reference will be created in the [repo]sitory and written to the disk. /// 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: /// Valid reference [name]s must follow one of two patterns:
/// - Top-level names must contain only capital letters and underscores, and /// - Top-level names must contain only capital letters and underscores, and
/// must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). /// 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', '$target must be either Oid or String reference name',
); );
} }
_finalizer.attach(this, _refPointer, detach: this);
} }
/// Lookups reference [name] in a [repo]sitory. /// Lookups reference [name] in a [repo]sitory.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// The [name] will be checked for validity. /// The [name] will be checked for validity.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Reference.lookup({required Repository repo, required String name}) { Reference.lookup({required Repository repo, required String name}) {
_refPointer = bindings.lookup(repoPointer: repo.pointer, name: name); _refPointer = bindings.lookup(repoPointer: repo.pointer, name: name);
_finalizer.attach(this, _refPointer, detach: this);
} }
late Pointer<git_reference> _refPointer; late Pointer<git_reference> _refPointer;
@ -89,7 +86,6 @@ class Reference {
static void delete({required Repository repo, required String name}) { static void delete({required Repository repo, required String name}) {
final ref = Reference.lookup(repo: repo, name: name); final ref = Reference.lookup(repo: repo, name: name);
bindings.delete(ref.pointer); bindings.delete(ref.pointer);
ref.free();
} }
/// Renames an existing reference with provided [oldName]. /// Renames an existing reference with provided [oldName].
@ -120,7 +116,6 @@ class Reference {
force: force, force: force,
logMessage: logMessage, logMessage: logMessage,
); );
ref.free();
} }
/// List of all the references names that can be found in a [repo]sitory. /// 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. /// Creates a copy of an existing reference.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Reference duplicate() => Reference(bindings.duplicate(_refPointer)); Reference duplicate() => Reference(bindings.duplicate(_refPointer));
/// Type of the reference. /// Type of the reference.
@ -192,7 +185,6 @@ class Reference {
oidPointer: target.pointer, oidPointer: target.pointer,
logMessage: logMessage, logMessage: logMessage,
); );
free();
_refPointer = newPointer; _refPointer = newPointer;
} else if (target is String) { } else if (target is String) {
final newPointer = bindings.setTargetSymbolic( final newPointer = bindings.setTargetSymbolic(
@ -200,7 +192,6 @@ class Reference {
target: target, target: target,
logMessage: logMessage, logMessage: logMessage,
); );
free();
_refPointer = newPointer; _refPointer = newPointer;
} else { } else {
throw ArgumentError.value( throw ArgumentError.value(
@ -261,8 +252,6 @@ class Reference {
} }
/// [RefLog] object. /// [RefLog] object.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
RefLog get log => RefLog(this); RefLog get log => RefLog(this);
/// Whether reference is a local branch. /// Whether reference is a local branch.
@ -292,7 +281,10 @@ class Reference {
bool notEquals(Reference other) => !equals(other); bool notEquals(Reference other) => !equals(other);
/// Releases memory allocated for reference object. /// Releases memory allocated for reference object.
void free() => bindings.free(_refPointer); void free() {
bindings.free(_refPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -301,3 +293,9 @@ class Reference {
'isTag: $isTag}'; 'isTag: $isTag}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_reference>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -6,13 +6,12 @@ import 'package:libgit2dart/src/bindings/reflog.dart' as bindings;
class RefLog with IterableMixin<RefLogEntry> { class RefLog with IterableMixin<RefLogEntry> {
/// Initializes a new instance of [RefLog] class from provided [Reference]. /// Initializes a new instance of [RefLog] class from provided [Reference].
///
/// **IMPORTANT**: Should be freed to release allocated memory.
RefLog(Reference ref) { RefLog(Reference ref) {
_reflogPointer = bindings.read( _reflogPointer = bindings.read(
repoPointer: ref.owner.pointer, repoPointer: ref.owner.pointer,
name: ref.name, name: ref.name,
); );
_finalizer.attach(this, _reflogPointer, detach: this);
} }
/// Pointer to memory address for allocated reflog object. /// Pointer to memory address for allocated reflog object.
@ -47,7 +46,7 @@ class RefLog with IterableMixin<RefLogEntry> {
/// Requesting the reflog entry with an index of 0 will return the most /// Requesting the reflog entry with an index of 0 will return the most
/// recently created entry. /// recently created entry.
RefLogEntry operator [](int index) { RefLogEntry operator [](int index) {
return RefLogEntry( return RefLogEntry._(
bindings.getByIndex( bindings.getByIndex(
reflogPointer: _reflogPointer, reflogPointer: _reflogPointer,
index: index, index: index,
@ -89,16 +88,25 @@ class RefLog with IterableMixin<RefLogEntry> {
void write() => bindings.write(_reflogPointer); void write() => bindings.write(_reflogPointer);
/// Releases memory allocated for reflog object. /// Releases memory allocated for reflog object.
void free() => bindings.free(_reflogPointer); void free() {
bindings.free(_reflogPointer);
_finalizer.detach(this);
}
@override @override
Iterator<RefLogEntry> get iterator => _RefLogIterator(_reflogPointer); Iterator<RefLogEntry> get iterator => _RefLogIterator(_reflogPointer);
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_reflog>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class RefLogEntry { class RefLogEntry {
/// Initializes a new instance of [RefLogEntry] class from provided /// Initializes a new instance of [RefLogEntry] class from provided
/// pointer to RefLogEntry object in memory. /// pointer to RefLogEntry object in memory.
const RefLogEntry(this._entryPointer); const RefLogEntry._(this._entryPointer);
/// Pointer to memory address for allocated reflog entry object. /// Pointer to memory address for allocated reflog entry object.
final Pointer<git_reflog_entry> _entryPointer; final Pointer<git_reflog_entry> _entryPointer;
@ -141,7 +149,7 @@ class _RefLogIterator implements Iterator<RefLogEntry> {
if (_index == _count) { if (_index == _count) {
return false; return false;
} else { } else {
_currentEntry = RefLogEntry( _currentEntry = RefLogEntry._(
bindings.getByIndex( bindings.getByIndex(
reflogPointer: _reflogPointer, reflogPointer: _reflogPointer,
index: _index, index: _index,

View file

@ -8,18 +8,15 @@ class Remote {
/// ///
/// The [name] will be checked for validity. /// The [name] will be checked for validity.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Remote.lookup({required Repository repo, required String name}) { Remote.lookup({required Repository repo, required String name}) {
_remotePointer = bindings.lookup(repoPointer: repo.pointer, name: 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 /// Adds remote with provided [name] and [url] to the [repo]sitory's
/// configuration with the default [fetch] refspec if none provided. /// configuration with the default [fetch] refspec if none provided.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Remote.create({ Remote.create({
required Repository repo, required Repository repo,
@ -41,12 +38,11 @@ class Remote {
fetch: fetch, fetch: fetch,
); );
} }
_finalizer.attach(this, _remotePointer, detach: this);
} }
late final Pointer<git_remote> _remotePointer;
/// Pointer to memory address for allocated remote object. /// Pointer to memory address for allocated remote object.
Pointer<git_remote> get pointer => _remotePointer; late final Pointer<git_remote> _remotePointer;
/// Deletes an existing persisted remote with provided [name]. /// Deletes an existing persisted remote with provided [name].
/// ///
@ -298,7 +294,10 @@ class Remote {
} }
/// Releases memory allocated for remote object. /// Releases memory allocated for remote object.
void free() => bindings.free(_remotePointer); void free() {
bindings.free(_remotePointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -307,6 +306,12 @@ class Remote {
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_remote>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
/// Provides callers information about the progress of indexing a packfile, /// Provides callers information about the progress of indexing a packfile,
/// either directly or part of a fetch or clone that downloads a packfile. /// either directly or part of a fetch or clone that downloads a packfile.
class TransferProgress { class TransferProgress {
@ -346,3 +351,21 @@ class TransferProgress {
'indexedDeltas: $indexedDeltas, receivedBytes: $receivedBytes}'; '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;
}

View file

@ -111,13 +111,11 @@ class Repository {
/// ///
/// [bare] whether cloned repo should be bare. /// [bare] whether cloned repo should be bare.
/// ///
/// [remote] is the callback function with /// [remoteCallback] is the [RemoteCallback] object values that will be used
/// `Remote Function(Repository repo, String name, String url)` signature. /// in creation and customization process of remote instead of default ones.
/// The [Remote] it returns will be used instead of default one.
/// ///
/// [repository] is the callback function matching the /// [repositoryCallback] is the [RepositoryCallback] object values that will
/// `Repository Function(String path, bool bare)` signature. The [Repository] /// be used in creation and customization process of repository.
/// it returns will be used instead of creating a new one.
/// ///
/// [checkoutBranch] is the name of the branch to checkout after the clone. /// [checkoutBranch] is the name of the branch to checkout after the clone.
/// Defaults to using the remote's default branch. /// Defaults to using the remote's default branch.
@ -132,8 +130,8 @@ class Repository {
required String url, required String url,
required String localPath, required String localPath,
bool bare = false, bool bare = false,
Remote Function(Repository, String, String)? remote, RemoteCallback? remoteCallback,
Repository Function(String, bool)? repository, RepositoryCallback? repositoryCallback,
String? checkoutBranch, String? checkoutBranch,
Callbacks callbacks = const Callbacks(), Callbacks callbacks = const Callbacks(),
}) { }) {
@ -143,8 +141,8 @@ class Repository {
url: url, url: url,
localPath: localPath, localPath: localPath,
bare: bare, bare: bare,
remote: remote, remoteCallback: remoteCallback,
repository: repository, repositoryCallback: repositoryCallback,
checkoutBranch: checkoutBranch, checkoutBranch: checkoutBranch,
callbacks: callbacks, callbacks: callbacks,
); );
@ -375,8 +373,6 @@ class Repository {
/// If a configuration file has not been set, the default config set for the /// If a configuration file has not been set, the default config set for the
/// repository will be returned, including global and system configurations /// repository will be returned, including global and system configurations
/// (if they are available). /// (if they are available).
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Config get config => Config(bindings.config(_repoPointer)); Config get config => Config(bindings.config(_repoPointer));
/// Snapshot of the repository's configuration. /// Snapshot of the repository's configuration.
@ -386,24 +382,16 @@ class Repository {
/// ///
/// The contents of this snapshot will not change, even if the underlying /// The contents of this snapshot will not change, even if the underlying
/// config files are modified. /// config files are modified.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer)); Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer));
/// Repository's head. /// Repository's head.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Reference get head => Reference(bindings.head(_repoPointer)); Reference get head => Reference(bindings.head(_repoPointer));
/// Index file for this repository. /// Index file for this repository.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Index get index => Index(bindings.index(_repoPointer)); Index get index => Index(bindings.index(_repoPointer));
/// ODB for this repository. /// ODB for this repository.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Odb get odb => Odb(bindings.odb(_repoPointer)); Odb get odb => Odb(bindings.odb(_repoPointer));
@ -419,23 +407,17 @@ class Repository {
/// List of all branches that can be found in a 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. /// Throws a [LibGit2Error] if error occured.
List<Branch> get branches => Branch.list(repo: this); List<Branch> get branches => Branch.list(repo: this);
/// List of local branches that can be found in a repository. /// 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. /// Throws a [LibGit2Error] if error occured.
List<Branch> get branchesLocal => List<Branch> get branchesLocal =>
Branch.list(repo: this, type: GitBranch.local); Branch.list(repo: this, type: GitBranch.local);
/// List of remote branches that can be found in a repository. /// 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. /// Throws a [LibGit2Error] if error occured.
List<Branch> get branchesRemote => List<Branch> get branchesRemote =>
Branch.list(repo: this, type: GitBranch.remote); Branch.list(repo: this, type: GitBranch.remote);
@ -465,8 +447,6 @@ class Repository {
/// ///
/// If [sorting] isn't provided default will be used (reverse chronological /// If [sorting] isn't provided default will be used (reverse chronological
/// order, like in git). /// order, like in git).
///
/// **IMPORTANT**: Commits should be freed to release allocated memory.
List<Commit> log({ List<Commit> log({
required Oid oid, required Oid oid,
Set<GitSort> sorting = const {GitSort.none}, Set<GitSort> sorting = const {GitSort.none},
@ -477,8 +457,6 @@ class Repository {
walker.push(oid); walker.push(oid);
final result = walker.walk(); final result = walker.walk();
walker.free();
return result; return result;
} }
@ -737,6 +715,7 @@ class Repository {
} }
} }
// ignore: no_leading_underscores_for_local_identifiers
final _packDelegate = packDelegate ?? packAll; final _packDelegate = packDelegate ?? packAll;
final packbuilder = PackBuilder(this); final packbuilder = PackBuilder(this);
@ -745,10 +724,79 @@ class Repository {
} }
_packDelegate(packbuilder); _packDelegate(packbuilder);
packbuilder.write(path); packbuilder.write(path);
final result = packbuilder.writtenLength;
packbuilder.free(); return packbuilder.writtenLength;
return result;
} }
} }
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<GitRepositoryInit> 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;
}

View file

@ -15,8 +15,6 @@ class RevParse {
/// point to an intermediate reference. When such expressions are being /// point to an intermediate reference. When such expressions are being
/// passed in, reference_out will be valued as well. /// passed in, reference_out will be valued as well.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
RevParse.ext({required Repository repo, required String spec}) { RevParse.ext({required Repository repo, required String spec}) {
final pointers = bindings.revParseExt( final pointers = bindings.revParseExt(
@ -51,8 +49,6 @@ class RevParse {
/// final tag = RevParse.single(repo: repo, spec: 'v1.0') as Tag; /// 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. /// Throws a [LibGit2Error] if error occured.
static Object single({required Repository repo, required String spec}) { static Object single({required Repository repo, required String spec}) {
final object = bindings.revParseSingle( final object = bindings.revParseSingle(
@ -79,7 +75,7 @@ class RevParse {
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
static RevSpec range({required Repository repo, required String spec}) { static RevSpec range({required Repository repo, required String spec}) {
return RevSpec( return RevSpec._(
bindings.revParse( bindings.revParse(
repoPointer: repo.pointer, repoPointer: repo.pointer,
spec: spec, spec: spec,
@ -96,19 +92,15 @@ class RevParse {
class RevSpec { class RevSpec {
/// Initializes a new instance of [RevSpec] class from provided /// Initializes a new instance of [RevSpec] class from provided
/// pointer to revspec object in memory. /// pointer to revspec object in memory.
const RevSpec(this._revSpecPointer); const RevSpec._(this._revSpecPointer);
/// Pointer to memory address for allocated revspec object. /// Pointer to memory address for allocated revspec object.
final Pointer<git_revspec> _revSpecPointer; final Pointer<git_revspec> _revSpecPointer;
/// Left element of the revspec. /// Left element of the revspec.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit get from => Commit(_revSpecPointer.ref.from.cast()); Commit get from => Commit(_revSpecPointer.ref.from.cast());
/// Right element of the revspec. /// Right element of the revspec.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit? get to { Commit? get to {
return _revSpecPointer.ref.to == nullptr return _revSpecPointer.ref.to == nullptr
? null ? null

View file

@ -5,10 +5,9 @@ import 'package:libgit2dart/src/bindings/revwalk.dart' as bindings;
class RevWalk { class RevWalk {
/// Initializes a new instance of the [RevWalk] class. /// Initializes a new instance of the [RevWalk] class.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
RevWalk(Repository repo) { RevWalk(Repository repo) {
_revWalkPointer = bindings.create(repo.pointer); _revWalkPointer = bindings.create(repo.pointer);
_finalizer.attach(this, _revWalkPointer, detach: this);
} }
late final Pointer<git_revwalk> _revWalkPointer; late final Pointer<git_revwalk> _revWalkPointer;
@ -150,5 +149,14 @@ class RevWalk {
void simplifyFirstParent() => bindings.simplifyFirstParent(_revWalkPointer); void simplifyFirstParent() => bindings.simplifyFirstParent(_revWalkPointer);
/// Releases memory allocated for [RevWalk] object. /// 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<git_revwalk>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -10,17 +10,16 @@ import 'package:meta/meta.dart';
class Signature { class Signature {
/// Initializes a new instance of [Signature] class from provided pointer to /// Initializes a new instance of [Signature] class from provided pointer to
/// signature object in memory. /// signature object in memory.
/// Signature(Pointer<git_signature> pointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _signaturePointer = bindings.duplicate(pointer);
Signature(this._signaturePointer); _finalizer.attach(this, _signaturePointer, detach: this);
}
/// Creates new [Signature] from provided [name], [email], and optional [time] /// Creates new [Signature] from provided [name], [email], and optional [time]
/// in seconds from epoch and [offset] in minutes. /// in seconds from epoch and [offset] in minutes.
/// ///
/// If [time] isn't provided [Signature] will be created with a timestamp of /// If [time] isn't provided [Signature] will be created with a timestamp of
/// 'now'. /// 'now'.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Signature.create({ Signature.create({
required String name, required String name,
required String email, required String email,
@ -39,6 +38,7 @@ class Signature {
offset: offset, offset: offset,
); );
} }
_finalizer.attach(this, _signaturePointer, detach: this);
} }
/// Creates a new action signature with default user and now timestamp. /// Creates a new action signature with default user and now timestamp.
@ -48,6 +48,7 @@ class Signature {
/// on that information. /// on that information.
Signature.defaultSignature(Repository repo) { Signature.defaultSignature(Repository repo) {
_signaturePointer = bindings.defaultSignature(repo.pointer); _signaturePointer = bindings.defaultSignature(repo.pointer);
_finalizer.attach(this, _signaturePointer, detach: this);
} }
late final Pointer<git_signature> _signaturePointer; late final Pointer<git_signature> _signaturePointer;
@ -79,7 +80,10 @@ class Signature {
} }
/// Releases memory allocated for signature object. /// Releases memory allocated for signature object.
void free() => bindings.free(_signaturePointer); void free() {
bindings.free(_signaturePointer);
_finalizer.detach(this);
}
@override // coverage:ignore-line @override // coverage:ignore-line
int get hashCode => int get hashCode =>
@ -91,3 +95,9 @@ class Signature {
'offset: $offset}'; 'offset: $offset}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_signature>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -7,14 +7,13 @@ class Submodule {
/// Lookups submodule information by [name] or path (they are usually the /// Lookups submodule information by [name] or path (they are usually the
/// same). /// same).
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Submodule.lookup({required Repository repo, required String name}) { Submodule.lookup({required Repository repo, required String name}) {
_submodulePointer = bindings.lookup( _submodulePointer = bindings.lookup(
repoPointer: repo.pointer, repoPointer: repo.pointer,
name: name, name: name,
); );
_finalizer.attach(this, _submodulePointer, detach: this);
} }
/// Adds a submodule to the index. /// Adds a submodule to the index.
@ -29,8 +28,6 @@ class Submodule {
/// [callbacks] is the combination of callback functions from [Callbacks] /// [callbacks] is the combination of callback functions from [Callbacks]
/// object. /// object.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Submodule.add({ Submodule.add({
required Repository repo, required Repository repo,
@ -49,6 +46,8 @@ class Submodule {
bindings.clone(submodule: _submodulePointer, callbacks: callbacks); bindings.clone(submodule: _submodulePointer, callbacks: callbacks);
bindings.addFinalize(_submodulePointer); bindings.addFinalize(_submodulePointer);
_finalizer.attach(this, _submodulePointer, detach: this);
} }
/// Pointer to memory address for allocated submodule object. /// Pointer to memory address for allocated submodule object.
@ -264,7 +263,10 @@ class Submodule {
} }
/// Releases memory allocated for submodule object. /// Releases memory allocated for submodule object.
void free() => bindings.free(_submodulePointer); void free() {
bindings.free(_submodulePointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -273,3 +275,9 @@ class Submodule {
'workdirOid: $workdirOid, ignore: $ignore, updateRule: $updateRule}'; 'workdirOid: $workdirOid, ignore: $ignore, updateRule: $updateRule}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_submodule>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/tag.dart' as bindings;
class Tag { class Tag {
/// Initializes a new instance of [Tag] class from provided pointer to /// Initializes a new instance of [Tag] class from provided pointer to
/// tag object in memory. /// tag object in memory.
/// Tag(this._tagPointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _tagPointer, detach: this);
Tag(this._tagPointer); }
/// Lookups tag object for provided [oid] in a [repo]sitory. /// 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}) { Tag.lookup({required Repository repo, required Oid oid}) {
_tagPointer = bindings.lookup( _tagPointer = bindings.lookup(
repoPointer: repo.pointer, repoPointer: repo.pointer,
oidPointer: oid.pointer, oidPointer: oid.pointer,
); );
_finalizer.attach(this, _tagPointer, detach: this);
} }
/// Pointer to memory address for allocated tag object. /// 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 /// Returned object should be explicitly downcasted to one of four of git
/// object types. /// object types.
/// ///
/// **IMPORTANT**: returned object should be freed to release allocated
/// memory.
///
/// ```dart /// ```dart
/// final commit = tag.target as Commit; /// final commit = tag.target as Commit;
/// final tree = tag.target as Tree; /// final tree = tag.target as Tree;
@ -215,7 +211,10 @@ class Tag {
} }
/// Releases memory allocated for tag object. /// Releases memory allocated for tag object.
void free() => bindings.free(_tagPointer); void free() {
bindings.free(_tagPointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -223,3 +222,9 @@ class Tag {
'tagger: $tagger}'; 'tagger: $tagger}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_tag>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -7,18 +7,17 @@ import 'package:libgit2dart/src/bindings/tree.dart' as bindings;
class Tree { class Tree {
/// Initializes a new instance of [Tree] class from provided pointer to /// Initializes a new instance of [Tree] class from provided pointer to
/// tree object in memory. /// tree object in memory.
/// Tree(this._treePointer) {
/// **IMPORTANT**: Should be freed to release allocated memory. _finalizer.attach(this, _treePointer, detach: this);
Tree(this._treePointer); }
/// Lookups a tree object for provided [oid] in a [repo]sitory. /// 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}) { Tree.lookup({required Repository repo, required Oid oid}) {
_treePointer = bindings.lookup( _treePointer = bindings.lookup(
repoPointer: repo.pointer, repoPointer: repo.pointer,
oidPointer: oid.pointer, oidPointer: oid.pointer,
); );
_finalizer.attach(this, _treePointer, detach: this);
} }
late final Pointer<git_tree> _treePointer; late final Pointer<git_tree> _treePointer;
@ -63,7 +62,7 @@ class Tree {
), ),
); );
} else if (value is String && value.contains('/')) { } else if (value is String && value.contains('/')) {
return TreeEntry( return TreeEntry._byPath(
bindings.getByPath( bindings.getByPath(
rootPointer: _treePointer, rootPointer: _treePointer,
path: value, path: value,
@ -90,7 +89,10 @@ class Tree {
int get length => bindings.entryCount(_treePointer); int get length => bindings.entryCount(_treePointer);
/// Releases memory allocated for tree object. /// Releases memory allocated for tree object.
void free() => bindings.free(_treePointer); void free() {
bindings.free(_treePointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
@ -98,10 +100,24 @@ class Tree {
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_tree>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class TreeEntry { class TreeEntry {
/// Initializes a new instance of [TreeEntry] class from provided pointer to /// Initializes a new instance of [TreeEntry] class from provided pointer to
/// tree entry object in memory. /// 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. /// Pointer to memory address for allocated tree entry object.
final Pointer<git_tree_entry> _treeEntryPointer; final Pointer<git_tree_entry> _treeEntryPointer;
@ -119,8 +135,19 @@ class TreeEntry {
} }
/// Releases memory allocated for tree entry object. /// 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 @override
String toString() => 'TreeEntry{oid: $oid, name: $name, filemode: $filemode}'; String toString() => 'TreeEntry{oid: $oid, name: $name, filemode: $filemode}';
} }
// coverage:ignore-start
final _entryFinalizer = Finalizer<Pointer<git_tree_entry>>(
(pointer) => bindings.entryFree(pointer),
);
// coverage:ignore-end

View file

@ -7,14 +7,13 @@ class TreeBuilder {
/// Initializes a new instance of [TreeBuilder] class from provided /// Initializes a new instance of [TreeBuilder] class from provided
/// [repo]sitory and optional [tree] objects. /// [repo]sitory and optional [tree] objects.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
TreeBuilder({required Repository repo, Tree? tree}) { TreeBuilder({required Repository repo, Tree? tree}) {
_treeBuilderPointer = bindings.create( _treeBuilderPointer = bindings.create(
repoPointer: repo.pointer, repoPointer: repo.pointer,
sourcePointer: tree?.pointer ?? nullptr, sourcePointer: tree?.pointer ?? nullptr,
); );
_finalizer.attach(this, _treeBuilderPointer, detach: this);
} }
/// Pointer to memory address for allocated tree builder object. /// 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]. /// 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]. /// Throws [ArgumentError] if nothing found for provided [filename].
TreeEntry operator [](String filename) { TreeEntry operator [](String filename) {
return TreeEntry( return TreeEntry(
@ -83,10 +79,19 @@ class TreeBuilder {
} }
/// Releases memory allocated for tree builder object and all the entries. /// 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 @override
String toString() { String toString() {
return 'TreeBuilder{length: $length}'; return 'TreeBuilder{length: $length}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_treebuilder>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -15,8 +15,6 @@ class Worktree {
/// ///
/// [path] is the path to create working tree at. /// [path] is the path to create working tree at.
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Worktree.create({ Worktree.create({
required Repository repo, required Repository repo,
@ -30,15 +28,15 @@ class Worktree {
path: path, path: path,
refPointer: ref?.pointer, refPointer: ref?.pointer,
); );
_finalizer.attach(this, _worktreePointer, detach: this);
} }
/// Lookups existing worktree in [repo] with provided [name]. /// Lookups existing worktree in [repo] with provided [name].
/// ///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Worktree.lookup({required Repository repo, required String name}) { Worktree.lookup({required Repository repo, required String name}) {
_worktreePointer = bindings.lookup(repoPointer: repo.pointer, name: name); _worktreePointer = bindings.lookup(repoPointer: repo.pointer, name: name);
_finalizer.attach(this, _worktreePointer, detach: this);
} }
/// Pointer to memory address for allocated branch object. /// Pointer to memory address for allocated branch object.
@ -88,10 +86,19 @@ class Worktree {
bool get isValid => bindings.isValid(_worktreePointer); bool get isValid => bindings.isValid(_worktreePointer);
/// Releases memory allocated for worktree object. /// Releases memory allocated for worktree object.
void free() => bindings.free(_worktreePointer); void free() {
bindings.free(_worktreePointer);
_finalizer.detach(this);
}
@override @override
String toString() { String toString() {
return 'Worktree{name: $name, path: $path}'; return 'Worktree{name: $name, path: $path}';
} }
} }
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_worktree>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

@ -3,19 +3,20 @@ description: Dart bindings to libgit2
version: 0.0.1 version: 0.0.1
environment: environment:
sdk: ">=2.16.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"
flutter: ">=2.10.0" flutter: ">=2.13.0-0.0.pre.578"
dependencies: dependencies:
args: ^2.3.0 args: ^2.3.0
cli_util: ^0.3.5 cli_util: ^0.3.5
ffi: ^1.1.2 ffi: ^1.1.2
meta: ^1.7.0
path: ^1.8.1
pub_cache: ^0.3.1 pub_cache: ^0.3.1
dev_dependencies: dev_dependencies:
ffigen: ^4.1.2 ffigen: ^4.1.2
lints: ^1.0.1 lints: ^2.0.0
path: ^1.8.1
test: ^1.20.0 test: ^1.20.0
flutter: flutter:

View file

@ -29,8 +29,6 @@ void main() {
expect(annotated.oid, tip); expect(annotated.oid, tip);
expect(annotated.refName, ''); expect(annotated.refName, '');
annotated.free();
}); });
test('throws when trying to lookup annotated commit with invalid oid', () { test('throws when trying to lookup annotated commit with invalid oid', () {
@ -41,33 +39,27 @@ void main() {
}); });
test('creates annotated commit from provided reference', () { 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( final annotated = AnnotatedCommit.fromReference(
repo: repo, repo: repo,
reference: reference, reference: reference,
); );
expect(annotated.oid, reference.target); expect(annotated.oid, reference.target);
expect(annotated.refName, 'refs/heads/master'); expect(annotated.refName, refName);
annotated.free();
reference.free();
}); });
test( test(
'throws when trying to create annotated commit from provided ' 'throws when trying to create annotated commit from provided '
'reference and error occurs', () { 'reference and error occurs', () {
final reference = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect( expect(
() => AnnotatedCommit.fromReference( () => AnnotatedCommit.fromReference(
repo: Repository(nullptr), repo: Repository(nullptr),
reference: reference, reference: Reference.lookup(repo: repo, name: 'refs/heads/master'),
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
reference.free();
}); });
test('creates annotated commit from provided revspec', () { test('creates annotated commit from provided revspec', () {
@ -75,8 +67,6 @@ void main() {
expect(annotated.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(annotated.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
expect(annotated.refName, ''); expect(annotated.refName, '');
annotated.free();
}); });
test('throws when trying to create annotated commit from invalid revspec', test('throws when trying to create annotated commit from invalid revspec',
@ -98,8 +88,6 @@ void main() {
expect(annotated.oid, oid); expect(annotated.oid, oid);
expect(annotated.refName, 'master'); expect(annotated.refName, 'master');
annotated.free();
}); });
test( test(
@ -115,5 +103,10 @@ void main() {
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
test('manually releases allocated memory', () {
final annotated = AnnotatedCommit.lookup(repo: repo, oid: tip);
expect(() => annotated.free(), returnsNormally);
});
}); });
} }

View file

@ -52,8 +52,6 @@ void main() {
}); });
tearDown(() { tearDown(() {
sig1.free();
sig2.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
@ -82,8 +80,6 @@ void main() {
expect(blame[i].isBoundary, hunks[i]['isBoundary']); expect(blame[i].isBoundary, hunks[i]['isBoundary']);
expect(blame[i].originPath, 'feature_file'); expect(blame[i].originPath, 'feature_file');
} }
blame.free();
}); });
test('throws when provided file path is invalid', () { test('throws when provided file path is invalid', () {
@ -104,9 +100,6 @@ void main() {
expect(blameHunk.originCommitter, null); expect(blameHunk.originCommitter, null);
expect(blameHunk.finalCommitOid.sha, '0' * 40); expect(blameHunk.finalCommitOid.sha, '0' * 40);
expect(blameHunk.finalCommitter, null); expect(blameHunk.finalCommitter, null);
bufferBlame.free();
blame.free();
}); });
test('throws when trying to get blame for empty buffer', () { test('throws when trying to get blame for empty buffer', () {
@ -115,7 +108,6 @@ void main() {
() => Blame.buffer(reference: blame, buffer: ''), () => Blame.buffer(reference: blame, buffer: ''),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
blame.free();
}); });
test('returns the blame for provided file with minMatchCharacters set', () { test('returns the blame for provided file with minMatchCharacters set', () {
@ -127,8 +119,6 @@ void main() {
); );
expect(blame.length, 2); expect(blame.length, 2);
blame.free();
}); });
test('returns the blame for provided line', () { test('returns the blame for provided line', () {
@ -148,22 +138,16 @@ void main() {
expect(hunk.originCommitter, hunks[0]['originCommitter']); expect(hunk.originCommitter, hunks[0]['originCommitter']);
expect(hunk.isBoundary, hunks[0]['isBoundary']); expect(hunk.isBoundary, hunks[0]['isBoundary']);
expect(hunk.originPath, 'feature_file'); expect(hunk.originPath, 'feature_file');
blame.free();
}); });
test('throws when provided index for hunk is invalid', () { test('throws when provided index for hunk is invalid', () {
final blame = Blame.file(repo: repo, path: 'feature_file'); final blame = Blame.file(repo: repo, path: 'feature_file');
expect(() => blame[10], throwsA(isA<RangeError>())); expect(() => blame[10], throwsA(isA<RangeError>()));
blame.free();
}); });
test('throws when provided line number for hunk is invalid', () { test('throws when provided line number for hunk is invalid', () {
final blame = Blame.file(repo: repo, path: 'feature_file'); final blame = Blame.file(repo: repo, path: 'feature_file');
expect(() => blame.forLine(10), throwsA(isA<RangeError>())); expect(() => blame.forLine(10), throwsA(isA<RangeError>()));
blame.free();
}); });
test('returns the blame for provided file with newestCommit argument', () { test('returns the blame for provided file with newestCommit argument', () {
@ -190,8 +174,6 @@ void main() {
expect(hunk.originCommitter, hunks[0]['originCommitter']); expect(hunk.originCommitter, hunks[0]['originCommitter']);
expect(hunk.isBoundary, hunks[0]['isBoundary']); expect(hunk.isBoundary, hunks[0]['isBoundary']);
expect(hunk.originPath, 'feature_file'); expect(hunk.originPath, 'feature_file');
blame.free();
}); });
test('returns the blame for provided file with minLine and maxLine set', 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].isBoundary, hunks[i]['isBoundary']);
expect(blame[i].originPath, 'feature_file'); 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', () { test('returns string representation of BlameHunk object', () {
final blame = Blame.file(repo: repo, path: 'feature_file'); final blame = Blame.file(repo: repo, path: 'feature_file');
expect(blame.toString(), contains('BlameHunk{')); expect(blame.toString(), contains('BlameHunk{'));
blame.free();
}); });
}); });
} }

View file

@ -26,9 +26,7 @@ void main() {
group('Blob', () { group('Blob', () {
test('lookups blob with provided oid', () { test('lookups blob with provided oid', () {
final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); expect(Blob.lookup(repo: repo, oid: repo[blobSHA]), isA<Blob>());
expect(blob, isA<Blob>());
blob.free();
}); });
test('throws when trying to lookup with invalid oid', () { test('throws when trying to lookup with invalid oid', () {
@ -45,8 +43,6 @@ void main() {
expect(blob.isBinary, false); expect(blob.isBinary, false);
expect(blob.size, 13); expect(blob.size, 13);
expect(blob.content, blobContent); expect(blob.content, blobContent);
blob.free();
}); });
test('creates new blob with provided content', () { test('creates new blob with provided content', () {
@ -57,14 +53,11 @@ void main() {
expect(newBlob.isBinary, false); expect(newBlob.isBinary, false);
expect(newBlob.size, 9); expect(newBlob.size, 9);
expect(newBlob.content, newBlobContent); expect(newBlob.content, newBlobContent);
newBlob.free();
}); });
test('throws when trying to create new blob and error occurs', () { test('throws when trying to create new blob and error occurs', () {
final nullRepo = Repository(nullptr);
expect( expect(
() => Blob.create(repo: nullRepo, content: ''), () => Blob.create(repo: Repository(nullptr), content: ''),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
@ -80,8 +73,6 @@ void main() {
expect(newBlob.isBinary, false); expect(newBlob.isBinary, false);
expect(newBlob.size, 13); expect(newBlob.size, 13);
expect(newBlob.content, blobContent); expect(newBlob.content, blobContent);
newBlob.free();
}); });
test('throws when creating new blob from invalid path', () { test('throws when creating new blob from invalid path', () {
@ -103,8 +94,6 @@ void main() {
expect(newBlob, isA<Blob>()); expect(newBlob, isA<Blob>());
expect(newBlob.isBinary, false); expect(newBlob.isBinary, false);
newBlob.free();
}); });
test('throws when trying to create from invalid path', () { test('throws when trying to create from invalid path', () {
@ -119,9 +108,6 @@ void main() {
final dupBlob = blob.duplicate(); final dupBlob = blob.duplicate();
expect(blob.oid.sha, dupBlob.oid.sha); expect(blob.oid.sha, dupBlob.oid.sha);
dupBlob.free();
blob.free();
}); });
test('filters content of a blob', () { test('filters content of a blob', () {
@ -129,8 +115,6 @@ void main() {
final blob = Blob.lookup(repo: repo, oid: blobOid); final blob = Blob.lookup(repo: repo, oid: blobOid);
expect(blob.filterContent(asPath: 'file.crlf'), 'clrf\r\nclrf\r\n'); expect(blob.filterContent(asPath: 'file.crlf'), 'clrf\r\nclrf\r\n');
blob.free();
}); });
test('filters content of a blob with provided commit for attributes', () { test('filters content of a blob with provided commit for attributes', () {
@ -152,9 +136,6 @@ void main() {
), ),
'clrf\r\nclrf\r\n', 'clrf\r\nclrf\r\n',
); );
commit.free();
blob.free();
}); });
test('throws when trying to filter content of a blob and error occurs', () { 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', () { test('returns string representation of Blob object', () {
final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]);
expect(blob.toString(), contains('Blob{')); expect(blob.toString(), contains('Blob{'));
blob.free();
}); });
}); });
} }

View file

@ -34,8 +34,6 @@ void main() {
for (var i = 0; i < branches.length; i++) { for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]); expect(branches[i].name, branchesExpected[i]);
expect(aliasBranches[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++) { for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]); expect(branches[i].name, branchesExpected[i]);
expect(aliasBranches[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++) { for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]); expect(branches[i].name, branchesExpected[i]);
expect(aliasBranches[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', () { test('returns a branch with provided name', () {
final branch = Branch.lookup(repo: repo, name: 'master'); final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.target.sha, lastCommit.sha); expect(branch.target.sha, lastCommit.sha);
branch.free();
}); });
test('throws when provided name not found', () { test('throws when provided name not found', () {
@ -95,88 +88,66 @@ void main() {
}); });
test('checks if branch is current head', () { test('checks if branch is current head', () {
final masterBranch = Branch.lookup(repo: repo, name: 'master'); expect(Branch.lookup(repo: repo, name: 'master').isHead, true);
final featureBranch = Branch.lookup(repo: repo, name: 'feature'); expect(Branch.lookup(repo: repo, name: 'feature').isHead, false);
expect(masterBranch.isHead, true);
expect(featureBranch.isHead, false);
masterBranch.free();
featureBranch.free();
}); });
test('throws when checking if branch is current head and error occurs', () { test('throws when checking if branch is current head and error occurs', () {
final nullBranch = Branch(nullptr);
expect( expect(
() => nullBranch.isHead, () => Branch(nullptr).isHead,
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
test('checks if branch is checked out', () { test('checks if branch is checked out', () {
final masterBranch = Branch.lookup(repo: repo, name: 'master'); expect(Branch.lookup(repo: repo, name: 'master').isCheckedOut, true);
final featureBranch = Branch.lookup(repo: repo, name: 'feature'); expect(Branch.lookup(repo: repo, name: 'feature').isCheckedOut, false);
expect(masterBranch.isCheckedOut, true);
expect(featureBranch.isCheckedOut, false);
masterBranch.free();
featureBranch.free();
}); });
test('throws when checking if branch is checked out and error occurs', () { test('throws when checking if branch is checked out and error occurs', () {
final nullBranch = Branch(nullptr);
expect( expect(
() => nullBranch.isCheckedOut, () => Branch(nullptr).isCheckedOut,
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
test('returns name', () { test('returns name', () {
final branch = Branch.lookup(repo: repo, name: 'master'); expect(Branch.lookup(repo: repo, name: 'master').name, 'master');
expect(branch.name, 'master');
branch.free();
}); });
test('throws when getting name and error occurs', () { test('throws when getting name and error occurs', () {
final nullBranch = Branch(nullptr); expect(() => Branch(nullptr).name, throwsA(isA<LibGit2Error>()));
expect(() => nullBranch.name, throwsA(isA<LibGit2Error>()));
}); });
test('returns remote name of a remote-tracking branch', () { test('returns remote name of a remote-tracking branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first; final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(branch.remoteName, 'origin'); expect(branch.remoteName, 'origin');
branch.free();
}); });
test( test(
'throws when getting remote name of a remote-tracking branch and ' 'throws when getting remote name of a remote-tracking branch and '
'error occurs', () { 'error occurs', () {
final branch = Branch.lookup(repo: repo, name: 'master'); expect(
expect(() => branch.remoteName, throwsA(isA<LibGit2Error>())); () => Branch.lookup(repo: repo, name: 'master').remoteName,
throwsA(isA<LibGit2Error>()),
);
}); });
test('returns upstream of a local branch', () { test('returns upstream of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master'); final upstream = Branch.lookup(repo: repo, name: 'master').upstream;
final upstream = branch.upstream;
expect(upstream.isRemote, true); expect(upstream.isRemote, true);
expect(upstream.name, 'refs/remotes/origin/master'); expect(upstream.name, 'refs/remotes/origin/master');
upstream.free();
branch.free();
}); });
test('throws when trying to get upstream of a remote branch', () { test('throws when trying to get upstream of a remote branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first; final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(() => branch.upstream, throwsA(isA<LibGit2Error>())); expect(() => branch.upstream, throwsA(isA<LibGit2Error>()));
branch.free();
}); });
test('sets upstream of a branch', () { test('sets upstream of a branch', () {
final branch = Branch.lookup(repo: repo, name: 'master'); final branch = Branch.lookup(repo: repo, name: 'master');
var upstream = branch.upstream; expect(branch.upstream.name, 'refs/remotes/origin/master');
expect(upstream.name, 'refs/remotes/origin/master');
final ref = Reference.create( final ref = Reference.create(
repo: repo, repo: repo,
@ -185,24 +156,15 @@ void main() {
); );
branch.setUpstream(ref.shorthand); branch.setUpstream(ref.shorthand);
upstream = branch.upstream; expect(branch.upstream.name, 'refs/remotes/origin/new');
expect(upstream.name, 'refs/remotes/origin/new');
ref.free();
upstream.free();
branch.free();
}); });
test('unsets upstream of a branch', () { test('unsets upstream of a branch', () {
final branch = Branch.lookup(repo: repo, name: 'master'); final branch = Branch.lookup(repo: repo, name: 'master');
final upstream = branch.upstream; expect(branch.upstream.name, 'refs/remotes/origin/master');
expect(upstream.name, 'refs/remotes/origin/master');
branch.setUpstream(null); branch.setUpstream(null);
expect(() => branch.upstream, throwsA(isA<LibGit2Error>())); expect(() => branch.upstream, throwsA(isA<LibGit2Error>()));
upstream.free();
branch.free();
}); });
test('throws when trying to set upstream of a branch and error occurs', () { test('throws when trying to set upstream of a branch and error occurs', () {
@ -211,64 +173,61 @@ void main() {
() => branch.setUpstream('some/upstream'), () => branch.setUpstream('some/upstream'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
branch.free();
}); });
test('returns upstream name of a local branch', () { test('returns upstream name of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master'); expect(
expect(branch.upstreamName, 'refs/remotes/origin/master'); Branch.lookup(repo: repo, name: 'master').upstreamName,
branch.free(); 'refs/remotes/origin/master',
);
}); });
test('throws when trying to get upstream name of a branch and error occurs', test('throws when trying to get upstream name of a branch and error occurs',
() { () {
final branch = Branch.lookup(repo: repo, name: 'feature'); expect(
expect(() => branch.upstreamName, throwsA(isA<LibGit2Error>())); () => Branch.lookup(repo: repo, name: 'feature').upstreamName,
branch.free(); throwsA(isA<LibGit2Error>()),
);
}); });
test('returns upstream remote of a local branch', () { test('returns upstream remote of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master'); expect(
expect(branch.upstreamRemote, 'origin'); Branch.lookup(repo: repo, name: 'master').upstreamRemote,
branch.free(); 'origin',
);
}); });
test('throws when trying to get upstream remote of a remote branch', () { test('throws when trying to get upstream remote of a remote branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(
expect(() => branch.upstreamRemote, throwsA(isA<LibGit2Error>())); () => Branch.list(repo: repo, type: GitBranch.remote)
branch.free(); .first
.upstreamRemote,
throwsA(isA<LibGit2Error>()),
);
}); });
test('returns upstream merge of a local branch', () { test('returns upstream merge of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master'); expect(
expect(branch.upstreamMerge, 'refs/heads/master'); Branch.lookup(repo: repo, name: 'master').upstreamMerge,
branch.free(); 'refs/heads/master',
);
}); });
test('throws when trying to get upstream merge of a remote branch', () { test('throws when trying to get upstream merge of a remote branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first; final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(() => branch.upstreamMerge, throwsA(isA<LibGit2Error>())); expect(() => branch.upstreamMerge, throwsA(isA<LibGit2Error>()));
branch.free();
}); });
group('create()', () { group('create()', () {
test('creates branch', () { test('creates branch', () {
final commit = Commit.lookup(repo: repo, oid: lastCommit);
final branch = Branch.create( final branch = Branch.create(
repo: repo, repo: repo,
name: 'testing', 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); expect(branch.target, lastCommit);
for (final branch in branches) {
branch.free();
}
branch.free();
commit.free();
}); });
test('throws when name already exists', () { test('throws when name already exists', () {
@ -278,28 +237,18 @@ void main() {
() => Branch.create(repo: repo, name: 'feature', target: commit), () => Branch.create(repo: repo, name: 'feature', target: commit),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
commit.free();
}); });
test('creates branch with force flag when name already exists', () { test('creates branch with force flag when name already exists', () {
final commit = Commit.lookup(repo: repo, oid: lastCommit);
final branch = Branch.create( final branch = Branch.create(
repo: repo, repo: repo,
name: 'feature', name: 'feature',
target: commit, target: Commit.lookup(repo: repo, oid: lastCommit),
force: true, 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); expect(branch.target, lastCommit);
for (final branch in localBranches) {
branch.free();
}
branch.free();
commit.free();
}); });
}); });
@ -324,20 +273,16 @@ void main() {
group('rename()', () { group('rename()', () {
test('renames branch', () { test('renames branch', () {
Branch.rename(repo: repo, oldName: 'feature', newName: 'renamed'); 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( expect(
() => Branch.lookup(repo: repo, name: 'feature'), () => Branch.lookup(repo: repo, name: 'feature'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
expect(branch.target, featureCommit); expect(
Branch.lookup(repo: repo, name: 'renamed').target,
for (final branch in branches) { featureCommit,
branch.free(); );
}
branch.free();
}); });
test('throws when name already exists', () { test('throws when name already exists', () {
@ -358,11 +303,8 @@ void main() {
newName: 'feature', newName: 'feature',
force: true, force: true,
); );
final branch = Branch.lookup(repo: repo, name: 'feature');
expect(branch.target, lastCommit); expect(Branch.lookup(repo: repo, name: 'feature').target, lastCommit);
branch.free();
}); });
test('throws when name is invalid', () { 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', () { test('returns string representation of Branch object', () {
final branch = Branch.lookup(repo: repo, name: 'master'); final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.toString(), contains('Branch{')); expect(branch.toString(), contains('Branch{'));
branch.free();
}); });
}); });
} }

View file

@ -72,8 +72,7 @@ void main() {
}); });
test('checkouts reference', () { test('checkouts reference', () {
final masterHead = Commit.lookup(repo: repo, oid: repo['821ed6e']); final masterTree = Commit.lookup(repo: repo, oid: repo['821ed6e']).tree;
final masterTree = masterHead.tree;
expect( expect(
masterTree.entries.any((e) => e.name == 'another_feature_file'), masterTree.entries.any((e) => e.name == 'another_feature_file'),
false, false,
@ -82,19 +81,12 @@ void main() {
Checkout.reference(repo: repo, name: 'refs/heads/feature'); Checkout.reference(repo: repo, name: 'refs/heads/feature');
final featureHead = Commit.lookup(repo: repo, oid: repo['5aecfa0']); final featureHead = Commit.lookup(repo: repo, oid: repo['5aecfa0']);
final featureTree = featureHead.tree; final featureTree = featureHead.tree;
final repoHead = repo.head;
// does not change HEAD // does not change HEAD
expect(repoHead.target, isNot(featureHead.oid)); expect(repo.head.target, isNot(featureHead.oid));
expect( expect(
featureTree.entries.any((e) => e.name == 'another_feature_file'), featureTree.entries.any((e) => e.name == 'another_feature_file'),
true, true,
); );
repoHead.free();
featureTree.free();
featureHead.free();
masterTree.free();
masterHead.free();
}); });
test( test(
@ -111,20 +103,14 @@ void main() {
}); });
test('checkouts commit', () { test('checkouts commit', () {
final index = repo.index; expect(repo.index.find('another_feature_file'), equals(false));
expect(index.find('another_feature_file'), equals(false));
final featureHead = Commit.lookup(repo: repo, oid: repo['5aecfa0']); final featureHead = Commit.lookup(repo: repo, oid: repo['5aecfa0']);
Checkout.commit(repo: repo, commit: featureHead); Checkout.commit(repo: repo, commit: featureHead);
final repoHead = repo.head;
// does not change HEAD // does not change HEAD
expect(repoHead.target, isNot(featureHead.oid)); expect(repo.head.target, isNot(featureHead.oid));
expect(index.find('another_feature_file'), equals(true)); expect(repo.index.find('another_feature_file'), equals(true));
repoHead.free();
featureHead.free();
index.free();
}); });
test('checkouts commit with provided path', () { test('checkouts commit with provided path', () {
@ -135,35 +121,27 @@ void main() {
paths: ['another_feature_file'], paths: ['another_feature_file'],
); );
final repoHead = repo.head;
// does not change HEAD // does not change HEAD
expect(repoHead.target, isNot(featureHead.oid)); expect(repo.head.target, isNot(featureHead.oid));
expect( expect(
repo.status, repo.status,
{ {
'another_feature_file': {GitStatus.indexNew} 'another_feature_file': {GitStatus.indexNew}
}, },
); );
repoHead.free();
featureHead.free();
}); });
test( test(
'throws when trying to checkout commit with invalid alternative ' 'throws when trying to checkout commit with invalid alternative '
'directory', () { 'directory', () {
final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']);
expect( expect(
() => Checkout.commit( () => Checkout.commit(
repo: repo, repo: repo,
commit: commit, commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
directory: 'not/there', directory: 'not/there',
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
commit.free();
}); });
test('checkouts with alrenative directory', () { test('checkouts with alrenative directory', () {
@ -208,8 +186,7 @@ void main() {
}); });
test('performs dry run checkout', () { test('performs dry run checkout', () {
final index = repo.index; expect(repo.index.length, 4);
expect(index.length, 4);
final file = File(p.join(repo.workdir, 'another_feature_file')); final file = File(p.join(repo.workdir, 'another_feature_file'));
expect(file.existsSync(), false); expect(file.existsSync(), false);
@ -218,10 +195,8 @@ void main() {
name: 'refs/heads/feature', name: 'refs/heads/feature',
strategy: {GitCheckout.dryRun}, strategy: {GitCheckout.dryRun},
); );
expect(index.length, 4); expect(repo.index.length, 4);
expect(file.existsSync(), false); expect(file.existsSync(), false);
index.free();
}); });
}); });
} }

View file

@ -34,18 +34,13 @@ void main() {
}); });
tearDown(() { tearDown(() {
author.free();
committer.free();
tree.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
group('Commit', () { group('Commit', () {
test('lookups commit for provided oid', () { test('lookups commit for provided oid', () {
final commit = Commit.lookup(repo: repo, oid: tip); expect(Commit.lookup(repo: repo, oid: tip), isA<Commit>());
expect(commit, isA<Commit>());
commit.free();
}); });
test('throws when trying to lookup with invalid oid', () { test('throws when trying to lookup with invalid oid', () {
@ -63,18 +58,14 @@ void main() {
test('reverts commit affecting index and workdir', () { test('reverts commit affecting index and workdir', () {
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
final index = repo.index;
final file = File(p.join(repo.workdir, 'dir', 'dir_file.txt')); 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); expect(file.existsSync(), true);
commit.revert(); commit.revert();
expect(index.find('dir/dir_file.txt'), false); expect(repo.index.find('dir/dir_file.txt'), false);
expect(file.existsSync(), false); expect(file.existsSync(), false);
index.free();
commit.free();
}); });
test('throws when trying to revert and error occurs', () { test('throws when trying to revert and error occurs', () {
@ -82,21 +73,16 @@ void main() {
}); });
test('reverts commit to provided commit', () { 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')); 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); 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(revertIndex.find('dir/dir_file.txt'), false);
expect(file.existsSync(), true); expect(file.existsSync(), true);
revertIndex.free();
index.free();
to.free();
from.free();
}); });
test('throws when trying to revert commit and error occurs', () { 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(commit2.oid), true);
expect(commit1.descendantOf(commit1.oid), false); expect(commit1.descendantOf(commit1.oid), false);
expect(commit2.descendantOf(commit1.oid), false); expect(commit2.descendantOf(commit1.oid), false);
commit1.free();
commit2.free();
}); });
test('creates commit', () { test('creates commit', () {
final parent = Commit.lookup(repo: repo, oid: tip);
final oid = Commit.create( final oid = Commit.create(
repo: repo, repo: repo,
updateRef: 'HEAD', updateRef: 'HEAD',
@ -128,7 +110,7 @@ void main() {
author: author, author: author,
committer: committer, committer: committer,
tree: tree, tree: tree,
parents: [parent], parents: [Commit.lookup(repo: repo, oid: tip)],
); );
final commit = Commit.lookup(repo: repo, oid: oid); final commit = Commit.lookup(repo: repo, oid: oid);
@ -146,13 +128,9 @@ void main() {
expect(commit.treeOid, tree.oid); expect(commit.treeOid, tree.oid);
expect(commit.parents.length, 1); expect(commit.parents.length, 1);
expect(commit.parents[0], tip); expect(commit.parents[0], tip);
commit.free();
parent.free();
}); });
test('writes commit without parents into the buffer', () { test('writes commit without parents into the buffer', () {
final parent = Commit.lookup(repo: repo, oid: tip);
final commit = Commit.createBuffer( final commit = Commit.createBuffer(
repo: repo, repo: repo,
updateRef: 'HEAD', updateRef: 'HEAD',
@ -174,12 +152,9 @@ Some description.
"""; """;
expect(commit, expected); expect(commit, expected);
parent.free();
}); });
test('writes commit into the buffer', () { test('writes commit into the buffer', () {
final parent = Commit.lookup(repo: repo, oid: tip);
final commit = Commit.createBuffer( final commit = Commit.createBuffer(
repo: repo, repo: repo,
updateRef: 'HEAD', updateRef: 'HEAD',
@ -187,7 +162,7 @@ Some description.
author: author, author: author,
committer: committer, committer: committer,
tree: tree, tree: tree,
parents: [parent], parents: [Commit.lookup(repo: repo, oid: tip)],
); );
const expected = """ const expected = """
@ -202,8 +177,6 @@ Some description.
"""; """;
expect(commit, expected); expect(commit, expected);
parent.free();
}); });
test('creates commit without parents', () { test('creates commit without parents', () {
@ -227,8 +200,6 @@ Some description.
expect(commit.time, 124); expect(commit.time, 124);
expect(commit.treeOid, tree.oid); expect(commit.treeOid, tree.oid);
expect(commit.parents.length, 0); expect(commit.parents.length, 0);
commit.free();
}); });
test('creates commit with 2 parents', () { test('creates commit with 2 parents', () {
@ -255,59 +226,44 @@ Some description.
expect(commit.time, 124); expect(commit.time, 124);
expect(commit.treeOid, tree.oid); expect(commit.treeOid, tree.oid);
expect(commit.parents.length, 2); expect(commit.parents.length, 2);
expect(commit.parents[0], tip); expect(commit.parents[0], parent1.oid);
expect(commit.parents[1], parent2.oid); expect(commit.parents[1], parent2.oid);
parent1.free();
parent2.free();
commit.free();
}); });
test('throws when trying to create commit and error occurs', () { test('throws when trying to create commit and error occurs', () {
final parent = Commit.lookup(repo: repo, oid: tip);
final nullRepo = Repository(nullptr);
expect( expect(
() => Commit.create( () => Commit.create(
repo: nullRepo, repo: Repository(nullptr),
updateRef: 'HEAD', updateRef: 'HEAD',
message: message, message: message,
author: author, author: author,
committer: committer, committer: committer,
tree: tree, tree: tree,
parents: [parent], parents: [Commit.lookup(repo: repo, oid: tip)],
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
parent.free();
}); });
test('throws when trying to write commit into a buffer and error occurs', 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( expect(
() => Commit.createBuffer( () => Commit.createBuffer(
repo: nullRepo, repo: Repository(nullptr),
updateRef: 'HEAD', updateRef: 'HEAD',
message: message, message: message,
author: author, author: author,
committer: committer, committer: committer,
tree: tree, tree: tree,
parents: [parent], parents: [Commit.lookup(repo: repo, oid: tip)],
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
parent.free();
}); });
test('amends commit with default arguments', () { test('amends commit with default arguments', () {
final oldHead = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
expect(commit.oid, oldHead.target); expect(commit.oid, repo.head.target);
final amendedOid = Commit.amend( final amendedOid = Commit.amend(
repo: repo, repo: repo,
@ -316,25 +272,18 @@ Some description.
updateRef: 'HEAD', updateRef: 'HEAD',
); );
final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid); 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.message, 'amended commit\n');
expect(amendedCommit.author, commit.author); expect(amendedCommit.author, commit.author);
expect(amendedCommit.committer, commit.committer); expect(amendedCommit.committer, commit.committer);
expect(amendedCommit.treeOid, commit.treeOid); expect(amendedCommit.treeOid, commit.treeOid);
expect(amendedCommit.parents, commit.parents); expect(amendedCommit.parents, commit.parents);
amendedCommit.free();
commit.free();
newHead.free();
oldHead.free();
}); });
test('amends commit with provided arguments', () { test('amends commit with provided arguments', () {
final oldHead = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
expect(commit.oid, oldHead.target); expect(commit.oid, repo.head.target);
final amendedOid = Commit.amend( final amendedOid = Commit.amend(
repo: repo, repo: repo,
@ -346,25 +295,18 @@ Some description.
tree: tree, tree: tree,
); );
final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid); 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.message, 'amended commit\n');
expect(amendedCommit.author, author); expect(amendedCommit.author, author);
expect(amendedCommit.committer, committer); expect(amendedCommit.committer, committer);
expect(amendedCommit.treeOid, tree.oid); expect(amendedCommit.treeOid, tree.oid);
expect(amendedCommit.parents, commit.parents); expect(amendedCommit.parents, commit.parents);
amendedCommit.free();
commit.free();
newHead.free();
oldHead.free();
}); });
test('amends commit that is not the tip of the branch', () { test('amends commit that is not the tip of the branch', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']); final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']);
expect(commit.oid, isNot(head.target)); expect(commit.oid, isNot(repo.head.target));
expect( expect(
() => Commit.amend( () => Commit.amend(
@ -375,17 +317,13 @@ Some description.
), ),
returnsNormally, returnsNormally,
); );
commit.free();
head.free();
}); });
test( test(
'throws when trying to amend commit that is not the tip of the branch ' 'throws when trying to amend commit that is not the tip of the branch '
'with HEAD provided as update reference', () { 'with HEAD provided as update reference', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']); final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']);
expect(commit.oid, isNot(head.target)); expect(commit.oid, isNot(repo.head.target));
expect( expect(
() => Commit.amend( () => Commit.amend(
@ -396,9 +334,6 @@ Some description.
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
commit.free();
head.free();
}); });
test('creates an in-memory copy of a commit', () { test('creates an in-memory copy of a commit', () {
@ -406,9 +341,6 @@ Some description.
final dupCommit = commit.duplicate(); final dupCommit = commit.duplicate();
expect(dupCommit.oid, commit.oid); expect(dupCommit.oid, commit.oid);
dupCommit.free();
commit.free();
}); });
test('returns header field', () { test('returns header field', () {
@ -417,7 +349,6 @@ Some description.
commit.headerField('parent'), commit.headerField('parent'),
'78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8', '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8',
); );
commit.free();
}); });
test('throws when header field not found', () { test('throws when header field not found', () {
@ -426,24 +357,19 @@ Some description.
() => commit.headerField('not-there'), () => commit.headerField('not-there'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
commit.free();
}); });
test('returns nth generation ancestor commit', () { test('returns nth generation ancestor commit', () {
final commit = Commit.lookup(repo: repo, oid: tip); final ancestor = Commit.lookup(repo: repo, oid: tip).nthGenAncestor(3);
final ancestor = commit.nthGenAncestor(3);
expect(ancestor.oid.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); expect(ancestor.oid.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521');
ancestor.free();
commit.free();
}); });
test('throws when trying to get nth generation ancestor and none exists', test('throws when trying to get nth generation ancestor and none exists',
() { () {
final commit = Commit.lookup(repo: repo, oid: tip); expect(
expect(() => commit.nthGenAncestor(10), throwsA(isA<LibGit2Error>())); () => Commit.lookup(repo: repo, oid: tip).nthGenAncestor(10),
commit.free(); throwsA(isA<LibGit2Error>()),
);
}); });
test('returns parent at specified position', () { test('returns parent at specified position', () {
@ -453,22 +379,23 @@ Some description.
expect(firstParent.oid.sha, 'c68ff54aabf660fcdd9a2838d401583fe31249e3'); expect(firstParent.oid.sha, 'c68ff54aabf660fcdd9a2838d401583fe31249e3');
expect(secondParent.oid.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); expect(secondParent.oid.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b');
secondParent.free();
firstParent.free();
commit.free();
}); });
test('throws when trying to get the parent at invalid position', () { test('throws when trying to get the parent at invalid position', () {
expect(
() => Commit.lookup(repo: repo, oid: tip).parent(10),
throwsA(isA<LibGit2Error>()),
);
});
test('manually releases allocated memory', () {
final commit = Commit.lookup(repo: repo, oid: tip); final commit = Commit.lookup(repo: repo, oid: tip);
expect(() => commit.parent(10), throwsA(isA<LibGit2Error>())); expect(() => commit.free(), returnsNormally);
commit.free();
}); });
test('returns string representation of Commit object', () { test('returns string representation of Commit object', () {
final commit = Commit.lookup(repo: repo, oid: tip); final commit = Commit.lookup(repo: repo, oid: tip);
expect(commit.toString(), contains('Commit{')); expect(commit.toString(), contains('Commit{'));
commit.free();
}); });
}); });
} }

View file

@ -32,7 +32,6 @@ void main() {
}); });
tearDown(() { tearDown(() {
config.free();
File(filePath).deleteSync(); File(filePath).deleteSync();
}); });
@ -45,9 +44,7 @@ void main() {
'opens the global, XDG and system configuration files ' 'opens the global, XDG and system configuration files '
'(if they are present) if no path provided', () { '(if they are present) if no path provided', () {
try { try {
final config = Config.open(); expect(Config.open(), isA<Config>());
expect(config, isA<Config>());
config.free();
} catch (e) { } catch (e) {
expect(() => Config.open(), throwsA(isA<LibGit2Error>())); expect(() => Config.open(), throwsA(isA<LibGit2Error>()));
} }
@ -59,9 +56,7 @@ void main() {
test('opens system file or throws is there is none', () { test('opens system file or throws is there is none', () {
try { try {
final config = Config.system(); expect(Config.system(), isA<Config>());
expect(config, isA<Config>());
config.free();
} catch (e) { } catch (e) {
expect(() => Config.system(), throwsA(isA<LibGit2Error>())); expect(() => Config.system(), throwsA(isA<LibGit2Error>()));
} }
@ -69,9 +64,7 @@ void main() {
test('opens global file or throws is there is none', () { test('opens global file or throws is there is none', () {
try { try {
final config = Config.global(); expect(Config.global(), isA<Config>());
expect(config, isA<Config>());
config.free();
} catch (e) { } catch (e) {
expect(() => Config.global(), throwsA(isA<LibGit2Error>())); expect(() => Config.global(), throwsA(isA<LibGit2Error>()));
} }
@ -79,18 +72,14 @@ void main() {
test('opens xdg file or throws is there is none', () { test('opens xdg file or throws is there is none', () {
try { try {
final config = Config.xdg(); expect(Config.xdg(), isA<Config>());
expect(config, isA<Config>());
config.free();
} catch (e) { } catch (e) {
expect(() => Config.xdg(), throwsA(isA<LibGit2Error>())); expect(() => Config.xdg(), throwsA(isA<LibGit2Error>()));
} }
}); });
test('returns config snapshot', () { test('returns config snapshot', () {
final snapshot = config.snapshot; expect(config.snapshot, isA<Config>());
expect(snapshot, isA<Config>());
snapshot.free();
}); });
test('returns config entries and their values', () { 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', () { test('returns string representation of ConfigEntry object', () {
final entry = config.first; final entry = config.first;
expect(entry.toString(), contains('ConfigEntry{')); expect(entry.toString(), contains('ConfigEntry{'));

View file

@ -27,8 +27,10 @@ void main() {
}); });
test('throws when trying to describe and error occurs', () { test('throws when trying to describe and error occurs', () {
final nullRepo = Repository(nullptr); expect(
expect(() => nullRepo.describe(), throwsA(isA<LibGit2Error>())); () => Repository(nullptr).describe(),
throwsA(isA<LibGit2Error>()),
);
}); });
test('describes commit', () { test('describes commit', () {
@ -41,30 +43,32 @@ void main() {
}); });
test('throws when trying to describe and no reference found', () { test('throws when trying to describe and no reference found', () {
final commit = Commit.lookup(repo: repo, oid: repo['f17d0d4']); expect(
expect(() => repo.describe(commit: commit), throwsA(isA<LibGit2Error>())); () => repo.describe(
commit.free(); commit: Commit.lookup(repo: repo, oid: repo['f17d0d4']),
),
throwsA(isA<LibGit2Error>()),
);
}); });
test('returns oid when fallback argument is provided', () { test('returns oid when fallback argument is provided', () {
final commit = Commit.lookup(repo: repo, oid: repo['f17d0d4']);
expect( expect(
repo.describe(commit: commit, showCommitOidAsFallback: true), repo.describe(
commit: Commit.lookup(repo: repo, oid: repo['f17d0d4']),
showCommitOidAsFallback: true,
),
'f17d0d4', 'f17d0d4',
); );
commit.free();
}); });
test('describes with provided strategy', () { test('describes with provided strategy', () {
final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']);
expect( expect(
repo.describe( repo.describe(
commit: commit, commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
describeStrategy: GitDescribeStrategy.all, describeStrategy: GitDescribeStrategy.all,
), ),
'heads/feature', 'heads/feature',
); );
commit.free();
}); });
test('describes with provided pattern', () { test('describes with provided pattern', () {
@ -73,7 +77,6 @@ void main() {
email: 'author@email.com', email: 'author@email.com',
time: 1234, time: 1234,
); );
final commit = Commit.lookup(repo: repo, oid: repo['fc38877']);
Tag.createAnnotated( Tag.createAnnotated(
repo: repo, repo: repo,
tagName: 'test/tag1', tagName: 'test/tag1',
@ -84,28 +87,25 @@ void main() {
); );
expect( expect(
repo.describe(commit: commit, pattern: 'test/*'), repo.describe(
commit: Commit.lookup(repo: repo, oid: repo['fc38877']),
pattern: 'test/*',
),
'test/tag1-2-gfc38877', 'test/tag1-2-gfc38877',
); );
commit.free();
signature.free();
}); });
test('describes and follows first parent only', () { test('describes and follows first parent only', () {
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
Tag.delete(repo: repo, name: 'v0.2'); Tag.delete(repo: repo, name: 'v0.2');
expect( expect(
repo.describe( repo.describe(
commit: commit, commit: Commit.lookup(repo: repo, oid: repo['821ed6e']),
onlyFollowFirstParent: true, onlyFollowFirstParent: true,
describeStrategy: GitDescribeStrategy.tags, describeStrategy: GitDescribeStrategy.tags,
), ),
'v0.1-1-g821ed6e', 'v0.1-1-g821ed6e',
); );
commit.free();
}); });
test('describes with provided abbreviated size', () { test('describes with provided abbreviated size', () {
@ -129,8 +129,6 @@ void main() {
), ),
'v0.1', 'v0.1',
); );
commit.free();
}); });
test('describes with long format', () { test('describes with long format', () {
@ -138,21 +136,13 @@ void main() {
}); });
test('describes and appends dirty suffix', () { test('describes and appends dirty suffix', () {
final index = repo.index; repo.index.clear();
index.clear();
expect(repo.describe(dirtySuffix: '-dirty'), 'v0.2-dirty'); expect(repo.describe(dirtySuffix: '-dirty'), 'v0.2-dirty');
index.free();
}); });
test('describes with max candidates tags flag set', () { test('describes with max candidates tags flag set', () {
final index = repo.index; repo.index.clear();
index.clear();
expect(repo.describe(maxCandidatesTags: 0), 'v0.2'); expect(repo.describe(maxCandidatesTags: 0), 'v0.2');
index.free();
}); });
}); });
} }

View file

@ -128,76 +128,53 @@ index e69de29..c217c63 100644
tearDown(() { tearDown(() {
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); if (Platform.isLinux || Platform.isMacOS) {
tmpDir.deleteSync(recursive: true);
}
}); });
group('Diff', () { group('Diff', () {
test('returns diff between index and workdir', () { test('returns diff between index and workdir', () {
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
expect(diff.length, 8); expect(diff.length, 8);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToWorkdir[i]); expect(diff.deltas[i].newFile.path, indexToWorkdir[i]);
} }
diff.free();
index.free();
}); });
test('returns diff between index and tree', () { test('returns diff between index and tree', () {
final index = repo.index; final diff = Diff.treeToIndex(
final head = repo.head; repo: repo,
final commit = Commit.lookup(repo: repo, oid: head.target); tree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
final tree = commit.tree; index: repo.index,
final diff = Diff.treeToIndex(repo: repo, tree: tree, index: index); );
expect(diff.length, 8); expect(diff.length, 8);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToTree[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', () { test('returns diff between index and empty tree', () {
final index = repo.index; final diff = Diff.treeToIndex(repo: repo, tree: null, 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);
expect(diff.length, 12); expect(diff.length, 12);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToIndex[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', () { test('returns diff between tree and workdir', () {
final head = repo.head; final diff = Diff.treeToWorkdir(
final commit = Commit.lookup(repo: repo, oid: head.target); repo: repo,
final tree = commit.tree; tree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
final diff = Diff.treeToWorkdir(repo: repo, tree: tree); );
expect(diff.length, 9); expect(diff.length, 9);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToWorkdir[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', 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', () { test('returns diff between tree and workdir with index', () {
final head = repo.head; final diff = Diff.treeToWorkdirWithIndex(
final commit = Commit.lookup(repo: repo, oid: head.target); repo: repo,
final tree = commit.tree; tree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
);
final diff = Diff.treeToWorkdirWithIndex(repo: repo, tree: tree);
expect(diff.length, 11); expect(diff.length, 11);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToWorkdirWithIndex[i]); expect(diff.deltas[i].newFile.path, treeToWorkdirWithIndex[i]);
} }
diff.free();
tree.free();
commit.free();
head.free();
}); });
test( test(
@ -238,68 +210,51 @@ index e69de29..c217c63 100644
}); });
test('returns diff between tree and tree', () { test('returns diff between tree and tree', () {
final head = repo.head; final diff = Diff.treeToTree(
final commit = Commit.lookup(repo: repo, oid: head.target); repo: repo,
final tree1 = commit.tree; oldTree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']); newTree: Tree.lookup(repo: repo, oid: repo['b85d53c']),
final diff = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2); );
expect(diff.length, 10); expect(diff.length, 10);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToTree[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', () { test('returns diff between tree and empty tree', () {
final head = repo.head; final diff = Diff.treeToTree(
final commit = Commit.lookup(repo: repo, oid: head.target); repo: repo,
final tree = commit.tree; oldTree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
newTree: null,
final diff = Diff.treeToTree(repo: repo, oldTree: tree, newTree: null); );
expect(diff.length, 11); expect(diff.length, 11);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToEmptyTree[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', () { test('returns diff between empty tree and tree', () {
final head = repo.head; final diff = Diff.treeToTree(
final commit = Commit.lookup(repo: repo, oid: head.target); repo: repo,
final tree = commit.tree; oldTree: null,
newTree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
final diff = Diff.treeToTree(repo: repo, oldTree: null, newTree: tree); );
expect(diff.length, 11); expect(diff.length, 11);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToEmptyTree[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', test('throws when trying to diff between tree and tree and error occurs',
() { () {
final nullTree = Tree(nullptr);
expect( expect(
() => Diff.treeToTree( () => Diff.treeToTree(
repo: Repository(nullptr), repo: Repository(nullptr),
oldTree: nullTree, oldTree: Tree(nullptr),
newTree: nullTree, newTree: Tree(nullptr),
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
@ -313,60 +268,44 @@ index e69de29..c217c63 100644
}); });
test('returns diff between index and index', () { test('returns diff between index and index', () {
final index = repo.index;
final emptyIndex = Index.newInMemory();
final diff = Diff.indexToIndex( final diff = Diff.indexToIndex(
repo: repo, repo: repo,
oldIndex: index, oldIndex: repo.index,
newIndex: emptyIndex, newIndex: Index.newInMemory(),
); );
expect(diff.length, 12); expect(diff.length, 12);
for (var i = 0; i < diff.deltas.length; i++) { for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToIndex[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', test('throws when trying to diff between index and index and error occurs',
() { () {
final index = repo.index;
expect( expect(
() => Diff.indexToIndex( () => Diff.indexToIndex(
repo: repo, repo: repo,
oldIndex: index, oldIndex: repo.index,
newIndex: Index(nullptr), newIndex: Index(nullptr),
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
index.free();
}); });
test('merges diffs', () { test('merges diffs', () {
final head = repo.head; final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final commit = Commit.lookup(repo: repo, oid: head.target); final diff1 = Diff.treeToTree(
final tree1 = commit.tree; repo: repo,
final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']); oldTree: commit.tree,
final diff1 = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2); newTree: Tree.lookup(repo: repo, oid: repo['b85d53c']),
final diff2 = Diff.treeToWorkdir(repo: repo, tree: tree1); );
final diff2 = Diff.treeToWorkdir(repo: repo, tree: commit.tree);
expect(diff1.length, 10); expect(diff1.length, 10);
expect(diff2.length, 9); expect(diff2.length, 9);
diff1.merge(diff2); diff1.merge(diff2);
expect(diff1.length, 11); expect(diff1.length, 11);
commit.free();
head.free();
tree1.free();
tree2.free();
diff1.free();
diff2.free();
}); });
test('parses provided diff', () { test('parses provided diff', () {
@ -377,15 +316,11 @@ index e69de29..c217c63 100644
expect(stats.filesChanged, 1); expect(stats.filesChanged, 1);
expect(stats.insertions, 1); expect(stats.insertions, 1);
expect(diff.patchOid.sha, '699556913185bc38632ae20a49d5c18b9233335e'); expect(diff.patchOid.sha, '699556913185bc38632ae20a49d5c18b9233335e');
stats.free();
diff.free();
}); });
group('apply', () { group('apply', () {
test('checks if diff can be applied to repository', () { test('checks if diff can be applied to repository', () {
final index = repo.index; final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff1 = Diff.indexToWorkdir(repo: repo, index: index);
expect( expect(
diff1.applies(repo: repo, location: GitApplyLocation.both), diff1.applies(repo: repo, location: GitApplyLocation.both),
false, false,
@ -397,16 +332,11 @@ index e69de29..c217c63 100644
diff2.applies(repo: repo, location: GitApplyLocation.both), diff2.applies(repo: repo, location: GitApplyLocation.both),
true, true,
); );
diff1.free();
diff2.free();
index.free();
}); });
test('checks if hunk with provided index can be applied to repository', test('checks if hunk with provided index can be applied to repository',
() { () {
final index = repo.index; final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff1 = Diff.indexToWorkdir(repo: repo, index: index);
expect( expect(
diff1.applies(repo: repo, location: GitApplyLocation.both), diff1.applies(repo: repo, location: GitApplyLocation.both),
false, false,
@ -423,23 +353,16 @@ index e69de29..c217c63 100644
), ),
true, true,
); );
diff1.free();
diff2.free();
index.free();
}); });
test('applies diff to repository', () { test('applies diff to repository', () {
final diff = Diff.parse(patchText);
final file = File(p.join(tmpDir.path, 'subdir', 'modified_file')); final file = File(p.join(tmpDir.path, 'subdir', 'modified_file'));
Checkout.head(repo: repo, strategy: {GitCheckout.force}); Checkout.head(repo: repo, strategy: {GitCheckout.force});
expect(file.readAsStringSync(), ''); expect(file.readAsStringSync(), '');
diff.apply(repo: repo); Diff.parse(patchText).apply(repo: repo);
expect(file.readAsStringSync(), 'Modified content\n'); expect(file.readAsStringSync(), 'Modified content\n');
diff.free();
}); });
test('throws when trying to apply diff and error occurs', () { test('throws when trying to apply diff and error occurs', () {
@ -455,9 +378,6 @@ index e69de29..c217c63 100644
expect(diff.length, 1); expect(diff.length, 1);
expect(patch.text, patchText); expect(patch.text, patchText);
patch.free();
diff.free();
}); });
test('applies hunk with provided index to repository', () { test('applies hunk with provided index to repository', () {
@ -470,112 +390,86 @@ index e69de29..c217c63 100644
diff.apply(repo: repo, hunkIndex: hunk.index); diff.apply(repo: repo, hunkIndex: hunk.index);
expect(file.readAsStringSync(), 'Modified content\n'); expect(file.readAsStringSync(), 'Modified content\n');
diff.free();
}); });
test('does not apply hunk with non existing index', () { test('does not apply hunk with non existing index', () {
final diff = Diff.parse(patchText);
final file = File(p.join(tmpDir.path, 'subdir', 'modified_file')); final file = File(p.join(tmpDir.path, 'subdir', 'modified_file'));
Checkout.head(repo: repo, strategy: {GitCheckout.force}); Checkout.head(repo: repo, strategy: {GitCheckout.force});
expect(file.readAsStringSync(), ''); expect(file.readAsStringSync(), '');
diff.apply(repo: repo, hunkIndex: 10); Diff.parse(patchText).apply(repo: repo, hunkIndex: 10);
expect(file.readAsStringSync(), ''); expect(file.readAsStringSync(), '');
diff.free();
}); });
test('applies diff to tree', () { test('applies diff to tree', () {
final diff = Diff.parse(patchText);
Checkout.head(repo: repo, strategy: {GitCheckout.force}); 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; expect(
final oldBlob = Blob.lookup( Blob.lookup(
repo: repo, repo: repo,
oid: oldIndex['subdir/modified_file'].oid, oid: repo.index['subdir/modified_file'].oid,
).content,
'',
); );
expect(oldBlob.content, '');
final newIndex = diff.applyToTree(repo: repo, tree: tree); final newIndex = Diff.parse(patchText).applyToTree(
final newBlob = Blob.lookup(
repo: repo, 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', () { 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 diff = Diff.parse(patchText);
final hunk = diff.patches.first.hunks.first; 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( final newIndex = diff.applyToTree(
repo: repo, repo: repo,
tree: tree, tree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
hunkIndex: hunk.index, hunkIndex: hunk.index,
); );
final newBlob = Blob.lookup( expect(
repo: repo, Blob.lookup(
oid: newIndex['subdir/modified_file'].oid, 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', () { test('throws when trying to apply diff to tree and error occurs', () {
final diff = Diff.parse(patchText);
expect( expect(
() => diff.applyToTree(repo: repo, tree: Tree(nullptr)), () => Diff.parse(patchText).applyToTree(
repo: repo,
tree: Tree(nullptr),
),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
}); });
test('finds similar entries', () { 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( final diff = Diff.treeToTree(
repo: repo, repo: repo,
oldTree: oldTree, oldTree: Commit.lookup(repo: repo, oid: repo.head.target).tree,
newTree: newTree, newTree: Tree.lookup(repo: repo, oid: repo.index.writeTree()),
); );
expect( expect(
diff.deltas.singleWhere((e) => e.newFile.path == 'staged_new').status, 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, diff.deltas.singleWhere((e) => e.newFile.path == 'staged_new').status,
GitDelta.renamed, 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', () { test('throws when trying to find similar entries and error occurs', () {
final nullDiff = Diff(nullptr); expect(() => Diff(nullptr).findSimilar(), throwsA(isA<LibGit2Error>()));
expect(() => nullDiff.findSimilar(), throwsA(isA<LibGit2Error>()));
}); });
test('throws when trying to get patch Oid and error occurs', () { test('throws when trying to get patch Oid and error occurs', () {
final nullDiff = Diff(nullptr); expect(() => Diff(nullptr).patchOid, throwsA(isA<LibGit2Error>()));
expect(() => nullDiff.patchOid, throwsA(isA<LibGit2Error>()));
}); });
test('returns deltas', () { test('returns deltas', () {
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
expect(diff.deltas[0].numberOfFiles, 1); expect(diff.deltas[0].numberOfFiles, 1);
expect(diff.deltas[0].status, GitDelta.deleted); expect(diff.deltas[0].status, GitDelta.deleted);
@ -635,110 +519,59 @@ index e69de29..c217c63 100644
); );
expect(diff.deltas[0].oldFile.mode, GitFilemode.blob); expect(diff.deltas[0].oldFile.mode, GitFilemode.blob);
diff.free();
index.free();
}); });
test('throws when trying to get delta with invalid index', () { test('throws when trying to get delta with invalid index', () {
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
expect(() => diff.deltas[-1], throwsA(isA<RangeError>())); expect(() => diff.deltas[-1], throwsA(isA<RangeError>()));
diff.free();
index.free();
}); });
test('returns patches', () { test('returns patches', () {
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final patches = diff.patches; final patches = diff.patches;
expect(patches.length, 8); expect(patches.length, 8);
expect(patches.first.delta.status, GitDelta.deleted); expect(patches.first.delta.status, GitDelta.deleted);
for (final p in patches) {
p.free();
}
diff.free();
index.free();
}); });
test('returns stats', () { test('returns stats', () {
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final stats = diff.stats; final stats = diff.stats;
expect(stats.insertions, 4); expect(stats.insertions, 4);
expect(stats.deletions, 2); expect(stats.deletions, 2);
expect(stats.filesChanged, 8); expect(stats.filesChanged, 8);
expect(stats.print(format: {GitDiffStats.full}, width: 80), statsPrint); 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', () { test('throws when trying to get stats and error occurs', () {
final nullDiff = Diff(nullptr); expect(() => Diff(nullptr).stats, throwsA(isA<LibGit2Error>()));
expect(() => nullDiff.stats, throwsA(isA<LibGit2Error>()));
}); });
test('throws when trying to print stats and error occurs', () { test('throws when trying to print stats and error occurs', () {
final nullStats = DiffStats(nullptr);
expect( expect(
() => nullStats.print(format: {GitDiffStats.full}, width: 80), () => DiffStats(nullptr).print(format: {GitDiffStats.full}, width: 80),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
test('returns patch diff string', () { test('returns patch diff string', () {
final diff = Diff.parse(patchText); expect(Diff.parse(patchText).patch, patchText);
expect(diff.patch, patchText);
diff.free();
}); });
test('returns hunks in a patch', () { test('manually releases allocated memory', () {
final diff = Diff.parse(patchText); final diff = Diff.parse(patchText);
final patch = Patch.fromDiff(diff: diff, index: 0); expect(() => diff.free(), returnsNormally);
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();
}); });
test('returns lines in a hunk', () { test('manually releases allocated memory for DiffStats object', () {
final diff = Diff.parse(patchText); final stats = Diff.parse(patchText).stats;
final patch = Patch.fromDiff(diff: diff, index: 0); expect(() => stats.free(), returnsNormally);
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( test(
'returns string representation of Diff, DiffDelta, DiffFile, ' 'returns string representation of Diff, DiffDelta, DiffFile '
'DiffHunk, DiffLine and DiffStats objects', () { ' and DiffStats objects', () {
final diff = Diff.parse(patchText); final diff = Diff.parse(patchText);
final patch = Patch.fromDiff(diff: diff, index: 0); final patch = Patch.fromDiff(diff: diff, index: 0);
final stats = diff.stats; final stats = diff.stats;
@ -746,13 +579,7 @@ index e69de29..c217c63 100644
expect(diff.toString(), contains('Diff{')); expect(diff.toString(), contains('Diff{'));
expect(patch.delta.toString(), contains('DiffDelta{')); expect(patch.delta.toString(), contains('DiffDelta{'));
expect(patch.delta.oldFile.toString(), contains('DiffFile{')); 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{')); expect(stats.toString(), contains('DiffStats{'));
stats.free();
patch.free();
diff.free();
}); });
}); });
} }

View file

@ -20,7 +20,6 @@ void main() {
}); });
tearDown(() { tearDown(() {
index.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
@ -152,30 +151,25 @@ void main() {
final bare = Repository.open( final bare = Repository.open(
p.join('test', 'assets', 'empty_bare.git'), p.join('test', 'assets', 'empty_bare.git'),
); );
final bareIndex = bare.index; expect(() => bare.index.add('config'), throwsA(isA<LibGit2Error>()));
expect(() => bareIndex.add('config'), throwsA(isA<LibGit2Error>()));
bareIndex.free();
bare.free(); bare.free();
}); });
}); });
group('addFromBuffer()', () { group('addFromBuffer()', () {
test('updates index entry from a buffer', () { test('updates index entry from a buffer', () {
final entry = index['file'];
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
index.addFromBuffer(entry: entry, buffer: 'updated'); index.addFromBuffer(entry: index['file'], buffer: 'updated');
expect(repo.status, { expect(repo.status, {
'file': {GitStatus.indexModified, GitStatus.wtModified} 'file': {GitStatus.indexModified, GitStatus.wtModified}
}); });
}); });
test('throws when trying to update entry and error occurs', () { test('throws when trying to update entry and error occurs', () {
final nullEntry = IndexEntry(nullptr);
expect( expect(
() => index.addFromBuffer(entry: nullEntry, buffer: ''), () => index.addFromBuffer(entry: IndexEntry(nullptr), buffer: ''),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
@ -208,11 +202,8 @@ void main() {
final bare = Repository.open( final bare = Repository.open(
p.join('test', 'assets', 'empty_bare.git'), p.join('test', 'assets', 'empty_bare.git'),
); );
final bareIndex = bare.index; expect(() => bare.index.addAll([]), throwsA(isA<LibGit2Error>()));
expect(() => bareIndex.addAll([]), throwsA(isA<LibGit2Error>()));
bareIndex.free();
bare.free(); bare.free();
}); });
}); });
@ -234,14 +225,11 @@ void main() {
final bare = Repository.open( final bare = Repository.open(
p.join('test', 'assets', 'empty_bare.git'), p.join('test', 'assets', 'empty_bare.git'),
); );
final bareIndex = bare.index;
expect( expect(
() => bareIndex.updateAll(['not_there']), () => bare.index.updateAll(['not_there']),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
bareIndex.free();
bare.free(); bare.free();
}); });
}); });
@ -293,9 +281,8 @@ void main() {
}); });
test('reads tree with provided SHA hex', () { test('reads tree with provided SHA hex', () {
final tree = Tree.lookup(repo: repo, oid: repo['df2b8fc']);
expect(index.length, 4); expect(index.length, 4);
index.readTree(tree); index.readTree(Tree.lookup(repo: repo, oid: repo['df2b8fc']));
expect(index.length, 1); expect(index.length, 1);
@ -305,8 +292,7 @@ void main() {
}); });
test('writes tree', () { test('writes tree', () {
final oid = index.writeTree(); expect(index.writeTree().sha, 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f');
expect(oid.sha, 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f');
}); });
test('throws when trying to write tree to invalid repository', () { test('throws when trying to write tree to invalid repository', () {
@ -320,20 +306,16 @@ void main() {
final tmpDir = setupRepo(Directory(mergeRepoPath)); final tmpDir = setupRepo(Directory(mergeRepoPath));
final repo = Repository.open(tmpDir.path); final repo = Repository.open(tmpDir.path);
final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch'); Merge.commit(
final index = repo.index;
final commit = AnnotatedCommit.lookup(
repo: repo, 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<LibGit2Error>()));
expect(() => index.writeTree(), throwsA(isA<LibGit2Error>()));
commit.free();
conflictBranch.free();
index.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
@ -356,30 +338,24 @@ void main() {
final repoDir = setupRepo(Directory(mergeRepoPath)); final repoDir = setupRepo(Directory(mergeRepoPath));
final conflictRepo = Repository.open(repoDir.path); 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'); Checkout.reference(repo: conflictRepo, name: 'refs/heads/feature');
conflictRepo.setHead('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 = conflictRepo.index.conflicts['feature_file']!;
final conflictedFile = index.conflicts['feature_file']!;
expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.ancestor?.path, 'feature_file');
expect(conflictedFile.our?.path, 'feature_file'); expect(conflictedFile.our?.path, 'feature_file');
expect(conflictedFile.their?.path, 'feature_file'); expect(conflictedFile.their?.path, 'feature_file');
expect(conflictedFile.toString(), contains('ConflictEntry{')); expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free(); conflictRepo.free();
repoDir.deleteSync(recursive: true); repoDir.deleteSync(recursive: true);
}); });
@ -388,27 +364,23 @@ void main() {
final repoDir = setupRepo(Directory(mergeRepoPath)); final repoDir = setupRepo(Directory(mergeRepoPath));
final conflictRepo = Repository.open(repoDir.path); final conflictRepo = Repository.open(repoDir.path);
final conflictBranch = Branch.lookup( Merge.commit(
repo: conflictRepo, repo: conflictRepo,
name: 'conflict-branch', commit: AnnotatedCommit.lookup(
); repo: conflictRepo,
final commit = AnnotatedCommit.lookup( oid: Branch.lookup(
repo: conflictRepo, repo: conflictRepo,
oid: conflictBranch.target, name: 'conflict-branch',
).target,
),
); );
Merge.commit(repo: conflictRepo, commit: commit); final conflictedFile = conflictRepo.index.conflicts['conflict_file']!;
final index = conflictRepo.index;
final conflictedFile = index.conflicts['conflict_file']!;
expect(conflictedFile.ancestor?.path, null); expect(conflictedFile.ancestor?.path, null);
expect(conflictedFile.our?.path, 'conflict_file'); expect(conflictedFile.our?.path, 'conflict_file');
expect(conflictedFile.their?.path, 'conflict_file'); expect(conflictedFile.their?.path, 'conflict_file');
expect(conflictedFile.toString(), contains('ConflictEntry{')); expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free(); conflictRepo.free();
repoDir.deleteSync(recursive: true); repoDir.deleteSync(recursive: true);
}); });
@ -417,30 +389,24 @@ void main() {
final repoDir = setupRepo(Directory(mergeRepoPath)); final repoDir = setupRepo(Directory(mergeRepoPath));
final conflictRepo = Repository.open(repoDir.path); 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'); Checkout.reference(repo: conflictRepo, name: 'refs/heads/our-conflict');
conflictRepo.setHead('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 = conflictRepo.index.conflicts['feature_file']!;
final conflictedFile = index.conflicts['feature_file']!;
expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.ancestor?.path, 'feature_file');
expect(conflictedFile.our?.path, null); expect(conflictedFile.our?.path, null);
expect(conflictedFile.their?.path, 'feature_file'); expect(conflictedFile.their?.path, 'feature_file');
expect(conflictedFile.toString(), contains('ConflictEntry{')); expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free(); conflictRepo.free();
repoDir.deleteSync(recursive: true); repoDir.deleteSync(recursive: true);
}); });
@ -449,30 +415,23 @@ void main() {
final repoDir = setupRepo(Directory(mergeRepoPath)); final repoDir = setupRepo(Directory(mergeRepoPath));
final conflictRepo = Repository.open(repoDir.path); 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'); Checkout.reference(repo: conflictRepo, name: 'refs/heads/feature');
conflictRepo.setHead('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 = conflictRepo.index.conflicts['feature_file']!;
final conflictedFile = index.conflicts['feature_file']!;
expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.ancestor?.path, 'feature_file');
expect(conflictedFile.our?.path, 'feature_file'); expect(conflictedFile.our?.path, 'feature_file');
expect(conflictedFile.their?.path, null); expect(conflictedFile.their?.path, null);
expect(conflictedFile.toString(), contains('ConflictEntry{')); expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free(); conflictRepo.free();
repoDir.deleteSync(recursive: true); repoDir.deleteSync(recursive: true);
}); });
@ -481,17 +440,19 @@ void main() {
final repoDir = setupRepo(Directory(mergeRepoPath)); final repoDir = setupRepo(Directory(mergeRepoPath));
final conflictRepo = Repository.open(repoDir.path); 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; 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.hasConflicts, true);
expect(index['.gitignore'].isConflict, false); expect(index['.gitignore'].isConflict, false);
expect(index.conflicts['conflict_file']!.our!.isConflict, true); expect(index.conflicts['conflict_file']!.our!.isConflict, true);
@ -499,13 +460,11 @@ void main() {
final conflictedFile = index.conflicts['conflict_file']!; final conflictedFile = index.conflicts['conflict_file']!;
conflictedFile.remove(); conflictedFile.remove();
expect(index.hasConflicts, false); expect(index.hasConflicts, false);
expect(index.conflicts, isEmpty); expect(index.conflicts, isEmpty);
expect(index.conflicts['conflict_file'], null); expect(index.conflicts['conflict_file'], null);
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free(); conflictRepo.free();
repoDir.deleteSync(recursive: true); repoDir.deleteSync(recursive: true);
}); });
@ -522,27 +481,27 @@ void main() {
final repoDir = setupRepo(Directory(mergeRepoPath)); final repoDir = setupRepo(Directory(mergeRepoPath));
final conflictRepo = Repository.open(repoDir.path); 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; 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.hasConflicts, true);
expect(index.conflicts.length, 1); expect(index.conflicts.length, 1);
index.cleanupConflict(); index.cleanupConflict();
expect(index.hasConflicts, false); expect(index.hasConflicts, false);
expect(index.conflicts, isEmpty); expect(index.conflicts, isEmpty);
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free(); conflictRepo.free();
repoDir.deleteSync(recursive: true); 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', () { test('returns string representation of Index and IndexEntry objects', () {
final index = repo.index; final index = repo.index;
expect(index.toString(), contains('Index{')); expect(index.toString(), contains('Index{'));
expect(index['file'].toString(), contains('IndexEntry{')); expect(index['file'].toString(), contains('IndexEntry{'));
index.free();
}); });
}); });
} }

View file

@ -142,10 +142,7 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
group('Mailmap', () { group('Mailmap', () {
test('initializes empty mailmap object', () { test('initializes empty mailmap object', () {
final empty = Mailmap.empty(); expect(Mailmap.empty(), isA<Mailmap>());
expect(empty, isA<Mailmap>());
empty.free();
}); });
test('initializes from provided buffer', () { test('initializes from provided buffer', () {
@ -158,8 +155,6 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
[entry['realName'], entry['realEmail']], [entry['realName'], entry['realEmail']],
); );
} }
mailmap.free();
}); });
test('initializes from repository', () { test('initializes from repository', () {
@ -172,8 +167,6 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
[entry['realName'], entry['realEmail']], [entry['realName'], entry['realEmail']],
); );
} }
mailmap.free();
}); });
test('throws when initializing from repository and error occurs', () { test('throws when initializing from repository and error occurs', () {
@ -192,8 +185,6 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
[entry['name'], entry['email']], [entry['name'], entry['email']],
); );
} }
mailmap.free();
}); });
test('adds entries and resolves them', () { test('adds entries and resolves them', () {
@ -214,21 +205,15 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
[entry['realName'], entry['realEmail']], [entry['realName'], entry['realEmail']],
); );
} }
mailmap.free();
}); });
test('throws when trying to add entry with empty replace email', () { test('throws when trying to add entry with empty replace email', () {
final mailmap = Mailmap.empty();
expect( expect(
() => mailmap.addEntry( () => Mailmap.empty().addEntry(
replaceEmail: ' ', replaceEmail: ' ',
), ),
throwsA(isA<ArgumentError>()), throwsA(isA<ArgumentError>()),
); );
mailmap.free();
}); });
test('resolves signature', () { test('resolves signature', () {
@ -249,8 +234,10 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
); );
expect(mailmap.resolveSignature(signature), realSignature); expect(mailmap.resolveSignature(signature), realSignature);
});
mailmap.free(); test('manually releases allocated memory', () {
expect(() => Mailmap.empty().free(), returnsNormally);
}); });
}); });
} }

View file

@ -26,11 +26,13 @@ void main() {
group('Merge', () { group('Merge', () {
group('analysis', () { group('analysis', () {
test('is up to date when no reference is provided', () { test('is up to date when no reference is provided', () {
final result = Merge.analysis(repo: repo, theirHead: repo['c68ff54']); expect(
expect(result, [ Merge.analysis(repo: repo, theirHead: repo['c68ff54']),
{GitMergeAnalysis.upToDate}, [
GitMergePreference.none, {GitMergeAnalysis.upToDate},
]); GitMergePreference.none,
],
);
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
}); });
@ -45,11 +47,10 @@ void main() {
}); });
test('is fast forward', () { test('is fast forward', () {
final ffCommit = Commit.lookup(repo: repo, oid: repo['f17d0d4']);
final ffBranch = Branch.create( final ffBranch = Branch.create(
repo: repo, repo: repo,
name: 'ff-branch', name: 'ff-branch',
target: ffCommit, target: Commit.lookup(repo: repo, oid: repo['f17d0d4']),
); );
final result = Merge.analysis( final result = Merge.analysis(
@ -62,9 +63,6 @@ void main() {
{GitMergeAnalysis.fastForward, GitMergeAnalysis.normal}, {GitMergeAnalysis.fastForward, GitMergeAnalysis.normal},
); );
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
ffBranch.free();
ffCommit.free();
}); });
test('is not fast forward and there is no conflicts', () { test('is not fast forward and there is no conflicts', () {
@ -76,10 +74,6 @@ void main() {
test('writes conflicts to index', () { test('writes conflicts to index', () {
final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch'); final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch');
final commit = AnnotatedCommit.lookup(
repo: repo,
oid: conflictBranch.target,
);
final index = repo.index; final index = repo.index;
final result = Merge.analysis( final result = Merge.analysis(
@ -88,7 +82,10 @@ void main() {
); );
expect(result[0], {GitMergeAnalysis.normal}); 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.hasConflicts, true);
expect(index.conflicts.length, 1); expect(index.conflicts.length, 1);
expect(repo.state, GitRepositoryState.merge); expect(repo.state, GitRepositoryState.merge);
@ -114,10 +111,6 @@ void main() {
'conflict_file': {GitStatus.indexModified} 'conflict_file': {GitStatus.indexModified}
}, },
); );
index.free();
commit.free();
conflictBranch.free();
}); });
group('merge file from index', () { group('merge file from index', () {
@ -129,19 +122,16 @@ master conflict edit
conflict branch edit conflict branch edit
>>>>>>> conflict_file >>>>>>> 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( final diff = Merge.fileFromIndex(
repo: repo, repo: repo,
ancestor: null, ancestor: null,
@ -150,10 +140,6 @@ conflict branch edit
); );
expect(diff, diffExpected); expect(diff, diffExpected);
index.free();
commit.free();
conflictBranch.free();
}); });
test('merges with ancestor', () { test('merges with ancestor', () {
@ -164,21 +150,19 @@ Feature edit on feature branch
Another feature edit Another feature edit
>>>>>>> feature_file >>>>>>> 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'); Checkout.reference(repo: repo, name: 'refs/heads/feature');
repo.setHead('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( final diff = Merge.fileFromIndex(
repo: repo, repo: repo,
ancestor: conflictedFile.ancestor, ancestor: conflictedFile.ancestor,
@ -187,10 +171,6 @@ Another feature edit
); );
expect(diff, diffExpected); expect(diff, diffExpected);
index.free();
commit.free();
conflictBranch.free();
}); });
test('merges with provided merge flags and file flags', () { test('merges with provided merge flags and file flags', () {
@ -201,24 +181,18 @@ master conflict edit
conflict branch edit conflict branch edit
>>>>>>> conflict_file >>>>>>> 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( Merge.commit(
repo: repo, repo: repo,
commit: commit, commit: AnnotatedCommit.lookup(
repo: repo,
oid: Branch.lookup(repo: repo, name: 'conflict-branch').target,
),
mergeFlags: {GitMergeFlag.noRecursive}, mergeFlags: {GitMergeFlag.noRecursive},
fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL}, fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL},
); );
final conflictedFile = index.conflicts['conflict_file']!; final conflictedFile = repo.index.conflicts['conflict_file']!;
final diff = Merge.fileFromIndex( final diff = Merge.fileFromIndex(
repo: repo, repo: repo,
ancestor: null, ancestor: null,
@ -227,34 +201,23 @@ conflict branch edit
); );
expect(diff, diffExpected); expect(diff, diffExpected);
index.free();
commit.free();
conflictBranch.free();
}); });
test('merges with provided merge favor', () { test('merges with provided merge favor', () {
final conflictBranch = Branch.lookup( Merge.commit(
repo: repo, 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(repo.index.conflicts, isEmpty);
expect(index.conflicts, isEmpty);
expect( expect(
File(p.join(repo.workdir, 'conflict_file')).readAsStringSync(), File(p.join(repo.workdir, 'conflict_file')).readAsStringSync(),
'master conflict edit\n', 'master conflict edit\n',
); );
index.free();
commit.free();
conflictBranch.free();
}); });
test('throws when error occurs', () { test('throws when error occurs', () {
@ -328,59 +291,40 @@ theirs content
group('merge commits', () { group('merge commits', () {
test('merges with default values', () { test('merges with default values', () {
final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); 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( final mergeIndex = Merge.commits(
repo: repo, repo: repo,
ourCommit: ourCommit, ourCommit: Commit.lookup(repo: repo, oid: repo['1490545']),
theirCommit: theirCommit, theirCommit: theirCommit,
); );
expect(mergeIndex.conflicts, isEmpty); expect(mergeIndex.conflicts, isEmpty);
final mergeCommitsTree = mergeIndex.writeTree(repo); final mergeCommitsTree = mergeIndex.writeTree(repo);
Merge.commit(repo: repo, commit: theirCommitAnnotated); Merge.commit(
final index = repo.index; repo: repo,
expect(index.conflicts, isEmpty); commit: AnnotatedCommit.lookup(repo: repo, oid: theirCommit.oid),
final mergeTree = index.writeTree(); );
expect(repo.index.conflicts, isEmpty);
final mergeTree = repo.index.writeTree();
expect(mergeCommitsTree == mergeTree, true); expect(mergeCommitsTree == mergeTree, true);
index.free();
mergeIndex.free();
ourCommit.free();
theirCommitAnnotated.free();
theirCommit.free();
}); });
test('merges with provided favor', () { 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( final mergeIndex = Merge.commits(
repo: repo, repo: repo,
ourCommit: ourCommit, ourCommit: Commit.lookup(repo: repo, oid: repo['1490545']),
theirCommit: theirCommit, theirCommit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
favor: GitMergeFileFavor.ours, favor: GitMergeFileFavor.ours,
); );
expect(mergeIndex.conflicts, isEmpty); expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ourCommit.free();
theirCommit.free();
}); });
test('merges with provided merge and file flags', () { 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( final mergeIndex = Merge.commits(
repo: repo, repo: repo,
ourCommit: ourCommit, ourCommit: Commit.lookup(repo: repo, oid: repo['1490545']),
theirCommit: theirCommit, theirCommit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
mergeFlags: { mergeFlags: {
GitMergeFlag.findRenames, GitMergeFlag.findRenames,
GitMergeFlag.noRecursive, GitMergeFlag.noRecursive,
@ -392,10 +336,6 @@ theirs content
}, },
); );
expect(mergeIndex.conflicts, isEmpty); expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ourCommit.free();
theirCommit.free();
}); });
test('throws when error occurs', () { test('throws when error occurs', () {
@ -411,31 +351,33 @@ theirs content
}); });
test('finds merge base for two commits', () { test('finds merge base for two commits', () {
var base = Merge.base( expect(
repo: repo, Merge.base(repo: repo, commits: [repo['1490545'], repo['5aecfa0']]).sha,
commits: [repo['1490545'], repo['5aecfa0']], 'fc38877b2552ab554752d9a77e1f48f738cca79b',
); );
expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b');
base = Merge.base( expect(
repo: repo, Merge.base(repo: repo, commits: [repo['f17d0d4'], repo['5aecfa0']]).sha,
commits: [repo['f17d0d4'], repo['5aecfa0']], 'f17d0d48eae3aa08cecf29128a35e310c97b3521',
); );
expect(base.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521');
}); });
test('finds merge base for many commits', () { test('finds merge base for many commits', () {
var base = Merge.base( expect(
repo: repo, Merge.base(
commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], repo: repo,
commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']],
).sha,
'fc38877b2552ab554752d9a77e1f48f738cca79b',
); );
expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b');
base = Merge.base( expect(
repo: repo, Merge.base(
commits: [repo['f17d0d4'], repo['5aecfa0'], repo['0e409d6']], 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', () { test('throws when trying to find merge base for invalid oid', () {
@ -457,11 +399,13 @@ theirs content
}); });
test('finds octopus merge base', () { test('finds octopus merge base', () {
final base = Merge.octopusBase( expect(
repo: repo, Merge.octopusBase(
commits: [repo['1490545'], repo['0e409d6'], repo['5aecfa0']], 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', () { test('throws when trying to find octopus merge base for invalid oid', () {
@ -477,10 +421,6 @@ theirs content
group('merge trees', () { group('merge trees', () {
test('merges with default values', () { test('merges with default values', () {
final theirCommit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); 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 ourCommit = Commit.lookup(repo: repo, oid: repo['1490545']);
final baseCommit = Commit.lookup( final baseCommit = Commit.lookup(
repo: repo, repo: repo,
@ -489,36 +429,25 @@ theirs content
commits: [ourCommit.oid, theirCommit.oid], commits: [ourCommit.oid, theirCommit.oid],
), ),
); );
final theirTree = theirCommit.tree;
final ourTree = ourCommit.tree;
final ancestorTree = baseCommit.tree;
final mergeIndex = Merge.trees( final mergeIndex = Merge.trees(
repo: repo, repo: repo,
ancestorTree: ancestorTree, ancestorTree: baseCommit.tree,
ourTree: ourTree, ourTree: ourCommit.tree,
theirTree: theirTree, theirTree: theirCommit.tree,
); );
expect(mergeIndex.conflicts, isEmpty); expect(mergeIndex.conflicts, isEmpty);
final mergeTreesTree = mergeIndex.writeTree(repo); final mergeTreesTree = mergeIndex.writeTree(repo);
repo.setHead(ourCommit.oid); repo.setHead(ourCommit.oid);
Merge.commit(repo: repo, commit: theirCommitAnnotated); Merge.commit(
final index = repo.index; repo: repo,
expect(index.conflicts, isEmpty); commit: AnnotatedCommit.lookup(repo: repo, oid: theirCommit.oid),
final mergeTree = index.writeTree(); );
expect(repo.index.conflicts, isEmpty);
final mergeTree = repo.index.writeTree();
expect(mergeTreesTree == mergeTree, true); 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', () { test('merges with provided favor', () {
@ -531,26 +460,15 @@ theirs content
commits: [ourCommit.oid, theirCommit.oid], commits: [ourCommit.oid, theirCommit.oid],
), ),
); );
final theirTree = theirCommit.tree;
final ourTree = ourCommit.tree;
final ancestorTree = baseCommit.tree;
final mergeIndex = Merge.trees( final mergeIndex = Merge.trees(
repo: repo, repo: repo,
ancestorTree: ancestorTree, ancestorTree: baseCommit.tree,
ourTree: ourTree, ourTree: ourCommit.tree,
theirTree: theirTree, theirTree: theirCommit.tree,
favor: GitMergeFileFavor.ours, favor: GitMergeFileFavor.ours,
); );
expect(mergeIndex.conflicts, isEmpty); expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ancestorTree.free();
ourTree.free();
theirTree.free();
baseCommit.free();
ourCommit.free();
theirCommit.free();
}); });
test('throws when error occurs', () { test('throws when error occurs', () {
@ -567,21 +485,17 @@ theirs content
}); });
test('cherry-picks commit', () { test('cherry-picks commit', () {
final cherry = Commit.lookup(repo: repo, oid: repo['5aecfa0']); Merge.cherryPick(
Merge.cherryPick(repo: repo, commit: cherry); repo: repo,
commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
);
expect(repo.state, GitRepositoryState.cherrypick); expect(repo.state, GitRepositoryState.cherrypick);
expect(repo.message, 'add another feature file\n'); expect(repo.message, 'add another feature file\n');
final index = repo.index; expect(repo.index.conflicts, isEmpty);
expect(index.conflicts, isEmpty);
// pretend we've done commit // pretend we've done commit
repo.removeMessage(); repo.removeMessage();
expect( expect(() => repo.message, throwsA(isA<LibGit2Error>()));
() => repo.message,
throwsA(isA<LibGit2Error>()),
);
index.free();
}); });
test('throws when error occurs', () { test('throws when error occurs', () {

View file

@ -43,7 +43,6 @@ void main() {
expect(notes[i].oid.sha, notesExpected[i]['oid']); expect(notes[i].oid.sha, notesExpected[i]['oid']);
expect(notes[i].message, notesExpected[i]['message']); expect(notes[i].message, notesExpected[i]['message']);
expect(notes[i].annotatedOid.sha, notesExpected[i]['annotatedOid']); expect(notes[i].annotatedOid.sha, notesExpected[i]['annotatedOid']);
notes[i].free();
} }
}); });
@ -53,15 +52,11 @@ void main() {
}); });
test('lookups note', () { test('lookups note', () {
final head = repo.head; final note = Note.lookup(repo: repo, annotatedOid: repo.head.target);
final note = Note.lookup(repo: repo, annotatedOid: head.target);
expect(note.oid.sha, notesExpected[1]['oid']); expect(note.oid.sha, notesExpected[1]['oid']);
expect(note.message, notesExpected[1]['message']); expect(note.message, notesExpected[1]['message']);
expect(note.annotatedOid.sha, notesExpected[1]['annotatedOid']); expect(note.annotatedOid.sha, notesExpected[1]['annotatedOid']);
note.free();
head.free();
}); });
test('creates note', () { test('creates note', () {
@ -70,23 +65,20 @@ void main() {
email: 'author@email.com', email: 'author@email.com',
time: 1234, time: 1234,
); );
final head = repo.head;
final noteOid = Note.create( final noteOid = Note.create(
repo: repo, repo: repo,
author: signature, author: signature,
committer: signature, committer: signature,
annotatedOid: head.target, annotatedOid: repo.head.target,
note: 'New note for HEAD', note: 'New note for HEAD',
force: true, force: true,
); );
final noteBlob = Blob.lookup(repo: repo, oid: noteOid);
expect(noteOid.sha, 'ffd6e2ceaf91c00ea6d29e2e897f906da720529f'); expect(noteOid.sha, 'ffd6e2ceaf91c00ea6d29e2e897f906da720529f');
expect(noteBlob.content, 'New note for HEAD'); expect(
Blob.lookup(repo: repo, oid: noteOid).content,
noteBlob.free(); 'New note for HEAD',
head.free(); );
signature.free();
}); });
test('throws when trying to create note and error occurs', () { test('throws when trying to create note and error occurs', () {
@ -108,7 +100,6 @@ void main() {
email: 'author@email.com', email: 'author@email.com',
time: 1234, time: 1234,
); );
final head = repo.head;
Note.delete( Note.delete(
repo: repo, repo: repo,
@ -118,12 +109,9 @@ void main() {
); );
expect( expect(
() => Note.lookup(repo: repo, annotatedOid: head.target), () => Note.lookup(repo: repo, annotatedOid: repo.head.target),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
head.free();
signature.free();
}); });
test('throws when trying to delete note and error occurs', () { 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', () { test('returns string representation of Note object', () {
final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']); final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']);
expect(note.toString(), contains('Note{')); expect(note.toString(), contains('Note{'));
note.free();
}); });
}); });
} }

View file

@ -26,9 +26,7 @@ void main() {
group('Odb', () { group('Odb', () {
test('successfully initializes', () { test('successfully initializes', () {
final odb = repo.odb; expect(repo.odb, isA<Odb>());
expect(odb, isA<Odb>());
odb.free();
}); });
test('throws when trying to get odb and error occurs', () { test('throws when trying to get odb and error occurs', () {
@ -36,9 +34,7 @@ void main() {
}); });
test('creates new odb with no backends', () { test('creates new odb with no backends', () {
final odb = Odb.create(); expect(Odb.create(), isA<Odb>());
expect(odb, isA<Odb>());
odb.free();
}); });
test('adds disk alternate', () { test('adds disk alternate', () {
@ -46,33 +42,22 @@ void main() {
odb.addDiskAlternate(p.join(repo.path, 'objects')); odb.addDiskAlternate(p.join(repo.path, 'objects'));
expect(odb.contains(repo[blobSha]), true); expect(odb.contains(repo[blobSha]), true);
odb.free();
}); });
test('reads object', () { test('reads object', () {
final oid = repo[blobSha]; final object = repo.odb.read(repo[blobSha]);
final odb = repo.odb;
final object = odb.read(oid);
expect(object.oid, oid); expect(object.oid, repo[blobSha]);
expect(object.type, GitObject.blob); expect(object.type, GitObject.blob);
expect(object.data, blobContent); expect(object.data, blobContent);
expect(object.size, 13); expect(object.size, 13);
object.free();
odb.free();
}); });
test('throws when trying to read object and error occurs', () { test('throws when trying to read object and error occurs', () {
final odb = repo.odb;
expect( expect(
() => odb.read(repo['0' * 40]), () => repo.odb.read(repo['0' * 40]),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
odb.free();
}); });
test("returns list of all objects oid's in database", () { test("returns list of all objects oid's in database", () {
@ -80,8 +65,6 @@ void main() {
expect(odb.objects, isNot(isEmpty)); expect(odb.objects, isNot(isEmpty));
expect(odb.objects.contains(repo[commitSha]), true); expect(odb.objects.contains(repo[commitSha]), true);
odb.free();
}); });
test('throws when trying to get list of all objects and error occurs', () { test('throws when trying to get list of all objects and error occurs', () {
@ -92,8 +75,6 @@ void main() {
() => odb.objects, () => odb.objects,
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
odb.free();
}); });
test('writes data', () { test('writes data', () {
@ -103,19 +84,13 @@ void main() {
expect(odb.contains(oid), true); expect(odb.contains(oid), true);
expect(object.data, 'testing'); expect(object.data, 'testing');
object.free();
odb.free();
}); });
test('throws when trying to write with invalid object type', () { test('throws when trying to write with invalid object type', () {
final odb = repo.odb;
expect( expect(
() => odb.write(type: GitObject.any, data: 'testing'), () => repo.odb.write(type: GitObject.any, data: 'testing'),
throwsA(isA<ArgumentError>()), throwsA(isA<ArgumentError>()),
); );
odb.free();
}); });
test('throws when trying to write alternate odb to disk', () { test('throws when trying to write alternate odb to disk', () {
@ -126,15 +101,20 @@ void main() {
() => odb.write(type: GitObject.blob, data: ''), () => odb.write(type: GitObject.blob, data: ''),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
});
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', () { test('returns string representation of OdbObject object', () {
final odb = repo.odb; final object = repo.odb.read(repo[blobSha]);
final object = odb.read(repo[blobSha]);
expect(object.toString(), contains('OdbObject{')); expect(object.toString(), contains('OdbObject{'));
odb.free();
}); });
}); });
} }

View file

@ -29,8 +29,6 @@ void main() {
expect(packbuilder, isA<PackBuilder>()); expect(packbuilder, isA<PackBuilder>());
expect(packbuilder.length, 0); expect(packbuilder.length, 0);
packbuilder.free();
}); });
test('throws when trying to initialize and error occurs', () { test('throws when trying to initialize and error occurs', () {
@ -49,20 +47,13 @@ void main() {
packbuilder.add(odb.objects[1]); packbuilder.add(odb.objects[1]);
expect(packbuilder.length, 2); expect(packbuilder.length, 2);
odb.free();
packbuilder.free();
}); });
test('throws when trying to add object and error occurs', () { test('throws when trying to add object and error occurs', () {
final packbuilder = PackBuilder(repo);
expect( expect(
() => packbuilder.add(Oid(nullptr)), () => PackBuilder(repo).add(Oid(nullptr)),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
packbuilder.free();
}); });
test('adds object recursively', () { test('adds object recursively', () {
@ -71,119 +62,86 @@ void main() {
packbuilder.addRecursively(oid); packbuilder.addRecursively(oid);
expect(packbuilder.length, 3); expect(packbuilder.length, 3);
packbuilder.free();
}); });
test('throws when trying to add object recursively and error occurs', () { test('throws when trying to add object recursively and error occurs', () {
final packbuilder = PackBuilder(repo);
expect( expect(
() => packbuilder.addRecursively(Oid(nullptr)), () => PackBuilder(repo).addRecursively(Oid(nullptr)),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
packbuilder.free();
}); });
test('adds commit', () { test('adds commit', () {
final packbuilder = PackBuilder(repo); final packbuilder = PackBuilder(repo);
final oid = repo['f17d0d4'];
packbuilder.addCommit(oid); packbuilder.addCommit(repo['f17d0d4']);
expect(packbuilder.length, 3); expect(packbuilder.length, 3);
packbuilder.free();
}); });
test('throws when trying to add commit with invalid oid', () { test('throws when trying to add commit with invalid oid', () {
final packbuilder = PackBuilder(repo);
final oid = Oid.fromSHA(repo: repo, sha: '0' * 40); final oid = Oid.fromSHA(repo: repo, sha: '0' * 40);
expect(() => packbuilder.addCommit(oid), throwsA(isA<LibGit2Error>())); expect(
() => PackBuilder(repo).addCommit(oid),
packbuilder.free(); throwsA(isA<LibGit2Error>()),
);
}); });
test('adds tree', () { test('adds tree', () {
final packbuilder = PackBuilder(repo); final packbuilder = PackBuilder(repo);
final oid = repo['df2b8fc'];
packbuilder.addTree(oid); packbuilder.addTree(repo['df2b8fc']);
expect(packbuilder.length, 2); expect(packbuilder.length, 2);
packbuilder.free();
}); });
test('throws when trying to add tree with invalid oid', () { test('throws when trying to add tree with invalid oid', () {
final packbuilder = PackBuilder(repo);
final oid = Oid.fromSHA(repo: repo, sha: '0' * 40); final oid = Oid.fromSHA(repo: repo, sha: '0' * 40);
expect(() => packbuilder.addTree(oid), throwsA(isA<LibGit2Error>())); expect(
() => PackBuilder(repo).addTree(oid),
packbuilder.free(); throwsA(isA<LibGit2Error>()),
);
}); });
test('adds objects with walker', () { test('adds objects with walker', () {
final oid = repo['f17d0d4'];
final packbuilder = PackBuilder(repo); final packbuilder = PackBuilder(repo);
final walker = RevWalk(repo); final walker = RevWalk(repo);
walker.sorting({GitSort.none}); walker.sorting({GitSort.none});
walker.push(oid); walker.push(repo['f17d0d4']);
packbuilder.addWalk(walker); packbuilder.addWalk(walker);
expect(packbuilder.length, 3); expect(packbuilder.length, 3);
walker.free();
packbuilder.free();
}); });
test('sets number of threads', () { test('sets number of threads', () {
final packbuilder = PackBuilder(repo); expect(PackBuilder(repo).setThreads(1), 1);
expect(packbuilder.setThreads(1), 1);
packbuilder.free();
}); });
test('returns name of packfile', () { test('returns name of packfile', () {
final packbuilder = PackBuilder(repo); final packbuilder = PackBuilder(repo);
final odb = repo.odb;
packbuilder.add(odb.objects[0]); packbuilder.add(repo.odb.objects[0]);
Directory(packDirPath).createSync(); Directory(packDirPath).createSync();
expect(packbuilder.name, isEmpty); expect(packbuilder.name, isEmpty);
packbuilder.write(null); packbuilder.write(null);
expect(packbuilder.name, isNotEmpty); expect(packbuilder.name, isNotEmpty);
packbuilder.free();
}); });
test('packs with default arguments', () { test('packs with default arguments', () {
final odb = repo.odb; final objectsCount = repo.odb.objects.length;
final objectsCount = odb.objects.length;
Directory(packDirPath).createSync(); Directory(packDirPath).createSync();
expect(repo.pack(), objectsCount);
final writtenCount = repo.pack();
expect(writtenCount, objectsCount);
odb.free();
}); });
test('packs into provided path with threads set', () { 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'); final testPackPath = p.join(repo.workdir, 'test-pack');
Directory(testPackPath).createSync(); Directory(testPackPath).createSync();
final writtenCount = repo.pack(path: testPackPath, threads: 1); final writtenCount = repo.pack(path: testPackPath, threads: 1);
expect(writtenCount, objectsCount); expect(writtenCount, repo.odb.objects.length);
expect(Directory(testPackPath).listSync().isNotEmpty, true); expect(Directory(testPackPath).listSync().isNotEmpty, true);
odb.free();
}); });
test('packs with provided packDelegate', () { test('packs with provided packDelegate', () {
@ -198,32 +156,26 @@ void main() {
); );
for (final commit in repo.log(oid: ref.target)) { for (final commit in repo.log(oid: ref.target)) {
packBuilder.addRecursively(commit.oid); packBuilder.addRecursively(commit.oid);
commit.free();
} }
ref.free();
branch.free();
} }
} }
final writtenCount = repo.pack(packDelegate: packDelegate); expect(repo.pack(packDelegate: packDelegate), 18);
expect(writtenCount, 18);
}); });
test('throws when trying to write pack into invalid path', () { test('throws when trying to write pack into invalid path', () {
final packbuilder = PackBuilder(repo);
expect( expect(
() => packbuilder.write('invalid/path'), () => PackBuilder(repo).write('invalid/path'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
});
packbuilder.free(); test('manually releases allocated memory', () {
expect(() => PackBuilder(repo).free(), returnsNormally);
}); });
test('returns string representation of PackBuilder object', () { test('returns string representation of PackBuilder object', () {
final packbuilder = PackBuilder(repo); expect(PackBuilder(repo).toString(), contains('PackBuilder{'));
expect(packbuilder.toString(), contains('PackBuilder{'));
packbuilder.free();
}); });
}); });
} }

View file

@ -65,8 +65,6 @@ index e69de29..0000000
expect(patch.size(), 14); expect(patch.size(), 14);
expect(patch.text, blobPatch); expect(patch.text, blobPatch);
patch.free();
}); });
test('creates from one buffer (add)', () { test('creates from one buffer (add)', () {
@ -78,8 +76,6 @@ index e69de29..0000000
); );
expect(patch.text, blobPatchAdd); expect(patch.text, blobPatchAdd);
patch.free();
}); });
test('creates from one buffer (delete)', () { test('creates from one buffer (delete)', () {
@ -91,65 +87,50 @@ index e69de29..0000000
); );
expect(patch.text, blobPatchDelete); expect(patch.text, blobPatchDelete);
patch.free();
}); });
test('creates from blobs', () { test('creates from blobs', () {
final oldBlob = Blob.lookup(repo: repo, oid: oldBlobOid);
final newBlob = Blob.lookup(repo: repo, oid: newBlobOid);
final patch = Patch.fromBlobs( final patch = Patch.fromBlobs(
oldBlob: oldBlob, oldBlob: Blob.lookup(repo: repo, oid: oldBlobOid),
newBlob: newBlob, newBlob: Blob.lookup(repo: repo, oid: newBlobOid),
oldBlobPath: path, oldBlobPath: path,
newBlobPath: path, newBlobPath: path,
); );
expect(patch.text, blobPatch); expect(patch.text, blobPatch);
patch.free();
}); });
test('creates from one blob (add)', () { test('creates from one blob (add)', () {
final newBlob = Blob.lookup(repo: repo, oid: newBlobOid);
final patch = Patch.fromBlobs( final patch = Patch.fromBlobs(
oldBlob: null, oldBlob: null,
newBlob: newBlob, newBlob: Blob.lookup(repo: repo, oid: newBlobOid),
oldBlobPath: path, oldBlobPath: path,
newBlobPath: path, newBlobPath: path,
); );
expect(patch.text, blobPatchAdd); expect(patch.text, blobPatchAdd);
patch.free();
}); });
test('creates from one blob (delete)', () { test('creates from one blob (delete)', () {
final oldBlob = Blob.lookup(repo: repo, oid: oldBlobOid);
final patch = Patch.fromBlobs( final patch = Patch.fromBlobs(
oldBlob: oldBlob, oldBlob: Blob.lookup(repo: repo, oid: oldBlobOid),
newBlob: null, newBlob: null,
oldBlobPath: path, oldBlobPath: path,
newBlobPath: path, newBlobPath: path,
); );
expect(patch.text, blobPatchDelete); expect(patch.text, blobPatchDelete);
patch.free();
}); });
test('creates from blob and buffer', () { test('creates from blob and buffer', () {
final blob = Blob.lookup(repo: repo, oid: oldBlobOid);
final patch = Patch.fromBlobAndBuffer( final patch = Patch.fromBlobAndBuffer(
blob: blob, blob: Blob.lookup(repo: repo, oid: oldBlobOid),
buffer: newBuffer, buffer: newBuffer,
blobPath: path, blobPath: path,
bufferPath: path, bufferPath: path,
); );
expect(patch.text, blobPatch); expect(patch.text, blobPatch);
patch.free();
}); });
test('creates from empty blob and buffer', () { test('creates from empty blob and buffer', () {
@ -161,8 +142,6 @@ index e69de29..0000000
); );
expect(patch.text, blobPatchAdd); expect(patch.text, blobPatchAdd);
patch.free();
}); });
test('throws when trying to create from diff and error occurs', () { test('throws when trying to create from diff and error occurs', () {
@ -176,6 +155,43 @@ index e69de29..0000000
expect(() => Patch(nullptr).text, throwsA(isA<LibGit2Error>())); expect(() => Patch(nullptr).text, throwsA(isA<LibGit2Error>()));
}); });
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', () { test('returns line counts of each type in a patch', () {
final patch = Patch.fromBuffers( final patch = Patch.fromBuffers(
oldBuffer: oldBuffer, oldBuffer: oldBuffer,
@ -189,11 +205,21 @@ index e69de29..0000000
expect(stats.insertions, equals(1)); expect(stats.insertions, equals(1));
expect(stats.deletions, equals(0)); expect(stats.deletions, equals(0));
expect(stats.toString(), contains('PatchStats{')); 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( final patch = Patch.fromBuffers(
oldBuffer: oldBuffer, oldBuffer: oldBuffer,
newBuffer: newBuffer, newBuffer: newBuffer,
@ -202,8 +228,8 @@ index e69de29..0000000
); );
expect(patch.toString(), contains('Patch{')); expect(patch.toString(), contains('Patch{'));
expect(patch.hunks[0].toString(), contains('DiffHunk{'));
patch.free(); expect(patch.hunks[0].lines[0].toString(), contains('DiffLine{'));
}); });
}); });
} }

View file

@ -33,15 +33,7 @@ void main() {
time: 1234, time: 1234,
); );
final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); 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 feature = Reference.lookup(repo: repo, name: 'refs/heads/feature');
final ontoHead = AnnotatedCommit.fromReference(
repo: repo,
reference: feature,
);
Checkout.reference(repo: repo, name: feature.name); Checkout.reference(repo: repo, name: feature.name);
repo.setHead(feature.name); repo.setHead(feature.name);
@ -49,8 +41,8 @@ void main() {
final rebase = Rebase.init( final rebase = Rebase.init(
repo: repo, repo: repo,
branch: branchHead, branch: AnnotatedCommit.fromReference(repo: repo, reference: master),
onto: ontoHead, onto: AnnotatedCommit.fromReference(repo: repo, reference: feature),
); );
expect(rebase.origHeadOid, master.target); expect(rebase.origHeadOid, master.target);
@ -76,13 +68,6 @@ void main() {
rebase.finish(); rebase.finish();
expect(repo.index['.gitignore'], isA<IndexEntry>()); expect(repo.index['.gitignore'], isA<IndexEntry>());
rebase.free();
ontoHead.free();
branchHead.free();
feature.free();
master.free();
signature.free();
}); });
test('performs rebase without branch provided', () { test('performs rebase without branch provided', () {
@ -92,11 +77,10 @@ void main() {
time: 1234, time: 1234,
); );
final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature'); final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature');
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target);
final rebase = Rebase.init( final rebase = Rebase.init(
repo: repo, repo: repo,
onto: ontoHead, onto: AnnotatedCommit.lookup(repo: repo, oid: feature.target),
); );
expect( expect(
@ -124,11 +108,6 @@ void main() {
} }
rebase.finish(); rebase.finish();
rebase.free();
ontoHead.free();
feature.free();
signature.free();
}); });
test('performs rebase with provided upstream', () { test('performs rebase with provided upstream', () {
@ -138,9 +117,7 @@ void main() {
time: 1234, time: 1234,
); );
final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); 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 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); Checkout.reference(repo: repo, name: feature.name);
repo.setHead(feature.name); repo.setHead(feature.name);
@ -148,8 +125,8 @@ void main() {
final rebase = Rebase.init( final rebase = Rebase.init(
repo: repo, repo: repo,
branch: branchHead, branch: AnnotatedCommit.lookup(repo: repo, oid: master.target),
upstream: upstream, upstream: AnnotatedCommit.lookup(repo: repo, oid: repo[shas[1]]),
); );
expect(rebase.origHeadOid, master.target); expect(rebase.origHeadOid, master.target);
@ -170,13 +147,6 @@ void main() {
rebase.finish(); rebase.finish();
expect(repo.index['conflict_file'], isA<IndexEntry>()); expect(repo.index['conflict_file'], isA<IndexEntry>());
rebase.free();
upstream.free();
branchHead.free();
feature.free();
master.free();
signature.free();
}); });
test( test(
@ -191,21 +161,21 @@ void main() {
email: 'author@email.com', email: 'author@email.com',
time: 1234, 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( final conflict = Reference.lookup(
repo: repo, repo: repo,
name: 'refs/heads/conflict-branch', name: 'refs/heads/conflict-branch',
); );
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target);
Checkout.reference(repo: repo, name: conflict.name); Checkout.reference(repo: repo, name: conflict.name);
repo.setHead(conflict.name); repo.setHead(conflict.name);
final rebase = Rebase.init( final rebase = Rebase.init(
repo: repo, repo: repo,
branch: branchHead, branch: AnnotatedCommit.lookup(
onto: ontoHead, 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); expect(rebase.operations.length, 1);
@ -216,67 +186,48 @@ void main() {
() => rebase.commit(committer: signature), () => rebase.commit(committer: signature),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
rebase.free();
ontoHead.free();
branchHead.free();
conflict.free();
master.free();
signature.free();
}); });
test('throws when trying to perfrom next rebase operation and error occurs', 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( final conflict = Reference.lookup(
repo: repo, repo: repo,
name: 'refs/heads/conflict-branch', name: 'refs/heads/conflict-branch',
); );
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target);
Checkout.reference(repo: repo, name: conflict.name); Checkout.reference(repo: repo, name: conflict.name);
repo.setHead(conflict.name); repo.setHead(conflict.name);
final rebase = Rebase.init( final rebase = Rebase.init(
repo: repo, repo: repo,
branch: branchHead, branch: AnnotatedCommit.lookup(
onto: ontoHead, 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); expect(rebase.operations.length, 1);
rebase.next(); // repo now have conflicts rebase.next(); // repo now have conflicts
expect(() => rebase.next(), throwsA(isA<LibGit2Error>())); expect(() => rebase.next(), throwsA(isA<LibGit2Error>()));
rebase.free();
ontoHead.free();
branchHead.free();
conflict.free();
master.free();
signature.free();
}); });
test('aborts rebase in progress', () { 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( final conflict = Reference.lookup(
repo: repo, repo: repo,
name: 'refs/heads/conflict-branch', name: 'refs/heads/conflict-branch',
); );
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: conflict.target);
Checkout.reference(repo: repo, name: conflict.name); Checkout.reference(repo: repo, name: conflict.name);
repo.setHead(conflict.name); repo.setHead(conflict.name);
final rebase = Rebase.init( final rebase = Rebase.init(
repo: repo, repo: repo,
branch: branchHead, branch: AnnotatedCommit.lookup(
onto: ontoHead, 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); expect(rebase.operations.length, 1);
@ -287,35 +238,36 @@ void main() {
rebase.abort(); rebase.abort();
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
expect(repo.state, GitRepositoryState.none); expect(repo.state, GitRepositoryState.none);
rebase.free();
ontoHead.free();
branchHead.free();
conflict.free();
master.free();
}); });
test('opens an existing rebase', () { 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( final rebase = Rebase.init(
repo: repo, repo: repo,
onto: ontoHead, onto: AnnotatedCommit.lookup(
repo: repo,
oid: Reference.lookup(repo: repo, name: 'refs/heads/feature').target,
),
); );
expect(rebase.operations.length, 3); expect(rebase.operations.length, 3);
final openRebase = Rebase.open(repo); final openRebase = Rebase.open(repo);
expect(openRebase.operations.length, 3); 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', () { test('throws when trying to open an existing rebase but there is none', () {
expect(() => Rebase.open(repo), throwsA(isA<LibGit2Error>())); expect(() => Rebase.open(repo), throwsA(isA<LibGit2Error>()));
}); });
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);
});
}); });
} }

View file

@ -46,25 +46,22 @@ void main() {
}); });
test('returns correct type of reference', () { test('returns correct type of reference', () {
final head = repo.head; expect(repo.head.type, ReferenceType.direct);
expect(head.type, ReferenceType.direct); expect(
head.free(); Reference.lookup(repo: repo, name: 'HEAD').type,
ReferenceType.symbolic,
final ref = Reference.lookup(repo: repo, name: 'HEAD'); );
expect(ref.type, ReferenceType.symbolic);
ref.free();
}); });
test('returns SHA hex of direct reference', () { test('returns SHA hex of direct reference', () {
final head = repo.head; expect(repo.head.target.sha, lastCommit);
expect(head.target.sha, lastCommit);
head.free();
}); });
test('returns SHA hex of symbolic reference', () { test('returns SHA hex of symbolic reference', () {
final ref = Reference.lookup(repo: repo, name: 'HEAD'); expect(
expect(ref.target.sha, lastCommit); Reference.lookup(repo: repo, name: 'HEAD').target.sha,
ref.free(); lastCommit,
);
}); });
test('throws when trying to resolve invalid reference', () { test('throws when trying to resolve invalid reference', () {
@ -75,9 +72,7 @@ void main() {
}); });
test('returns the full name', () { test('returns the full name', () {
final head = repo.head; expect(repo.head.name, 'refs/heads/master');
expect(head.name, 'refs/heads/master');
head.free();
}); });
test('returns the short name', () { test('returns the short name', () {
@ -87,25 +82,22 @@ void main() {
target: repo[lastCommit], target: repo[lastCommit],
); );
final head = repo.head; expect(repo.head.shorthand, 'master');
expect(head.shorthand, 'master');
expect(ref.shorthand, 'origin/upstream'); expect(ref.shorthand, 'origin/upstream');
head.free();
ref.free();
}); });
test('checks if reference is a local branch', () { test('checks if reference is a local branch', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/feature'); expect(
expect(ref.isBranch, true); Reference.lookup(repo: repo, name: 'refs/heads/feature').isBranch,
ref.free(); true,
);
}); });
test('checks if reference is a note', () { test('checks if reference is a note', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(
expect(ref.isNote, false); Reference.lookup(repo: repo, name: 'refs/heads/master').isNote,
ref.free(); false,
);
}); });
test('checks if reference is a remote branch', () { test('checks if reference is a remote branch', () {
@ -114,23 +106,25 @@ void main() {
name: 'refs/remotes/origin/master', name: 'refs/remotes/origin/master',
); );
expect(ref.isRemote, true); expect(ref.isRemote, true);
ref.free();
}); });
test('checks if reference is a tag', () { test('checks if reference is a tag', () {
final ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1'); expect(
expect(ref.isTag, true); Reference.lookup(repo: repo, name: 'refs/tags/v0.1').isTag,
ref.free(); true,
);
}); });
test('checks if reflog exists for the reference', () { test('checks if reflog exists for the reference', () {
var ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(
expect(ref.hasLog, true); Reference.lookup(repo: repo, name: 'refs/heads/master').hasLog,
true,
);
ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1'); expect(
expect(ref.hasLog, false); Reference.lookup(repo: repo, name: 'refs/tags/v0.1').hasLog,
false,
ref.free(); );
}); });
test('ensures updates to the reference will append to its log', () { test('ensures updates to the reference will append to its log', () {
@ -141,12 +135,8 @@ void main() {
name: 'refs/tags/tag', name: 'refs/tags/tag',
target: repo[lastCommit], target: repo[lastCommit],
); );
final reflog = ref.log;
expect(reflog.length, 1); expect(ref.log.length, 1);
reflog.free();
ref.free();
}); });
test('throws when trying to ensure there is a reflog and error occurs', () { 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(repo.references.length, 6);
expect(duplicate.equals(ref), true); expect(duplicate.equals(ref), true);
duplicate.free();
ref.free();
}); });
group('create direct', () { group('create direct', () {
test('creates with oid as target', () { test('creates with oid as target', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); Reference.create(
final refFromOid = Reference.create(
repo: repo, repo: repo,
name: 'refs/tags/from.oid', name: 'refs/tags/from.oid',
target: ref.target, target: repo.head.target,
); );
expect(repo.references, contains('refs/tags/from.oid')); expect(repo.references, contains('refs/tags/from.oid'));
refFromOid.free();
ref.free();
}); });
test('creates with log message', () { test('creates with log message', () {
@ -196,15 +179,11 @@ void main() {
logMessage: 'log message', logMessage: 'log message',
); );
final reflog = ref.log; final reflogEntry = ref.log[0];
final reflogEntry = reflog[0];
expect(reflogEntry.message, 'log message'); expect(reflogEntry.message, 'log message');
expect(reflogEntry.committer.name, 'name'); expect(reflogEntry.committer.name, 'name');
expect(reflogEntry.committer.email, 'email'); expect(reflogEntry.committer.email, 'email');
reflog.free();
ref.free();
}); });
test('throws if target is not valid', () { test('throws if target is not valid', () {
@ -239,7 +218,7 @@ void main() {
}); });
test('creates with force flag if name already exists', () { test('creates with force flag if name already exists', () {
final ref = Reference.create( Reference.create(
repo: repo, repo: repo,
name: 'refs/tags/test', name: 'refs/tags/test',
target: repo[lastCommit], target: repo[lastCommit],
@ -253,13 +232,10 @@ void main() {
); );
expect(forceRef.target.sha, lastCommit); expect(forceRef.target.sha, lastCommit);
ref.free();
forceRef.free();
}); });
test('throws if name already exists', () { test('throws if name already exists', () {
final ref = Reference.create( Reference.create(
repo: repo, repo: repo,
name: 'refs/tags/test', name: 'refs/tags/test',
target: repo[lastCommit], target: repo[lastCommit],
@ -273,8 +249,6 @@ void main() {
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
ref.free();
}); });
}); });
@ -288,12 +262,10 @@ void main() {
expect(repo.references, contains('refs/tags/symbolic')); expect(repo.references, contains('refs/tags/symbolic'));
expect(ref.type, ReferenceType.symbolic); expect(ref.type, ReferenceType.symbolic);
ref.free();
}); });
test('creates with force flag if name already exists', () { test('creates with force flag if name already exists', () {
final ref = Reference.create( Reference.create(
repo: repo, repo: repo,
name: 'refs/tags/test', name: 'refs/tags/test',
target: 'refs/heads/master', target: 'refs/heads/master',
@ -308,13 +280,10 @@ void main() {
expect(forceRef.target.sha, lastCommit); expect(forceRef.target.sha, lastCommit);
expect(forceRef.type, ReferenceType.symbolic); expect(forceRef.type, ReferenceType.symbolic);
ref.free();
forceRef.free();
}); });
test('throws if name already exists', () { test('throws if name already exists', () {
final ref = Reference.create( Reference.create(
repo: repo, repo: repo,
name: 'refs/tags/exists', name: 'refs/tags/exists',
target: 'refs/heads/master', target: 'refs/heads/master',
@ -328,8 +297,6 @@ void main() {
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
ref.free();
}); });
test('throws if name is not valid', () { test('throws if name is not valid', () {
@ -353,21 +320,16 @@ void main() {
logMessage: 'log message', logMessage: 'log message',
); );
final reflog = ref.log; final reflogEntry = ref.log[0];
final reflogEntry = reflog[0];
expect(reflogEntry.message, 'log message'); expect(reflogEntry.message, 'log message');
expect(reflogEntry.committer.name, 'name'); expect(reflogEntry.committer.name, 'name');
expect(reflogEntry.committer.email, 'email'); expect(reflogEntry.committer.email, 'email');
reflog.free();
ref.free();
}); });
}); });
test('deletes reference', () { test('deletes reference', () {
expect(repo.references, contains('refs/tags/v0.1')); expect(repo.references, contains('refs/tags/v0.1'));
Reference.delete(repo: repo, name: 'refs/tags/v0.1'); Reference.delete(repo: repo, name: 'refs/tags/v0.1');
expect(repo.references, isNot(contains('refs/tags/v0.1'))); expect(repo.references, isNot(contains('refs/tags/v0.1')));
}); });
@ -376,7 +338,6 @@ void main() {
test('with provided name', () { test('with provided name', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(ref.target.sha, lastCommit); expect(ref.target.sha, lastCommit);
ref.free();
}); });
test('throws when error occured', () { test('throws when error occured', () {
@ -389,11 +350,7 @@ void main() {
test('returns log for reference', () { test('returns log for reference', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
final reflog = ref.log; expect(ref.log.last.message, 'commit (initial): init');
expect(reflog.last.message, 'commit (initial): init');
reflog.free();
ref.free();
}); });
group('set target', () { group('set target', () {
@ -401,8 +358,6 @@ void main() {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
ref.setTarget(target: repo[newCommit]); ref.setTarget(target: repo[newCommit]);
expect(ref.target.sha, newCommit); expect(ref.target.sha, newCommit);
ref.free();
}); });
test('sets symbolic target with provided reference name', () { test('sets symbolic target with provided reference name', () {
@ -411,8 +366,6 @@ void main() {
ref.setTarget(target: 'refs/heads/feature'); ref.setTarget(target: 'refs/heads/feature');
expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
ref.free();
}); });
test('sets target with log message', () { test('sets target with log message', () {
@ -422,13 +375,10 @@ void main() {
repo.setIdentity(name: 'name', email: 'email'); repo.setIdentity(name: 'name', email: 'email');
ref.setTarget(target: 'refs/heads/feature', logMessage: 'log message'); ref.setTarget(target: 'refs/heads/feature', logMessage: 'log message');
expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
final reflog = ref.log; final logEntry = ref.log.first;
expect(reflog.first.message, 'log message'); expect(logEntry.message, 'log message');
expect(reflog.first.committer.name, 'name'); expect(logEntry.committer.name, 'name');
expect(reflog.first.committer.email, 'email'); expect(logEntry.committer.email, 'email');
reflog.free();
ref.free();
}); });
test('throws on invalid target', () { test('throws on invalid target', () {
@ -444,8 +394,6 @@ void main() {
); );
expect(() => ref.setTarget(target: 0), throwsA(isA<ArgumentError>())); expect(() => ref.setTarget(target: 0), throwsA(isA<ArgumentError>()));
ref.free();
}); });
}); });
@ -508,9 +456,6 @@ void main() {
); );
expect(repo.references, isNot(contains('refs/tags/v0.1'))); expect(repo.references, isNot(contains('refs/tags/v0.1')));
expect(repo.references.length, 5); expect(repo.references.length, 5);
ref1.free();
ref2.free();
}); });
}); });
@ -523,10 +468,6 @@ void main() {
expect(ref1.notEquals(ref2), false); expect(ref1.notEquals(ref2), false);
expect(ref1.equals(ref3), false); expect(ref1.equals(ref3), false);
expect(ref1.notEquals(ref3), true); expect(ref1.notEquals(ref3), true);
ref1.free();
ref2.free();
ref3.free();
}); });
test('peels to non-tag object when no type is provided', () { test('peels to non-tag object when no type is provided', () {
@ -535,23 +476,17 @@ void main() {
final peeled = ref.peel() as Commit; final peeled = ref.peel() as Commit;
expect(peeled.oid, commit.oid); expect(peeled.oid, commit.oid);
peeled.free();
commit.free();
ref.free();
}); });
test('peels to object of provided type', () { test('peels to object of provided type', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
final blob = Blob.lookup(repo: repo, oid: repo['9c78c21']);
final blobRef = Reference.create( final blobRef = Reference.create(
repo: repo, repo: repo,
name: 'refs/tags/blob', 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 tagRef = Reference.lookup(repo: repo, name: 'refs/tags/v0.2');
final commit = Commit.lookup(repo: repo, oid: ref.target); final commit = Commit.lookup(repo: repo, oid: ref.target);
final tree = commit.tree;
final peeledCommit = ref.peel(GitObject.commit) as Commit; final peeledCommit = ref.peel(GitObject.commit) as Commit;
final peeledTree = ref.peel(GitObject.tree) as Tree; final peeledTree = ref.peel(GitObject.tree) as Tree;
@ -559,20 +494,9 @@ void main() {
final peeledTag = tagRef.peel(GitObject.tag) as Tag; final peeledTag = tagRef.peel(GitObject.tag) as Tag;
expect(peeledCommit.oid, commit.oid); expect(peeledCommit.oid, commit.oid);
expect(peeledTree.oid, tree.oid); expect(peeledTree.oid, commit.tree.oid);
expect(peeledBlob.content, 'Feature edit\n'); expect(peeledBlob.content, 'Feature edit\n');
expect(peeledTag.name, 'v0.2'); 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', () { 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', () { test('returns string representation of Reference object', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(ref.toString(), contains('Reference{')); expect(ref.toString(), contains('Reference{'));
ref.free();
}); });
}); });
} }

View file

@ -10,19 +10,15 @@ import 'helpers/util.dart';
void main() { void main() {
late Repository repo; late Repository repo;
late RefLog reflog; late RefLog reflog;
late Reference head;
late Directory tmpDir; late Directory tmpDir;
setUp(() { setUp(() {
tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo'))); tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo')));
repo = Repository.open(tmpDir.path); repo = Repository.open(tmpDir.path);
head = repo.head; reflog = RefLog(repo.head);
reflog = RefLog(head);
}); });
tearDown(() { tearDown(() {
reflog.free();
head.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
@ -52,9 +48,9 @@ void main() {
}); });
test('deletes the reflog of provided reference', () { test('deletes the reflog of provided reference', () {
expect(head.hasLog, true); expect(repo.head.hasLog, true);
RefLog.delete(head); RefLog.delete(repo.head);
expect(head.hasLog, false); expect(repo.head.hasLog, false);
}); });
test('renames existing reflog', () { test('renames existing reflog', () {
@ -94,19 +90,21 @@ void main() {
); );
expect(reflog.length, 4); expect(reflog.length, 4);
reflog.add(oid: head.target, committer: committer); reflog.add(oid: repo.head.target, committer: committer);
expect(reflog.length, 5); 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.length, 6);
expect(reflog[0].message, 'new entry'); expect(reflog[0].message, 'new entry');
committer.free();
}); });
test('throws when trying to add new entry', () { test('throws when trying to add new entry', () {
expect( expect(
() => reflog.add(oid: head.target, committer: Signature(nullptr)), () => reflog.add(oid: repo.head.target, committer: Signature(nullptr)),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
@ -132,12 +130,12 @@ void main() {
reflog.remove(0); reflog.remove(0);
// making sure change is only in memory // making sure change is only in memory
final oldReflog = RefLog(head); final oldReflog = RefLog(repo.head);
expect(oldReflog.length, 4); expect(oldReflog.length, 4);
reflog.write(); reflog.write();
final newReflog = RefLog(head); final newReflog = RefLog(repo.head);
expect(newReflog.length, 3); expect(newReflog.length, 3);
}); });
@ -147,9 +145,10 @@ void main() {
Reference.delete(repo: repo, name: ref.name); Reference.delete(repo: repo, name: ref.name);
expect(() => reflog.write(), throwsA(isA<LibGit2Error>())); expect(() => reflog.write(), throwsA(isA<LibGit2Error>()));
});
reflog.free(); test('manually releases allocated memory', () {
ref.free(); expect(() => RefLog(repo.head).free(), returnsNormally);
}); });
test('returns string representation of RefLogEntry object', () { test('returns string representation of RefLogEntry object', () {

View file

@ -30,7 +30,6 @@ void main() {
}); });
tearDown(() { tearDown(() {
remote.free();
originRepo.free(); originRepo.free();
clonedRepo.free(); clonedRepo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
@ -41,35 +40,26 @@ void main() {
group('Remote', () { group('Remote', () {
test('fetch() does not prune branch by default', () { test('fetch() does not prune branch by default', () {
remote.fetch(); remote.fetch();
expect(
final branches = clonedRepo.branches; clonedRepo.branches.any((branch) => branch.name == 'origin/feature'),
expect(branches.any((branch) => branch.name == 'origin/feature'), true); true,
);
for (final branch in branches) {
branch.free();
}
}); });
test('fetch() prunes branch with provided flag', () { test('fetch() prunes branch with provided flag', () {
remote.fetch(prune: GitFetchPrune.prune); remote.fetch(prune: GitFetchPrune.prune);
expect(
final branches = clonedRepo.branches; clonedRepo.branches.any((branch) => branch.name == 'origin/feature'),
expect(branches.any((branch) => branch.name == 'origin/feature'), false); false,
);
for (final branch in branches) {
branch.free();
}
}); });
test('fetch() does not prune branch with provided flag', () { test('fetch() does not prune branch with provided flag', () {
remote.fetch(prune: GitFetchPrune.noPrune); remote.fetch(prune: GitFetchPrune.noPrune);
expect(
final branches = clonedRepo.branches; clonedRepo.branches.any((branch) => branch.name == 'origin/feature'),
expect(branches.any((branch) => branch.name == 'origin/feature'), true); true,
);
for (final branch in branches) {
branch.free();
}
}); });
test('prune() prunes branches', () { test('prune() prunes branches', () {
@ -81,22 +71,18 @@ void main() {
final callbacks = Callbacks(updateTips: updateTips); final callbacks = Callbacks(updateTips: updateTips);
remote.fetch(prune: GitFetchPrune.noPrune); remote.fetch(prune: GitFetchPrune.noPrune);
var branches = clonedRepo.branches; expect(
expect(branches.any((branch) => branch.name == 'origin/feature'), true); clonedRepo.branches.any((branch) => branch.name == 'origin/feature'),
true,
for (final branch in branches) { );
branch.free();
}
remote.prune(callbacks); remote.prune(callbacks);
branches = clonedRepo.branches;
expect(pruned, contains('refs/remotes/origin/feature')); expect(pruned, contains('refs/remotes/origin/feature'));
expect(branches.any((branch) => branch.name == 'origin/feature'), false); expect(
clonedRepo.branches.any((branch) => branch.name == 'origin/feature'),
for (final branch in branches) { false,
branch.free(); );
}
}); });
test( test(

View file

@ -34,8 +34,6 @@ void main() {
expect(remote.url, remoteUrl); expect(remote.url, remoteUrl);
expect(remote.pushUrl, ''); expect(remote.pushUrl, '');
expect(remote.toString(), contains('Remote{')); expect(remote.toString(), contains('Remote{'));
remote.free();
}); });
test('throws when provided name for lookup is not found', () { test('throws when provided name for lookup is not found', () {
@ -56,8 +54,6 @@ void main() {
expect(remote.name, 'upstream'); expect(remote.name, 'upstream');
expect(remote.url, remoteUrl); expect(remote.url, remoteUrl);
expect(remote.pushUrl, ''); expect(remote.pushUrl, '');
remote.free();
}); });
test('creates with provided fetchspec', () { test('creates with provided fetchspec', () {
@ -74,19 +70,12 @@ void main() {
expect(remote.url, remoteUrl); expect(remote.url, remoteUrl);
expect(remote.pushUrl, ''); expect(remote.pushUrl, '');
expect(remote.fetchRefspecs, [spec]); expect(remote.fetchRefspecs, [spec]);
remote.free();
}); });
test('throws when trying to create with fetchspec with invalid remote name', test('throws when trying to create with fetchspec with invalid remote name',
() { () {
expect( expect(
() => Remote.create( () => Remote.create(repo: repo, name: '', url: '', fetch: ''),
repo: repo,
name: '',
url: '',
fetch: '',
),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
@ -101,8 +90,6 @@ void main() {
Remote.delete(repo: repo, name: remote.name); Remote.delete(repo: repo, name: remote.name);
expect(repo.remotes.length, 1); expect(repo.remotes.length, 1);
remote.free();
}); });
test('throws when trying to delete non existing remote', () { test('throws when trying to delete non existing remote', () {
@ -118,16 +105,13 @@ void main() {
final problems = Remote.rename( final problems = Remote.rename(
repo: repo, repo: repo,
oldName: remoteName, oldName: remoteName,
newName: 'new', newName: 'renamed',
); );
expect(problems, isEmpty); expect(problems, isEmpty);
expect(remote.name, isNot('new')); expect(remote.name, isNot('renamed'));
final newRemote = Remote.lookup(repo: repo, name: 'new'); final renamedRemote = Remote.lookup(repo: repo, name: 'renamed');
expect(newRemote.name, 'new'); expect(renamedRemote.name, 'renamed');
newRemote.free();
remote.free();
}); });
test('returns list of non-default refspecs that cannot be 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'), Remote.rename(repo: repo, oldName: remote.name, newName: 'renamed'),
['+refs/*:refs/*'], ['+refs/*:refs/*'],
); );
remote.free();
}); });
test('throws when renaming with invalid names', () { test('throws when renaming with invalid names', () {
@ -154,17 +136,12 @@ void main() {
}); });
test('sets url', () { test('sets url', () {
final remote = Remote.lookup(repo: repo, name: remoteName); expect(Remote.lookup(repo: repo, name: remoteName).url, remoteUrl);
expect(remote.url, remoteUrl);
const newUrl = 'git://new/url.git'; const newUrl = 'git://new/url.git';
Remote.setUrl(repo: repo, remote: remoteName, url: newUrl); Remote.setUrl(repo: repo, remote: remoteName, url: newUrl);
final newRemote = Remote.lookup(repo: repo, name: remoteName); expect(Remote.lookup(repo: repo, name: remoteName).url, newUrl);
expect(newRemote.url, newUrl);
newRemote.free();
remote.free();
}); });
test('throws when trying to set invalid url name', () { test('throws when trying to set invalid url name', () {
@ -178,10 +155,7 @@ void main() {
const newUrl = 'git://new/url.git'; const newUrl = 'git://new/url.git';
Remote.setPushUrl(repo: repo, remote: remoteName, url: newUrl); Remote.setPushUrl(repo: repo, remote: remoteName, url: newUrl);
final remote = Remote.lookup(repo: repo, name: remoteName); expect(Remote.lookup(repo: repo, name: remoteName).pushUrl, newUrl);
expect(remote.pushUrl, newUrl);
remote.free();
}); });
test('throws when trying to set invalid push url name', () { test('throws when trying to set invalid push url name', () {
@ -215,14 +189,11 @@ void main() {
refspec.rTransform('refs/remotes/origin/master'), refspec.rTransform('refs/remotes/origin/master'),
'refs/heads/master', 'refs/heads/master',
); );
remote.free();
}); });
test('throws when trying to transform refspec with invalid reference name', test('throws when trying to transform refspec with invalid reference name',
() { () {
final remote = Remote.lookup(repo: repo, name: 'origin'); final refspec = Remote.lookup(repo: repo, name: 'origin').getRefspec(0);
final refspec = remote.getRefspec(0);
expect( expect(
() => refspec.transform('invalid/name'), () => refspec.transform('invalid/name'),
@ -233,8 +204,6 @@ void main() {
() => refspec.rTransform('invalid/name'), () => refspec.rTransform('invalid/name'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
remote.free();
}); });
test('adds fetch refspec', () { test('adds fetch refspec', () {
@ -252,8 +221,6 @@ void main() {
'+refs/test/*:refs/test/remotes/*', '+refs/test/*:refs/test/remotes/*',
], ],
); );
remote.free();
}); });
test('throws when trying to add fetch refspec for invalid remote name', () { 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'); final remote = Remote.lookup(repo: repo, name: 'origin');
expect(remote.pushRefspecs.length, 1); expect(remote.pushRefspecs.length, 1);
expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']); expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']);
remote.free();
}); });
test('throws when trying to add push refspec for invalid remote name', () { test('throws when trying to add push refspec for invalid remote name', () {
@ -308,8 +273,6 @@ void main() {
(refs.first['oid']! as Oid).sha, (refs.first['oid']! as Oid).sha,
'49322bb17d3acc9146f98c97d078513228bbf3c0', '49322bb17d3acc9146f98c97d078513228bbf3c0',
); );
remote.free();
}); });
test( test(
@ -319,8 +282,6 @@ void main() {
final remote = Remote.lookup(repo: repo, name: 'libgit2'); final remote = Remote.lookup(repo: repo, name: 'libgit2');
expect(() => remote.ls(), throwsA(isA<LibGit2Error>())); expect(() => remote.ls(), throwsA(isA<LibGit2Error>()));
remote.free();
}); });
test( test(
@ -350,8 +311,6 @@ void main() {
expect(stats.indexedDeltas, 3); expect(stats.indexedDeltas, 3);
expect(stats.receivedBytes, 0); expect(stats.receivedBytes, 0);
expect(stats.toString(), contains('TransferProgress{')); expect(stats.toString(), contains('TransferProgress{'));
remote.free();
}, },
tags: 'remote_fetch', tags: 'remote_fetch',
); );
@ -384,8 +343,6 @@ void main() {
expect(stats.indexedDeltas, 3); expect(stats.indexedDeltas, 3);
expect(stats.receivedBytes, 0); expect(stats.receivedBytes, 0);
expect(stats.toString(), contains('TransferProgress{')); expect(stats.toString(), contains('TransferProgress{'));
remote.free();
}, },
tags: 'remote_fetch', tags: 'remote_fetch',
); );
@ -412,8 +369,6 @@ void main() {
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
remote.free();
}, },
tags: 'remote_fetch', tags: 'remote_fetch',
); );
@ -426,8 +381,6 @@ void main() {
() => remote.fetch(), () => remote.fetch(),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
remote.free();
}); });
test( test(
@ -442,9 +395,8 @@ void main() {
TransferProgress? callbackStats; TransferProgress? callbackStats;
void tp(TransferProgress stats) => callbackStats = stats; 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.totalObjects == callbackStats?.totalObjects, true);
expect(stats.indexedObjects == callbackStats?.indexedObjects, true); expect(stats.indexedObjects == callbackStats?.indexedObjects, true);
@ -453,8 +405,6 @@ void main() {
expect(stats.totalDeltas == callbackStats?.totalDeltas, true); expect(stats.totalDeltas == callbackStats?.totalDeltas, true);
expect(stats.indexedDeltas == callbackStats?.indexedDeltas, true); expect(stats.indexedDeltas == callbackStats?.indexedDeltas, true);
expect(stats.receivedBytes == callbackStats?.receivedBytes, true); expect(stats.receivedBytes == callbackStats?.receivedBytes, true);
remote.free();
}, },
tags: 'remote_fetch', 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 remote = Remote.lookup(repo: repo, name: 'libgit2');
final sidebandOutput = StringBuffer(); final sidebandOutput = StringBuffer();
void sideband(String message) { void sideband(String message) => sidebandOutput.write(message);
sidebandOutput.write(message);
}
final callbacks = Callbacks(sidebandProgress: sideband); remote.fetch(callbacks: Callbacks(sidebandProgress: sideband));
remote.fetch(callbacks: callbacks);
expect(sidebandOutput.toString(), sidebandMessage); expect(sidebandOutput.toString(), sidebandMessage);
remote.free();
}, },
tags: 'remote_fetch', 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(updateTips: updateTips));
remote.fetch(callbacks: callbacks);
expect(updateTipsOutput, tipsExpected); expect(updateTipsOutput, tipsExpected);
remote.free();
}, },
tags: 'remote_fetch', tags: 'remote_fetch',
); );
@ -557,16 +497,16 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
updateRefOutput[refname] = message; updateRefOutput[refname] = message;
} }
final callbacks = Callbacks(pushUpdateReference: updateRef); remote.push(
refspecs: ['refs/heads/master'],
remote.push(refspecs: ['refs/heads/master'], callbacks: callbacks); callbacks: Callbacks(pushUpdateReference: updateRef),
);
expect( expect(
Commit.lookup(repo: originRepo, oid: originRepo.head.target).oid.sha, Commit.lookup(repo: originRepo, oid: originRepo.head.target).oid.sha,
'821ed6e80627b8769d170a293862f9fc60825226', '821ed6e80627b8769d170a293862f9fc60825226',
); );
expect(updateRefOutput, {'refs/heads/master': ''}); expect(updateRefOutput, {'refs/heads/master': ''});
remote.free();
originRepo.free(); originRepo.free();
originDir.delete(recursive: true); 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']), () => remote.push(refspecs: ['refs/heads/master']),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
});
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<RemoteCallback>());
expect(remoteCallback.name, 'name');
expect(remoteCallback.url, 'url');
expect(remoteCallback.fetch, 'fetchRefspec');
}); });
}); });
} }

View file

@ -9,7 +9,6 @@ import 'package:test/test.dart';
import 'helpers/util.dart'; import 'helpers/util.dart';
void main() { void main() {
late Repository repo;
late Directory tmpDir; late Directory tmpDir;
final cloneDir = Directory( final cloneDir = Directory(
p.join(Directory.systemTemp.path, 'repository_cloned'), p.join(Directory.systemTemp.path, 'repository_cloned'),
@ -17,14 +16,12 @@ void main() {
setUp(() { setUp(() {
tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo'))); tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo')));
repo = Repository.open(tmpDir.path);
if (cloneDir.existsSync()) { if (cloneDir.existsSync()) {
cloneDir.delete(recursive: true); cloneDir.delete(recursive: true);
} }
}); });
tearDown(() { tearDown(() {
repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
if (cloneDir.existsSync()) { if (cloneDir.existsSync()) {
cloneDir.deleteSync(recursive: true); cloneDir.deleteSync(recursive: true);
@ -72,14 +69,13 @@ void main() {
clonedRepo.free(); clonedRepo.free();
}); });
test('clones repository with provided remote callback', () { test(
Remote remote(Repository repo, String name, String url) => 'clones repository with provided remote callback having default fetch '
Remote.create(repo: repo, name: 'test', url: tmpDir.path); 'refspec value', () {
final clonedRepo = Repository.clone( final clonedRepo = Repository.clone(
url: tmpDir.path, url: tmpDir.path,
localPath: cloneDir.path, localPath: cloneDir.path,
remote: remote, remoteCallback: RemoteCallback(name: 'test', url: tmpDir.path),
); );
expect(clonedRepo.isEmpty, false); expect(clonedRepo.isEmpty, false);
@ -90,15 +86,35 @@ void main() {
clonedRepo.free(); clonedRepo.free();
}); });
test('throws when cloning repository with invalid remote callback', () { test('clones repository with provided remote callback ', () {
Remote remote(Repository repo, String name, String url) => const fetchRefspec = '+refs/heads/*:refs/remotes/spec/*';
Remote.create(repo: repo, name: '', url: ''); 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( expect(
() => Repository.clone( () => Repository.clone(
url: tmpDir.path, url: tmpDir.path,
localPath: cloneDir.path, localPath: cloneDir.path,
remote: remote, remoteCallback: const RemoteCallback(name: '', url: ''),
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
@ -113,13 +129,10 @@ void main() {
} }
callbackPath.createSync(); callbackPath.createSync();
Repository repository(String path, bool bare) =>
Repository.init(path: callbackPath.path);
final clonedRepo = Repository.clone( final clonedRepo = Repository.clone(
url: tmpDir.path, url: tmpDir.path,
localPath: cloneDir.path, localPath: cloneDir.path,
repository: repository, repositoryCallback: RepositoryCallback(path: callbackPath.path),
); );
expect(clonedRepo.isEmpty, false); expect(clonedRepo.isEmpty, false);
@ -131,17 +144,41 @@ void main() {
}); });
test('throws when cloning repository with invalid repository callback', () { test('throws when cloning repository with invalid repository callback', () {
Repository repository(String path, bool bare) =>
Repository.init(path: '');
expect( expect(
() => Repository.clone( () => Repository.clone(
url: tmpDir.path, url: tmpDir.path,
localPath: cloneDir.path, localPath: cloneDir.path,
repository: repository, repositoryCallback: const RepositoryCallback(path: ''),
), ),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
}); });
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<RepositoryCallback>());
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');
});
});
} }

View file

@ -5,7 +5,6 @@ import 'package:path/path.dart' as p;
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
late Repository repo;
final initDir = Directory(p.join(Directory.systemTemp.path, 'init_repo')); final initDir = Directory(p.join(Directory.systemTemp.path, 'init_repo'));
setUp(() { setUp(() {
@ -17,27 +16,30 @@ void main() {
}); });
tearDown(() { tearDown(() {
repo.free();
initDir.deleteSync(recursive: true); initDir.deleteSync(recursive: true);
}); });
group('Repository.init', () { group('Repository.init', () {
test('creates new bare repo at provided path', () { 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.path, contains('init_repo'));
expect(repo.isBare, true); expect(repo.isBare, true);
repo.free();
}); });
test('creates new standard repo at provided path', () { 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.path, contains('init_repo/.git/'));
expect(repo.isBare, false); expect(repo.isBare, false);
expect(repo.isEmpty, true); expect(repo.isEmpty, true);
repo.free();
}); });
test('creates new standard repo with provided options', () { test('creates new standard repo with provided options', () {
repo = Repository.init( final repo = Repository.init(
path: initDir.path, path: initDir.path,
description: 'test repo', description: 'test repo',
originUrl: 'test.url', originUrl: 'test.url',
@ -52,6 +54,8 @@ void main() {
'test repo', 'test repo',
); );
expect(Remote.lookup(repo: repo, name: 'origin').url, 'test.url'); expect(Remote.lookup(repo: repo, name: 'origin').url, 'test.url');
repo.free();
}); });
}); });
} }

View file

@ -25,22 +25,17 @@ void main() {
group('Repository', () { group('Repository', () {
test('returns config for repository', () { test('returns config for repository', () {
final config = repo.config;
expect( expect(
config['remote.origin.url'].value, repo.config['remote.origin.url'].value,
'git://github.com/SkinnyMind/libgit2dart.git', 'git://github.com/SkinnyMind/libgit2dart.git',
); );
config.free();
}); });
test('returns snapshot of repository config', () { test('returns snapshot of repository config', () {
final snapshot = repo.configSnapshot;
expect( expect(
snapshot['remote.origin.url'].value, repo.configSnapshot['remote.origin.url'].value,
'git://github.com/SkinnyMind/libgit2dart.git', 'git://github.com/SkinnyMind/libgit2dart.git',
); );
snapshot.free();
}); });
test('returns list of commits by walking from provided starting oid', () { 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++) { for (var i = 0; i < commits.length; i++) {
expect(commits[i].oid.sha, log[i]); expect(commits[i].oid.sha, log[i]);
} }
for (final c in commits) {
c.free();
}
}); });
group('.discover()', () { group('.discover()', () {
@ -119,11 +110,6 @@ void main() {
}); });
group('setHead', () { group('setHead', () {
late Reference head;
setUp(() => head = repo.head);
tearDown(() => head.free());
test('sets head when target is reference', () { test('sets head when target is reference', () {
expect(repo.head.name, 'refs/heads/master'); expect(repo.head.name, 'refs/heads/master');
expect(repo.head.target.sha, lastCommit); expect(repo.head.target.sha, lastCommit);
@ -164,9 +150,8 @@ void main() {
test('returns status of a repository', () { test('returns status of a repository', () {
File(p.join(tmpDir.path, 'new_file.txt')).createSync(); File(p.join(tmpDir.path, 'new_file.txt')).createSync();
final index = repo.index; repo.index.remove('file');
index.remove('file'); repo.index.add('new_file.txt');
index.add('new_file.txt');
expect( expect(
repo.status, repo.status,
{ {
@ -174,8 +159,6 @@ void main() {
'new_file.txt': {GitStatus.indexNew} 'new_file.txt': {GitStatus.indexNew}
}, },
); );
index.free();
}); });
test('throws when trying to get status of bare repository', () { test('throws when trying to get status of bare repository', () {
@ -188,14 +171,14 @@ void main() {
test('cleans up state', () { test('cleans up state', () {
expect(repo.state, GitRepositoryState.none); expect(repo.state, GitRepositoryState.none);
final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); Merge.cherryPick(
Merge.cherryPick(repo: repo, commit: commit); repo: repo,
commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
);
expect(repo.state, GitRepositoryState.cherrypick); expect(repo.state, GitRepositoryState.cherrypick);
repo.stateCleanup(); repo.stateCleanup();
expect(repo.state, GitRepositoryState.none); expect(repo.state, GitRepositoryState.none);
commit.free();
}); });
test('throws when trying to clean up state and error occurs', () { 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', () { test('returns status of a single file for provided path', () {
final index = repo.index; repo.index.remove('file');
index.remove('file');
expect( expect(
repo.statusFile('file'), repo.statusFile('file'),
{GitStatus.indexDeleted, GitStatus.wtNew}, {GitStatus.indexDeleted, GitStatus.wtNew},
); );
expect(repo.statusFile('.gitignore'), {GitStatus.current}); expect(repo.statusFile('.gitignore'), {GitStatus.current});
index.free();
}); });
test('throws when checking status of a single file for invalid path', () { test('throws when checking status of a single file for invalid path', () {
@ -229,9 +209,6 @@ void main() {
final signature = repo.defaultSignature; final signature = repo.defaultSignature;
expect(signature.name, 'Some Name'); expect(signature.name, 'Some Name');
expect(signature.email, 'some@email.com'); expect(signature.email, 'some@email.com');
signature.free();
config.free();
}); });
test('returns attribute value', () { test('returns attribute value', () {
@ -265,9 +242,11 @@ void main() {
repo.aheadBehind(local: commit1.oid, upstream: commit1.oid), repo.aheadBehind(local: commit1.oid, upstream: commit1.oid),
[0, 0], [0, 0],
); );
});
commit1.free(); test('manually releases allocated memory', () {
commit2.free(); final repo = Repository.open(tmpDir.path);
expect(() => repo.free(), returnsNormally);
}); });
test('returns string representation of Repository object', () { test('returns string representation of Repository object', () {

View file

@ -25,68 +25,48 @@ void main() {
group('Reset', () { group('Reset', () {
test('resets with hard', () { test('resets with hard', () {
var contents = file.readAsStringSync(); expect(file.readAsStringSync(), 'Feature edit\n');
expect(contents, 'Feature edit\n');
repo.reset(oid: repo[sha], resetType: GitReset.hard); repo.reset(oid: repo[sha], resetType: GitReset.hard);
contents = file.readAsStringSync(); expect(file.readAsStringSync(), isEmpty);
expect(contents, isEmpty);
}); });
test('resets with soft', () { test('resets with soft', () {
var contents = file.readAsStringSync(); expect(file.readAsStringSync(), 'Feature edit\n');
expect(contents, 'Feature edit\n');
repo.reset(oid: repo[sha], resetType: GitReset.soft); repo.reset(oid: repo[sha], resetType: GitReset.soft);
contents = file.readAsStringSync(); expect(file.readAsStringSync(), 'Feature edit\n');
expect(contents, 'Feature edit\n');
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
expect(diff.deltas, isEmpty); expect(diff.deltas, isEmpty);
index.free();
}); });
test('resets with mixed', () { test('resets with mixed', () {
var contents = file.readAsStringSync(); expect(file.readAsStringSync(), 'Feature edit\n');
expect(contents, 'Feature edit\n');
repo.reset(oid: repo[sha], resetType: GitReset.mixed); repo.reset(oid: repo[sha], resetType: GitReset.mixed);
contents = file.readAsStringSync(); expect(file.readAsStringSync(), 'Feature edit\n');
expect(contents, 'Feature edit\n');
final index = repo.index; final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final diff = Diff.indexToWorkdir(repo: repo, index: index);
expect(diff.deltas.length, 1); expect(diff.deltas.length, 1);
index.free();
}); });
group('resetDefault', () { group('resetDefault', () {
test('updates entry in the index', () { test('updates entry in the index', () {
file.writeAsStringSync('new edit'); file.writeAsStringSync('new edit');
final index = repo.index; repo.index.add('feature_file');
index.add('feature_file');
expect(repo.status['feature_file'], {GitStatus.indexModified}); expect(repo.status['feature_file'], {GitStatus.indexModified});
final head = repo.head; repo.resetDefault(oid: repo.head.target, pathspec: ['feature_file']);
repo.resetDefault(oid: head.target, pathspec: ['feature_file']);
expect(repo.status['feature_file'], {GitStatus.wtModified}); expect(repo.status['feature_file'], {GitStatus.wtModified});
head.free();
index.free();
}); });
test('throws when pathspec list is empty', () { test('throws when pathspec list is empty', () {
final head = repo.head;
expect( expect(
() => repo.resetDefault(oid: head.target, pathspec: []), () => repo.resetDefault(oid: repo.head.target, pathspec: []),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
head.free();
}); });
}); });
}); });

View file

@ -42,11 +42,6 @@ void main() {
final tag = RevParse.single(repo: repo, spec: 'v0.2') as Tag; final tag = RevParse.single(repo: repo, spec: 'v0.2') as Tag;
expect(tag, isA<Tag>()); expect(tag, isA<Tag>());
expect(tag.message, 'annotated tag\n'); expect(tag.message, 'annotated tag\n');
commit.free();
tree.free();
blob.free();
tag.free();
}); });
test('.single() throws when spec string not found or invalid', () { test('.single() throws when spec string not found or invalid', () {
@ -68,10 +63,6 @@ void main() {
expect(headParse.reference?.equals(masterRef), true); expect(headParse.reference?.equals(masterRef), true);
expect(headParse.toString(), contains('RevParse{')); expect(headParse.toString(), contains('RevParse{'));
masterRef.free();
headParse.object.free();
headParse.reference?.free();
final featureRef = Reference.lookup( final featureRef = Reference.lookup(
repo: repo, repo: repo,
name: 'refs/heads/feature', name: 'refs/heads/feature',
@ -83,10 +74,6 @@ void main() {
'5aecfa0fb97eadaac050ccb99f03c3fb65460ad4', '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4',
); );
expect(headParse.reference?.equals(featureRef), true); expect(headParse.reference?.equals(featureRef), true);
featureRef.free();
headParse.object.free();
headParse.reference?.free();
}); });
test('.ext() returns only commit when no intermidiate reference found', () { test('.ext() returns only commit when no intermidiate reference found', () {
@ -94,8 +81,6 @@ void main() {
expect(headParse.object.oid.sha, parentSHA); expect(headParse.object.oid.sha, parentSHA);
expect(headParse.reference, isNull); expect(headParse.reference, isNull);
headParse.object.free();
}); });
test('.ext() throws when spec string not found or invalid', () { test('.ext() throws when spec string not found or invalid', () {
@ -119,17 +104,12 @@ void main() {
expect(revspec.flags, {GitRevSpec.single}); expect(revspec.flags, {GitRevSpec.single});
expect(revspec.toString(), contains('RevSpec{')); expect(revspec.toString(), contains('RevSpec{'));
revspec.from.free();
revspec = RevParse.range(repo: repo, spec: 'HEAD^1..5aecfa'); revspec = RevParse.range(repo: repo, spec: 'HEAD^1..5aecfa');
expect(revspec.from.oid.sha, parentSHA); expect(revspec.from.oid.sha, parentSHA);
expect(revspec.to?.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(revspec.to?.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
expect(revspec.flags, {GitRevSpec.range}); expect(revspec.flags, {GitRevSpec.range});
revspec.from.free();
revspec.to?.free();
revspec = RevParse.range(repo: repo, spec: 'HEAD...feature'); revspec = RevParse.range(repo: repo, spec: 'HEAD...feature');
expect(revspec.from.oid.sha, headSHA); expect(revspec.from.oid.sha, headSHA);
@ -139,9 +119,6 @@ void main() {
Merge.base(repo: repo, commits: [revspec.from.oid, revspec.to!.oid]), Merge.base(repo: repo, commits: [revspec.from.oid, revspec.to!.oid]),
isA<Oid>(), isA<Oid>(),
); );
revspec.from.free();
revspec.to?.free();
}); });
test('throws on invalid range spec', () { test('throws on invalid range spec', () {

View file

@ -30,9 +30,7 @@ void main() {
group('RevWalk', () { group('RevWalk', () {
test('initializes walker', () { test('initializes walker', () {
final walker = RevWalk(repo); expect(RevWalk(repo), isA<RevWalk>());
expect(walker, isA<RevWalk>());
walker.free();
}); });
test('throws when trying to initialize and error occurs', () { test('throws when trying to initialize and error occurs', () {
@ -44,55 +42,42 @@ void main() {
test('returns list of commits with default sorting', () { test('returns list of commits with default sorting', () {
final walker = RevWalk(repo); 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(); final commits = walker.walk();
for (var i = 0; i < commits.length; i++) { for (var i = 0; i < commits.length; i++) {
expect(commits[i].oid.sha, log[i]); expect(commits[i].oid.sha, log[i]);
commits[i].free();
} }
walker.free();
}); });
test('returns list of commits with reverse sorting', () { test('returns list of commits with reverse sorting', () {
final walker = RevWalk(repo); 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}); walker.sorting({GitSort.reverse});
final commits = walker.walk(); final commits = walker.walk();
for (var i = 0; i < commits.length; i++) { for (var i = 0; i < commits.length; i++) {
expect(commits[i].oid.sha, log.reversed.toList()[i]); expect(commits[i].oid.sha, log.reversed.toList()[i]);
commits[i].free();
} }
walker.free();
}); });
test('changes sorting', () { test('changes sorting', () {
final walker = RevWalk(repo); 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(); final timeSortedCommits = walker.walk();
for (var i = 0; i < timeSortedCommits.length; i++) { for (var i = 0; i < timeSortedCommits.length; i++) {
expect(timeSortedCommits[i].oid.sha, log[i]); expect(timeSortedCommits[i].oid.sha, log[i]);
timeSortedCommits[i].free();
} }
walker.sorting({GitSort.time, GitSort.reverse}); walker.sorting({GitSort.time, GitSort.reverse});
final reverseSortedCommits = walker.walk(); final reverseSortedCommits = walker.walk();
for (var i = 0; i < reverseSortedCommits.length; i++) { for (var i = 0; i < reverseSortedCommits.length; i++) {
expect(reverseSortedCommits[i].oid.sha, log.reversed.toList()[i]); expect(reverseSortedCommits[i].oid.sha, log.reversed.toList()[i]);
reverseSortedCommits[i].free();
} }
walker.free();
}); });
test('adds matching references for traversal with provided glob', () { test('adds matching references for traversal with provided glob', () {
@ -101,11 +86,6 @@ void main() {
walker.pushGlob('heads'); walker.pushGlob('heads');
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 7); expect(commits.length, 7);
for (final c in commits) {
c.free();
}
walker.free();
}); });
test("adds repository's head for traversal", () { test("adds repository's head for traversal", () {
@ -114,11 +94,6 @@ void main() {
walker.pushHead(); walker.pushHead();
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 6); expect(commits.length, 6);
for (final c in commits) {
c.free();
}
walker.free();
}); });
test('adds reference for traversal with provided name', () { test('adds reference for traversal with provided name', () {
@ -127,23 +102,14 @@ void main() {
walker.pushReference('refs/heads/master'); walker.pushReference('refs/heads/master');
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 6); 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', test('throws when trying to add reference for traversal with invalid name',
() { () {
final walker = RevWalk(repo);
expect( expect(
() => walker.pushReference('invalid'), () => RevWalk(repo).pushReference('invalid'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
walker.free();
}); });
test('adds range for traversal', () { test('adds range for traversal', () {
@ -152,22 +118,13 @@ void main() {
walker.pushRange('HEAD..@{-1}'); walker.pushRange('HEAD..@{-1}');
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 1); expect(commits.length, 1);
for (final c in commits) {
c.free();
}
walker.free();
}); });
test('throws when trying to add invalid range for traversal', () { test('throws when trying to add invalid range for traversal', () {
final walker = RevWalk(repo);
expect( expect(
() => walker.pushRange('HEAD..invalid'), () => RevWalk(repo).pushRange('HEAD..invalid'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
walker.free();
}); });
test('hides commit and its ancestors', () { test('hides commit and its ancestors', () {
@ -178,22 +135,13 @@ void main() {
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 2); expect(commits.length, 2);
for (final c in commits) {
c.free();
}
walker.free();
}); });
test('throws when trying to hide commit oid and error occurs', () { test('throws when trying to hide commit oid and error occurs', () {
final walker = RevWalk(repo);
expect( expect(
() => walker.hide(repo['0' * 40]), () => RevWalk(repo).hide(repo['0' * 40]),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
walker.free();
}); });
test('hides oids of references for provided glob pattern', () { test('hides oids of references for provided glob pattern', () {
@ -207,105 +155,72 @@ void main() {
walker.hideGlob('*master'); walker.hideGlob('*master');
final hiddenCommits = walker.walk(); final hiddenCommits = walker.walk();
expect(hiddenCommits.length, 1); expect(hiddenCommits.length, 1);
hiddenCommits[0].free();
for (final c in commits) {
c.free();
}
walker.free();
}); });
test('hides head', () { test('hides head', () {
final head = repo.head;
final walker = RevWalk(repo); final walker = RevWalk(repo);
walker.push(head.target); walker.push(repo.head.target);
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 6); expect(commits.length, 6);
walker.push(head.target); walker.push(repo.head.target);
walker.hideHead(); walker.hideHead();
final hiddenCommits = walker.walk(); final hiddenCommits = walker.walk();
expect(hiddenCommits.length, 0); expect(hiddenCommits.length, 0);
for (final c in commits) {
c.free();
}
head.free();
walker.free();
}); });
test('hides oids of reference with provided name', () { test('hides oids of reference with provided name', () {
final head = repo.head;
final walker = RevWalk(repo); final walker = RevWalk(repo);
walker.push(head.target); walker.push(repo.head.target);
final commits = walker.walk(); final commits = walker.walk();
expect(commits.length, 6); expect(commits.length, 6);
walker.push(head.target); walker.push(repo.head.target);
walker.hideReference('refs/heads/master'); walker.hideReference('refs/heads/master');
final hiddenCommits = walker.walk(); final hiddenCommits = walker.walk();
expect(hiddenCommits.length, 0); 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', () { test('throws when trying to hide oids of reference with invalid name', () {
final walker = RevWalk(repo);
expect( expect(
() => walker.hideReference('invalid'), () => RevWalk(repo).hideReference('invalid'),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
walker.free();
}); });
test('resets walker', () { test('resets walker', () {
final walker = RevWalk(repo); final walker = RevWalk(repo);
final start = Oid.fromSHA(repo: repo, sha: log.first);
walker.push(start); walker.push(repo[log.first]);
walker.reset(); walker.reset();
final commits = walker.walk(); final commits = walker.walk();
expect(commits, <Commit>[]); expect(commits, <Commit>[]);
walker.free();
}); });
test('simplifies walker by enqueuing only first parent for each commit', test('simplifies walker by enqueuing only first parent for each commit',
() { () {
final walker = RevWalk(repo); final walker = RevWalk(repo);
final start = Oid.fromSHA(repo: repo, sha: log.first);
walker.push(start); walker.push(repo[log.first]);
walker.simplifyFirstParent(); walker.simplifyFirstParent();
final commits = walker.walk(); final commits = walker.walk();
for (var i = 0; i < commits.length; i++) { expect(commits.length, 3);
expect(commits.length, 3);
commits[i].free();
}
walker.free();
}); });
test('throws when trying to add new root for traversal and error occurs', test('throws when trying to add new root for traversal and error occurs',
() { () {
final walker = RevWalk(repo);
expect( expect(
() => walker.push(repo['0' * 40]), () => RevWalk(repo).push(repo['0' * 40]),
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
});
walker.free(); test('manually releases allocated memory', () {
expect(() => RevWalk(repo).free(), returnsNormally);
}); });
}); });
} }

View file

@ -16,9 +16,6 @@ void main() {
); );
}); });
tearDown(() {
signature.free();
});
group('Signature', () { group('Signature', () {
test('creates with provided time and offset', () { test('creates with provided time and offset', () {
expect(signature, isA<Signature>()); expect(signature, isA<Signature>());
@ -50,7 +47,6 @@ void main() {
lessThan(5), lessThan(5),
); );
expect(sig.offset, isA<int>()); expect(sig.offset, isA<int>());
sig.free();
}); });
test('returns correct values', () { test('returns correct values', () {
@ -66,9 +62,16 @@ void main() {
email: email, email: email,
time: time, 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', () { test('returns string representation of Signature object', () {

View file

@ -24,7 +24,6 @@ void main() {
}); });
tearDown(() { tearDown(() {
stasher.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
@ -62,14 +61,11 @@ void main() {
test('leaves changes added to index intact', () { test('leaves changes added to index intact', () {
File(filePath).writeAsStringSync('edit', mode: FileMode.append); File(filePath).writeAsStringSync('edit', mode: FileMode.append);
final index = repo.index; repo.index.add('file');
index.add('file');
Stash.create(repo: repo, stasher: stasher, flags: {GitStash.keepIndex}); Stash.create(repo: repo, stasher: stasher, flags: {GitStash.keepIndex});
expect(repo.status.isEmpty, false); expect(repo.status.isEmpty, false);
expect(repo.stashes.length, 1); expect(repo.stashes.length, 1);
index.free();
}); });
test('applies changes from stash', () { test('applies changes from stash', () {
@ -109,8 +105,6 @@ void main() {
Stash.apply(repo: repo, reinstateIndex: true); Stash.apply(repo: repo, reinstateIndex: true);
expect(repo.status, contains('stash.this')); expect(repo.status, contains('stash.this'));
expect(index.find('stash.this'), true); expect(index.find('stash.this'), true);
index.free();
}); });
test('throws when trying to apply with wrong index', () { test('throws when trying to apply with wrong index', () {
@ -182,8 +176,6 @@ void main() {
Stash.pop(repo: repo, reinstateIndex: true); Stash.pop(repo: repo, reinstateIndex: true);
expect(repo.status, contains('stash.this')); expect(repo.status, contains('stash.this'));
expect(index.find('stash.this'), true); expect(index.find('stash.this'), true);
index.free();
}); });
test('throws when trying to pop with wrong index', () { test('throws when trying to pop with wrong index', () {

View file

@ -47,8 +47,6 @@ void main() {
expect(submodule.ignore, GitSubmoduleIgnore.none); expect(submodule.ignore, GitSubmoduleIgnore.none);
expect(submodule.updateRule, GitSubmoduleUpdate.checkout); expect(submodule.updateRule, GitSubmoduleUpdate.checkout);
expect(submodule.toString(), contains('Submodule{')); expect(submodule.toString(), contains('Submodule{'));
submodule.free();
}); });
test('throws when trying to lookup and submodule not found', () { test('throws when trying to lookup and submodule not found', () {
@ -70,8 +68,11 @@ void main() {
}); });
test('updates with provided init flag', () { test('updates with provided init flag', () {
final submoduleFilePath = final submoduleFilePath = p.join(
p.join(repo.workdir, testSubmodule, 'master.txt'); repo.workdir,
testSubmodule,
'master.txt',
);
expect(File(submoduleFilePath).existsSync(), false); expect(File(submoduleFilePath).existsSync(), false);
Submodule.update(repo: repo, name: testSubmodule, init: true); Submodule.update(repo: repo, name: testSubmodule, init: true);
@ -92,24 +93,20 @@ void main() {
Submodule.update(repo: repo, name: testSubmodule); Submodule.update(repo: repo, name: testSubmodule);
final submoduleRepo = submodule.open(); final submoduleRepo = submodule.open();
final subHead = submoduleRepo.head;
expect(submoduleRepo, isA<Repository>()); expect(submoduleRepo, isA<Repository>());
expect(subHead.target.sha, submoduleHeadSha); expect(submoduleRepo.head.target.sha, submoduleHeadSha);
expect( expect(
submodule.workdirOid?.sha, submodule.workdirOid?.sha,
'49322bb17d3acc9146f98c97d078513228bbf3c0', '49322bb17d3acc9146f98c97d078513228bbf3c0',
); );
subHead.free();
submoduleRepo.free(); submoduleRepo.free();
submodule.free();
}); });
test('throws when trying to open repository for not initialized submodule', test('throws when trying to open repository for not initialized submodule',
() { () {
final submodule = Submodule.lookup(repo: repo, name: testSubmodule); final submodule = Submodule.lookup(repo: repo, name: testSubmodule);
expect(() => submodule.open(), throwsA(isA<LibGit2Error>())); expect(() => submodule.open(), throwsA(isA<LibGit2Error>()));
submodule.free();
}); });
test('adds submodule', () { test('adds submodule', () {
@ -125,7 +122,6 @@ void main() {
expect(submoduleRepo.isEmpty, false); expect(submoduleRepo.isEmpty, false);
submoduleRepo.free(); submoduleRepo.free();
submodule.free();
}); });
test('throws when trying to add submodule with wrong url', () { test('throws when trying to add submodule with wrong url', () {
@ -170,9 +166,6 @@ void main() {
expect(updatedSubmodule.branch, 'updated'); expect(updatedSubmodule.branch, 'updated');
expect(updatedSubmodule.ignore, GitSubmoduleIgnore.all); expect(updatedSubmodule.ignore, GitSubmoduleIgnore.all);
expect(updatedSubmodule.updateRule, GitSubmoduleUpdate.rebase); expect(updatedSubmodule.updateRule, GitSubmoduleUpdate.rebase);
updatedSubmodule.free();
submodule.free();
}); });
test('syncs', () { test('syncs', () {
@ -205,13 +198,8 @@ void main() {
'https://updated.com/', 'https://updated.com/',
); );
updatedSubmRepoConfig.free();
submRepo.free(); submRepo.free();
updatedSubmRepo.free(); updatedSubmRepo.free();
updatedSubmodule.free();
submRepoConfig.free();
repoConfig.free();
submodule.free();
}); });
test('reloads info', () { test('reloads info', () {
@ -222,8 +210,6 @@ void main() {
submodule.reload(); submodule.reload();
expect(submodule.url, 'updated'); expect(submodule.url, 'updated');
submodule.free();
}); });
test('returns status for a submodule', () { test('returns status for a submodule', () {
@ -259,8 +245,11 @@ void main() {
GitSubmoduleStatus.inWorkdir, GitSubmoduleStatus.inWorkdir,
}, },
); );
});
submodule.free(); test('manually releases allocated memory', () {
final submodule = Submodule.lookup(repo: repo, name: testSubmodule);
expect(() => submodule.free(), returnsNormally);
}); });
}); });
} }

View file

@ -21,7 +21,6 @@ void main() {
}); });
tearDown(() { tearDown(() {
tag.free();
repo.free(); repo.free();
tmpDir.deleteSync(recursive: true); tmpDir.deleteSync(recursive: true);
}); });
@ -50,18 +49,14 @@ void main() {
offset: 180, offset: 180,
); );
final target = tag.target as Commit; final target = tag.target as Commit;
final tagger = tag.tagger;
expect(tag.oid, tagOid); expect(tag.oid, tagOid);
expect(tag.name, 'v0.2'); expect(tag.name, 'v0.2');
expect(tag.message, 'annotated tag\n'); expect(tag.message, 'annotated tag\n');
expect(tag.targetType, GitObject.commit); expect(tag.targetType, GitObject.commit);
expect(target.message, 'add subdirectory file\n'); expect(target.message, 'add subdirectory file\n');
expect(tagger, signature); expect(tag.tagger, signature);
expect(tag.toString(), contains('Tag{')); expect(tag.toString(), contains('Tag{'));
signature.free();
target.free();
}); });
test('creates new annotated tag with commit as target', () { test('creates new annotated tag with commit as target', () {
@ -85,19 +80,14 @@ void main() {
); );
final newTag = Tag.lookup(repo: repo, oid: oid); final newTag = Tag.lookup(repo: repo, oid: oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Commit; final newTagTarget = newTag.target as Commit;
expect(newTag.oid, oid); expect(newTag.oid, oid);
expect(newTag.name, tagName); expect(newTag.name, tagName);
expect(newTag.message, message); expect(newTag.message, message);
expect(newTag.targetOid.sha, targetSHA); expect(newTag.targetOid.sha, targetSHA);
expect(tagger, signature); expect(newTag.tagger, signature);
expect(newTagTarget.oid, target); expect(newTagTarget.oid, target);
newTag.free();
newTagTarget.free();
signature.free();
}); });
test('creates new lightweight tag with commit as target', () { test('creates new lightweight tag with commit as target', () {
@ -115,8 +105,6 @@ void main() {
expect(newTag.shorthand, tagName); expect(newTag.shorthand, tagName);
expect(newTag.target, target); expect(newTag.target, target);
newTag.free();
}); });
test('creates new annotated tag with tree as target', () { test('creates new annotated tag with tree as target', () {
@ -139,18 +127,13 @@ void main() {
); );
final newTag = Tag.lookup(repo: repo, oid: oid); final newTag = Tag.lookup(repo: repo, oid: oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Tree; final newTagTarget = newTag.target as Tree;
expect(newTag.oid, oid); expect(newTag.oid, oid);
expect(newTag.name, tagName); expect(newTag.name, tagName);
expect(newTag.message, message); expect(newTag.message, message);
expect(tagger, signature); expect(newTag.tagger, signature);
expect(newTagTarget.oid, target); expect(newTagTarget.oid, target);
newTag.free();
newTagTarget.free();
signature.free();
}); });
test('creates new lightweight tag with tree as target', () { test('creates new lightweight tag with tree as target', () {
@ -168,8 +151,6 @@ void main() {
expect(newTag.shorthand, tagName); expect(newTag.shorthand, tagName);
expect(newTag.target, target); expect(newTag.target, target);
newTag.free();
}); });
test('creates new annotated tag with blob as target', () { test('creates new annotated tag with blob as target', () {
@ -192,18 +173,13 @@ void main() {
); );
final newTag = Tag.lookup(repo: repo, oid: oid); final newTag = Tag.lookup(repo: repo, oid: oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Blob; final newTagTarget = newTag.target as Blob;
expect(newTag.oid, oid); expect(newTag.oid, oid);
expect(newTag.name, tagName); expect(newTag.name, tagName);
expect(newTag.message, message); expect(newTag.message, message);
expect(tagger, signature); expect(newTag.tagger, signature);
expect(newTagTarget.oid, target); expect(newTagTarget.oid, target);
newTag.free();
newTagTarget.free();
signature.free();
}); });
test('creates new lightweight tag with blob as target', () { test('creates new lightweight tag with blob as target', () {
@ -221,8 +197,6 @@ void main() {
expect(newTag.shorthand, tagName); expect(newTag.shorthand, tagName);
expect(newTag.target, target); expect(newTag.target, target);
newTag.free();
}); });
test('creates new annotated tag with tag as target', () { test('creates new annotated tag with tag as target', () {
@ -244,18 +218,13 @@ void main() {
); );
final newTag = Tag.lookup(repo: repo, oid: oid); final newTag = Tag.lookup(repo: repo, oid: oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Tag; final newTagTarget = newTag.target as Tag;
expect(newTag.oid, oid); expect(newTag.oid, oid);
expect(newTag.name, tagName); expect(newTag.name, tagName);
expect(newTag.message, message); expect(newTag.message, message);
expect(tagger, signature); expect(newTag.tagger, signature);
expect(newTagTarget.oid, tag.oid); expect(newTagTarget.oid, tag.oid);
newTag.free();
newTagTarget.free();
signature.free();
}); });
test('creates new lightweight tag with tag as target', () { test('creates new lightweight tag with tag as target', () {
@ -272,8 +241,6 @@ void main() {
expect(newTag.shorthand, tagName); expect(newTag.shorthand, tagName);
expect(newTag.target, tag.oid); expect(newTag.target, tag.oid);
newTag.free();
}); });
test( test(
@ -303,20 +270,15 @@ void main() {
); );
final newTag = Tag.lookup(repo: repo, oid: oid); final newTag = Tag.lookup(repo: repo, oid: oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Commit; final newTagTarget = newTag.target as Commit;
expect(newTag.oid, oid); expect(newTag.oid, oid);
expect(newTag.name, tagName); expect(newTag.name, tagName);
expect(newTag.message, message); expect(newTag.message, message);
expect(newTag.targetOid.sha, targetSHA); expect(newTag.targetOid.sha, targetSHA);
expect(tagger, signature); expect(newTag.tagger, signature);
expect(newTagTarget.oid, target); expect(newTagTarget.oid, target);
expect(repo.tags.length, equals(2)); expect(repo.tags.length, equals(2));
newTag.free();
newTagTarget.free();
signature.free();
}); });
test( test(
@ -342,8 +304,6 @@ void main() {
expect(newTag.shorthand, tagName); expect(newTag.shorthand, tagName);
expect(newTag.target, target); expect(newTag.target, target);
expect(repo.tags.length, equals(2)); expect(repo.tags.length, equals(2));
newTag.free();
}); });
test('throws when trying to create annotated tag with invalid name', () { test('throws when trying to create annotated tag with invalid name', () {
@ -420,5 +380,10 @@ void main() {
throwsA(isA<LibGit2Error>()), throwsA(isA<LibGit2Error>()),
); );
}); });
test('manually releases allocated memory', () {
tag = Tag.lookup(repo: repo, oid: tagOid);
expect(() => tag.free(), returnsNormally);
});
}); });
} }

Some files were not shown because too many files have changed in this diff Show more