refactor!: use Finalizer to automatically free allocated memory for objects

BREAKING CHANGE: signature change for remote and repository callbacks
during repository clone operation.
This commit is contained in:
Aleksey Kulikov 2022-04-21 14:07:11 +03:00
parent 94c40f9a94
commit b589097c8c
73 changed files with 1073 additions and 1618 deletions

View file

@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
channel: stable
channel: master
- name: Install dependencies
run: flutter pub get
@ -47,7 +47,7 @@ jobs:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
channel: stable
channel: master
- name: Install dependencies
run: flutter pub get

View file

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

View file

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

View file

@ -281,3 +281,11 @@ void deleteMultivar({
/// Free the configuration and its associated memory and files.
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

@ -78,8 +78,7 @@ int _forEachCb(
Pointer<git_oid> oid,
Pointer<Void> payload,
) {
final _oid = oid_bindings.copy(oid);
_objects.add(Oid(_oid));
_objects.add(Oid(oid_bindings.copy(oid)));
return 0;
}

View file

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

View file

@ -113,8 +113,9 @@ Pointer<git_repository> clone({
required String url,
required String localPath,
required bool bare,
Remote Function(Repository, String, String)? remote,
Repository Function(String, bool)? repository,
RemoteCallback? remoteCallback,
// Repository Function(String, bool)? repository,
RepositoryCallback? repositoryCallback,
String? checkoutBranch,
required Callbacks callbacks,
}) {
@ -138,14 +139,14 @@ Pointer<git_repository> clone({
const except = -1;
git_remote_create_cb remoteCb = nullptr;
if (remote != null) {
RemoteCallbacks.remoteFunction = remote;
if (remoteCallback != null) {
RemoteCallbacks.remoteCbData = remoteCallback;
remoteCb = Pointer.fromFunction(RemoteCallbacks.remoteCb, except);
}
git_repository_create_cb repositoryCb = nullptr;
if (repository != null) {
RemoteCallbacks.repositoryFunction = repository;
if (repositoryCallback != null) {
RemoteCallbacks.repositoryCbData = repositoryCallback;
repositoryCb = Pointer.fromFunction(RemoteCallbacks.repositoryCb, except);
}

View file

@ -64,5 +64,14 @@ Pointer<git_signature> defaultSignature(Pointer<git_repository> repo) {
return out.value;
}
/// 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.
void free(Pointer<git_signature> sig) => libgit2.git_signature_free(sig);

View file

@ -77,8 +77,10 @@ String name(Pointer<git_tag> tag) =>
libgit2.git_tag_name(tag).cast<Utf8>().toDartString();
/// Get the message of a tag.
String message(Pointer<git_tag> tag) =>
libgit2.git_tag_message(tag).cast<Utf8>().toDartString();
String message(Pointer<git_tag> tag) {
final result = libgit2.git_tag_message(tag);
return result == nullptr ? '' : result.cast<Utf8>().toDartString();
}
/// Get the tagger (author) of a tag.
Pointer<git_signature> tagger(Pointer<git_tag> tag) =>

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
/// line of the file.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Blame.file({
required Repository repo,
@ -57,6 +55,7 @@ class Blame with IterableMixin<BlameHunk> {
minLine: minLine,
maxLine: maxLine,
);
_finalizer.attach(this, _blamePointer, detach: this);
}
/// Returns blame data for a file that has been modified in memory. The
@ -74,6 +73,7 @@ class Blame with IterableMixin<BlameHunk> {
reference: reference._blamePointer,
buffer: buffer,
);
_finalizer.attach(this, _blamePointer, detach: this);
}
/// Pointer to memory address for allocated blame object.
@ -105,12 +105,21 @@ class Blame with IterableMixin<BlameHunk> {
}
/// Releases memory allocated for blame object.
void free() => bindings.free(_blamePointer);
void free() {
bindings.free(_blamePointer);
_finalizer.detach(this);
}
@override
Iterator<BlameHunk> get iterator => _BlameIterator(_blamePointer);
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_blame>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class BlameHunk {
/// Initializes a new instance of the [BlameHunk] class from
/// provided pointer to blame hunk object in memory.

View file

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

View file

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

View file

@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
class Commit {
/// Initializes a new instance of [Commit] class from provided pointer to
/// commit object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit(this._commitPointer);
Commit(this._commitPointer) {
_finalizer.attach(this, _commitPointer, detach: this);
}
/// Lookups commit object for provided [oid] in the [repo]sitory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit.lookup({required Repository repo, required Oid oid}) {
_commitPointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
);
_finalizer.attach(this, _commitPointer, detach: this);
}
late final Pointer<git_commit> _commitPointer;
@ -194,8 +193,6 @@ class Commit {
///
/// [mainline] is parent of the commit if it is a merge (i.e. 1, 2).
///
/// **IMPORTANT**: produced index should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Index revertTo({
required Commit commit,
@ -221,7 +218,7 @@ class Commit {
/// leading newlines.
String get message => bindings.message(_commitPointer);
/// Returns the short "summary" of the git commit message.
/// Short "summary" of the git commit message.
///
/// The returned message is the summary of the commit, comprising the first
/// paragraph of the message with whitespace trimmed and squashed.
@ -229,7 +226,7 @@ class Commit {
/// Throws a [LibGit2Error] if error occured.
String get summary => bindings.summary(_commitPointer);
/// Returns the long "body" of the commit message.
/// Long "body" of the commit message.
///
/// The returned message is the body of the commit, comprising everything but
/// the first paragraph of the message. Leading and trailing whitespaces are
@ -272,8 +269,6 @@ class Commit {
/// Returns the specified parent of the commit at provided 0-based [position].
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Commit parent(int position) {
return Commit(
@ -303,8 +298,6 @@ class Commit {
/// Passing 0 as the generation number returns another instance of the base
/// commit itself.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Commit nthGenAncestor(int n) {
return Commit(bindings.nthGenAncestor(commitPointer: _commitPointer, n: n));
@ -323,12 +316,13 @@ class Commit {
}
/// Creates an in-memory copy of a commit.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit duplicate() => Commit(bindings.duplicate(_commitPointer));
/// Releases memory allocated for commit object.
void free() => bindings.free(_commitPointer);
void free() {
bindings.free(_commitPointer);
_finalizer.detach(this);
}
@override
String toString() {
@ -337,3 +331,9 @@ class Commit {
' author: $author}';
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<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> {
/// Initializes a new instance of [Config] class from provided
/// pointer to config object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Config(this._configPointer);
Config(this._configPointer) {
_finalizer.attach(this, _configPointer, detach: this);
}
/// Opens config file at provided [path].
///
@ -23,8 +23,6 @@ class Config with IterableMixin<ConfigEntry> {
/// Git config file following the default Git config syntax (see
/// `man git-config`).
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws an [Exception] if file not found at provided path.
Config.open([String? path]) {
libgit2.git_libgit2_init();
@ -38,39 +36,44 @@ class Config with IterableMixin<ConfigEntry> {
throw Exception('File not found');
}
}
_finalizer.attach(this, _configPointer, detach: this);
}
/// Opens the system configuration file.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Config.system() {
libgit2.git_libgit2_init();
_configPointer = bindings.open(bindings.findSystem());
// coverage:ignore-start
_finalizer.attach(this, _configPointer, detach: this);
// coverage:ignore-end
}
/// Opens the global configuration file.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Config.global() {
libgit2.git_libgit2_init();
_configPointer = bindings.open(bindings.findGlobal());
// coverage:ignore-start
_finalizer.attach(this, _configPointer, detach: this);
// coverage:ignore-end
}
/// Opens the global XDG configuration file.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Config.xdg() {
libgit2.git_libgit2_init();
_configPointer = bindings.open(bindings.findXdg());
// coverage:ignore-start
_finalizer.attach(this, _configPointer, detach: this);
// coverage:ignore-end
}
/// Pointer to memory address for allocated config object.
@ -83,11 +86,24 @@ class Config with IterableMixin<ConfigEntry> {
/// Returns the [ConfigEntry] of a [variable].
ConfigEntry operator [](String variable) {
return ConfigEntry(
bindings.getEntry(
configPointer: _configPointer,
variable: variable,
),
final entryPointer = bindings.getEntry(
configPointer: _configPointer,
variable: variable,
);
final name = entryPointer.ref.name.cast<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.
void free() => bindings.free(_configPointer);
void free() {
bindings.free(_configPointer);
_finalizer.detach(this);
}
@override
Iterator<ConfigEntry> get iterator =>
_ConfigIterator(bindings.iterator(_configPointer));
}
class ConfigEntry {
/// Initializes a new instance of [ConfigEntry] class from provided
/// pointer to config entry object in memory.
const ConfigEntry(this._configEntryPointer);
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_config>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
/// Pointer to memory address for allocated config entry object.
final Pointer<git_config_entry> _configEntryPointer;
class ConfigEntry {
ConfigEntry._({
required this.name,
required this.value,
required this.includeDepth,
required this.level,
});
/// Name of the entry (normalised).
String get name => _configEntryPointer.ref.name.cast<Utf8>().toDartString();
final String name;
/// Value of the entry.
String get value => _configEntryPointer.ref.value.cast<Utf8>().toDartString();
final String value;
/// Depth of includes where this variable was found
int get includeDepth => _configEntryPointer.ref.include_depth;
final int includeDepth;
/// Which config file this was found in.
GitConfigLevel get level {
return GitConfigLevel.values.singleWhere(
(e) => _configEntryPointer.ref.level == e.value,
);
}
final GitConfigLevel level;
@override
String toString() {
@ -206,7 +227,9 @@ class ConfigEntry {
}
class _ConfigIterator implements Iterator<ConfigEntry> {
_ConfigIterator(this._iteratorPointer);
_ConfigIterator(this._iteratorPointer) {
_iteratorFinalizer.attach(this, _iteratorPointer);
}
/// Pointer to memory address for allocated config iterator.
final Pointer<git_config_iterator> _iteratorPointer;
@ -225,7 +248,20 @@ class _ConfigIterator implements Iterator<ConfigEntry> {
} else {
error = libgit2.git_config_next(entry, _iteratorPointer);
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;
} else {
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

@ -10,9 +10,9 @@ import 'package:libgit2dart/src/util.dart';
class Diff {
/// Initializes a new instance of [Diff] class from provided
/// pointer to diff object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Diff(this._diffPointer);
Diff(this._diffPointer) {
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Creates a diff between the [repo]sitory [index] and the workdir directory.
///
@ -30,8 +30,6 @@ class Diff {
/// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Diff.indexToWorkdir({
required Repository repo,
@ -47,6 +45,7 @@ class Diff {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Creates a diff between a [tree] and [repo]sitory [index].
@ -83,6 +82,7 @@ class Diff {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Creates a diff between a [tree] and the working directory.
@ -124,6 +124,7 @@ class Diff {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Creates a diff between a [tree] and the working directory using index
@ -145,8 +146,6 @@ class Diff {
/// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Diff.treeToWorkdirWithIndex({
required Repository repo,
@ -162,6 +161,7 @@ class Diff {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Creates a diff with the difference between two [Tree] objects.
@ -204,6 +204,7 @@ class Diff {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Creates a diff with the difference between two [Index] objects.
@ -239,6 +240,7 @@ class Diff {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _diffPointer, detach: this);
}
/// Reads the [content]s of a git patch file into a git diff object.
@ -255,6 +257,7 @@ class Diff {
Diff.parse(String content) {
libgit2.git_libgit2_init();
_diffPointer = bindings.parse(content);
_finalizer.attach(this, _diffPointer, detach: this);
}
late final Pointer<git_diff> _diffPointer;
@ -441,7 +444,10 @@ class Diff {
Oid get patchOid => Oid(bindings.patchOid(_diffPointer));
/// Releases memory allocated for diff object.
void free() => bindings.free(_diffPointer);
void free() {
bindings.free(_diffPointer);
_finalizer.detach(this);
}
@override
String toString() {
@ -449,6 +455,12 @@ class Diff {
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_diff>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class DiffDelta {
/// Initializes a new instance of [DiffDelta] class from provided
/// pointer to diff delta object in memory.
@ -543,9 +555,9 @@ class DiffFile {
class DiffStats {
/// Initializes a new instance of [DiffStats] class from provided
/// pointer to diff stats object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
const DiffStats(this._diffStatsPointer);
DiffStats(this._diffStatsPointer) {
_statsFinalizer.attach(this, _diffStatsPointer, detach: this);
}
/// Pointer to memory address for allocated diff delta object.
final Pointer<git_diff_stats> _diffStatsPointer;
@ -573,7 +585,10 @@ class DiffStats {
}
/// Releases memory allocated for diff stats object.
void free() => bindings.statsFree(_diffStatsPointer);
void free() {
bindings.statsFree(_diffStatsPointer);
_statsFinalizer.detach(this);
}
@override
String toString() {
@ -582,6 +597,12 @@ class DiffStats {
}
}
// coverage:ignore-start
final _statsFinalizer = Finalizer<Pointer<git_diff_stats>>(
(pointer) => bindings.statsFree(pointer),
);
// coverage:ignore-end
class DiffHunk {
/// Initializes a new instance of [DiffHunk] class from provided
/// pointers to patch object and diff hunk object in memory and number of

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,9 +8,9 @@ import 'package:libgit2dart/src/util.dart';
class Patch {
/// Initializes a new instance of [Patch] class from provided
/// pointer to patch object in memory and pointers to old and new blobs/buffers.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Patch(this._patchPointer);
Patch(this._patchPointer) {
_finalizer.attach(this, _patchPointer, detach: this);
}
/// Directly generates a [Patch] from the difference between two blobs.
///
@ -30,8 +30,6 @@ class Patch {
/// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Patch.fromBlobs({
required Blob? oldBlob,
@ -51,6 +49,7 @@ class Patch {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _patchPointer, detach: this);
}
/// Directly generates a [Patch] from the difference between the blob and a
@ -72,8 +71,6 @@ class Patch {
/// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Patch.fromBlobAndBuffer({
required Blob? blob,
@ -93,6 +90,7 @@ class Patch {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _patchPointer, detach: this);
}
/// Directly generates a [Patch] from the difference between two buffers
@ -113,8 +111,6 @@ class Patch {
/// [interhunkLines] is the maximum number of unchanged lines between hunk
/// boundaries before the hunks will be merged into one. Defaults to 0.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Patch.fromBuffers({
required String? oldBuffer,
@ -136,6 +132,7 @@ class Patch {
contextLines: contextLines,
interhunkLines: interhunkLines,
);
_finalizer.attach(this, _patchPointer, detach: this);
}
/// Creates a patch for an entry in the diff list.
@ -144,11 +141,10 @@ class Patch {
///
/// [index] is the position of an entry in diff list.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Patch.fromDiff({required Diff diff, required int index}) {
_patchPointer = bindings.fromDiff(diffPointer: diff.pointer, index: index);
_finalizer.attach(this, _patchPointer, detach: this);
}
late final Pointer<git_patch> _patchPointer;
@ -217,12 +213,21 @@ class Patch {
}
/// Releases memory allocated for patch object.
void free() => bindings.free(_patchPointer);
void free() {
bindings.free(_patchPointer);
_finalizer.detach(this);
}
@override
String toString() => 'Patch{size: ${size()}, delta: $delta}';
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_patch>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
/// Line counts of each type in a patch.
class PatchStats {
const PatchStats({

View file

@ -17,8 +17,6 @@ class Rebase {
/// [onto] is the branch to rebase onto, default is to rebase onto the given
/// [upstream].
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Rebase.init({
required Repository repo,
@ -32,16 +30,16 @@ class Rebase {
upstreamPointer: upstream?.pointer,
ontoPointer: onto?.pointer,
);
_finalizer.attach(this, _rebasePointer, detach: this);
}
/// Opens an existing rebase that was previously started by either an
/// invocation of [Rebase.init] or by another client.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Rebase.open(Repository repo) {
_rebasePointer = bindings.open(repo.pointer);
_finalizer.attach(this, _rebasePointer, detach: this);
}
/// Pointer to memory address for allocated rebase object.
@ -138,9 +136,18 @@ class Rebase {
void abort() => bindings.abort(_rebasePointer);
/// Releases memory allocated for rebase object.
void free() => bindings.free(_rebasePointer);
void free() {
bindings.free(_rebasePointer);
_finalizer.detach(this);
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_rebase>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class RebaseOperation {
/// Initializes a new instance of the [RebaseOperation] class from
/// provided pointer to rebase operation object in memory.

View file

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

View file

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

View file

@ -111,13 +111,11 @@ class Repository {
///
/// [bare] whether cloned repo should be bare.
///
/// [remote] is the callback function with
/// `Remote Function(Repository repo, String name, String url)` signature.
/// The [Remote] it returns will be used instead of default one.
/// [remoteCallback] is the [RemoteCallback] object values that will be used
/// in creation and customization process of remote instead of default ones.
///
/// [repository] is the callback function matching the
/// `Repository Function(String path, bool bare)` signature. The [Repository]
/// it returns will be used instead of creating a new one.
/// [repositoryCallback] is the [RepositoryCallback] object values that will
/// be used in creation and customization process of repository.
///
/// [checkoutBranch] is the name of the branch to checkout after the clone.
/// Defaults to using the remote's default branch.
@ -132,8 +130,8 @@ class Repository {
required String url,
required String localPath,
bool bare = false,
Remote Function(Repository, String, String)? remote,
Repository Function(String, bool)? repository,
RemoteCallback? remoteCallback,
RepositoryCallback? repositoryCallback,
String? checkoutBranch,
Callbacks callbacks = const Callbacks(),
}) {
@ -143,8 +141,8 @@ class Repository {
url: url,
localPath: localPath,
bare: bare,
remote: remote,
repository: repository,
remoteCallback: remoteCallback,
repositoryCallback: repositoryCallback,
checkoutBranch: checkoutBranch,
callbacks: callbacks,
);
@ -375,8 +373,6 @@ class Repository {
/// If a configuration file has not been set, the default config set for the
/// repository will be returned, including global and system configurations
/// (if they are available).
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Config get config => Config(bindings.config(_repoPointer));
/// Snapshot of the repository's configuration.
@ -386,24 +382,16 @@ class Repository {
///
/// The contents of this snapshot will not change, even if the underlying
/// config files are modified.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer));
/// Repository's head.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Reference get head => Reference(bindings.head(_repoPointer));
/// Index file for this repository.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Index get index => Index(bindings.index(_repoPointer));
/// ODB for this repository.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Odb get odb => Odb(bindings.odb(_repoPointer));
@ -419,23 +407,17 @@ class Repository {
/// List of all branches that can be found in a repository.
///
/// **IMPORTANT**: Branches should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
List<Branch> get branches => Branch.list(repo: this);
/// List of local branches that can be found in a repository.
///
/// **IMPORTANT**: Branches should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
List<Branch> get branchesLocal =>
Branch.list(repo: this, type: GitBranch.local);
/// List of remote branches that can be found in a repository.
///
/// **IMPORTANT**: Branches should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
List<Branch> get branchesRemote =>
Branch.list(repo: this, type: GitBranch.remote);
@ -465,8 +447,6 @@ class Repository {
///
/// If [sorting] isn't provided default will be used (reverse chronological
/// order, like in git).
///
/// **IMPORTANT**: Commits should be freed to release allocated memory.
List<Commit> log({
required Oid oid,
Set<GitSort> sorting = const {GitSort.none},
@ -477,8 +457,6 @@ class Repository {
walker.push(oid);
final result = walker.walk();
walker.free();
return result;
}
@ -737,6 +715,7 @@ class Repository {
}
}
// ignore: no_leading_underscores_for_local_identifiers
final _packDelegate = packDelegate ?? packAll;
final packbuilder = PackBuilder(this);
@ -745,10 +724,79 @@ class Repository {
}
_packDelegate(packbuilder);
packbuilder.write(path);
final result = packbuilder.writtenLength;
packbuilder.free();
return result;
return packbuilder.writtenLength;
}
}
class RepositoryCallback {
/// Values used to override the repository creation and customization process
/// during a clone operation.
///
/// [path] is the path to the repository.
///
/// [bare] whether new repository should be bare.
///
/// [flags] is a combination of [GitRepositoryInit] flags. Defaults to
/// [GitRepositoryInit.mkdir].
///
/// [mode] is the permissions for the folder. Default to 0 (permissions
/// configured by umask).
///
/// [workdirPath] is the path to the working directory. Can be null for
/// default path.
///
/// [description] if set will be used to initialize the "description" file in
/// the repository, instead of using the template content.
///
/// [templatePath] is the the path to use for the template directory if
/// [GitRepositoryInit.externalTemplate] is set. Defaults to the config or
/// default directory options.
///
/// [initialHead] is the name of the head to point HEAD at. If null, then
/// this will be treated as "master" and the HEAD ref will be set to
/// "refs/heads/master". If this begins with "refs/" it will be used
/// verbatim, otherwise "refs/heads/" will be prefixed.
///
/// [originUrl] if set, then after the rest of the repository initialization
/// is completed, an "origin" remote will be added pointing to this URL.
const RepositoryCallback({
required this.path,
this.bare = false,
this.flags = const {GitRepositoryInit.mkpath},
this.mode = 0,
this.workdirPath,
this.description,
this.templatePath,
this.initialHead,
this.originUrl,
});
/// Path to the repository.
final String path;
/// Whether repository is bare.
final bool bare;
/// Combination of [GitRepositoryInit] flags.
final Set<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
/// passed in, reference_out will be valued as well.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
RevParse.ext({required Repository repo, required String spec}) {
final pointers = bindings.revParseExt(
@ -51,8 +49,6 @@ class RevParse {
/// final tag = RevParse.single(repo: repo, spec: 'v1.0') as Tag;
/// ```
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
static Object single({required Repository repo, required String spec}) {
final object = bindings.revParseSingle(
@ -102,13 +98,9 @@ class RevSpec {
final Pointer<git_revspec> _revSpecPointer;
/// Left element of the revspec.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit get from => Commit(_revSpecPointer.ref.from.cast());
/// Right element of the revspec.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Commit? get to {
return _revSpecPointer.ref.to == nullptr
? null

View file

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

View file

@ -10,17 +10,16 @@ import 'package:meta/meta.dart';
class Signature {
/// Initializes a new instance of [Signature] class from provided pointer to
/// signature object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Signature(this._signaturePointer);
Signature(Pointer<git_signature> pointer) {
_signaturePointer = bindings.duplicate(pointer);
_finalizer.attach(this, _signaturePointer, detach: this);
}
/// Creates new [Signature] from provided [name], [email], and optional [time]
/// in seconds from epoch and [offset] in minutes.
///
/// If [time] isn't provided [Signature] will be created with a timestamp of
/// 'now'.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Signature.create({
required String name,
required String email,
@ -39,6 +38,7 @@ class Signature {
offset: offset,
);
}
_finalizer.attach(this, _signaturePointer, detach: this);
}
/// Creates a new action signature with default user and now timestamp.
@ -48,6 +48,7 @@ class Signature {
/// on that information.
Signature.defaultSignature(Repository repo) {
_signaturePointer = bindings.defaultSignature(repo.pointer);
_finalizer.attach(this, _signaturePointer, detach: this);
}
late final Pointer<git_signature> _signaturePointer;
@ -79,7 +80,10 @@ class Signature {
}
/// Releases memory allocated for signature object.
void free() => bindings.free(_signaturePointer);
void free() {
bindings.free(_signaturePointer);
_finalizer.detach(this);
}
@override // coverage:ignore-line
int get hashCode =>
@ -91,3 +95,9 @@ class Signature {
'offset: $offset}';
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<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
/// same).
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Submodule.lookup({required Repository repo, required String name}) {
_submodulePointer = bindings.lookup(
repoPointer: repo.pointer,
name: name,
);
_finalizer.attach(this, _submodulePointer, detach: this);
}
/// Adds a submodule to the index.
@ -29,8 +28,6 @@ class Submodule {
/// [callbacks] is the combination of callback functions from [Callbacks]
/// object.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Submodule.add({
required Repository repo,
@ -49,6 +46,8 @@ class Submodule {
bindings.clone(submodule: _submodulePointer, callbacks: callbacks);
bindings.addFinalize(_submodulePointer);
_finalizer.attach(this, _submodulePointer, detach: this);
}
/// Pointer to memory address for allocated submodule object.
@ -264,7 +263,10 @@ class Submodule {
}
/// Releases memory allocated for submodule object.
void free() => bindings.free(_submodulePointer);
void free() {
bindings.free(_submodulePointer);
_finalizer.detach(this);
}
@override
String toString() {
@ -273,3 +275,9 @@ class Submodule {
'workdirOid: $workdirOid, ignore: $ignore, updateRule: $updateRule}';
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<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 {
/// Initializes a new instance of [Tag] class from provided pointer to
/// tag object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Tag(this._tagPointer);
Tag(this._tagPointer) {
_finalizer.attach(this, _tagPointer, detach: this);
}
/// Lookups tag object for provided [oid] in a [repo]sitory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Tag.lookup({required Repository repo, required Oid oid}) {
_tagPointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
);
_finalizer.attach(this, _tagPointer, detach: this);
}
/// Pointer to memory address for allocated tag object.
@ -160,9 +159,6 @@ class Tag {
/// Returned object should be explicitly downcasted to one of four of git
/// object types.
///
/// **IMPORTANT**: returned object should be freed to release allocated
/// memory.
///
/// ```dart
/// final commit = tag.target as Commit;
/// final tree = tag.target as Tree;
@ -215,7 +211,10 @@ class Tag {
}
/// Releases memory allocated for tag object.
void free() => bindings.free(_tagPointer);
void free() {
bindings.free(_tagPointer);
_finalizer.detach(this);
}
@override
String toString() {
@ -223,3 +222,9 @@ class Tag {
'tagger: $tagger}';
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<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 {
/// Initializes a new instance of [Tree] class from provided pointer to
/// tree object in memory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Tree(this._treePointer);
Tree(this._treePointer) {
_finalizer.attach(this, _treePointer, detach: this);
}
/// Lookups a tree object for provided [oid] in a [repo]sitory.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
Tree.lookup({required Repository repo, required Oid oid}) {
_treePointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
);
_finalizer.attach(this, _treePointer, detach: this);
}
late final Pointer<git_tree> _treePointer;
@ -63,7 +62,7 @@ class Tree {
),
);
} else if (value is String && value.contains('/')) {
return TreeEntry(
return TreeEntry._byPath(
bindings.getByPath(
rootPointer: _treePointer,
path: value,
@ -90,7 +89,10 @@ class Tree {
int get length => bindings.entryCount(_treePointer);
/// Releases memory allocated for tree object.
void free() => bindings.free(_treePointer);
void free() {
bindings.free(_treePointer);
_finalizer.detach(this);
}
@override
String toString() {
@ -98,10 +100,24 @@ class Tree {
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_tree>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end
class TreeEntry {
/// Initializes a new instance of [TreeEntry] class from provided pointer to
/// tree entry object in memory.
const TreeEntry(this._treeEntryPointer);
TreeEntry(this._treeEntryPointer);
/// Initializes a new instance of [TreeEntry] class from provided pointer to
/// tree entry object in memory.
///
/// Unlike the other lookup methods, must be freed.
TreeEntry._byPath(this._treeEntryPointer) {
_entryFinalizer.attach(this, _treeEntryPointer, detach: this);
}
/// Pointer to memory address for allocated tree entry object.
final Pointer<git_tree_entry> _treeEntryPointer;
@ -119,8 +135,19 @@ class TreeEntry {
}
/// Releases memory allocated for tree entry object.
void free() => bindings.entryFree(_treeEntryPointer);
///
/// **IMPORTANT**: Only tree entries looked up by path should be freed.
void free() {
bindings.entryFree(_treeEntryPointer);
_entryFinalizer.detach(this);
}
@override
String toString() => 'TreeEntry{oid: $oid, name: $name, filemode: $filemode}';
}
// coverage:ignore-start
final _entryFinalizer = Finalizer<Pointer<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
/// [repo]sitory and optional [tree] objects.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
TreeBuilder({required Repository repo, Tree? tree}) {
_treeBuilderPointer = bindings.create(
repoPointer: repo.pointer,
sourcePointer: tree?.pointer ?? nullptr,
);
_finalizer.attach(this, _treeBuilderPointer, detach: this);
}
/// Pointer to memory address for allocated tree builder object.
@ -31,9 +30,6 @@ class TreeBuilder {
/// Returns an entry from the tree builder with provided [filename].
///
/// **IMPORTANT**: the returned entry is owned by the tree builder and
/// should not be freed manually.
///
/// Throws [ArgumentError] if nothing found for provided [filename].
TreeEntry operator [](String filename) {
return TreeEntry(
@ -83,10 +79,19 @@ class TreeBuilder {
}
/// Releases memory allocated for tree builder object and all the entries.
void free() => bindings.free(_treeBuilderPointer);
void free() {
bindings.free(_treeBuilderPointer);
_finalizer.detach(this);
}
@override
String toString() {
return 'TreeBuilder{length: $length}';
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<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.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Worktree.create({
required Repository repo,
@ -30,15 +28,15 @@ class Worktree {
path: path,
refPointer: ref?.pointer,
);
_finalizer.attach(this, _worktreePointer, detach: this);
}
/// Lookups existing worktree in [repo] with provided [name].
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Worktree.lookup({required Repository repo, required String name}) {
_worktreePointer = bindings.lookup(repoPointer: repo.pointer, name: name);
_finalizer.attach(this, _worktreePointer, detach: this);
}
/// Pointer to memory address for allocated branch object.
@ -88,10 +86,19 @@ class Worktree {
bool get isValid => bindings.isValid(_worktreePointer);
/// Releases memory allocated for worktree object.
void free() => bindings.free(_worktreePointer);
void free() {
bindings.free(_worktreePointer);
_finalizer.detach(this);
}
@override
String toString() {
return 'Worktree{name: $name, path: $path}';
}
}
// coverage:ignore-start
final _finalizer = Finalizer<Pointer<git_worktree>>(
(pointer) => bindings.free(pointer),
);
// coverage:ignore-end

View file

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

View file

@ -29,8 +29,6 @@ void main() {
expect(annotated.oid, tip);
expect(annotated.refName, '');
annotated.free();
});
test('throws when trying to lookup annotated commit with invalid oid', () {
@ -49,25 +47,18 @@ void main() {
expect(annotated.oid, reference.target);
expect(annotated.refName, 'refs/heads/master');
annotated.free();
reference.free();
});
test(
'throws when trying to create annotated commit from provided '
'reference and error occurs', () {
final reference = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(
() => AnnotatedCommit.fromReference(
repo: Repository(nullptr),
reference: reference,
reference: Reference.lookup(repo: repo, name: 'refs/heads/master'),
),
throwsA(isA<LibGit2Error>()),
);
reference.free();
});
test('creates annotated commit from provided revspec', () {
@ -75,8 +66,6 @@ void main() {
expect(annotated.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
expect(annotated.refName, '');
annotated.free();
});
test('throws when trying to create annotated commit from invalid revspec',
@ -98,8 +87,6 @@ void main() {
expect(annotated.oid, oid);
expect(annotated.refName, 'master');
annotated.free();
});
test(
@ -115,5 +102,10 @@ void main() {
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(() {
sig1.free();
sig2.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
@ -82,8 +80,6 @@ void main() {
expect(blame[i].isBoundary, hunks[i]['isBoundary']);
expect(blame[i].originPath, 'feature_file');
}
blame.free();
});
test('throws when provided file path is invalid', () {
@ -104,9 +100,6 @@ void main() {
expect(blameHunk.originCommitter, null);
expect(blameHunk.finalCommitOid.sha, '0' * 40);
expect(blameHunk.finalCommitter, null);
bufferBlame.free();
blame.free();
});
test('throws when trying to get blame for empty buffer', () {
@ -115,7 +108,6 @@ void main() {
() => Blame.buffer(reference: blame, buffer: ''),
throwsA(isA<LibGit2Error>()),
);
blame.free();
});
test('returns the blame for provided file with minMatchCharacters set', () {
@ -127,8 +119,6 @@ void main() {
);
expect(blame.length, 2);
blame.free();
});
test('returns the blame for provided line', () {
@ -148,22 +138,16 @@ void main() {
expect(hunk.originCommitter, hunks[0]['originCommitter']);
expect(hunk.isBoundary, hunks[0]['isBoundary']);
expect(hunk.originPath, 'feature_file');
blame.free();
});
test('throws when provided index for hunk is invalid', () {
final blame = Blame.file(repo: repo, path: 'feature_file');
expect(() => blame[10], throwsA(isA<RangeError>()));
blame.free();
});
test('throws when provided line number for hunk is invalid', () {
final blame = Blame.file(repo: repo, path: 'feature_file');
expect(() => blame.forLine(10), throwsA(isA<RangeError>()));
blame.free();
});
test('returns the blame for provided file with newestCommit argument', () {
@ -190,8 +174,6 @@ void main() {
expect(hunk.originCommitter, hunks[0]['originCommitter']);
expect(hunk.isBoundary, hunks[0]['isBoundary']);
expect(hunk.originPath, 'feature_file');
blame.free();
});
test('returns the blame for provided file with minLine and maxLine set',
@ -219,14 +201,16 @@ void main() {
expect(blame[i].isBoundary, hunks[i]['isBoundary']);
expect(blame[i].originPath, 'feature_file');
}
});
blame.free();
test('manually releases allocated memory', () {
final blame = Blame.file(repo: repo, path: 'feature_file');
expect(() => blame.free(), returnsNormally);
});
test('returns string representation of BlameHunk object', () {
final blame = Blame.file(repo: repo, path: 'feature_file');
expect(blame.toString(), contains('BlameHunk{'));
blame.free();
});
});
}

View file

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

View file

@ -34,8 +34,6 @@ void main() {
for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]);
expect(aliasBranches[i].name, branchesExpected[i]);
branches[i].free();
aliasBranches[i].free();
}
});
@ -47,8 +45,6 @@ void main() {
for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]);
expect(aliasBranches[i].name, branchesExpected[i]);
branches[i].free();
aliasBranches[i].free();
}
});
@ -60,8 +56,6 @@ void main() {
for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]);
expect(aliasBranches[i].name, branchesExpected[i]);
branches[i].free();
aliasBranches[i].free();
}
});
@ -75,7 +69,6 @@ void main() {
test('returns a branch with provided name', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.target.sha, lastCommit.sha);
branch.free();
});
test('throws when provided name not found', () {
@ -100,15 +93,11 @@ void main() {
expect(masterBranch.isHead, true);
expect(featureBranch.isHead, false);
masterBranch.free();
featureBranch.free();
});
test('throws when checking if branch is current head and error occurs', () {
final nullBranch = Branch(nullptr);
expect(
() => nullBranch.isHead,
() => Branch(nullptr).isHead,
throwsA(isA<LibGit2Error>()),
);
});
@ -119,15 +108,11 @@ void main() {
expect(masterBranch.isCheckedOut, true);
expect(featureBranch.isCheckedOut, false);
masterBranch.free();
featureBranch.free();
});
test('throws when checking if branch is checked out and error occurs', () {
final nullBranch = Branch(nullptr);
expect(
() => nullBranch.isCheckedOut,
() => Branch(nullptr).isCheckedOut,
throwsA(isA<LibGit2Error>()),
);
});
@ -135,18 +120,15 @@ void main() {
test('returns name', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.name, 'master');
branch.free();
});
test('throws when getting name and error occurs', () {
final nullBranch = Branch(nullptr);
expect(() => nullBranch.name, throwsA(isA<LibGit2Error>()));
expect(() => Branch(nullptr).name, throwsA(isA<LibGit2Error>()));
});
test('returns remote name of a remote-tracking branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(branch.remoteName, 'origin');
branch.free();
});
test(
@ -162,21 +144,16 @@ void main() {
expect(upstream.isRemote, true);
expect(upstream.name, 'refs/remotes/origin/master');
upstream.free();
branch.free();
});
test('throws when trying to get upstream of a remote branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(() => branch.upstream, throwsA(isA<LibGit2Error>()));
branch.free();
});
test('sets upstream of a branch', () {
final branch = Branch.lookup(repo: repo, name: 'master');
var upstream = branch.upstream;
expect(upstream.name, 'refs/remotes/origin/master');
expect(branch.upstream.name, 'refs/remotes/origin/master');
final ref = Reference.create(
repo: repo,
@ -185,24 +162,15 @@ void main() {
);
branch.setUpstream(ref.shorthand);
upstream = branch.upstream;
expect(upstream.name, 'refs/remotes/origin/new');
ref.free();
upstream.free();
branch.free();
expect(branch.upstream.name, 'refs/remotes/origin/new');
});
test('unsets upstream of a branch', () {
final branch = Branch.lookup(repo: repo, name: 'master');
final upstream = branch.upstream;
expect(upstream.name, 'refs/remotes/origin/master');
expect(branch.upstream.name, 'refs/remotes/origin/master');
branch.setUpstream(null);
expect(() => branch.upstream, throwsA(isA<LibGit2Error>()));
upstream.free();
branch.free();
});
test('throws when trying to set upstream of a branch and error occurs', () {
@ -211,64 +179,50 @@ void main() {
() => branch.setUpstream('some/upstream'),
throwsA(isA<LibGit2Error>()),
);
branch.free();
});
test('returns upstream name of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.upstreamName, 'refs/remotes/origin/master');
branch.free();
});
test('throws when trying to get upstream name of a branch and error occurs',
() {
final branch = Branch.lookup(repo: repo, name: 'feature');
expect(() => branch.upstreamName, throwsA(isA<LibGit2Error>()));
branch.free();
});
test('returns upstream remote of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.upstreamRemote, 'origin');
branch.free();
});
test('throws when trying to get upstream remote of a remote branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(() => branch.upstreamRemote, throwsA(isA<LibGit2Error>()));
branch.free();
});
test('returns upstream merge of a local branch', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.upstreamMerge, 'refs/heads/master');
branch.free();
});
test('throws when trying to get upstream merge of a remote branch', () {
final branch = Branch.list(repo: repo, type: GitBranch.remote).first;
expect(() => branch.upstreamMerge, throwsA(isA<LibGit2Error>()));
branch.free();
});
group('create()', () {
test('creates branch', () {
final commit = Commit.lookup(repo: repo, oid: lastCommit);
final branch = Branch.create(
repo: repo,
name: 'testing',
target: commit,
target: Commit.lookup(repo: repo, oid: lastCommit),
);
final branches = Branch.list(repo: repo);
expect(branches.length, 4);
expect(branch.target, lastCommit);
for (final branch in branches) {
branch.free();
}
branch.free();
commit.free();
});
test('throws when name already exists', () {
@ -278,28 +232,19 @@ void main() {
() => Branch.create(repo: repo, name: 'feature', target: commit),
throwsA(isA<LibGit2Error>()),
);
commit.free();
});
test('creates branch with force flag when name already exists', () {
final commit = Commit.lookup(repo: repo, oid: lastCommit);
final branch = Branch.create(
repo: repo,
name: 'feature',
target: commit,
target: Commit.lookup(repo: repo, oid: lastCommit),
force: true,
);
final localBranches = Branch.list(repo: repo, type: GitBranch.local);
expect(localBranches.length, 2);
expect(branch.target, lastCommit);
for (final branch in localBranches) {
branch.free();
}
branch.free();
commit.free();
});
});
@ -333,11 +278,6 @@ void main() {
throwsA(isA<LibGit2Error>()),
);
expect(branch.target, featureCommit);
for (final branch in branches) {
branch.free();
}
branch.free();
});
test('throws when name already exists', () {
@ -361,8 +301,6 @@ void main() {
final branch = Branch.lookup(repo: repo, name: 'feature');
expect(branch.target, lastCommit);
branch.free();
});
test('throws when name is invalid', () {
@ -377,10 +315,14 @@ void main() {
});
});
test('manually releases allocated memory', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(() => branch.free(), returnsNormally);
});
test('returns string representation of Branch object', () {
final branch = Branch.lookup(repo: repo, name: 'master');
expect(branch.toString(), contains('Branch{'));
branch.free();
});
});
}

View file

@ -89,12 +89,6 @@ void main() {
featureTree.entries.any((e) => e.name == 'another_feature_file'),
true,
);
repoHead.free();
featureTree.free();
featureHead.free();
masterTree.free();
masterHead.free();
});
test(
@ -121,10 +115,6 @@ void main() {
// does not change HEAD
expect(repoHead.target, isNot(featureHead.oid));
expect(index.find('another_feature_file'), equals(true));
repoHead.free();
featureHead.free();
index.free();
});
test('checkouts commit with provided path', () {
@ -144,26 +134,19 @@ void main() {
'another_feature_file': {GitStatus.indexNew}
},
);
repoHead.free();
featureHead.free();
});
test(
'throws when trying to checkout commit with invalid alternative '
'directory', () {
final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']);
expect(
() => Checkout.commit(
repo: repo,
commit: commit,
commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']),
directory: 'not/there',
),
throwsA(isA<LibGit2Error>()),
);
commit.free();
});
test('checkouts with alrenative directory', () {
@ -220,8 +203,6 @@ void main() {
);
expect(index.length, 4);
expect(file.existsSync(), false);
index.free();
});
});
}

View file

@ -34,18 +34,13 @@ void main() {
});
tearDown(() {
author.free();
committer.free();
tree.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
group('Commit', () {
test('lookups commit for provided oid', () {
final commit = Commit.lookup(repo: repo, oid: tip);
expect(commit, isA<Commit>());
commit.free();
expect(Commit.lookup(repo: repo, oid: tip), isA<Commit>());
});
test('throws when trying to lookup with invalid oid', () {
@ -72,9 +67,6 @@ void main() {
expect(index.find('dir/dir_file.txt'), false);
expect(file.existsSync(), false);
index.free();
commit.free();
});
test('throws when trying to revert and error occurs', () {
@ -92,11 +84,6 @@ void main() {
final revertIndex = from.revertTo(commit: to);
expect(revertIndex.find('dir/dir_file.txt'), false);
expect(file.existsSync(), true);
revertIndex.free();
index.free();
to.free();
from.free();
});
test('throws when trying to revert commit and error occurs', () {
@ -114,9 +101,6 @@ void main() {
expect(commit1.descendantOf(commit2.oid), true);
expect(commit1.descendantOf(commit1.oid), false);
expect(commit2.descendantOf(commit1.oid), false);
commit1.free();
commit2.free();
});
test('creates commit', () {
@ -146,13 +130,9 @@ void main() {
expect(commit.treeOid, tree.oid);
expect(commit.parents.length, 1);
expect(commit.parents[0], tip);
commit.free();
parent.free();
});
test('writes commit without parents into the buffer', () {
final parent = Commit.lookup(repo: repo, oid: tip);
final commit = Commit.createBuffer(
repo: repo,
updateRef: 'HEAD',
@ -174,8 +154,6 @@ Some description.
""";
expect(commit, expected);
parent.free();
});
test('writes commit into the buffer', () {
@ -202,8 +180,6 @@ Some description.
""";
expect(commit, expected);
parent.free();
});
test('creates commit without parents', () {
@ -227,8 +203,6 @@ Some description.
expect(commit.time, 124);
expect(commit.treeOid, tree.oid);
expect(commit.parents.length, 0);
commit.free();
});
test('creates commit with 2 parents', () {
@ -257,57 +231,42 @@ Some description.
expect(commit.parents.length, 2);
expect(commit.parents[0], tip);
expect(commit.parents[1], parent2.oid);
parent1.free();
parent2.free();
commit.free();
});
test('throws when trying to create commit and error occurs', () {
final parent = Commit.lookup(repo: repo, oid: tip);
final nullRepo = Repository(nullptr);
expect(
() => Commit.create(
repo: nullRepo,
repo: Repository(nullptr),
updateRef: 'HEAD',
message: message,
author: author,
committer: committer,
tree: tree,
parents: [parent],
parents: [Commit.lookup(repo: repo, oid: tip)],
),
throwsA(isA<LibGit2Error>()),
);
parent.free();
});
test('throws when trying to write commit into a buffer and error occurs',
() {
final parent = Commit.lookup(repo: repo, oid: tip);
final nullRepo = Repository(nullptr);
expect(
() => Commit.createBuffer(
repo: nullRepo,
repo: Repository(nullptr),
updateRef: 'HEAD',
message: message,
author: author,
committer: committer,
tree: tree,
parents: [parent],
parents: [Commit.lookup(repo: repo, oid: tip)],
),
throwsA(isA<LibGit2Error>()),
);
parent.free();
});
test('amends commit with default arguments', () {
final oldHead = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
expect(commit.oid, oldHead.target);
expect(commit.oid, repo.head.target);
final amendedOid = Commit.amend(
repo: repo,
@ -316,25 +275,18 @@ Some description.
updateRef: 'HEAD',
);
final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid);
final newHead = repo.head;
expect(amendedCommit.oid, newHead.target);
expect(amendedCommit.oid, repo.head.target);
expect(amendedCommit.message, 'amended commit\n');
expect(amendedCommit.author, commit.author);
expect(amendedCommit.committer, commit.committer);
expect(amendedCommit.treeOid, commit.treeOid);
expect(amendedCommit.parents, commit.parents);
amendedCommit.free();
commit.free();
newHead.free();
oldHead.free();
});
test('amends commit with provided arguments', () {
final oldHead = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
expect(commit.oid, oldHead.target);
expect(commit.oid, repo.head.target);
final amendedOid = Commit.amend(
repo: repo,
@ -346,25 +298,18 @@ Some description.
tree: tree,
);
final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid);
final newHead = repo.head;
expect(amendedCommit.oid, newHead.target);
expect(amendedCommit.oid, repo.head.target);
expect(amendedCommit.message, 'amended commit\n');
expect(amendedCommit.author, author);
expect(amendedCommit.committer, committer);
expect(amendedCommit.treeOid, tree.oid);
expect(amendedCommit.parents, commit.parents);
amendedCommit.free();
commit.free();
newHead.free();
oldHead.free();
});
test('amends commit that is not the tip of the branch', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']);
expect(commit.oid, isNot(head.target));
expect(commit.oid, isNot(repo.head.target));
expect(
() => Commit.amend(
@ -375,17 +320,13 @@ Some description.
),
returnsNormally,
);
commit.free();
head.free();
});
test(
'throws when trying to amend commit that is not the tip of the branch '
'with HEAD provided as update reference', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']);
expect(commit.oid, isNot(head.target));
expect(commit.oid, isNot(repo.head.target));
expect(
() => Commit.amend(
@ -396,9 +337,6 @@ Some description.
),
throwsA(isA<LibGit2Error>()),
);
commit.free();
head.free();
});
test('creates an in-memory copy of a commit', () {
@ -406,9 +344,6 @@ Some description.
final dupCommit = commit.duplicate();
expect(dupCommit.oid, commit.oid);
dupCommit.free();
commit.free();
});
test('returns header field', () {
@ -417,7 +352,6 @@ Some description.
commit.headerField('parent'),
'78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8',
);
commit.free();
});
test('throws when header field not found', () {
@ -426,7 +360,6 @@ Some description.
() => commit.headerField('not-there'),
throwsA(isA<LibGit2Error>()),
);
commit.free();
});
test('returns nth generation ancestor commit', () {
@ -434,16 +367,12 @@ Some description.
final ancestor = commit.nthGenAncestor(3);
expect(ancestor.oid.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521');
ancestor.free();
commit.free();
});
test('throws when trying to get nth generation ancestor and none exists',
() {
final commit = Commit.lookup(repo: repo, oid: tip);
expect(() => commit.nthGenAncestor(10), throwsA(isA<LibGit2Error>()));
commit.free();
});
test('returns parent at specified position', () {
@ -453,22 +382,21 @@ Some description.
expect(firstParent.oid.sha, 'c68ff54aabf660fcdd9a2838d401583fe31249e3');
expect(secondParent.oid.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b');
secondParent.free();
firstParent.free();
commit.free();
});
test('throws when trying to get the parent at invalid position', () {
final commit = Commit.lookup(repo: repo, oid: tip);
expect(() => commit.parent(10), throwsA(isA<LibGit2Error>()));
commit.free();
});
test('manually releases allocated memory', () {
final commit = Commit.lookup(repo: repo, oid: tip);
expect(() => commit.free(), returnsNormally);
});
test('returns string representation of Commit object', () {
final commit = Commit.lookup(repo: repo, oid: tip);
expect(commit.toString(), contains('Commit{'));
commit.free();
});
});
}

View file

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

View file

@ -43,7 +43,6 @@ void main() {
test('throws when trying to describe and no reference found', () {
final commit = Commit.lookup(repo: repo, oid: repo['f17d0d4']);
expect(() => repo.describe(commit: commit), throwsA(isA<LibGit2Error>()));
commit.free();
});
test('returns oid when fallback argument is provided', () {
@ -52,7 +51,6 @@ void main() {
repo.describe(commit: commit, showCommitOidAsFallback: true),
'f17d0d4',
);
commit.free();
});
test('describes with provided strategy', () {
@ -64,7 +62,6 @@ void main() {
),
'heads/feature',
);
commit.free();
});
test('describes with provided pattern', () {
@ -87,9 +84,6 @@ void main() {
repo.describe(commit: commit, pattern: 'test/*'),
'test/tag1-2-gfc38877',
);
commit.free();
signature.free();
});
test('describes and follows first parent only', () {
@ -104,8 +98,6 @@ void main() {
),
'v0.1-1-g821ed6e',
);
commit.free();
});
test('describes with provided abbreviated size', () {
@ -129,8 +121,6 @@ void main() {
),
'v0.1',
);
commit.free();
});
test('describes with long format', () {
@ -138,21 +128,13 @@ void main() {
});
test('describes and appends dirty suffix', () {
final index = repo.index;
index.clear();
repo.index.clear();
expect(repo.describe(dirtySuffix: '-dirty'), 'v0.2-dirty');
index.free();
});
test('describes with max candidates tags flag set', () {
final index = repo.index;
index.clear();
repo.index.clear();
expect(repo.describe(maxCandidatesTags: 0), 'v0.2');
index.free();
});
});
}

View file

@ -133,71 +133,45 @@ index e69de29..c217c63 100644
group('Diff', () {
test('returns diff between index and workdir', () {
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(diff.length, 8);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToWorkdir[i]);
}
diff.free();
index.free();
});
test('returns diff between index and tree', () {
final index = repo.index;
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final diff = Diff.treeToIndex(repo: repo, tree: tree, index: index);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final diff = Diff.treeToIndex(
repo: repo,
tree: commit.tree,
index: repo.index,
);
expect(diff.length, 8);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToTree[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
index.free();
});
test('returns diff between index and empty tree', () {
final index = repo.index;
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final diff = Diff.treeToIndex(repo: repo, tree: null, index: index);
final diff = Diff.treeToIndex(repo: repo, tree: null, index: repo.index);
expect(diff.length, 12);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToIndex[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
index.free();
});
test('returns diff between tree and workdir', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final diff = Diff.treeToWorkdir(repo: repo, tree: tree);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final diff = Diff.treeToWorkdir(repo: repo, tree: commit.tree);
expect(diff.length, 9);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToWorkdir[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
});
test('throws when trying to diff between tree and workdir and error occurs',
@ -209,20 +183,13 @@ index e69de29..c217c63 100644
});
test('returns diff between tree and workdir with index', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final diff = Diff.treeToWorkdirWithIndex(repo: repo, tree: commit.tree);
final diff = Diff.treeToWorkdirWithIndex(repo: repo, tree: tree);
expect(diff.length, 11);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToWorkdirWithIndex[i]);
}
diff.free();
tree.free();
commit.free();
head.free();
});
test(
@ -238,68 +205,55 @@ index e69de29..c217c63 100644
});
test('returns diff between tree and tree', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree1 = commit.tree;
final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']);
final diff = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final newTree = Tree.lookup(repo: repo, oid: repo['b85d53c']);
final diff = Diff.treeToTree(
repo: repo,
oldTree: commit.tree,
newTree: newTree,
);
expect(diff.length, 10);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToTree[i]);
}
commit.free();
head.free();
tree1.free();
tree2.free();
diff.free();
});
test('returns diff between tree and empty tree', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final diff = Diff.treeToTree(repo: repo, oldTree: tree, newTree: null);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final diff = Diff.treeToTree(
repo: repo,
oldTree: commit.tree,
newTree: null,
);
expect(diff.length, 11);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToEmptyTree[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
});
test('returns diff between empty tree and tree', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final diff = Diff.treeToTree(repo: repo, oldTree: null, newTree: tree);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final diff = Diff.treeToTree(
repo: repo,
oldTree: null,
newTree: commit.tree,
);
expect(diff.length, 11);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, treeToEmptyTree[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
});
test('throws when trying to diff between tree and tree and error occurs',
() {
final nullTree = Tree(nullptr);
expect(
() => Diff.treeToTree(
repo: Repository(nullptr),
oldTree: nullTree,
newTree: nullTree,
oldTree: Tree(nullptr),
newTree: Tree(nullptr),
),
throwsA(isA<LibGit2Error>()),
);
@ -313,60 +267,45 @@ index e69de29..c217c63 100644
});
test('returns diff between index and index', () {
final index = repo.index;
final emptyIndex = Index.newInMemory();
final diff = Diff.indexToIndex(
repo: repo,
oldIndex: index,
newIndex: emptyIndex,
oldIndex: repo.index,
newIndex: Index.newInMemory(),
);
expect(diff.length, 12);
for (var i = 0; i < diff.deltas.length; i++) {
expect(diff.deltas[i].newFile.path, indexToIndex[i]);
}
index.free();
emptyIndex.free();
});
test('throws when trying to diff between index and index and error occurs',
() {
final index = repo.index;
expect(
() => Diff.indexToIndex(
repo: repo,
oldIndex: index,
oldIndex: repo.index,
newIndex: Index(nullptr),
),
throwsA(isA<LibGit2Error>()),
);
index.free();
});
test('merges diffs', () {
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree1 = commit.tree;
final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']);
final diff1 = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2);
final diff2 = Diff.treeToWorkdir(repo: repo, tree: tree1);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final newTree = Tree.lookup(repo: repo, oid: repo['b85d53c']);
final diff1 = Diff.treeToTree(
repo: repo,
oldTree: commit.tree,
newTree: newTree,
);
final diff2 = Diff.treeToWorkdir(repo: repo, tree: commit.tree);
expect(diff1.length, 10);
expect(diff2.length, 9);
diff1.merge(diff2);
expect(diff1.length, 11);
commit.free();
head.free();
tree1.free();
tree2.free();
diff1.free();
diff2.free();
});
test('parses provided diff', () {
@ -377,15 +316,11 @@ index e69de29..c217c63 100644
expect(stats.filesChanged, 1);
expect(stats.insertions, 1);
expect(diff.patchOid.sha, '699556913185bc38632ae20a49d5c18b9233335e');
stats.free();
diff.free();
});
group('apply', () {
test('checks if diff can be applied to repository', () {
final index = repo.index;
final diff1 = Diff.indexToWorkdir(repo: repo, index: index);
final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(
diff1.applies(repo: repo, location: GitApplyLocation.both),
false,
@ -397,16 +332,11 @@ index e69de29..c217c63 100644
diff2.applies(repo: repo, location: GitApplyLocation.both),
true,
);
diff1.free();
diff2.free();
index.free();
});
test('checks if hunk with provided index can be applied to repository',
() {
final index = repo.index;
final diff1 = Diff.indexToWorkdir(repo: repo, index: index);
final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(
diff1.applies(repo: repo, location: GitApplyLocation.both),
false,
@ -423,10 +353,6 @@ index e69de29..c217c63 100644
),
true,
);
diff1.free();
diff2.free();
index.free();
});
test('applies diff to repository', () {
@ -438,8 +364,6 @@ index e69de29..c217c63 100644
diff.apply(repo: repo);
expect(file.readAsStringSync(), 'Modified content\n');
diff.free();
});
test('throws when trying to apply diff and error occurs', () {
@ -455,9 +379,6 @@ index e69de29..c217c63 100644
expect(diff.length, 1);
expect(patch.text, patchText);
patch.free();
diff.free();
});
test('applies hunk with provided index to repository', () {
@ -470,8 +391,6 @@ index e69de29..c217c63 100644
diff.apply(repo: repo, hunkIndex: hunk.index);
expect(file.readAsStringSync(), 'Modified content\n');
diff.free();
});
test('does not apply hunk with non existing index', () {
@ -483,61 +402,44 @@ index e69de29..c217c63 100644
diff.apply(repo: repo, hunkIndex: 10);
expect(file.readAsStringSync(), '');
diff.free();
});
test('applies diff to tree', () {
final diff = Diff.parse(patchText);
Checkout.head(repo: repo, strategy: {GitCheckout.force});
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final oldIndex = repo.index;
final oldBlob = Blob.lookup(
repo: repo,
oid: oldIndex['subdir/modified_file'].oid,
oid: repo.index['subdir/modified_file'].oid,
);
expect(oldBlob.content, '');
final newIndex = diff.applyToTree(repo: repo, tree: tree);
final diff = Diff.parse(patchText);
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final newIndex = diff.applyToTree(repo: repo, tree: commit.tree);
final newBlob = Blob.lookup(
repo: repo,
oid: newIndex['subdir/modified_file'].oid,
);
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', () {
final diff = Diff.parse(patchText);
final hunk = diff.patches.first.hunks.first;
Checkout.head(repo: repo, strategy: {GitCheckout.force});
final head = repo.head;
final commit = Commit.lookup(repo: repo, oid: head.target);
final tree = commit.tree;
final oldIndex = repo.index;
final oldBlob = Blob.lookup(
repo: repo,
oid: oldIndex['subdir/modified_file'].oid,
oid: repo.index['subdir/modified_file'].oid,
);
expect(oldBlob.content, '');
final diff = Diff.parse(patchText);
final hunk = diff.patches.first.hunks.first;
final commit = Commit.lookup(repo: repo, oid: repo.head.target);
final newIndex = diff.applyToTree(
repo: repo,
tree: tree,
tree: commit.tree,
hunkIndex: hunk.index,
);
final newBlob = Blob.lookup(
@ -545,15 +447,6 @@ index e69de29..c217c63 100644
oid: newIndex['subdir/modified_file'].oid,
);
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', () {
@ -566,15 +459,12 @@ index e69de29..c217c63 100644
});
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 commit = Commit.lookup(repo: repo, oid: repo.head.target);
final newTree = Tree.lookup(repo: repo, oid: repo.index.writeTree());
final diff = Diff.treeToTree(
repo: repo,
oldTree: oldTree,
oldTree: commit.tree,
newTree: newTree,
);
expect(
@ -587,28 +477,18 @@ index e69de29..c217c63 100644
diff.deltas.singleWhere((e) => e.newFile.path == 'staged_new').status,
GitDelta.renamed,
);
commit.free();
head.free();
diff.free();
index.free();
oldTree.free();
newTree.free();
});
test('throws when trying to find similar entries and error occurs', () {
final nullDiff = Diff(nullptr);
expect(() => nullDiff.findSimilar(), throwsA(isA<LibGit2Error>()));
expect(() => Diff(nullptr).findSimilar(), throwsA(isA<LibGit2Error>()));
});
test('throws when trying to get patch Oid and error occurs', () {
final nullDiff = Diff(nullptr);
expect(() => nullDiff.patchOid, throwsA(isA<LibGit2Error>()));
expect(() => Diff(nullptr).patchOid, throwsA(isA<LibGit2Error>()));
});
test('returns deltas', () {
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(diff.deltas[0].numberOfFiles, 1);
expect(diff.deltas[0].status, GitDelta.deleted);
@ -635,70 +515,45 @@ index e69de29..c217c63 100644
);
expect(diff.deltas[0].oldFile.mode, GitFilemode.blob);
diff.free();
index.free();
});
test('throws when trying to get delta with invalid index', () {
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(() => diff.deltas[-1], throwsA(isA<RangeError>()));
diff.free();
index.free();
});
test('returns patches', () {
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final patches = diff.patches;
expect(patches.length, 8);
expect(patches.first.delta.status, GitDelta.deleted);
for (final p in patches) {
p.free();
}
diff.free();
index.free();
});
test('returns stats', () {
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
final stats = diff.stats;
expect(stats.insertions, 4);
expect(stats.deletions, 2);
expect(stats.filesChanged, 8);
expect(stats.print(format: {GitDiffStats.full}, width: 80), statsPrint);
stats.free();
diff.free();
index.free();
});
test('throws when trying to get stats and error occurs', () {
final nullDiff = Diff(nullptr);
expect(() => nullDiff.stats, throwsA(isA<LibGit2Error>()));
expect(() => Diff(nullptr).stats, throwsA(isA<LibGit2Error>()));
});
test('throws when trying to print stats and error occurs', () {
final nullStats = DiffStats(nullptr);
expect(
() => nullStats.print(format: {GitDiffStats.full}, width: 80),
() => DiffStats(nullptr).print(format: {GitDiffStats.full}, width: 80),
throwsA(isA<LibGit2Error>()),
);
});
test('returns patch diff string', () {
final diff = Diff.parse(patchText);
expect(diff.patch, patchText);
diff.free();
});
test('returns hunks in a patch', () {
@ -713,9 +568,6 @@ index e69de29..c217c63 100644
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', () {
@ -731,9 +583,16 @@ index e69de29..c217c63 100644
expect(line.numLines, 1);
expect(line.contentOffset, 155);
expect(line.content, 'Modified content\n');
});
patch.free();
diff.free();
test('manually releases allocated memory', () {
final diff = Diff.parse(patchText);
expect(() => diff.free(), returnsNormally);
});
test('manually releases allocated memory for DiffStats object', () {
final stats = Diff.parse(patchText).stats;
expect(() => stats.free(), returnsNormally);
});
test(
@ -749,10 +608,6 @@ index e69de29..c217c63 100644
expect(patch.hunks[0].toString(), contains('DiffHunk{'));
expect(patch.hunks[0].lines[0].toString(), contains('DiffLine{'));
expect(stats.toString(), contains('DiffStats{'));
stats.free();
patch.free();
diff.free();
});
});
}

View file

@ -20,7 +20,6 @@ void main() {
});
tearDown(() {
index.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
@ -152,11 +151,8 @@ void main() {
final bare = Repository.open(
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();
});
});
@ -173,9 +169,8 @@ void main() {
});
test('throws when trying to update entry and error occurs', () {
final nullEntry = IndexEntry(nullptr);
expect(
() => index.addFromBuffer(entry: nullEntry, buffer: ''),
() => index.addFromBuffer(entry: IndexEntry(nullptr), buffer: ''),
throwsA(isA<LibGit2Error>()),
);
});
@ -208,11 +203,8 @@ void main() {
final bare = Repository.open(
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();
});
});
@ -234,14 +226,11 @@ void main() {
final bare = Repository.open(
p.join('test', 'assets', 'empty_bare.git'),
);
final bareIndex = bare.index;
expect(
() => bareIndex.updateAll(['not_there']),
() => bare.index.updateAll(['not_there']),
throwsA(isA<LibGit2Error>()),
);
bareIndex.free();
bare.free();
});
});
@ -321,7 +310,6 @@ void main() {
final repo = Repository.open(tmpDir.path);
final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch');
final index = repo.index;
final commit = AnnotatedCommit.lookup(
repo: repo,
oid: conflictBranch.target,
@ -329,11 +317,8 @@ void main() {
Merge.commit(repo: repo, commit: commit);
expect(() => index.writeTree(), throwsA(isA<LibGit2Error>()));
expect(() => repo.index.writeTree(), throwsA(isA<LibGit2Error>()));
commit.free();
conflictBranch.free();
index.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
@ -370,16 +355,12 @@ void main() {
Merge.commit(repo: conflictRepo, commit: commit);
final index = conflictRepo.index;
final conflictedFile = index.conflicts['feature_file']!;
final conflictedFile = conflictRepo.index.conflicts['feature_file']!;
expect(conflictedFile.ancestor?.path, 'feature_file');
expect(conflictedFile.our?.path, 'feature_file');
expect(conflictedFile.their?.path, 'feature_file');
expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free();
repoDir.deleteSync(recursive: true);
});
@ -399,16 +380,12 @@ void main() {
Merge.commit(repo: conflictRepo, commit: commit);
final index = conflictRepo.index;
final conflictedFile = index.conflicts['conflict_file']!;
final conflictedFile = conflictRepo.index.conflicts['conflict_file']!;
expect(conflictedFile.ancestor?.path, null);
expect(conflictedFile.our?.path, 'conflict_file');
expect(conflictedFile.their?.path, 'conflict_file');
expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free();
repoDir.deleteSync(recursive: true);
});
@ -431,16 +408,12 @@ void main() {
Merge.commit(repo: conflictRepo, commit: commit);
final index = conflictRepo.index;
final conflictedFile = index.conflicts['feature_file']!;
final conflictedFile = conflictRepo.index.conflicts['feature_file']!;
expect(conflictedFile.ancestor?.path, 'feature_file');
expect(conflictedFile.our?.path, null);
expect(conflictedFile.their?.path, 'feature_file');
expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free();
repoDir.deleteSync(recursive: true);
});
@ -463,16 +436,12 @@ void main() {
Merge.commit(repo: conflictRepo, commit: commit);
final index = conflictRepo.index;
final conflictedFile = index.conflicts['feature_file']!;
final conflictedFile = conflictRepo.index.conflicts['feature_file']!;
expect(conflictedFile.ancestor?.path, 'feature_file');
expect(conflictedFile.our?.path, 'feature_file');
expect(conflictedFile.their?.path, null);
expect(conflictedFile.toString(), contains('ConflictEntry{'));
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free();
repoDir.deleteSync(recursive: true);
});
@ -492,6 +461,7 @@ void main() {
final index = conflictRepo.index;
Merge.commit(repo: conflictRepo, commit: commit);
expect(index.hasConflicts, true);
expect(index['.gitignore'].isConflict, false);
expect(index.conflicts['conflict_file']!.our!.isConflict, true);
@ -499,13 +469,11 @@ void main() {
final conflictedFile = index.conflicts['conflict_file']!;
conflictedFile.remove();
expect(index.hasConflicts, false);
expect(index.conflicts, isEmpty);
expect(index.conflicts['conflict_file'], null);
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free();
repoDir.deleteSync(recursive: true);
});
@ -533,16 +501,15 @@ void main() {
final index = conflictRepo.index;
Merge.commit(repo: conflictRepo, commit: commit);
expect(index.hasConflicts, true);
expect(index.conflicts.length, 1);
index.cleanupConflict();
expect(index.hasConflicts, false);
expect(index.conflicts, isEmpty);
index.free();
commit.free();
conflictBranch.free();
conflictRepo.free();
repoDir.deleteSync(recursive: true);
});
@ -554,13 +521,15 @@ void main() {
);
});
test('manually releases allocated memory', () {
expect(() => repo.index.free(), returnsNormally);
});
test('returns string representation of Index and IndexEntry objects', () {
final index = repo.index;
expect(index.toString(), contains('Index{'));
expect(index['file'].toString(), contains('IndexEntry{'));
index.free();
});
});
}

View file

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

View file

@ -62,9 +62,6 @@ void main() {
{GitMergeAnalysis.fastForward, GitMergeAnalysis.normal},
);
expect(repo.status, isEmpty);
ffBranch.free();
ffCommit.free();
});
test('is not fast forward and there is no conflicts', () {
@ -114,10 +111,6 @@ void main() {
'conflict_file': {GitStatus.indexModified}
},
);
index.free();
commit.free();
conflictBranch.free();
});
group('merge file from index', () {
@ -150,10 +143,6 @@ conflict branch edit
);
expect(diff, diffExpected);
index.free();
commit.free();
conflictBranch.free();
});
test('merges with ancestor', () {
@ -187,10 +176,6 @@ Another feature edit
);
expect(diff, diffExpected);
index.free();
commit.free();
conflictBranch.free();
});
test('merges with provided merge flags and file flags', () {
@ -227,10 +212,6 @@ conflict branch edit
);
expect(diff, diffExpected);
index.free();
commit.free();
conflictBranch.free();
});
test('merges with provided merge favor', () {
@ -251,10 +232,6 @@ conflict branch edit
File(p.join(repo.workdir, 'conflict_file')).readAsStringSync(),
'master conflict edit\n',
);
index.free();
commit.free();
conflictBranch.free();
});
test('throws when error occurs', () {
@ -348,12 +325,6 @@ theirs content
final mergeTree = index.writeTree();
expect(mergeCommitsTree == mergeTree, true);
index.free();
mergeIndex.free();
ourCommit.free();
theirCommitAnnotated.free();
theirCommit.free();
});
test('merges with provided favor', () {
@ -367,10 +338,6 @@ theirs content
favor: GitMergeFileFavor.ours,
);
expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ourCommit.free();
theirCommit.free();
});
test('merges with provided merge and file flags', () {
@ -392,10 +359,6 @@ theirs content
},
);
expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ourCommit.free();
theirCommit.free();
});
test('throws when error occurs', () {
@ -489,15 +452,12 @@ theirs content
commits: [ourCommit.oid, theirCommit.oid],
),
);
final theirTree = theirCommit.tree;
final ourTree = ourCommit.tree;
final ancestorTree = baseCommit.tree;
final mergeIndex = Merge.trees(
repo: repo,
ancestorTree: ancestorTree,
ourTree: ourTree,
theirTree: theirTree,
ancestorTree: baseCommit.tree,
ourTree: ourCommit.tree,
theirTree: theirCommit.tree,
);
expect(mergeIndex.conflicts, isEmpty);
final mergeTreesTree = mergeIndex.writeTree(repo);
@ -509,16 +469,6 @@ theirs content
final mergeTree = index.writeTree();
expect(mergeTreesTree == mergeTree, true);
index.free();
mergeIndex.free();
ancestorTree.free();
ourTree.free();
theirTree.free();
baseCommit.free();
ourCommit.free();
theirCommitAnnotated.free();
theirCommit.free();
});
test('merges with provided favor', () {
@ -531,26 +481,15 @@ theirs content
commits: [ourCommit.oid, theirCommit.oid],
),
);
final theirTree = theirCommit.tree;
final ourTree = ourCommit.tree;
final ancestorTree = baseCommit.tree;
final mergeIndex = Merge.trees(
repo: repo,
ancestorTree: ancestorTree,
ourTree: ourTree,
theirTree: theirTree,
ancestorTree: baseCommit.tree,
ourTree: ourCommit.tree,
theirTree: theirCommit.tree,
favor: GitMergeFileFavor.ours,
);
expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ancestorTree.free();
ourTree.free();
theirTree.free();
baseCommit.free();
ourCommit.free();
theirCommit.free();
});
test('throws when error occurs', () {
@ -580,8 +519,6 @@ theirs content
() => repo.message,
throwsA(isA<LibGit2Error>()),
);
index.free();
});
test('throws when error occurs', () {

View file

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

View file

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

View file

@ -29,8 +29,6 @@ void main() {
expect(packbuilder, isA<PackBuilder>());
expect(packbuilder.length, 0);
packbuilder.free();
});
test('throws when trying to initialize and error occurs', () {
@ -49,20 +47,13 @@ void main() {
packbuilder.add(odb.objects[1]);
expect(packbuilder.length, 2);
odb.free();
packbuilder.free();
});
test('throws when trying to add object and error occurs', () {
final packbuilder = PackBuilder(repo);
expect(
() => packbuilder.add(Oid(nullptr)),
() => PackBuilder(repo).add(Oid(nullptr)),
throwsA(isA<LibGit2Error>()),
);
packbuilder.free();
});
test('adds object recursively', () {
@ -71,19 +62,13 @@ void main() {
packbuilder.addRecursively(oid);
expect(packbuilder.length, 3);
packbuilder.free();
});
test('throws when trying to add object recursively and error occurs', () {
final packbuilder = PackBuilder(repo);
expect(
() => packbuilder.addRecursively(Oid(nullptr)),
() => PackBuilder(repo).addRecursively(Oid(nullptr)),
throwsA(isA<LibGit2Error>()),
);
packbuilder.free();
});
test('adds commit', () {
@ -92,17 +77,15 @@ void main() {
packbuilder.addCommit(oid);
expect(packbuilder.length, 3);
packbuilder.free();
});
test('throws when trying to add commit with invalid oid', () {
final packbuilder = PackBuilder(repo);
final oid = Oid.fromSHA(repo: repo, sha: '0' * 40);
expect(() => packbuilder.addCommit(oid), throwsA(isA<LibGit2Error>()));
packbuilder.free();
expect(
() => PackBuilder(repo).addCommit(oid),
throwsA(isA<LibGit2Error>()),
);
});
test('adds tree', () {
@ -111,17 +94,15 @@ void main() {
packbuilder.addTree(oid);
expect(packbuilder.length, 2);
packbuilder.free();
});
test('throws when trying to add tree with invalid oid', () {
final packbuilder = PackBuilder(repo);
final oid = Oid.fromSHA(repo: repo, sha: '0' * 40);
expect(() => packbuilder.addTree(oid), throwsA(isA<LibGit2Error>()));
packbuilder.free();
expect(
() => PackBuilder(repo).addTree(oid),
throwsA(isA<LibGit2Error>()),
);
});
test('adds objects with walker', () {
@ -133,48 +114,34 @@ void main() {
packbuilder.addWalk(walker);
expect(packbuilder.length, 3);
walker.free();
packbuilder.free();
});
test('sets number of threads', () {
final packbuilder = PackBuilder(repo);
expect(packbuilder.setThreads(1), 1);
packbuilder.free();
expect(PackBuilder(repo).setThreads(1), 1);
});
test('returns name of packfile', () {
final packbuilder = PackBuilder(repo);
final odb = repo.odb;
packbuilder.add(odb.objects[0]);
packbuilder.add(repo.odb.objects[0]);
Directory(packDirPath).createSync();
expect(packbuilder.name, isEmpty);
packbuilder.write(null);
expect(packbuilder.name, isNotEmpty);
packbuilder.free();
});
test('packs with default arguments', () {
final odb = repo.odb;
final objectsCount = odb.objects.length;
final objectsCount = repo.odb.objects.length;
Directory(packDirPath).createSync();
final writtenCount = repo.pack();
expect(writtenCount, objectsCount);
odb.free();
});
test('packs into provided path with threads set', () {
final odb = repo.odb;
final objectsCount = odb.objects.length;
final objectsCount = repo.odb.objects.length;
final testPackPath = p.join(repo.workdir, 'test-pack');
Directory(testPackPath).createSync();
@ -182,8 +149,6 @@ void main() {
expect(writtenCount, objectsCount);
expect(Directory(testPackPath).listSync().isNotEmpty, true);
odb.free();
});
test('packs with provided packDelegate', () {
@ -198,10 +163,7 @@ void main() {
);
for (final commit in repo.log(oid: ref.target)) {
packBuilder.addRecursively(commit.oid);
commit.free();
}
ref.free();
branch.free();
}
}
@ -210,20 +172,18 @@ void main() {
});
test('throws when trying to write pack into invalid path', () {
final packbuilder = PackBuilder(repo);
expect(
() => packbuilder.write('invalid/path'),
() => PackBuilder(repo).write('invalid/path'),
throwsA(isA<LibGit2Error>()),
);
});
packbuilder.free();
test('manually releases allocated memory', () {
expect(() => PackBuilder(repo).free(), returnsNormally);
});
test('returns string representation of PackBuilder object', () {
final packbuilder = PackBuilder(repo);
expect(packbuilder.toString(), contains('PackBuilder{'));
packbuilder.free();
expect(PackBuilder(repo).toString(), contains('PackBuilder{'));
});
});
}

View file

@ -65,8 +65,6 @@ index e69de29..0000000
expect(patch.size(), 14);
expect(patch.text, blobPatch);
patch.free();
});
test('creates from one buffer (add)', () {
@ -78,8 +76,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatchAdd);
patch.free();
});
test('creates from one buffer (delete)', () {
@ -91,8 +87,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatchDelete);
patch.free();
});
test('creates from blobs', () {
@ -106,8 +100,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatch);
patch.free();
});
test('creates from one blob (add)', () {
@ -120,8 +112,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatchAdd);
patch.free();
});
test('creates from one blob (delete)', () {
@ -134,8 +124,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatchDelete);
patch.free();
});
test('creates from blob and buffer', () {
@ -148,8 +136,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatch);
patch.free();
});
test('creates from empty blob and buffer', () {
@ -161,8 +147,6 @@ index e69de29..0000000
);
expect(patch.text, blobPatchAdd);
patch.free();
});
test('throws when trying to create from diff and error occurs', () {
@ -189,8 +173,16 @@ index e69de29..0000000
expect(stats.insertions, equals(1));
expect(stats.deletions, equals(0));
expect(stats.toString(), contains('PatchStats{'));
});
patch.free();
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 object', () {
@ -202,8 +194,6 @@ index e69de29..0000000
);
expect(patch.toString(), contains('Patch{'));
patch.free();
});
});
}

View file

@ -76,13 +76,6 @@ void main() {
rebase.finish();
expect(repo.index['.gitignore'], isA<IndexEntry>());
rebase.free();
ontoHead.free();
branchHead.free();
feature.free();
master.free();
signature.free();
});
test('performs rebase without branch provided', () {
@ -124,11 +117,6 @@ void main() {
}
rebase.finish();
rebase.free();
ontoHead.free();
feature.free();
signature.free();
});
test('performs rebase with provided upstream', () {
@ -170,13 +158,6 @@ void main() {
rebase.finish();
expect(repo.index['conflict_file'], isA<IndexEntry>());
rebase.free();
upstream.free();
branchHead.free();
feature.free();
master.free();
signature.free();
});
test(
@ -216,22 +197,10 @@ void main() {
() => rebase.commit(committer: signature),
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',
() {
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(
@ -252,13 +221,6 @@ void main() {
rebase.next(); // repo now have conflicts
expect(() => rebase.next(), throwsA(isA<LibGit2Error>()));
rebase.free();
ontoHead.free();
branchHead.free();
conflict.free();
master.free();
signature.free();
});
test('aborts rebase in progress', () {
@ -287,12 +249,6 @@ void main() {
rebase.abort();
expect(repo.status, isEmpty);
expect(repo.state, GitRepositoryState.none);
rebase.free();
ontoHead.free();
branchHead.free();
conflict.free();
master.free();
});
test('opens an existing rebase', () {
@ -307,15 +263,21 @@ void main() {
final openRebase = Rebase.open(repo);
expect(openRebase.operations.length, 3);
openRebase.free();
rebase.free();
ontoHead.free();
feature.free();
});
test('throws when trying to open an existing rebase but there is none', () {
expect(() => Rebase.open(repo), throwsA(isA<LibGit2Error>()));
});
test('manually releases allocated memory', () {
final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature');
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target);
final rebase = Rebase.init(
repo: repo,
onto: ontoHead,
);
expect(() => rebase.free(), returnsNormally);
});
});
}

View file

@ -46,25 +46,19 @@ void main() {
});
test('returns correct type of reference', () {
final head = repo.head;
expect(head.type, ReferenceType.direct);
head.free();
expect(repo.head.type, ReferenceType.direct);
final ref = Reference.lookup(repo: repo, name: 'HEAD');
expect(ref.type, ReferenceType.symbolic);
ref.free();
});
test('returns SHA hex of direct reference', () {
final head = repo.head;
expect(head.target.sha, lastCommit);
head.free();
expect(repo.head.target.sha, lastCommit);
});
test('returns SHA hex of symbolic reference', () {
final ref = Reference.lookup(repo: repo, name: 'HEAD');
expect(ref.target.sha, lastCommit);
ref.free();
});
test('throws when trying to resolve invalid reference', () {
@ -75,9 +69,7 @@ void main() {
});
test('returns the full name', () {
final head = repo.head;
expect(head.name, 'refs/heads/master');
head.free();
expect(repo.head.name, 'refs/heads/master');
});
test('returns the short name', () {
@ -87,25 +79,18 @@ void main() {
target: repo[lastCommit],
);
final head = repo.head;
expect(head.shorthand, 'master');
expect(repo.head.shorthand, 'master');
expect(ref.shorthand, 'origin/upstream');
head.free();
ref.free();
});
test('checks if reference is a local branch', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/feature');
expect(ref.isBranch, true);
ref.free();
});
test('checks if reference is a note', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(ref.isNote, false);
ref.free();
});
test('checks if reference is a remote branch', () {
@ -114,13 +99,11 @@ void main() {
name: 'refs/remotes/origin/master',
);
expect(ref.isRemote, true);
ref.free();
});
test('checks if reference is a tag', () {
final ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1');
expect(ref.isTag, true);
ref.free();
});
test('checks if reflog exists for the reference', () {
@ -129,8 +112,6 @@ void main() {
ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1');
expect(ref.hasLog, false);
ref.free();
});
test('ensures updates to the reference will append to its log', () {
@ -141,12 +122,8 @@ void main() {
name: 'refs/tags/tag',
target: repo[lastCommit],
);
final reflog = ref.log;
expect(reflog.length, 1);
reflog.free();
ref.free();
expect(ref.log.length, 1);
});
test('throws when trying to ensure there is a reflog and error occurs', () {
@ -167,24 +144,19 @@ void main() {
expect(repo.references.length, 6);
expect(duplicate.equals(ref), true);
duplicate.free();
ref.free();
});
group('create direct', () {
test('creates with oid as target', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
final refFromOid = Reference.create(
Reference.create(
repo: repo,
name: 'refs/tags/from.oid',
target: ref.target,
);
expect(repo.references, contains('refs/tags/from.oid'));
refFromOid.free();
ref.free();
});
test('creates with log message', () {
@ -196,15 +168,11 @@ void main() {
logMessage: 'log message',
);
final reflog = ref.log;
final reflogEntry = reflog[0];
final reflogEntry = ref.log[0];
expect(reflogEntry.message, 'log message');
expect(reflogEntry.committer.name, 'name');
expect(reflogEntry.committer.email, 'email');
reflog.free();
ref.free();
});
test('throws if target is not valid', () {
@ -239,7 +207,7 @@ void main() {
});
test('creates with force flag if name already exists', () {
final ref = Reference.create(
Reference.create(
repo: repo,
name: 'refs/tags/test',
target: repo[lastCommit],
@ -253,13 +221,10 @@ void main() {
);
expect(forceRef.target.sha, lastCommit);
ref.free();
forceRef.free();
});
test('throws if name already exists', () {
final ref = Reference.create(
Reference.create(
repo: repo,
name: 'refs/tags/test',
target: repo[lastCommit],
@ -273,8 +238,6 @@ void main() {
),
throwsA(isA<LibGit2Error>()),
);
ref.free();
});
});
@ -288,12 +251,10 @@ void main() {
expect(repo.references, contains('refs/tags/symbolic'));
expect(ref.type, ReferenceType.symbolic);
ref.free();
});
test('creates with force flag if name already exists', () {
final ref = Reference.create(
Reference.create(
repo: repo,
name: 'refs/tags/test',
target: 'refs/heads/master',
@ -308,13 +269,10 @@ void main() {
expect(forceRef.target.sha, lastCommit);
expect(forceRef.type, ReferenceType.symbolic);
ref.free();
forceRef.free();
});
test('throws if name already exists', () {
final ref = Reference.create(
Reference.create(
repo: repo,
name: 'refs/tags/exists',
target: 'refs/heads/master',
@ -328,8 +286,6 @@ void main() {
),
throwsA(isA<LibGit2Error>()),
);
ref.free();
});
test('throws if name is not valid', () {
@ -353,15 +309,11 @@ void main() {
logMessage: 'log message',
);
final reflog = ref.log;
final reflogEntry = reflog[0];
final reflogEntry = ref.log[0];
expect(reflogEntry.message, 'log message');
expect(reflogEntry.committer.name, 'name');
expect(reflogEntry.committer.email, 'email');
reflog.free();
ref.free();
});
});
@ -376,7 +328,6 @@ void main() {
test('with provided name', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(ref.target.sha, lastCommit);
ref.free();
});
test('throws when error occured', () {
@ -389,11 +340,7 @@ void main() {
test('returns log for reference', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
final reflog = ref.log;
expect(reflog.last.message, 'commit (initial): init');
reflog.free();
ref.free();
expect(ref.log.last.message, 'commit (initial): init');
});
group('set target', () {
@ -401,8 +348,6 @@ void main() {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
ref.setTarget(target: repo[newCommit]);
expect(ref.target.sha, newCommit);
ref.free();
});
test('sets symbolic target with provided reference name', () {
@ -411,8 +356,6 @@ void main() {
ref.setTarget(target: 'refs/heads/feature');
expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
ref.free();
});
test('sets target with log message', () {
@ -422,13 +365,10 @@ void main() {
repo.setIdentity(name: 'name', email: 'email');
ref.setTarget(target: 'refs/heads/feature', logMessage: 'log message');
expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
final reflog = ref.log;
expect(reflog.first.message, 'log message');
expect(reflog.first.committer.name, 'name');
expect(reflog.first.committer.email, 'email');
reflog.free();
ref.free();
final logEntry = ref.log.first;
expect(logEntry.message, 'log message');
expect(logEntry.committer.name, 'name');
expect(logEntry.committer.email, 'email');
});
test('throws on invalid target', () {
@ -444,8 +384,6 @@ void main() {
);
expect(() => ref.setTarget(target: 0), throwsA(isA<ArgumentError>()));
ref.free();
});
});
@ -508,9 +446,6 @@ void main() {
);
expect(repo.references, isNot(contains('refs/tags/v0.1')));
expect(repo.references.length, 5);
ref1.free();
ref2.free();
});
});
@ -523,10 +458,6 @@ void main() {
expect(ref1.notEquals(ref2), false);
expect(ref1.equals(ref3), false);
expect(ref1.notEquals(ref3), true);
ref1.free();
ref2.free();
ref3.free();
});
test('peels to non-tag object when no type is provided', () {
@ -535,10 +466,6 @@ void main() {
final peeled = ref.peel() as Commit;
expect(peeled.oid, commit.oid);
peeled.free();
commit.free();
ref.free();
});
test('peels to object of provided type', () {
@ -562,17 +489,6 @@ void main() {
expect(peeledTree.oid, tree.oid);
expect(peeledBlob.content, 'Feature edit\n');
expect(peeledTag.name, 'v0.2');
peeledTag.free();
peeledBlob.free();
peeledTree.free();
peeledCommit.free();
tagRef.free();
blobRef.free();
blob.free();
commit.free();
tree.free();
ref.free();
});
test('throws when trying to peel and error occurs', () {
@ -598,10 +514,14 @@ void main() {
);
});
test('manually releases allocated memory', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(() => ref.free(), returnsNormally);
});
test('returns string representation of Reference object', () {
final ref = Reference.lookup(repo: repo, name: 'refs/heads/master');
expect(ref.toString(), contains('Reference{'));
ref.free();
});
});
}

View file

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

View file

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

View file

@ -34,8 +34,6 @@ void main() {
expect(remote.url, remoteUrl);
expect(remote.pushUrl, '');
expect(remote.toString(), contains('Remote{'));
remote.free();
});
test('throws when provided name for lookup is not found', () {
@ -56,8 +54,6 @@ void main() {
expect(remote.name, 'upstream');
expect(remote.url, remoteUrl);
expect(remote.pushUrl, '');
remote.free();
});
test('creates with provided fetchspec', () {
@ -74,8 +70,6 @@ void main() {
expect(remote.url, remoteUrl);
expect(remote.pushUrl, '');
expect(remote.fetchRefspecs, [spec]);
remote.free();
});
test('throws when trying to create with fetchspec with invalid remote name',
@ -101,8 +95,6 @@ void main() {
Remote.delete(repo: repo, name: remote.name);
expect(repo.remotes.length, 1);
remote.free();
});
test('throws when trying to delete non existing remote', () {
@ -125,9 +117,6 @@ void main() {
final newRemote = Remote.lookup(repo: repo, name: 'new');
expect(newRemote.name, 'new');
newRemote.free();
remote.free();
});
test('returns list of non-default refspecs that cannot be renamed', () {
@ -142,8 +131,6 @@ void main() {
Remote.rename(repo: repo, oldName: remote.name, newName: 'renamed'),
['+refs/*:refs/*'],
);
remote.free();
});
test('throws when renaming with invalid names', () {
@ -162,9 +149,6 @@ void main() {
final newRemote = Remote.lookup(repo: repo, name: remoteName);
expect(newRemote.url, newUrl);
newRemote.free();
remote.free();
});
test('throws when trying to set invalid url name', () {
@ -180,8 +164,6 @@ void main() {
final remote = Remote.lookup(repo: repo, name: remoteName);
expect(remote.pushUrl, newUrl);
remote.free();
});
test('throws when trying to set invalid push url name', () {
@ -215,8 +197,6 @@ void main() {
refspec.rTransform('refs/remotes/origin/master'),
'refs/heads/master',
);
remote.free();
});
test('throws when trying to transform refspec with invalid reference name',
@ -233,8 +213,6 @@ void main() {
() => refspec.rTransform('invalid/name'),
throwsA(isA<LibGit2Error>()),
);
remote.free();
});
test('adds fetch refspec', () {
@ -252,8 +230,6 @@ void main() {
'+refs/test/*:refs/test/remotes/*',
],
);
remote.free();
});
test('throws when trying to add fetch refspec for invalid remote name', () {
@ -276,8 +252,6 @@ void main() {
final remote = Remote.lookup(repo: repo, name: 'origin');
expect(remote.pushRefspecs.length, 1);
expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']);
remote.free();
});
test('throws when trying to add push refspec for invalid remote name', () {
@ -308,8 +282,6 @@ void main() {
(refs.first['oid']! as Oid).sha,
'49322bb17d3acc9146f98c97d078513228bbf3c0',
);
remote.free();
});
test(
@ -319,8 +291,6 @@ void main() {
final remote = Remote.lookup(repo: repo, name: 'libgit2');
expect(() => remote.ls(), throwsA(isA<LibGit2Error>()));
remote.free();
});
test(
@ -350,8 +320,6 @@ void main() {
expect(stats.indexedDeltas, 3);
expect(stats.receivedBytes, 0);
expect(stats.toString(), contains('TransferProgress{'));
remote.free();
},
tags: 'remote_fetch',
);
@ -384,8 +352,6 @@ void main() {
expect(stats.indexedDeltas, 3);
expect(stats.receivedBytes, 0);
expect(stats.toString(), contains('TransferProgress{'));
remote.free();
},
tags: 'remote_fetch',
);
@ -412,8 +378,6 @@ void main() {
),
throwsA(isA<LibGit2Error>()),
);
remote.free();
},
tags: 'remote_fetch',
);
@ -426,8 +390,6 @@ void main() {
() => remote.fetch(),
throwsA(isA<LibGit2Error>()),
);
remote.free();
});
test(
@ -453,8 +415,6 @@ void main() {
expect(stats.totalDeltas == callbackStats?.totalDeltas, true);
expect(stats.indexedDeltas == callbackStats?.indexedDeltas, true);
expect(stats.receivedBytes == callbackStats?.receivedBytes, true);
remote.free();
},
tags: 'remote_fetch',
);
@ -483,8 +443,6 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
remote.fetch(callbacks: callbacks);
expect(sidebandOutput.toString(), sidebandMessage);
remote.free();
},
tags: 'remote_fetch',
);
@ -529,8 +487,6 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
remote.fetch(callbacks: callbacks);
expect(updateTipsOutput, tipsExpected);
remote.free();
},
tags: 'remote_fetch',
);
@ -566,7 +522,6 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
);
expect(updateRefOutput, {'refs/heads/master': ''});
remote.free();
originRepo.free();
originDir.delete(recursive: true);
});
@ -579,8 +534,26 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
() => remote.push(refspecs: ['refs/heads/master']),
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';
void main() {
late Repository repo;
late Directory tmpDir;
final cloneDir = Directory(
p.join(Directory.systemTemp.path, 'repository_cloned'),
@ -17,14 +16,12 @@ void main() {
setUp(() {
tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo')));
repo = Repository.open(tmpDir.path);
if (cloneDir.existsSync()) {
cloneDir.delete(recursive: true);
}
});
tearDown(() {
repo.free();
tmpDir.deleteSync(recursive: true);
if (cloneDir.existsSync()) {
cloneDir.deleteSync(recursive: true);
@ -72,14 +69,13 @@ void main() {
clonedRepo.free();
});
test('clones repository with provided remote callback', () {
Remote remote(Repository repo, String name, String url) =>
Remote.create(repo: repo, name: 'test', url: tmpDir.path);
test(
'clones repository with provided remote callback having default fetch '
'refspec value', () {
final clonedRepo = Repository.clone(
url: tmpDir.path,
localPath: cloneDir.path,
remote: remote,
remoteCallback: RemoteCallback(name: 'test', url: tmpDir.path),
);
expect(clonedRepo.isEmpty, false);
@ -90,15 +86,35 @@ void main() {
clonedRepo.free();
});
test('throws when cloning repository with invalid remote callback', () {
Remote remote(Repository repo, String name, String url) =>
Remote.create(repo: repo, name: '', url: '');
test('clones repository with provided remote callback ', () {
const fetchRefspec = '+refs/heads/*:refs/remotes/spec/*';
final clonedRepo = Repository.clone(
url: tmpDir.path,
localPath: cloneDir.path,
remoteCallback: RemoteCallback(
name: 'test',
url: tmpDir.path,
fetch: fetchRefspec,
),
);
final remote = Remote.lookup(repo: clonedRepo, name: 'test');
expect(clonedRepo.isEmpty, false);
expect(clonedRepo.isBare, false);
expect(clonedRepo.remotes, ['test']);
expect(clonedRepo.references, contains('refs/remotes/spec/master'));
expect(remote.fetchRefspecs, [fetchRefspec]);
clonedRepo.free();
});
test('throws when cloning repository with invalid remote callback', () {
expect(
() => Repository.clone(
url: tmpDir.path,
localPath: cloneDir.path,
remote: remote,
remoteCallback: const RemoteCallback(name: '', url: ''),
),
throwsA(isA<LibGit2Error>()),
);
@ -113,13 +129,10 @@ void main() {
}
callbackPath.createSync();
Repository repository(String path, bool bare) =>
Repository.init(path: callbackPath.path);
final clonedRepo = Repository.clone(
url: tmpDir.path,
localPath: cloneDir.path,
repository: repository,
repositoryCallback: RepositoryCallback(path: callbackPath.path),
);
expect(clonedRepo.isEmpty, false);
@ -131,17 +144,41 @@ void main() {
});
test('throws when cloning repository with invalid repository callback', () {
Repository repository(String path, bool bare) =>
Repository.init(path: '');
expect(
() => Repository.clone(
url: tmpDir.path,
localPath: cloneDir.path,
repository: repository,
repositoryCallback: const RepositoryCallback(path: ''),
),
throwsA(isA<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';
void main() {
late Repository repo;
final initDir = Directory(p.join(Directory.systemTemp.path, 'init_repo'));
setUp(() {
@ -17,27 +16,30 @@ void main() {
});
tearDown(() {
repo.free();
initDir.deleteSync(recursive: true);
});
group('Repository.init', () {
test('creates new bare repo at provided path', () {
repo = Repository.init(path: initDir.path, bare: true);
final repo = Repository.init(path: initDir.path, bare: true);
expect(repo.path, contains('init_repo'));
expect(repo.isBare, true);
repo.free();
});
test('creates new standard repo at provided path', () {
repo = Repository.init(path: initDir.path);
final repo = Repository.init(path: initDir.path);
expect(repo.path, contains('init_repo/.git/'));
expect(repo.isBare, false);
expect(repo.isEmpty, true);
repo.free();
});
test('creates new standard repo with provided options', () {
repo = Repository.init(
final repo = Repository.init(
path: initDir.path,
description: 'test repo',
originUrl: 'test.url',
@ -52,6 +54,8 @@ void main() {
'test repo',
);
expect(Remote.lookup(repo: repo, name: 'origin').url, 'test.url');
repo.free();
});
});
}

View file

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

View file

@ -41,11 +41,8 @@ void main() {
contents = file.readAsStringSync();
expect(contents, 'Feature edit\n');
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(diff.deltas, isEmpty);
index.free();
});
test('resets with mixed', () {
@ -56,37 +53,26 @@ void main() {
contents = file.readAsStringSync();
expect(contents, 'Feature edit\n');
final index = repo.index;
final diff = Diff.indexToWorkdir(repo: repo, index: index);
final diff = Diff.indexToWorkdir(repo: repo, index: repo.index);
expect(diff.deltas.length, 1);
index.free();
});
group('resetDefault', () {
test('updates entry in the index', () {
file.writeAsStringSync('new edit');
final index = repo.index;
index.add('feature_file');
repo.index.add('feature_file');
expect(repo.status['feature_file'], {GitStatus.indexModified});
final head = repo.head;
repo.resetDefault(oid: head.target, pathspec: ['feature_file']);
repo.resetDefault(oid: repo.head.target, pathspec: ['feature_file']);
expect(repo.status['feature_file'], {GitStatus.wtModified});
head.free();
index.free();
});
test('throws when pathspec list is empty', () {
final head = repo.head;
expect(
() => repo.resetDefault(oid: head.target, pathspec: []),
() => repo.resetDefault(oid: repo.head.target, pathspec: []),
throwsA(isA<LibGit2Error>()),
);
head.free();
});
});
});

View file

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

View file

@ -30,9 +30,7 @@ void main() {
group('RevWalk', () {
test('initializes walker', () {
final walker = RevWalk(repo);
expect(walker, isA<RevWalk>());
walker.free();
expect(RevWalk(repo), isA<RevWalk>());
});
test('throws when trying to initialize and error occurs', () {
@ -51,10 +49,7 @@ void main() {
for (var i = 0; i < commits.length; i++) {
expect(commits[i].oid.sha, log[i]);
commits[i].free();
}
walker.free();
});
test('returns list of commits with reverse sorting', () {
@ -67,10 +62,7 @@ void main() {
for (var i = 0; i < commits.length; i++) {
expect(commits[i].oid.sha, log.reversed.toList()[i]);
commits[i].free();
}
walker.free();
});
test('changes sorting', () {
@ -82,17 +74,13 @@ void main() {
for (var i = 0; i < timeSortedCommits.length; i++) {
expect(timeSortedCommits[i].oid.sha, log[i]);
timeSortedCommits[i].free();
}
walker.sorting({GitSort.time, GitSort.reverse});
final reverseSortedCommits = walker.walk();
for (var i = 0; i < reverseSortedCommits.length; i++) {
expect(reverseSortedCommits[i].oid.sha, log.reversed.toList()[i]);
reverseSortedCommits[i].free();
}
walker.free();
});
test('adds matching references for traversal with provided glob', () {
@ -101,11 +89,6 @@ void main() {
walker.pushGlob('heads');
final commits = walker.walk();
expect(commits.length, 7);
for (final c in commits) {
c.free();
}
walker.free();
});
test("adds repository's head for traversal", () {
@ -114,11 +97,6 @@ void main() {
walker.pushHead();
final commits = walker.walk();
expect(commits.length, 6);
for (final c in commits) {
c.free();
}
walker.free();
});
test('adds reference for traversal with provided name', () {
@ -127,23 +105,14 @@ void main() {
walker.pushReference('refs/heads/master');
final commits = walker.walk();
expect(commits.length, 6);
for (final c in commits) {
c.free();
}
walker.free();
});
test('throws when trying to add reference for traversal with invalid name',
() {
final walker = RevWalk(repo);
expect(
() => walker.pushReference('invalid'),
() => RevWalk(repo).pushReference('invalid'),
throwsA(isA<LibGit2Error>()),
);
walker.free();
});
test('adds range for traversal', () {
@ -152,22 +121,13 @@ void main() {
walker.pushRange('HEAD..@{-1}');
final commits = walker.walk();
expect(commits.length, 1);
for (final c in commits) {
c.free();
}
walker.free();
});
test('throws when trying to add invalid range for traversal', () {
final walker = RevWalk(repo);
expect(
() => walker.pushRange('HEAD..invalid'),
() => RevWalk(repo).pushRange('HEAD..invalid'),
throwsA(isA<LibGit2Error>()),
);
walker.free();
});
test('hides commit and its ancestors', () {
@ -178,22 +138,13 @@ void main() {
final commits = walker.walk();
expect(commits.length, 2);
for (final c in commits) {
c.free();
}
walker.free();
});
test('throws when trying to hide commit oid and error occurs', () {
final walker = RevWalk(repo);
expect(
() => walker.hide(repo['0' * 40]),
() => RevWalk(repo).hide(repo['0' * 40]),
throwsA(isA<LibGit2Error>()),
);
walker.free();
});
test('hides oids of references for provided glob pattern', () {
@ -207,12 +158,6 @@ void main() {
walker.hideGlob('*master');
final hiddenCommits = walker.walk();
expect(hiddenCommits.length, 1);
hiddenCommits[0].free();
for (final c in commits) {
c.free();
}
walker.free();
});
test('hides head', () {
@ -227,12 +172,6 @@ void main() {
walker.hideHead();
final hiddenCommits = walker.walk();
expect(hiddenCommits.length, 0);
for (final c in commits) {
c.free();
}
head.free();
walker.free();
});
test('hides oids of reference with provided name', () {
@ -247,23 +186,13 @@ void main() {
walker.hideReference('refs/heads/master');
final hiddenCommits = walker.walk();
expect(hiddenCommits.length, 0);
for (final c in commits) {
c.free();
}
head.free();
walker.free();
});
test('throws when trying to hide oids of reference with invalid name', () {
final walker = RevWalk(repo);
expect(
() => walker.hideReference('invalid'),
() => RevWalk(repo).hideReference('invalid'),
throwsA(isA<LibGit2Error>()),
);
walker.free();
});
test('resets walker', () {
@ -275,8 +204,6 @@ void main() {
final commits = walker.walk();
expect(commits, <Commit>[]);
walker.free();
});
test('simplifies walker by enqueuing only first parent for each commit',
@ -290,22 +217,19 @@ void main() {
for (var i = 0; i < commits.length; i++) {
expect(commits.length, 3);
commits[i].free();
}
walker.free();
});
test('throws when trying to add new root for traversal and error occurs',
() {
final walker = RevWalk(repo);
expect(
() => walker.push(repo['0' * 40]),
() => RevWalk(repo).push(repo['0' * 40]),
throwsA(isA<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', () {
test('creates with provided time and offset', () {
expect(signature, isA<Signature>());
@ -50,7 +47,6 @@ void main() {
lessThan(5),
);
expect(sig.offset, isA<int>());
sig.free();
});
test('returns correct values', () {
@ -66,9 +62,16 @@ void main() {
email: email,
time: time,
);
expect(signature == otherSignature, true);
expect(signature, equals(otherSignature));
});
otherSignature.free();
test('manually releases allocated memory', () {
final signature = Signature.create(
name: name,
email: email,
time: time,
);
expect(() => signature.free(), returnsNormally);
});
test('returns string representation of Signature object', () {

View file

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

View file

@ -47,8 +47,6 @@ void main() {
expect(submodule.ignore, GitSubmoduleIgnore.none);
expect(submodule.updateRule, GitSubmoduleUpdate.checkout);
expect(submodule.toString(), contains('Submodule{'));
submodule.free();
});
test('throws when trying to lookup and submodule not found', () {
@ -92,24 +90,20 @@ void main() {
Submodule.update(repo: repo, name: testSubmodule);
final submoduleRepo = submodule.open();
final subHead = submoduleRepo.head;
expect(submoduleRepo, isA<Repository>());
expect(subHead.target.sha, submoduleHeadSha);
expect(submoduleRepo.head.target.sha, submoduleHeadSha);
expect(
submodule.workdirOid?.sha,
'49322bb17d3acc9146f98c97d078513228bbf3c0',
);
subHead.free();
submoduleRepo.free();
submodule.free();
});
test('throws when trying to open repository for not initialized submodule',
() {
final submodule = Submodule.lookup(repo: repo, name: testSubmodule);
expect(() => submodule.open(), throwsA(isA<LibGit2Error>()));
submodule.free();
});
test('adds submodule', () {
@ -125,7 +119,6 @@ void main() {
expect(submoduleRepo.isEmpty, false);
submoduleRepo.free();
submodule.free();
});
test('throws when trying to add submodule with wrong url', () {
@ -170,9 +163,6 @@ void main() {
expect(updatedSubmodule.branch, 'updated');
expect(updatedSubmodule.ignore, GitSubmoduleIgnore.all);
expect(updatedSubmodule.updateRule, GitSubmoduleUpdate.rebase);
updatedSubmodule.free();
submodule.free();
});
test('syncs', () {
@ -205,13 +195,8 @@ void main() {
'https://updated.com/',
);
updatedSubmRepoConfig.free();
submRepo.free();
updatedSubmRepo.free();
updatedSubmodule.free();
submRepoConfig.free();
repoConfig.free();
submodule.free();
});
test('reloads info', () {
@ -222,8 +207,6 @@ void main() {
submodule.reload();
expect(submodule.url, 'updated');
submodule.free();
});
test('returns status for a submodule', () {
@ -259,8 +242,11 @@ void main() {
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(() {
tag.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
@ -59,9 +58,6 @@ void main() {
expect(target.message, 'add subdirectory file\n');
expect(tagger, signature);
expect(tag.toString(), contains('Tag{'));
signature.free();
target.free();
});
test('creates new annotated tag with commit as target', () {
@ -94,10 +90,6 @@ void main() {
expect(newTag.targetOid.sha, targetSHA);
expect(tagger, signature);
expect(newTagTarget.oid, target);
newTag.free();
newTagTarget.free();
signature.free();
});
test('creates new lightweight tag with commit as target', () {
@ -115,8 +107,6 @@ void main() {
expect(newTag.shorthand, tagName);
expect(newTag.target, target);
newTag.free();
});
test('creates new annotated tag with tree as target', () {
@ -147,10 +137,6 @@ void main() {
expect(newTag.message, message);
expect(tagger, signature);
expect(newTagTarget.oid, target);
newTag.free();
newTagTarget.free();
signature.free();
});
test('creates new lightweight tag with tree as target', () {
@ -168,8 +154,6 @@ void main() {
expect(newTag.shorthand, tagName);
expect(newTag.target, target);
newTag.free();
});
test('creates new annotated tag with blob as target', () {
@ -200,10 +184,6 @@ void main() {
expect(newTag.message, message);
expect(tagger, signature);
expect(newTagTarget.oid, target);
newTag.free();
newTagTarget.free();
signature.free();
});
test('creates new lightweight tag with blob as target', () {
@ -221,8 +201,6 @@ void main() {
expect(newTag.shorthand, tagName);
expect(newTag.target, target);
newTag.free();
});
test('creates new annotated tag with tag as target', () {
@ -252,10 +230,6 @@ void main() {
expect(newTag.message, message);
expect(tagger, signature);
expect(newTagTarget.oid, tag.oid);
newTag.free();
newTagTarget.free();
signature.free();
});
test('creates new lightweight tag with tag as target', () {
@ -272,8 +246,6 @@ void main() {
expect(newTag.shorthand, tagName);
expect(newTag.target, tag.oid);
newTag.free();
});
test(
@ -313,10 +285,6 @@ void main() {
expect(tagger, signature);
expect(newTagTarget.oid, target);
expect(repo.tags.length, equals(2));
newTag.free();
newTagTarget.free();
signature.free();
});
test(
@ -342,8 +310,6 @@ void main() {
expect(newTag.shorthand, tagName);
expect(newTag.target, target);
expect(repo.tags.length, equals(2));
newTag.free();
});
test('throws when trying to create annotated tag with invalid name', () {
@ -420,5 +386,10 @@ void main() {
throwsA(isA<LibGit2Error>()),
);
});
test('manually releases allocated memory', () {
tag = Tag.lookup(repo: repo, oid: tagOid);
expect(() => tag.free(), returnsNormally);
});
});
}

View file

@ -19,7 +19,6 @@ void main() {
});
tearDown(() {
tree.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
@ -65,7 +64,6 @@ void main() {
final entry = tree['dir/dir_file.txt'];
expect(entry.oid.sha, 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391');
expect(entry.toString(), contains('TreeEntry{'));
entry.free();
});
test('throws when nothing found for provided path', () {
@ -92,9 +90,17 @@ void main() {
expect(entry.name, 'filename');
expect(entry.filemode, GitFilemode.blob);
expect(entry.oid, fileOid);
});
builder.free();
newTree.free();
test('manually releases allocated memory', () {
final tree = Tree.lookup(repo: repo, oid: repo['a8ae3dd']);
expect(() => tree.free(), returnsNormally);
});
test(
'manually releases allocated memory for tree entry '
'looked up by path', () {
expect(() => tree['dir/dir_file.txt'].free(), returnsNormally);
});
});
}

View file

@ -19,7 +19,6 @@ void main() {
});
tearDown(() {
tree.free();
repo.free();
tmpDir.deleteSync(recursive: true);
});
@ -29,7 +28,6 @@ void main() {
final builder = TreeBuilder(repo: repo);
expect(builder, isA<TreeBuilder>());
expect(builder.toString(), contains('TreeBuilder{'));
builder.free();
});
test('initializes tree builder with provided tree', () {
@ -39,8 +37,6 @@ void main() {
expect(builder, isA<TreeBuilder>());
expect(builder.length, tree.length);
expect(oid, tree.oid);
builder.free();
});
test('throws when trying to initialize and error occurs', () {
@ -56,8 +52,6 @@ void main() {
expect(builder.length, 4);
builder.clear();
expect(builder.length, 0);
builder.free();
});
test('builds the tree builder from entry of tree', () {
@ -72,8 +66,6 @@ void main() {
filemode: entry.filemode,
);
expect(builder[entry.name].name, entry.name);
builder.free();
});
test('throws when trying to add entry with invalid name or invalid oid',
@ -96,8 +88,6 @@ void main() {
),
throwsA(isA<LibGit2Error>()),
);
builder.free();
});
test('removes an entry', () {
@ -108,14 +98,17 @@ void main() {
builder.remove('.gitignore');
expect(() => builder['.gitignore'], throwsA(isA<ArgumentError>()));
expect(builder.length, tree.length - 1);
builder.free();
});
test('throws when trying to remove entry that is not in the tree', () {
final builder = TreeBuilder(repo: repo);
expect(() => builder.remove('not.there'), throwsA(isA<LibGit2Error>()));
builder.free();
expect(
() => TreeBuilder(repo: repo).remove('not.there'),
throwsA(isA<LibGit2Error>()),
);
});
test('manually releases allocated memory', () {
expect(() => TreeBuilder(repo: repo).free(), returnsNormally);
});
});
}

View file

@ -38,20 +38,14 @@ void main() {
name: worktreeName,
path: worktreeDir.path,
);
final branches = repo.branches;
expect(repo.worktrees, [worktreeName]);
expect(branches.any((branch) => branch.name == worktreeName), true);
expect(repo.branches.any((branch) => branch.name == worktreeName), true);
expect(worktree.name, worktreeName);
expect(worktree.path, contains('worktree'));
expect(worktree.isLocked, false);
expect(worktree.toString(), contains('Worktree{'));
expect(File(p.join(worktreeDir.path, '.git')).existsSync(), true);
for (final branch in branches) {
branch.free();
}
worktree.free();
});
test('creates worktree at provided path from provided reference', () {
@ -83,14 +77,6 @@ void main() {
expect(repo.worktrees, <String>[]);
expect(worktreeBranch.isCheckedOut, false);
expect(branches.any((branch) => branch.name == 'v1'), true);
for (final branch in branches) {
branch.free();
}
worktreeBranch.free();
ref.free();
head.free();
worktree.free();
});
test('throws when trying to create worktree with invalid name or path', () {
@ -113,19 +99,16 @@ void main() {
});
test('lookups worktree', () {
final worktree = Worktree.create(
Worktree.create(
repo: repo,
name: worktreeName,
path: worktreeDir.path,
);
final lookedupWorktree = Worktree.lookup(repo: repo, name: worktreeName);
final worktree = Worktree.lookup(repo: repo, name: worktreeName);
expect(lookedupWorktree.name, worktreeName);
expect(lookedupWorktree.path, contains('worktree'));
expect(lookedupWorktree.isLocked, false);
lookedupWorktree.free();
worktree.free();
expect(worktree.name, worktreeName);
expect(worktree.path, contains('worktree'));
expect(worktree.isLocked, false);
});
test('throws when trying to lookup and error occurs', () {
@ -148,8 +131,6 @@ void main() {
worktree.unlock();
expect(worktree.isLocked, false);
worktree.free();
});
test('prunes worktree', () {
@ -170,8 +151,6 @@ void main() {
worktree.prune();
expect(repo.worktrees, <String>[]);
worktree.free();
});
test('throws when trying get list of worktrees and error occurs', () {
@ -180,5 +159,14 @@ void main() {
throwsA(isA<LibGit2Error>()),
);
});
test('manually releases allocated memory', () {
final worktree = Worktree.create(
repo: repo,
name: worktreeName,
path: worktreeDir.path,
);
expect(() => worktree.free(), returnsNormally);
});
});
}