feat(repository)!: add more aliases for api methods

BREAKING CHANGE: Make repository entry point for most operations
This commit is contained in:
Aleksey Kulikov 2021-10-11 20:06:36 +03:00
parent 9205a3ad82
commit 3a0fa75929
51 changed files with 1380 additions and 1062 deletions

View file

@ -2,9 +2,9 @@ import 'dart:io';
import 'package:libgit2dart/libgit2dart.dart';
import '../test/helpers/util.dart';
void main() async {
void main() {
// Preparing example repository.
final tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
final tmpDir = setupRepo(Directory('test/assets/testrepo/'));
// Open system + global config file.
final config = Config.open();
@ -54,5 +54,5 @@ void main() async {
}
// Removing example repository.
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
}

View file

@ -2,17 +2,17 @@ import 'dart:io';
import 'package:libgit2dart/libgit2dart.dart';
import '../test/helpers/util.dart';
void main() async {
void main() {
// Preparing example repository.
final tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
final tmpDir = setupRepo(Directory('test/assets/testrepo/'));
final repo = Repository.open(tmpDir.path);
// Get list of repo's references.
print('Repository references: ${repo.references.list}');
print('Repository references: ${repo.references}');
// Get reference by name.
final ref = repo.references['refs/heads/master'];
final ref = repo.lookupReference('refs/heads/master');
print('Reference SHA hex: ${ref.target.sha}');
print('Is reference a local branch: ${ref.isBranch}');
@ -20,16 +20,16 @@ void main() async {
print('Reference shorthand name: ${ref.shorthand}');
// Create new reference (direct or symbolic).
final newRef = repo.references.create(
final newRef = repo.createReference(
name: 'refs/tags/v1',
target: 'refs/heads/master',
);
// Rename reference.
newRef.rename(newName: 'refs/tags/v1.1');
repo.renameReference(oldName: 'v1', newName: 'refs/tags/v1.1');
// Delete reference.
newRef.delete();
repo.deleteReference('v1.1');
// free() should be called on object to free memory when done.
ref.free();
@ -37,5 +37,5 @@ void main() async {
repo.free();
// Removing example repository.
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
}

View file

@ -9,7 +9,7 @@ import '../util.dart';
/// Return a list of branches.
///
/// Throws a [LibGit2Error] if error occured.
List<String> list({
List<Pointer<git_reference>> list({
required Pointer<git_repository> repoPointer,
required int flags,
}) {
@ -24,7 +24,7 @@ List<String> list({
throw LibGit2Error(libgit2.git_error_last());
}
var result = <String>[];
var result = <Pointer<git_reference>>[];
var error = 0;
while (error == 0) {
@ -32,10 +32,8 @@ List<String> list({
final refType = calloc<Int32>();
error = libgit2.git_branch_next(reference, refType, iterator.value);
if (error == 0) {
final refName = reference_bindings.shorthand(reference.value);
result.add(refName);
result.add(reference.value);
calloc.free(refType);
calloc.free(reference);
} else {
break;
}
@ -129,11 +127,8 @@ void delete(Pointer<git_reference> branch) {
///
/// The new branch name will be checked for validity.
///
/// Note that if the move succeeds, the old reference object will not be valid anymore,
/// and will be freed immediately.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_reference> rename({
void rename({
required Pointer<git_reference> branchPointer,
required String newBranchName,
required bool force,
@ -149,8 +144,7 @@ Pointer<git_reference> rename({
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
reference_bindings.free(branchPointer);
return out.value;
reference_bindings.free(out.value);
}
}

View file

@ -3,7 +3,6 @@ import 'package:ffi/ffi.dart';
import '../error.dart';
import 'libgit2_bindings.dart';
import '../util.dart';
import 'oid.dart' as oid_bindings;
/// Lookup a commit object from a repository.
///
@ -92,26 +91,21 @@ Pointer<git_oid> create({
required String message,
required Pointer<git_tree> treePointer,
required int parentCount,
required List<String> parents,
required List<Pointer<git_commit>> parents,
}) {
final out = calloc<git_oid>();
final updateRefC = updateRef?.toNativeUtf8().cast<Int8>() ?? nullptr;
final messageEncodingC =
messageEncoding?.toNativeUtf8().cast<Int8>() ?? nullptr;
final messageC = message.toNativeUtf8().cast<Int8>();
Pointer<Pointer<git_commit>> parentsC =
calloc.call<Pointer<git_commit>>(parentCount);
final parentsC = calloc<Pointer<git_commit>>(parentCount);
if (parents.isNotEmpty) {
for (var i = 0; i < parentCount; i++) {
final oid = oid_bindings.fromSHA(parents[i]);
var commit = calloc<IntPtr>();
commit = lookup(repoPointer: repoPointer, oidPointer: oid).cast();
parentsC[i] = commit.cast();
parentsC[i] = parents[i];
}
} else {
final commit = calloc<IntPtr>();
parentsC[0] = commit.cast();
parentsC[0] = nullptr;
}
final error = libgit2.git_commit_create(
@ -130,9 +124,6 @@ Pointer<git_oid> create({
calloc.free(updateRefC);
calloc.free(messageEncodingC);
calloc.free(messageC);
for (var i = 0; i < parentCount; i++) {
free(parentsC[i]);
}
calloc.free(parentsC);
if (error < 0) {

View file

@ -107,10 +107,10 @@ Pointer<git_oid> create({
}
}
/// Remove the note for an object.
/// Delete the note for an object.
///
/// Throws a [LibGit2Error] if error occured.
void remove({
void delete({
required Pointer<git_repository> repoPointer,
String notesRef = 'refs/notes/commits',
required Pointer<git_signature> authorPointer,

View file

@ -130,5 +130,5 @@ void remove({
}
}
/// Free a tree builder to release memory.
/// Free a tree builder and all the entries to release memory.
void free(Pointer<git_treebuilder> bld) => libgit2.git_treebuilder_free(bld);

View file

@ -20,13 +20,22 @@ Pointer<git_worktree> create({
final out = calloc<Pointer<git_worktree>>();
final nameC = name.toNativeUtf8().cast<Int8>();
final pathC = path.toNativeUtf8().cast<Int8>();
final opts =
calloc<git_worktree_add_options>(sizeOf<git_worktree_add_options>());
opts.ref.version = 1;
opts.ref.lock = 0;
final opts = calloc<git_worktree_add_options>();
final optsError = libgit2.git_worktree_add_options_init(
opts,
GIT_WORKTREE_ADD_OPTIONS_VERSION,
);
if (optsError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
opts.ref.ref = nullptr;
if (refPointer != null) {
opts.ref.ref = refPointer;
}
final error = libgit2.git_worktree_add(out, repoPointer, nameC, pathC, opts);
calloc.free(nameC);
@ -82,6 +91,7 @@ List<String> list(Pointer<git_repository> repo) {
final result = <String>[];
if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
for (var i = 0; i < out.ref.count; i++) {

View file

@ -8,18 +8,16 @@ class Blob {
/// Initializes a new instance of [Blob] class from provided pointer to
/// blob object in memory.
///
/// Should be freed with `free()` to release allocated memory.
/// Should be freed to release allocated memory.
Blob(this._blobPointer);
/// Initializes a new instance of [Blob] class from provided
/// [Repository] object and [sha] hex string.
/// Lookups a blob object for provided [id] in a [repo]sitory.
///
/// Should be freed with `free()` to release allocated memory.
Blob.lookup({required Repository repo, required String sha}) {
final oid = Oid.fromSHA(repo: repo, sha: sha);
/// Should be freed to release allocated memory.
Blob.lookup({required Repository repo, required Oid id}) {
_blobPointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
oidPointer: id.pointer,
);
}

View file

@ -4,134 +4,131 @@ import 'bindings/libgit2_bindings.dart';
import 'bindings/branch.dart' as bindings;
import 'bindings/reference.dart' as reference_bindings;
class Branches {
/// Initializes a new instance of the [Branches] class
/// from provided [Repository] object.
Branches(Repository repo) {
_repoPointer = repo.pointer;
}
/// Pointer to memory address for allocated repository object.
late final Pointer<git_repository> _repoPointer;
/// Returns a list of all branches that can be found in a repository.
class Branch {
/// Initializes a new instance of [Branch] class from provided pointer to
/// branch object in memory.
///
/// Throws a [LibGit2Error] if error occured.
List<String> list() {
return bindings.list(
repoPointer: _repoPointer,
flags: GitBranch.all.value,
);
}
/// Returns a list of local branches that can be found in a repository.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get local {
return bindings.list(
repoPointer: _repoPointer,
flags: GitBranch.local.value,
);
}
/// Returns a list of remote branches that can be found in a repository.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get remote {
return bindings.list(
repoPointer: _repoPointer,
flags: GitBranch.remote.value,
);
}
/// Lookups a branch by its name in a repository.
///
/// The generated reference must be freed. The branch name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Branch operator [](String branchName) {
final ref = Reference(
reference_bindings.lookupDWIM(
repoPointer: _repoPointer,
name: branchName,
),
);
late final GitBranch type;
ref.isBranch ? type = GitBranch.local : GitBranch.remote;
ref.free();
return Branch(bindings.lookup(
repoPointer: _repoPointer,
branchName: branchName,
branchType: type.value,
));
}
/// Should be freed with [free] to release allocated memory when no longer
/// needed.
Branch(this._branchPointer);
/// Creates a new branch pointing at a [target] commit.
///
/// A new direct reference will be created pointing to this target commit.
/// If [force] is true and a reference already exists with the given name, it'll be replaced.
///
/// The returned reference must be freed.
/// Should be freed with [free] to release allocated memory when no longer
/// needed.
///
/// The branch name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Reference create({
Branch.create({
required Repository repo,
required String name,
required Commit target,
bool force = false,
}) {
return Reference(bindings.create(
repoPointer: _repoPointer,
_branchPointer = bindings.create(
repoPointer: repo.pointer,
branchName: name,
targetPointer: target.pointer,
force: force,
));
}
);
}
class Branch {
/// Initializes a new instance of [Branch] class from provided pointer to
/// branch object in memory.
/// Lookups a branch by its [name] in a [repo]sitory.
///
/// Should be freed with `free()` to release allocated memory.
const Branch(this._branchPointer);
/// The branch name will be checked for validity.
///
/// Should be freed with [free] to release allocated memory when no longer
/// needed.
///
/// Throws a [LibGit2Error] if error occured.
Branch.lookup({required Repository repo, required String name}) {
final ref = Reference(
reference_bindings.lookupDWIM(
repoPointer: repo.pointer,
name: name,
),
);
late final GitBranch type;
ref.isBranch ? type = GitBranch.local : GitBranch.remote;
ref.free();
_branchPointer = bindings.lookup(
repoPointer: repo.pointer,
branchName: name,
branchType: type.value,
);
}
late final Pointer<git_reference> _branchPointer;
/// Pointer to memory address for allocated branch object.
final Pointer<git_reference> _branchPointer;
Pointer<git_reference> get pointer => _branchPointer;
/// Returns the OID pointed to by a branch.
/// Returns a list of branches that can be found in a [repo]sitory for provided [type].
/// Default is all branches (local and remote).
///
/// Throws an exception if error occured.
Oid get target => Oid(reference_bindings.target(_branchPointer));
/// IMPORTANT: Branches must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
static List<Branch> list({
required Repository repo,
GitBranch type = GitBranch.all,
}) {
final pointers = bindings.list(
repoPointer: repo.pointer,
flags: type.value,
);
final result = <Branch>[];
for (var pointer in pointers) {
result.add(Branch(pointer));
}
return result;
}
/// Deletes an existing branch reference.
///
/// Note that if the deletion succeeds, the reference object will not be valid anymore,
/// and will be freed.
///
/// Throws a [LibGit2Error] if error occured.
void delete() => bindings.delete(_branchPointer);
static void delete({required Repository repo, required String name}) {
final branch = Branch.lookup(repo: repo, name: name);
bindings.delete(branch.pointer);
}
/// Renames an existing local branch reference.
///
/// The new branch name will be checked for validity.
///
/// Note that if the move succeeds, the old reference object will not be valid anymore,
/// and will be freed immediately.
///
/// If [force] is true, existing branch will be overwritten.
///
/// Throws a [LibGit2Error] if error occured.
Branch rename({required String newName, bool force = false}) {
return Branch(bindings.rename(
branchPointer: _branchPointer,
static void rename({
required Repository repo,
required String oldName,
required String newName,
bool force = false,
}) {
final branch = Branch.lookup(repo: repo, name: oldName);
bindings.rename(
branchPointer: branch.pointer,
newBranchName: newName,
force: force,
));
);
branch.free();
}
/// Returns the OID pointed to by a branch.
///
/// Throws an exception if error occured.
Oid get target => Oid(reference_bindings.target(_branchPointer));
/// Checks if HEAD points to the given branch.
///
/// Throws a [LibGit2Error] if error occured.
@ -155,4 +152,9 @@ class Branch {
/// Releases memory allocated for branch object.
void free() => bindings.free(_branchPointer);
@override
String toString() {
return 'Branch{name: $name, target: $target}';
}
}

View file

@ -8,18 +8,16 @@ class Commit {
/// Initializes a new instance of [Commit] class from provided pointer to
/// commit object in memory.
///
/// Should be freed with `free()` to release allocated memory.
/// Should be freed to release allocated memory.
Commit(this._commitPointer);
/// Initializes a new instance of [Commit] class from provided [Repository] object
/// and [sha] hex string.
/// Lookups commit object for provided [id] in a [repo]sitory.
///
/// Should be freed with `free()` to release allocated memory.
Commit.lookup({required Repository repo, required String sha}) {
final oid = Oid.fromSHA(repo: repo, sha: sha);
/// Should be freed to release allocated memory.
Commit.lookup({required Repository repo, required Oid id}) {
_commitPointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
oidPointer: id.pointer,
);
}
@ -42,14 +40,14 @@ class Commit {
required String message,
required Signature author,
required Signature commiter,
required String treeSHA,
required List<String> parents,
required Tree tree,
required List<Commit> parents,
String? updateRef,
String? messageEncoding,
}) {
final tree = Tree.lookup(repo: repo, sha: treeSHA);
final parentsPointers = parents.map((parent) => parent.pointer).toList();
final result = Oid(bindings.create(
return Oid(bindings.create(
repoPointer: repo.pointer,
updateRef: updateRef,
authorPointer: author.pointer,
@ -58,12 +56,8 @@ class Commit {
message: message,
treePointer: tree.pointer,
parentCount: parents.length,
parents: parents,
parents: parentsPointers,
));
tree.free();
return result;
}
/// Returns the encoding for the message of a commit, as a string

View file

@ -139,25 +139,9 @@ class Index with IterableMixin<IndexEntry> {
bindings.read(indexPointer: _indexPointer, force: force);
/// Updates the contents of an existing index object in memory by reading from the
/// specified tree.
void readTree(Object target) {
late final Tree tree;
if (target is Oid) {
final repo = Repository(bindings.owner(_indexPointer));
tree = Tree.lookup(repo: repo, sha: target.sha);
} else if (target is Tree) {
tree = target;
} else if (target is String) {
final repo = Repository(bindings.owner(_indexPointer));
tree = Tree.lookup(repo: repo, sha: target);
} else {
throw ArgumentError.value(
'$target should be either Oid object, SHA hex string or Tree object');
}
/// specified [tree].
void readTree(Tree tree) {
bindings.readTree(indexPointer: _indexPointer, treePointer: tree.pointer);
tree.free();
}
/// Writes an existing index object from memory back to disk using an atomic file lock.

View file

@ -3,43 +3,44 @@ import 'package:libgit2dart/libgit2dart.dart';
import 'bindings/libgit2_bindings.dart';
import 'bindings/note.dart' as bindings;
class Notes {
/// Initializes a new instance of the [Notes] class from
/// provided [Repository] object.
Notes(Repository repo) {
_repoPointer = repo.pointer;
}
class Note {
/// Initializes a new instance of the [Note] class from provided
/// pointer to note and annotatedId objects in memory.
Note(this._notePointer, this._annotatedIdPointer);
/// Pointer to memory address for allocated repository object.
late final Pointer<git_repository> _repoPointer;
/// Reads the note for an object.
/// Reads the note for an [annotatedId].
///
/// The note must be freed manually by the user.
/// IMPORTANT: Notes must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
static Note lookup({
Note.lookup({
required Repository repo,
required Oid annotatedId,
String notesRef = 'refs/notes/commits',
}) {
final note = bindings.lookup(
_notePointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: annotatedId.pointer,
notesRef: notesRef,
);
return Note(note, annotatedId.pointer, repo.pointer);
_annotatedIdPointer = annotatedId.pointer;
}
/// Adds a note for an [object].
/// Pointer to memory address for allocated note object.
late final Pointer<git_note> _notePointer;
/// Pointer to memory address for allocated annotetedId object.
late final Pointer<git_oid> _annotatedIdPointer;
/// Adds a note for an [annotatedId].
///
/// Throws a [LibGit2Error] if error occured.
static Oid create({
required Repository repo,
required Signature author,
required Signature committer,
required Oid object,
required Oid annotatedId,
required String note,
String notesRef = 'refs/notes/commits',
bool force = false,
@ -48,62 +49,49 @@ class Notes {
repoPointer: repo.pointer,
authorPointer: author.pointer,
committerPointer: committer.pointer,
oidPointer: object.pointer,
oidPointer: annotatedId.pointer,
note: note,
notesRef: notesRef,
force: force,
));
}
/// Returns list of notes for repository.
///
/// Notes must be freed manually by the user.
/// Deletes the note for an [annotatedId].
///
/// Throws a [LibGit2Error] if error occured.
List<Note> get list {
final notesPointers = bindings.list(_repoPointer);
static void delete({
required Repository repo,
required Oid annotatedId,
required Signature author,
required Signature committer,
String notesRef = 'refs/notes/commits',
}) {
bindings.delete(
repoPointer: repo.pointer,
authorPointer: author.pointer,
committerPointer: committer.pointer,
oidPointer: annotatedId.pointer,
);
}
/// Returns list of notes for repository.
///
/// IMPORTANT: Notes must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
static List<Note> list(Repository repo) {
final notesPointers = bindings.list(repo.pointer);
var result = <Note>[];
for (var note in notesPointers) {
result.add(Note(
note['note'] as Pointer<git_note>,
note['annotatedId'] as Pointer<git_oid>,
_repoPointer,
));
}
return result;
}
}
class Note {
/// Initializes a new instance of the [Note] class from provided
/// pointer to note object in memory.
Note(this._notePointer, this._annotatedIdPointer, this._repoPointer);
/// Pointer to memory address for allocated note object.
final Pointer<git_note> _notePointer;
/// Pointer to memory address for allocated annotetedId object.
final Pointer<git_oid> _annotatedIdPointer;
/// Pointer to memory address for allocated repository object.
final Pointer<git_repository> _repoPointer;
/// Removes the note for an [object].
///
/// Throws a [LibGit2Error] if error occured.
void remove({
required Signature author,
required Signature committer,
String notesRef = 'refs/notes/commits',
}) {
bindings.remove(
repoPointer: _repoPointer,
authorPointer: author.pointer,
committerPointer: committer.pointer,
oidPointer: annotatedId.pointer,
);
}
/// Returns the note object's [Oid].
Oid get id => Oid(bindings.id(_notePointer));

View file

@ -7,53 +7,31 @@ import 'bindings/refdb.dart' as refdb_bindings;
import 'bindings/repository.dart' as repository_bindings;
import 'util.dart';
class References {
/// Initializes a new instance of the [References] class
/// from provided [Repository] object.
References(Repository repo) {
_repoPointer = repo.pointer;
}
/// Pointer to memory address for allocated repository object.
late final Pointer<git_repository> _repoPointer;
/// Returns a list of all the references that can be found in a repository.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get list => bindings.list(_repoPointer);
/// Returns number of all the references that can be found in a repository.
int get length => list.length;
/// Returns a [Reference] by lookingup [name] in a repository.
///
/// Should be freed with `free()` to release allocated memory.
///
/// The name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Reference operator [](String name) {
return Reference(bindings.lookup(repoPointer: _repoPointer, name: name));
}
class Reference {
/// Initializes a new instance of the [Reference] class.
/// Should be freed to release allocated memory.
Reference(this._refPointer);
/// Creates a new reference.
///
/// The reference will be created in the repository and written to the disk.
/// The reference will be created in the [repo]sitory and written to the disk.
/// The generated [Reference] object must be freed by the user.
///
/// Valid reference names must follow one of two patterns:
/// Valid reference [name]s must follow one of two patterns:
///
/// Top-level names must contain only capital letters and underscores, and must begin and end
/// with a letter. (e.g. "HEAD", "ORIG_HEAD").
/// Names prefixed with "refs/" can be almost anything. You must avoid the characters
/// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have
/// special meaning to revparse.
/// Throws a [LibGit2Error] if a reference already exists with the given name
/// unless force is true, in which case it will be overwritten.
///
/// The message for the reflog will be ignored if the reference does not belong in the
/// Throws a [LibGit2Error] if a reference already exists with the given [name]
/// unless [force] is true, in which case it will be overwritten.
///
/// The [logMessage] message for the reflog will be ignored if the reference does not belong in the
/// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog.
Reference create({
Reference.create({
required Repository repo,
required String name,
required Object target,
bool force = false,
@ -66,7 +44,6 @@ class References {
oid = target;
isDirect = true;
} else if (isValidShaHex(target as String)) {
final repo = Repository(_repoPointer);
oid = Oid.fromSHA(repo: repo, sha: target);
isDirect = true;
} else {
@ -74,46 +51,97 @@ class References {
}
if (isDirect) {
return Reference(bindings.createDirect(
repoPointer: _repoPointer,
_refPointer = bindings.createDirect(
repoPointer: repo.pointer,
name: name,
oidPointer: oid.pointer,
force: force,
logMessage: logMessage,
));
);
} else {
return Reference(bindings.createSymbolic(
repoPointer: _repoPointer,
_refPointer = bindings.createSymbolic(
repoPointer: repo.pointer,
name: name,
target: target as String,
force: force,
logMessage: logMessage,
));
);
}
}
/// Suggests that the given refdb compress or optimize its references.
/// This mechanism is implementation specific. For on-disk reference databases,
/// for example, this may pack all loose references.
/// Lookups reference [name] in a [repo]sitory.
///
/// Should be freed to release allocated memory.
///
/// The [name] will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
void compress() {
final refdb = repository_bindings.refdb(_repoPointer);
refdb_bindings.compress(refdb);
refdb_bindings.free(refdb);
Reference.lookup({required Repository repo, required String name}) {
_refPointer = bindings.lookup(repoPointer: repo.pointer, name: name);
}
}
class Reference {
/// Initializes a new instance of the [Reference] class.
/// Should be freed with `free()` to release allocated memory.
Reference(this._refPointer);
late Pointer<git_reference> _refPointer;
/// Pointer to memory address for allocated reference object.
Pointer<git_reference> get pointer => _refPointer;
/// Deletes an existing reference with provided [name].
///
/// This method works for both direct and symbolic references.
///
/// Throws a [LibGit2Error] if error occured.
static void delete({required Repository repo, required String name}) {
final ref = Reference.lookup(repo: repo, name: name);
bindings.delete(ref.pointer);
ref.free();
}
/// Renames an existing reference.
///
/// This method works for both direct and symbolic references.
///
/// The [newName] will be checked for validity.
///
/// If the [force] flag is set to false, and there's already a reference with the given name,
/// the renaming will fail.
///
/// IMPORTANT: The user needs to write a proper reflog entry [logMessage] if the reflog is
/// enabled for the repository. We only rename the reflog if it exists.
///
/// Throws a [LibGit2Error] if error occured.
static void rename({
required Repository repo,
required String oldName,
required String newName,
bool force = false,
String? logMessage,
}) {
final ref = Reference.lookup(repo: repo, name: oldName);
bindings.rename(
refPointer: ref.pointer,
newName: newName,
force: force,
logMessage: logMessage,
);
ref.free();
}
/// Returns a list of all the references that can be found in a [repo]sitory.
///
/// Throws a [LibGit2Error] if error occured.
static List<String> list(Repository repo) => bindings.list(repo.pointer);
/// Suggests that the [repo]sitory's refdb compress or optimize its references.
/// This mechanism is implementation specific. For on-disk reference databases,
/// for example, this may pack all loose references.
///
/// Throws a [LibGit2Error] if error occured.
static void compress(Repository repo) {
final refdb = repository_bindings.refdb(repo.pointer);
refdb_bindings.compress(refdb);
refdb_bindings.free(refdb);
}
/// Returns the type of the reference.
ReferenceType get type {
return bindings.referenceType(_refPointer) == 1
@ -180,6 +208,8 @@ class Reference {
/// ```dart
/// final commit = ref.peel(GitObject.commit) as Commit;
/// final tree = ref.peel(GitObject.tree) as Tree;
/// final blob = ref.peel(GitObject.blob) as Blob;
/// final tag = ref.peel(GitObject.tag) as Tag;
/// ```
///
/// Throws a [LibGit2Error] if error occured.
@ -207,32 +237,6 @@ class Reference {
/// If no shortname is appropriate, it will return the full name.
String get shorthand => bindings.shorthand(_refPointer);
/// Renames an existing reference.
///
/// This method works for both direct and symbolic references.
///
/// The new name will be checked for validity.
///
/// If the force flag is not enabled, and there's already a reference with the given name,
/// the renaming will fail.
///
/// IMPORTANT: The user needs to write a proper reflog entry if the reflog is enabled for
/// the repository. We only rename the reflog if it exists.
///
/// Throws a [LibGit2Error] if error occured.
void rename({
required String newName,
bool force = false,
String? logMessage,
}) {
_refPointer = bindings.rename(
refPointer: _refPointer,
newName: newName,
force: force,
logMessage: logMessage,
);
}
/// Checks if a reflog exists for the specified reference [name].
///
/// Throws a [LibGit2Error] if error occured.
@ -261,14 +265,6 @@ class Reference {
/// Returns the repository where a reference resides.
Repository get owner => Repository(bindings.owner(_refPointer));
/// Delete an existing reference.
///
/// This method works for both direct and symbolic references.
/// The reference will be immediately removed on disk but the memory will not be freed.
///
/// Throws a [LibGit2Error] if the reference has changed from the time it was looked up.
void delete() => bindings.delete(_refPointer);
@override
bool operator ==(other) {
return (other is Reference) &&
@ -283,4 +279,9 @@ class Reference {
/// Releases memory allocated for reference object.
void free() => bindings.free(_refPointer);
@override
String toString() {
return 'Reference{name: $name, target: $target}';
}
}

View file

@ -3,70 +3,63 @@ import 'package:libgit2dart/libgit2dart.dart';
import 'bindings/libgit2_bindings.dart';
import 'bindings/remote.dart' as bindings;
class Remotes {
/// Initializes a new instance of the [Remotes] class from
/// provided [Repository] object.
Remotes(Repository repo) {
_repoPointer = repo.pointer;
}
class Remote {
/// Initializes a new instance of [Remote] class from provided pointer
/// to remote object in memory.
Remote(this._remotePointer);
/// Pointer to memory address for allocated repository object.
late final Pointer<git_repository> _repoPointer;
/// Returns a list of the configured remotes for a repo.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get list {
return bindings.list(_repoPointer);
}
/// Returns number of the configured remotes for a repo.
int get length => list.length;
/// Returns [Remote] by looking up [name] in a repository.
/// Initializes a new instance of [Remote] class by looking up remote with
/// provided [name] in a [repo]sitory.
///
/// The name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Remote operator [](String name) {
return Remote(bindings.lookup(repoPointer: _repoPointer, name: name));
Remote.lookup({required Repository repo, required String name}) {
_remotePointer = bindings.lookup(repoPointer: repo.pointer, name: name);
}
/// Adds a remote to the repository's configuration with the default [fetch]
/// refspec if none provided .
/// Initializes a new instance of [Remote] class by adding a remote with
/// provided [name] and [url] to the [repo]sitory's configuration with the
/// default [fetch] refspec if none provided .
///
/// Throws a [LibGit2Error] if error occured.
Remote create({
Remote.create({
required Repository repo,
required String name,
required String url,
String? fetch,
}) {
if (fetch == null) {
return Remote(bindings.create(
repoPointer: _repoPointer,
_remotePointer = bindings.create(
repoPointer: repo.pointer,
name: name,
url: url,
));
);
} else {
return Remote(bindings.createWithFetchSpec(
repoPointer: _repoPointer,
_remotePointer = bindings.createWithFetchSpec(
repoPointer: repo.pointer,
name: name,
url: url,
fetch: fetch,
));
);
}
}
late final Pointer<git_remote> _remotePointer;
/// Pointer to memory address for allocated remote object.
Pointer<git_remote> get pointer => _remotePointer;
/// Deletes an existing persisted remote.
///
/// All remote-tracking branches and configuration settings for the remote will be removed.
///
/// Throws a [LibGit2Error] if error occured.
void delete(String name) {
bindings.delete(repoPointer: _repoPointer, name: name);
static void delete({required Repository repo, required String name}) {
bindings.delete(repoPointer: repo.pointer, name: name);
}
/// Give the remote a new name.
/// Gives the remote a new name.
///
/// Returns list of non-default refspecs that cannot be renamed.
///
@ -78,23 +71,38 @@ class Remotes {
/// their list of refspecs.
///
/// Throws a [LibGit2Error] if error occured.
List<String> rename({required String remote, required String newName}) {
static List<String> rename({
required Repository repo,
required String oldName,
required String newName,
}) {
return bindings.rename(
repoPointer: _repoPointer,
name: remote,
repoPointer: repo.pointer,
name: oldName,
newName: newName,
);
}
/// Returns a list of the configured remotes for a [repo]sitory.
///
/// Throws a [LibGit2Error] if error occured.
static List<String> list(Repository repo) {
return bindings.list(repo.pointer);
}
/// Sets the remote's url in the configuration.
///
/// Remote objects already in memory will not be affected. This assumes the common
/// case of a single-url remote and will otherwise return an error.
///
/// Throws a [LibGit2Error] if error occured.
void setUrl({required String remote, required String url}) {
static void setUrl({
required Repository repo,
required String remote,
required String url,
}) {
bindings.setUrl(
repoPointer: _repoPointer,
repoPointer: repo.pointer,
remote: remote,
url: url,
);
@ -106,9 +114,13 @@ class Remotes {
/// case of a single-url remote and will otherwise return an error.
///
/// Throws a [LibGit2Error] if error occured.
void setPushUrl({required String remote, required String url}) {
static void setPushUrl({
required Repository repo,
required String remote,
required String url,
}) {
bindings.setPushUrl(
repoPointer: _repoPointer,
repoPointer: repo.pointer,
remote: remote,
url: url,
);
@ -119,9 +131,13 @@ class Remotes {
/// No loaded remote instances will be affected.
///
/// Throws a [LibGit2Error] if error occured.
void addFetch({required String remote, required String refspec}) {
static void addFetch({
required Repository repo,
required String remote,
required String refspec,
}) {
bindings.addFetch(
repoPointer: _repoPointer,
repoPointer: repo.pointer,
remote: remote,
refspec: refspec,
);
@ -132,24 +148,17 @@ class Remotes {
/// No loaded remote instances will be affected.
///
/// Throws a [LibGit2Error] if error occured.
void addPush({required String remote, required String refspec}) {
static void addPush({
required Repository repo,
required String remote,
required String refspec,
}) {
bindings.addPush(
repoPointer: _repoPointer,
repoPointer: repo.pointer,
remote: remote,
refspec: refspec,
);
}
}
class Remote {
/// Initializes a new instance of [Remote] class from provided pointer
/// to remote object in memory.
const Remote(this._remotePointer);
final Pointer<git_remote> _remotePointer;
/// Pointer to memory address for allocated remote object.
Pointer<git_remote> get pointer => _remotePointer;
/// Returns the remote's name.
String get name => bindings.name(_remotePointer);

View file

@ -130,6 +130,16 @@ class Repository {
);
}
/// Returns [Oid] object if it can be found in the ODB of repository with
/// provided hexadecimal [sha] string that is 40 characters long or shorter.
///
/// Throws [ArgumentError] if provided [sha] hex string is not valid.
///
/// Throws a [LibGit2Error] if error occured.
Oid operator [](String sha) {
return Oid.fromSHA(repo: this, sha: sha);
}
/// Returns path to the `.git` folder for normal repositories
/// or path to the repository itself for bare repositories.
String get path => bindings.path(_repoPointer);
@ -322,8 +332,89 @@ class Repository {
/// Must be freed once it's no longer being used.
Reference get head => Reference(bindings.head(_repoPointer));
/// Returns [References] object.
References get references => References(this);
/// Returns a list of all the references that can be found in a repository.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get references => Reference.list(this);
/// Lookups reference [name] in a [repo]sitory.
///
/// Should be freed to release allocated memory.
///
/// The [name] will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Reference lookupReference(String name) {
return Reference.lookup(repo: this, name: name);
}
/// Creates a new reference.
///
/// The reference will be created in the repository and written to the disk.
/// The generated [Reference] object must be freed by the user.
///
/// Valid reference [name]s must follow one of two patterns:
///
/// Top-level names must contain only capital letters and underscores, and must begin and end
/// with a letter. (e.g. "HEAD", "ORIG_HEAD").
/// Names prefixed with "refs/" can be almost anything. You must avoid the characters
/// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have
/// special meaning to revparse.
///
/// Throws a [LibGit2Error] if a reference already exists with the given [name]
/// unless [force] is true, in which case it will be overwritten.
///
/// The [logMessage] message for the reflog will be ignored if the reference does not belong in the
/// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog.
Reference createReference({
required String name,
required Object target,
bool force = false,
String? logMessage,
}) {
return Reference.create(
repo: this,
name: name,
target: target,
force: force,
logMessage: logMessage,
);
}
/// Deletes an existing reference with provided [name].
///
/// This method works for both direct and symbolic references.
///
/// Throws a [LibGit2Error] if error occured.
void deleteReference(String name) => Reference.delete(repo: this, name: name);
/// Renames an existing reference.
///
/// This method works for both direct and symbolic references.
///
/// The [newName] will be checked for validity.
///
/// If the [force] flag is set to false, and there's already a reference with the given name,
/// the renaming will fail.
///
/// IMPORTANT: The user needs to write a proper reflog entry [logMessage] if the reflog is
/// enabled for the repository. We only rename the reflog if it exists.
///
/// Throws a [LibGit2Error] if error occured.
void renameReference({
required String oldName,
required String newName,
bool force = false,
String? logMessage,
}) {
Reference.rename(
repo: this,
oldName: oldName,
newName: newName,
force: force,
logMessage: logMessage,
);
}
/// Returns [Index] file for this repository.
///
@ -337,39 +428,11 @@ class Repository {
/// Throws a [LibGit2Error] if error occured.
Odb get odb => Odb(bindings.odb(_repoPointer));
/// Looksup git object (commit, tree, blob, tag) for provided [sha] hex string.
/// Lookups a tree object for provided [id].
///
/// Returned object should be explicitly downcasted to one of four of git object types.
///
/// ```dart
/// final commit = repo['s0m3sh4'] as Commit;
/// final tree = repo['s0m3sh4'] as Tree;
/// final blob = repo['s0m3sh4'] as Blob;
/// final tag = repo['s0m3sh4'] as Tag;
/// ```
///
/// Throws [ArgumentError] if provided [sha] is not pointing to commit, tree, blob or tag.
Object operator [](String sha) {
final oid = Oid.fromSHA(repo: this, sha: sha);
final object = object_bindings.lookup(
repoPointer: _repoPointer,
oidPointer: oid.pointer,
type: GitObject.any.value,
);
final type = object_bindings.type(object);
if (type == GitObject.commit.value) {
return Commit(object.cast());
} else if (type == GitObject.tree.value) {
return Tree(object.cast());
} else if (type == GitObject.blob.value) {
return Blob(object.cast());
} else if (type == GitObject.tag.value) {
return Tag(object.cast());
} else {
throw ArgumentError.value(
'$sha should be pointing to either commit, tree, blob or a tag');
}
/// Should be freed to release allocated memory.
Tree lookupTree(Oid id) {
return Tree.lookup(repo: this, id: id);
}
/// Creates a new action signature with default user and now timestamp.
@ -410,6 +473,13 @@ class Repository {
return RevParse.single(repo: this, spec: spec);
}
/// Lookups commit object for provided [id].
///
/// Should be freed to release allocated memory.
Commit lookupCommit(Oid id) {
return Commit.lookup(repo: this, id: id);
}
/// Creates new commit in the repository.
///
/// [updateRef] is name of the reference that will be updated to point to this commit.
@ -423,8 +493,8 @@ class Repository {
required String message,
required Signature author,
required Signature commiter,
required String treeSHA,
required List<String> parents,
required Tree tree,
required List<Commit> parents,
String? updateRef,
String? messageEncoding,
}) {
@ -433,7 +503,7 @@ class Repository {
message: message,
author: author,
commiter: commiter,
treeSHA: treeSHA,
tree: tree,
parents: parents,
);
}
@ -464,6 +534,13 @@ class Repository {
return RevParse.range(repo: this, spec: spec);
}
/// Lookups a blob object for provided [id].
///
/// Should be freed to release allocated memory.
Blob lookupBlob(Oid id) {
return Blob.lookup(repo: this, id: id);
}
/// Creates a new blob from a [content] string and writes it to ODB.
///
/// Throws a [LibGit2Error] if error occured.
@ -487,12 +564,22 @@ class Repository {
return Blob.createFromDisk(repo: this, path: path);
}
/// Creates a new tag in the repository from provided Oid object.
/// Returns a list with all the tags in the repository.
///
/// A new reference will also be created pointing to this tag object. If force is true
/// Throws a [LibGit2Error] if error occured.
List<String> get tags => Tag.list(this);
/// Lookups tag object for provided [id].
///
/// Should be freed to release allocated memory.
Tag lookupTag(Oid id) => Tag.lookup(repo: this, id: id);
/// Creates a new tag in the repository for provided [target] object.
///
/// A new reference will also be created pointing to this tag object. If [force] is true
/// and a reference already exists with the given name, it'll be replaced.
///
/// The message will not be cleaned up.
/// The [message] will not be cleaned up.
///
/// The tag name will be checked for validity. You must avoid the characters
/// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have
@ -501,7 +588,7 @@ class Repository {
/// Throws a [LibGit2Error] if error occured.
Oid createTag({
required String tagName,
required String target,
required Oid target,
required GitObject targetType,
required Signature tagger,
required String message,
@ -517,13 +604,93 @@ class Repository {
force: force);
}
/// Returns a list with all the tags in the repository.
/// Deletes an existing tag reference with provided [name].
///
/// The tag [name] will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get tags => Tag.list(this);
void deleteTag(String name) => Tag.delete(repo: this, name: name);
/// Returns a [Branches] object.
Branches get branches => Branches(this);
/// Returns a list of all branches that can be found in a repository.
///
/// IMPORTANT: Branches must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
List<Branch> get branches => Branch.list(repo: this);
/// Returns a list of local branches that can be found in a repository.
///
/// IMPORTANT: Branches must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
List<Branch> get branchesLocal =>
Branch.list(repo: this, type: GitBranch.local);
/// Returns a list of remote branches that can be found in a repository.
///
/// IMPORTANT: Branches must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
List<Branch> get branchesRemote =>
Branch.list(repo: this, type: GitBranch.remote);
/// Lookups a branch by its [name] in a repository.
///
/// The branch name will be checked for validity.
///
/// Should be freed to release allocated memory when no longer needed.
///
/// Throws a [LibGit2Error] if error occured.
Branch lookupBranch(String name) {
return Branch.lookup(repo: this, name: name);
}
/// Creates a new branch pointing at a [target] commit.
///
/// A new direct reference will be created pointing to this target commit.
/// If [force] is true and a reference already exists with the given name, it'll be replaced.
///
/// Should be freed with [free] to release allocated memory when no longer
/// needed.
///
/// The branch name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Branch createBranch({
required String name,
required Commit target,
bool force = false,
}) {
return Branch.create(
repo: this,
name: name,
target: target,
force: force,
);
}
/// Deletes an existing branch reference.
///
/// Throws a [LibGit2Error] if error occured.
void deleteBranch(String name) => Branch.delete(repo: this, name: name);
/// Renames an existing local branch reference.
///
/// The new branch name will be checked for validity.
///
/// If [force] is true, existing branch will be overwritten.
///
/// Throws a [LibGit2Error] if error occured.
void renameBranch({
required String oldName,
required String newName,
bool force = false,
}) {
Branch.rename(repo: this, oldName: oldName, newName: newName, force: force);
}
/// Checks status of the repository and returns map of file paths and their statuses.
///
@ -619,7 +786,7 @@ class Repository {
required Oid theirHead,
String ourRef = 'HEAD',
}) {
final ref = references[ourRef];
final ref = lookupReference(ourRef);
final head = commit_bindings.annotatedLookup(
repoPointer: _repoPointer,
oidPointer: theirHead.pointer,
@ -831,7 +998,7 @@ class Repository {
paths: paths,
);
} else {
final ref = references[refName];
final ref = lookupReference(refName);
final treeish = object_bindings.lookup(
repoPointer: _repoPointer,
oidPointer: ref.target.pointer,
@ -1062,8 +1229,57 @@ class Repository {
return stash_bindings.list(_repoPointer);
}
/// Returns [Remotes] object.
Remotes get remotes => Remotes(this);
/// Returns a list of the configured remotes for a repository.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get remotes => Remote.list(this);
/// Lookups remote with provided [name].
///
/// The name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Remote lookupRemote(String name) {
return Remote.lookup(repo: this, name: name);
}
/// Adds a remote with provided [name] and [url] to the repository's
/// configuration with the default [fetch] refspec if none provided .
///
/// Throws a [LibGit2Error] if error occured.
Remote createRemote({
required String name,
required String url,
String? fetch,
}) {
return Remote.create(repo: this, name: name, url: url, fetch: fetch);
}
/// Deletes an existing persisted remote.
///
/// All remote-tracking branches and configuration settings for the remote will be removed.
///
/// Throws a [LibGit2Error] if error occured.
void deleteRemote(String name) => Remote.delete(repo: this, name: name);
/// Gives the remote a new name.
///
/// Returns list of non-default refspecs that cannot be renamed.
///
/// All remote-tracking branches and configuration settings for the remote are updated.
///
/// The new name will be checked for validity.
///
/// No loaded instances of a the remote with the old name will change their name or
/// their list of refspecs.
///
/// Throws a [LibGit2Error] if error occured.
List<String> renameRemote({
required String oldName,
required String newName,
}) {
return Remote.rename(repo: this, oldName: oldName, newName: newName);
}
/// Looks up the value of one git attribute for path.
///
@ -1132,49 +1348,69 @@ class Repository {
/// Returns list of notes for repository.
///
/// Notes must be freed manually.
/// IMPORTANT: Notes must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
List<Note> get notes => Notes(this).list;
List<Note> get notes => Note.list(this);
/// Reads the note for an object.
/// Reads the note for an [annotatedId].
///
/// The note must be freed manually.
/// IMPORTANT: Notes must be freed manually when no longer needed to prevent
/// memory leak.
///
/// Throws a [LibGit2Error] if error occured.
Note lookupNote({
required Oid annotatedId,
String notesRef = 'refs/notes/commits',
}) {
return Notes.lookup(
return Note.lookup(
repo: this,
annotatedId: annotatedId,
notesRef: notesRef,
);
}
/// Adds a note for an [object].
/// Adds a note for an [annotatedId].
///
/// Throws a [LibGit2Error] if error occured.
Oid createNote({
required Signature author,
required Signature committer,
required Oid object,
required Oid annotatedId,
required String note,
String notesRef = 'refs/notes/commits',
bool force = false,
}) {
return Notes.create(
return Note.create(
repo: this,
author: author,
committer: committer,
object: object,
annotatedId: annotatedId,
note: note,
notesRef: notesRef,
force: force,
);
}
/// Deletes the note for an [annotatedId].
///
/// Throws a [LibGit2Error] if error occured.
void deleteNote({
required Oid annotatedId,
required Signature author,
required Signature committer,
String notesRef = 'refs/notes/commits',
}) {
Note.delete(
repo: this,
annotatedId: annotatedId,
author: author,
committer: committer,
notesRef: notesRef,
);
}
/// Checks if a commit is the descendant of another commit.
///
/// Note that a commit is not considered a descendant of itself, in contrast to
@ -1397,4 +1633,34 @@ class Repository {
callbacks: callbacks,
);
}
/// Returns list of names of linked working trees.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get worktrees => Worktree.list(this);
/// Lookups up existing worktree for provided [name].
///
/// Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Worktree lookupWorktree(String name) {
return Worktree.lookup(repo: this, name: name);
}
/// Creates new worktree.
///
/// If [ref] is provided, no new branch will be created but specified [ref] will
/// be used instead.
///
/// Should be freed with `free()` to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Worktree createWorktree({
required String name,
required String path,
Reference? ref,
}) {
return Worktree.create(repo: this, name: name, path: path, ref: ref);
}
}

View file

@ -8,18 +8,16 @@ class Tag {
/// Initializes a new instance of [Tag] class from provided pointer to
/// tag object in memory.
///
/// Should be freed with `free()` to release allocated memory.
/// Should be freed to release allocated memory.
Tag(this._tagPointer);
/// Initializes a new instance of [Tag] class from provided
/// [Repository] object and [sha] hex string.
/// Lookups tag object for provided [id] in a [repo]sitory.
///
/// Should be freed with `free()` to release allocated memory.
Tag.lookup({required Repository repo, required String sha}) {
final oid = Oid.fromSHA(repo: repo, sha: sha);
/// Should be freed to release allocated memory.
Tag.lookup({required Repository repo, required Oid id}) {
_tagPointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
oidPointer: id.pointer,
);
}
@ -28,12 +26,12 @@ class Tag {
/// Pointer to memory address for allocated tag object.
Pointer<git_tag> get pointer => _tagPointer;
/// Creates a new tag in the repository from provided Oid object.
/// Creates a new tag in the repository for provided [target] object.
///
/// A new reference will also be created pointing to this tag object. If force is true
/// A new reference will also be created pointing to this tag object. If [force] is true
/// and a reference already exists with the given name, it'll be replaced.
///
/// The message will not be cleaned up.
/// The [message] will not be cleaned up.
///
/// The tag name will be checked for validity. You must avoid the characters
/// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have
@ -43,16 +41,15 @@ class Tag {
static Oid create({
required Repository repo,
required String tagName,
required String target,
required Oid target,
required GitObject targetType,
required Signature tagger,
required String message,
bool force = false,
}) {
final targetOid = Oid.fromSHA(repo: repo, sha: target);
final object = object_bindings.lookup(
repoPointer: repo.pointer,
oidPointer: targetOid.pointer,
oidPointer: target.pointer,
type: targetType.value,
);
final result = bindings.create(
@ -68,6 +65,15 @@ class Tag {
return Oid(result);
}
/// Deletes an existing tag reference with provided [name] in a [repo]sitory.
///
/// The tag [name] will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
static void delete({required Repository repo, required String name}) {
bindings.delete(repoPointer: repo.pointer, tagName: name);
}
/// Returns a list with all the tags in the repository.
///
/// Throws a [LibGit2Error] if error occured.
@ -123,16 +129,6 @@ class Tag {
}
}
/// Deletes an existing tag reference.
///
/// The tag name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
void delete() {
final owner = bindings.owner(_tagPointer);
bindings.delete(repoPointer: owner, tagName: name);
}
/// Releases memory allocated for tag object.
void free() => bindings.free(_tagPointer);
}

View file

@ -8,18 +8,16 @@ class Tree {
/// Initializes a new instance of [Tree] class from provided pointer to
/// tree object in memory.
///
/// Should be freed with `free()` to release allocated memory.
/// Should be freed to release allocated memory.
Tree(this._treePointer);
/// Initializes a new instance of [Tree] class from provided
/// [Repository] object and [sha] hex string.
/// Lookups a tree object for provided [id] in a [repo]sitory.
///
/// Should be freed with `free()` to release allocated memory.
Tree.lookup({required Repository repo, required String sha}) {
final oid = Oid.fromSHA(repo: repo, sha: sha);
/// Should be freed to release allocated memory.
Tree.lookup({required Repository repo, required Oid id}) {
_treePointer = bindings.lookup(
repoPointer: repo.pointer,
oidPointer: oid.pointer,
oidPointer: id.pointer,
);
}

View file

@ -5,9 +5,9 @@ import 'bindings/treebuilder.dart' as bindings;
class TreeBuilder {
/// Initializes a new instance of [TreeBuilder] class from provided
/// [Repository] and optional [Tree] objects.
/// [repo]sitory and optional [tree] objects.
///
/// Should be freed with `free()` to release allocated memory.
/// Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
TreeBuilder({required Repository repo, Tree? tree}) {
@ -79,6 +79,6 @@ class TreeBuilder {
);
}
/// Releases memory allocated for tree builder object.
/// Releases memory allocated for tree builder object and all the entries.
void free() => bindings.free(_treeBuilderPointer);
}

View file

@ -10,8 +10,8 @@ void main() {
late Signature sig2;
var hunks = <Map<String, dynamic>>[];
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/blamerepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/blamerepo/'));
repo = Repository.open(tmpDir.path);
sig1 = Signature.create(
name: 'Aleksey Kulikov',
@ -48,11 +48,11 @@ void main() {
];
});
tearDown(() async {
tearDown(() {
sig1.free();
sig2.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Blame', () {

View file

@ -11,16 +11,16 @@ void main() {
const blobContent = 'Feature edit\n';
const newBlobContent = 'New blob\n';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
blob = Blob.lookup(repo: repo, sha: blobSHA);
blob = repo.lookupBlob(repo[blobSHA]);
});
tearDown(() async {
tearDown(() {
blob.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Blob', () {
@ -36,8 +36,8 @@ void main() {
});
test('successfully creates new blob', () {
final oid = Blob.create(repo: repo, content: newBlobContent);
final newBlob = Blob.lookup(repo: repo, sha: oid.sha);
final oid = repo.createBlob(newBlobContent);
final newBlob = repo.lookupBlob(oid);
expect(newBlob.id.sha, '18fdaeef018e57a92bcad2d4a35b577f34089af6');
expect(newBlob.isBinary, false);
@ -49,11 +49,8 @@ void main() {
test('successfully creates new blob from file at provided relative path',
() {
final oid = Blob.createFromWorkdir(
repo: repo,
relativePath: 'feature_file',
);
final newBlob = Blob.lookup(repo: repo, sha: oid.sha);
final oid = repo.createBlobFromWorkdir('feature_file');
final newBlob = repo.lookupBlob(oid);
expect(newBlob.id.sha, blobSHA);
expect(newBlob.isBinary, false);
@ -65,10 +62,7 @@ void main() {
test('throws when creating new blob from invalid path', () {
expect(
() => Blob.createFromWorkdir(
repo: repo,
relativePath: 'invalid/path.txt',
),
() => repo.createBlobFromWorkdir('invalid/path.txt'),
throwsA(isA<LibGit2Error>()),
);
});
@ -79,10 +73,7 @@ void main() {
final outsideFile =
File('${Directory.current.absolute.path}/test/blob_test.dart');
expect(
() => Blob.createFromWorkdir(
repo: repo,
relativePath: outsideFile.path,
),
() => repo.createBlobFromWorkdir(outsideFile.path),
throwsA(isA<LibGit2Error>()),
);
});
@ -90,8 +81,8 @@ void main() {
test('successfully creates new blob from file at provided path', () {
final outsideFile =
File('${Directory.current.absolute.path}/test/blob_test.dart');
final oid = Blob.createFromDisk(repo: repo, path: outsideFile.path);
final newBlob = Blob.lookup(repo: repo, sha: oid.sha);
final oid = repo.createBlobFromDisk(outsideFile.path);
final newBlob = repo.lookupBlob(oid);
expect(newBlob, isA<Blob>());
expect(newBlob.isBinary, false);
@ -101,8 +92,6 @@ void main() {
group('diff', () {
const path = 'feature_file';
const oldBlobSha = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391';
const newBlobSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc';
const blobPatch = """
diff --git a/feature_file b/feature_file
index e69de29..9c78c21 100644
@ -120,8 +109,12 @@ index e69de29..0000000
+++ /dev/null
""";
test('successfully creates from blobs', () {
final a = repo[oldBlobSha] as Blob;
final b = repo[newBlobSha] as Blob;
final a = repo.lookupBlob(
repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'],
);
final b = repo.lookupBlob(
repo['9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'],
);
final patch = repo.diffBlobs(
a: a,
b: b,
@ -135,7 +128,9 @@ index e69de29..0000000
});
test('successfully creates from one blob (delete)', () {
final a = repo[oldBlobSha] as Blob;
final a = repo.lookupBlob(
repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'],
);
final patch = a.diff(
newBlob: null,
oldAsPath: path,
@ -148,7 +143,9 @@ index e69de29..0000000
});
test('successfully creates from blob and buffer', () {
final a = repo[oldBlobSha] as Blob;
final a = repo.lookupBlob(
repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'],
);
final patch = Patch.createFrom(
a: a,
b: 'Feature edit\n',
@ -162,7 +159,9 @@ index e69de29..0000000
});
test('successfully creates from blob and buffer (delete)', () {
final a = repo[oldBlobSha] as Blob;
final a = repo.lookupBlob(
repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'],
);
final patch = Patch.createFrom(
a: a,
b: null,

View file

@ -6,48 +6,59 @@ import 'helpers/util.dart';
void main() {
late Repository repo;
late Directory tmpDir;
const lastCommit = '821ed6e80627b8769d170a293862f9fc60825226';
const featureCommit = '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4';
late Oid lastCommit;
late Oid featureCommit;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
lastCommit = repo['821ed6e80627b8769d170a293862f9fc60825226'];
featureCommit = repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'];
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Branch', () {
test('returns a list of all branches', () {
final branches = Branches(repo);
expect(branches.list(), ['feature', 'master']);
const branchesExpected = ['feature', 'master'];
final branches = repo.branches;
for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]);
branches[i].free();
}
});
test('returns a list of local branches', () {
final branches = repo.branches.local;
expect(branches, ['feature', 'master']);
const branchesExpected = ['feature', 'master'];
final branches = repo.branchesLocal;
for (var i = 0; i < branches.length; i++) {
expect(branches[i].name, branchesExpected[i]);
branches[i].free();
}
});
test('returns a list of remote branches for provided type', () {
final branches = repo.branches.remote;
expect(branches, []);
test('returns a list of remote branches', () {
expect(repo.branchesRemote, []);
});
test('returns a branch with provided name', () {
final branch = repo.branches['master'];
expect(branch.target.sha, lastCommit);
final branch = repo.lookupBranch('master');
expect(branch.target.sha, lastCommit.sha);
branch.free();
});
test('throws when provided name not found', () {
expect(() => repo.branches['invalid'], throwsA(isA<LibGit2Error>()));
expect(() => repo.lookupBranch('invalid'), throwsA(isA<LibGit2Error>()));
});
test('checks if branch is current head', () {
final masterBranch = repo.branches['master'];
final featureBranch = repo.branches['feature'];
final masterBranch = repo.lookupBranch('master');
final featureBranch = repo.lookupBranch('feature');
expect(masterBranch.isHead, true);
expect(featureBranch.isHead, false);
@ -57,30 +68,33 @@ void main() {
});
test('returns name', () {
final branch = repo.branches['master'];
final branch = repo.lookupBranch('master');
expect(branch.name, 'master');
branch.free();
});
group('create()', () {
test('successfully creates', () {
final commit = repo[lastCommit] as Commit;
final commit = repo.lookupCommit(lastCommit);
final ref = repo.branches.create(name: 'testing', target: commit);
final branch = repo.branches['testing'];
expect(repo.branches.list().length, 3);
expect(branch.target.sha, lastCommit);
final branch = repo.createBranch(name: 'testing', target: commit);
final branches = repo.branches;
expect(repo.branches.length, 3);
expect(branch.target, lastCommit);
for (final branch in branches) {
branch.free();
}
branch.free();
ref.free();
commit.free();
});
test('throws when name already exists', () {
final commit = repo[lastCommit] as Commit;
final commit = repo.lookupCommit(lastCommit);
expect(
() => repo.branches.create(name: 'feature', target: commit),
() => repo.createBranch(name: 'feature', target: commit),
throwsA(isA<LibGit2Error>()),
);
@ -88,30 +102,39 @@ void main() {
});
test('successfully creates with force flag when name already exists', () {
final commit = repo[lastCommit] as Commit;
final commit = repo.lookupCommit(lastCommit);
final ref =
repo.branches.create(name: 'feature', target: commit, force: true);
final branch = repo.branches['feature'];
expect(repo.branches.local.length, 2);
expect(branch.target.sha, lastCommit);
final branch = repo.createBranch(
name: 'feature',
target: commit,
force: true,
);
final localBranches = repo.branchesLocal;
expect(localBranches.length, 2);
expect(branch.target, lastCommit);
for (final branch in localBranches) {
branch.free();
}
branch.free();
ref.free();
commit.free();
});
});
group('delete()', () {
test('successfully deletes', () {
repo.branches['feature'].delete();
expect(repo.branches.local.length, 1);
expect(() => repo.branches['feature'], throwsA(isA<LibGit2Error>()));
repo.deleteBranch('feature');
expect(
() => repo.lookupBranch('feature'),
throwsA(isA<LibGit2Error>()),
);
});
test('throws when trying to delete current HEAD', () {
expect(
() => repo.branches['master'].delete(),
() => repo.deleteBranch('master'),
throwsA(isA<LibGit2Error>()),
);
});
@ -119,43 +142,48 @@ void main() {
group('rename()', () {
test('successfully renames', () {
final renamed = repo.branches['feature'].rename(newName: 'renamed');
final branch = repo.branches['renamed'];
repo.renameBranch(oldName: 'feature', newName: 'renamed');
final branch = repo.lookupBranch('renamed');
final branches = repo.branches;
expect(renamed.target.sha, featureCommit);
expect(branch.target.sha, featureCommit);
expect(branches.length, 2);
expect(
() => repo.lookupBranch('feature'),
throwsA(isA<LibGit2Error>()),
);
expect(branch.target, featureCommit);
for (final branch in branches) {
branch.free();
}
branch.free();
renamed.free();
});
test('throws when name already exists', () {
final branch = repo.branches['feature'];
expect(
() => branch.rename(newName: 'master'),
() => repo.renameBranch(oldName: 'feature', newName: 'master'),
throwsA(isA<LibGit2Error>()),
);
branch.free();
});
test('successfully renames with force flag when name already exists', () {
final renamed = repo.branches['master'].rename(
repo.renameBranch(
oldName: 'master',
newName: 'feature',
force: true,
);
final branch = repo.lookupBranch('feature');
expect(renamed.target.sha, lastCommit);
expect(branch.target, lastCommit);
renamed.free();
branch.free();
});
test('throws when name is invalid', () {
final branch = repo.branches['feature'];
expect(
() => branch.rename(newName: 'inv@{id'),
() => repo.renameBranch(oldName: 'feature', newName: 'inv@{id'),
throwsA(isA<LibGit2Error>()),
);
branch.free();
});
});
});

View file

@ -8,14 +8,14 @@ void main() {
late Repository repo;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Checkout', () {
@ -39,8 +39,9 @@ void main() {
});
test('successfully checkouts tree', () {
final masterHead =
repo['821ed6e80627b8769d170a293862f9fc60825226'] as Commit;
final masterHead = repo.lookupCommit(
repo['821ed6e80627b8769d170a293862f9fc60825226'],
);
final masterTree = masterHead.tree;
expect(
masterTree.entries.any((e) => e.name == 'another_feature_file'),
@ -48,8 +49,9 @@ void main() {
);
repo.checkout(refName: 'refs/heads/feature');
final featureHead =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final featureHead = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final featureTree = featureHead.tree;
final repoHead = repo.head;
expect(repoHead.target.sha, featureHead.id.sha);

View file

@ -6,15 +6,14 @@ import 'helpers/util.dart';
void main() {
late Repository repo;
late Directory tmpDir;
const mergeCommit = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8';
const message = "Commit message.\n\nSome description.\n";
const tree = '7796359a96eb722939c24bafdb1afe9f07f2f628';
late Signature author;
late Signature commiter;
late Tree tree;
late Oid mergeCommit;
const message = "Commit message.\n\nSome description.\n";
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
author = Signature.create(
name: 'Author Name',
@ -26,31 +25,35 @@ void main() {
email: 'commiter@email.com',
time: 124,
);
mergeCommit = repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'];
tree = Tree.lookup(
repo: repo,
id: repo['7796359a96eb722939c24bafdb1afe9f07f2f628'],
);
});
tearDown(() async {
tearDown(() {
author.free();
commiter.free();
tree.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Commit', () {
test('successfully returns when 40 char sha hex is provided', () {
final commit = repo[mergeCommit] as Commit;
expect(commit, isA<Commit>());
commit.free();
});
test('successfully returns when sha hex is short', () {
final commit = repo[mergeCommit.substring(0, 5)] as Commit;
final commit = repo.lookupCommit(mergeCommit);
expect(commit, isA<Commit>());
commit.free();
});
test('successfully reverts commit', () {
final to = repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'] as Commit;
final from = repo['821ed6e80627b8769d170a293862f9fc60825226'] as Commit;
final to = repo.lookupCommit(
repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'],
);
final from = repo.lookupCommit(
repo['821ed6e80627b8769d170a293862f9fc60825226'],
);
final index = repo.index;
expect(index.find('dir/dir_file.txt'), true);
@ -64,16 +67,16 @@ void main() {
});
test('successfully creates commit', () {
final oid = Commit.create(
repo: repo,
final parent = repo.lookupCommit(mergeCommit);
final oid = repo.createCommit(
message: message,
author: author,
commiter: commiter,
treeSHA: tree,
parents: [mergeCommit],
tree: tree,
parents: [parent],
);
final commit = repo[oid.sha] as Commit;
final commit = repo.lookupCommit(oid);
expect(commit.id.sha, oid.sha);
expect(commit.message, message);
@ -81,11 +84,12 @@ void main() {
expect(commit.author, author);
expect(commit.committer, commiter);
expect(commit.time, 124);
expect(commit.tree.id.sha, tree);
expect(commit.tree.id, tree.id);
expect(commit.parents.length, 1);
expect(commit.parents[0].sha, mergeCommit);
expect(commit.parents[0], mergeCommit);
commit.free();
parent.free();
});
test('successfully creates commit without parents', () {
@ -93,11 +97,11 @@ void main() {
message: message,
author: author,
commiter: commiter,
treeSHA: tree,
tree: tree,
parents: [],
);
final commit = repo[oid.sha] as Commit;
final commit = repo.lookupCommit(oid);
expect(commit.id.sha, oid.sha);
expect(commit.message, message);
@ -105,23 +109,28 @@ void main() {
expect(commit.author, author);
expect(commit.committer, commiter);
expect(commit.time, 124);
expect(commit.tree.id.sha, tree);
expect(commit.tree.id, tree.id);
expect(commit.parents.length, 0);
commit.free();
});
test('successfully creates commit with 2 parents', () {
final parent1 = repo.lookupCommit(mergeCommit);
final parent2 = repo.lookupCommit(
repo['fc38877b2552ab554752d9a77e1f48f738cca79b'],
);
final oid = Commit.create(
repo: repo,
message: message,
author: author,
commiter: commiter,
treeSHA: tree,
parents: [mergeCommit, 'fc38877b2552ab554752d9a77e1f48f738cca79b'],
tree: tree,
parents: [parent1, parent2],
);
final commit = repo[oid.sha] as Commit;
final commit = repo.lookupCommit(oid);
expect(commit.id.sha, oid.sha);
expect(commit.message, message);
@ -129,36 +138,13 @@ void main() {
expect(commit.author, author);
expect(commit.committer, commiter);
expect(commit.time, 124);
expect(commit.tree.id.sha, tree);
expect(commit.tree.id, tree.id);
expect(commit.parents.length, 2);
expect(commit.parents[0].sha, mergeCommit);
expect(commit.parents[1].sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b');
commit.free();
});
test('successfully creates commit with short sha of tree', () {
final oid = Commit.create(
repo: repo,
message: message,
author: author,
commiter: commiter,
treeSHA: tree.substring(0, 5),
parents: [mergeCommit],
);
final commit = repo[oid.sha] as Commit;
expect(commit.id.sha, oid.sha);
expect(commit.message, message);
expect(commit.messageEncoding, 'utf-8');
expect(commit.author, author);
expect(commit.committer, commiter);
expect(commit.time, 124);
expect(commit.tree.id.sha, tree);
expect(commit.parents.length, 1);
expect(commit.parents[0].sha, mergeCommit);
expect(commit.parents[0], mergeCommit);
expect(commit.parents[1], parent2.id);
parent1.free();
parent2.free();
commit.free();
});
});

View file

@ -7,14 +7,14 @@ void main() {
late Repository repo;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Describe', () {
@ -23,25 +23,22 @@ void main() {
});
test('successfully describes commit', () {
final tag = Tag.lookup(repo: repo, sha: 'f0fdbf5');
tag.delete();
repo.deleteTag('v0.2');
expect(
repo.describe(describeStrategy: GitDescribeStrategy.tags),
'v0.1-1-g821ed6e',
);
tag.free();
});
test('throws when trying to describe and no reference found', () {
final commit = repo['f17d0d48'] as Commit;
final commit = repo.lookupCommit(repo['f17d0d48']);
expect(() => repo.describe(commit: commit), throwsA(isA<LibGit2Error>()));
commit.free();
});
test('returns oid when fallback argument is provided', () {
final commit = repo['f17d0d48'] as Commit;
final commit = repo.lookupCommit(repo['f17d0d48']);
expect(
repo.describe(commit: commit, showCommitOidAsFallback: true),
'f17d0d4',
@ -50,7 +47,7 @@ void main() {
});
test('successfully describes with provided strategy', () {
final commit = repo['5aecfa0'] as Commit;
final commit = repo.lookupCommit(repo['5aecfa0']);
expect(
repo.describe(
commit: commit,
@ -63,10 +60,10 @@ void main() {
test('successfully describes with provided pattern', () {
final signature = repo.defaultSignature;
final commit = repo['fc38877'] as Commit;
final commit = repo.lookupCommit(repo['fc38877']);
repo.createTag(
tagName: 'test/tag1',
target: 'f17d0d48',
target: repo['f17d0d48'],
targetType: GitObject.commit,
tagger: signature,
message: '',
@ -82,10 +79,9 @@ void main() {
});
test('successfully describes and follows first parent only', () {
final tag = Tag.lookup(repo: repo, sha: 'f0fdbf5');
tag.delete();
final commit = repo.lookupCommit(repo['821ed6e']);
repo.deleteTag('v0.2');
final commit = repo['821ed6e'] as Commit;
expect(
repo.describe(
commit: commit,
@ -95,15 +91,13 @@ void main() {
'v0.1-1-g821ed6e',
);
tag.free();
commit.free();
});
test('successfully describes with abbreviated size provided', () {
final tag = Tag.lookup(repo: repo, sha: 'f0fdbf5');
tag.delete();
test('successfully describes with provided abbreviated size', () {
final commit = repo.lookupCommit(repo['821ed6e']);
repo.deleteTag('v0.2');
final commit = repo['821ed6e'] as Commit;
expect(
repo.describe(
commit: commit,
@ -122,7 +116,6 @@ void main() {
'v0.1',
);
tag.free();
commit.free();
});

View file

@ -74,14 +74,14 @@ index e69de29..c217c63 100644
8 files changed, 4 insertions(+), 2 deletions(-)
""";
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/dirtyrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/dirtyrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Diff', () {
@ -100,7 +100,9 @@ index e69de29..c217c63 100644
test('successfully returns diff between index and tree', () {
final index = repo.index;
final tree = (repo[repo.head.target.sha] as Commit).tree;
final head = repo.head;
final commit = repo.lookupCommit(head.target);
final tree = commit.tree;
final diff = index.diffToTree(tree: tree);
expect(diff.length, 8);
@ -108,13 +110,17 @@ index e69de29..c217c63 100644
expect(diff.deltas[i].newFile.path, indexToTree[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
index.free();
});
test('successfully returns diff between tree and workdir', () {
final tree = (repo[repo.head.target.sha] as Commit).tree;
final head = repo.head;
final commit = repo.lookupCommit(head.target);
final tree = commit.tree;
final diff = repo.diff(a: tree);
expect(diff.length, 9);
@ -122,13 +128,17 @@ index e69de29..c217c63 100644
expect(diff.deltas[i].newFile.path, treeToWorkdir[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
});
test('successfully returns diff between tree and index', () {
final index = repo.index;
final tree = (repo[repo.head.target.sha] as Commit).tree;
final head = repo.head;
final commit = repo.lookupCommit(head.target);
final tree = commit.tree;
final diff = repo.diff(a: tree, cached: true);
expect(diff.length, 8);
@ -136,14 +146,20 @@ index e69de29..c217c63 100644
expect(diff.deltas[i].newFile.path, indexToTree[i]);
}
commit.free();
head.free();
tree.free();
diff.free();
index.free();
});
test('successfully returns diff between tree and tree', () {
final tree1 = (repo[repo.head.target.sha] as Commit).tree;
final tree2 = repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'] as Tree;
final head = repo.head;
final commit = repo.lookupCommit(head.target);
final tree1 = commit.tree;
final tree2 = repo.lookupTree(
repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'],
);
final diff = repo.diff(a: tree1, b: tree2);
expect(diff.length, 10);
@ -151,14 +167,20 @@ index e69de29..c217c63 100644
expect(diff.deltas[i].newFile.path, treeToTree[i]);
}
commit.free();
head.free();
tree1.free();
tree2.free();
diff.free();
});
test('successfully merges diffs', () {
final tree1 = (repo[repo.head.target.sha] as Commit).tree;
final tree2 = repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'] as Tree;
final head = repo.head;
final commit = repo.lookupCommit(head.target);
final tree1 = commit.tree;
final tree2 = repo.lookupTree(
repo['b85d53c9236e89aff2b62558adaa885fd1d6ff1c'],
);
final diff1 = tree1.diffToTree(tree: tree2);
final diff2 = tree1.diffToWorkdir();
@ -168,6 +190,8 @@ index e69de29..c217c63 100644
diff1.merge(diff2);
expect(diff1.length, 11);
commit.free();
head.free();
tree1.free();
tree2.free();
diff1.free();
@ -219,8 +243,10 @@ index e69de29..c217c63 100644
test('successfully finds similar entries', () {
final index = repo.index;
final oldTree = (repo[repo.head.target.sha] as Commit).tree;
final newTree = repo[index.writeTree().sha] as Tree;
final head = repo.head;
final commit = repo.lookupCommit(head.target);
final oldTree = commit.tree;
final newTree = repo.lookupTree(index.writeTree());
final diff = oldTree.diffToTree(tree: newTree);
expect(
@ -234,6 +260,8 @@ index e69de29..c217c63 100644
GitDelta.renamed,
);
commit.free();
head.free();
diff.free();
index.free();
oldTree.free();

View file

@ -3,29 +3,31 @@ import 'package:path/path.dart' as p;
final tmpDir = Directory.systemTemp.createTempSync('testrepo');
Future<Directory> setupRepo(Directory repoDir) async {
if (await tmpDir.exists()) {
await tmpDir.delete(recursive: true);
Directory setupRepo(Directory repoDir) {
if (tmpDir.existsSync()) {
tmpDir.deleteSync(recursive: true);
}
await copyRepo(from: repoDir, to: await tmpDir.create());
tmpDir.createSync();
copyRepo(from: repoDir, to: tmpDir);
return tmpDir;
}
Future<void> copyRepo({required Directory from, required Directory to}) async {
await for (final entity in from.list()) {
void copyRepo({required Directory from, required Directory to}) {
for (final entity in from.listSync()) {
if (entity is Directory) {
Directory newDir;
if (p.basename(entity.path) == '.gitdir') {
newDir = Directory(p.join(to.absolute.path, '.git'));
newDir = Directory(p.join(to.absolute.path, '.git'))..createSync();
} else {
newDir = Directory(p.join(to.absolute.path, p.basename(entity.path)));
newDir = Directory(p.join(to.absolute.path, p.basename(entity.path)))
..createSync();
}
await copyRepo(from: entity.absolute, to: await newDir.create());
copyRepo(from: entity.absolute, to: newDir);
} else if (entity is File) {
if (p.basename(entity.path) == 'gitignore') {
await entity.copy(p.join(to.path, '.gitignore'));
entity.copySync(p.join(to.path, '.gitignore'));
} else {
await entity.copy(p.join(to.path, p.basename(entity.path)));
entity.copySync(p.join(to.path, p.basename(entity.path)));
}
}
}

View file

@ -8,16 +8,16 @@ void main() {
late Index index;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
index = repo.index;
});
tearDown(() async {
tearDown(() {
index.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Index', () {
@ -156,11 +156,12 @@ void main() {
expect(index.find('feature_file'), false);
});
group('read tree', () {
const treeSha = 'df2b8fc99e1c1d4dbc0a854d9f72157f1d6ea078';
test('successfully reads with provided SHA hex', () {
test('successfully reads tree with provided SHA hex', () {
final tree = repo.lookupTree(
repo['df2b8fc99e1c1d4dbc0a854d9f72157f1d6ea078'],
);
expect(index.length, 4);
index.readTree(treeSha);
index.readTree(tree);
expect(index.length, 1);
@ -169,14 +170,6 @@ void main() {
expect(index.length, 4);
});
test('successfully reads with provided short SHA hex', () {
expect(index.length, 4);
index.readTree(treeSha.substring(0, 5));
expect(index.length, 1);
});
});
test('successfully writes tree', () {
final oid = index.writeTree();
expect(oid.sha, 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f');

View file

@ -126,14 +126,14 @@ Santa Claus <santa.claus@northpole.xx> <me@company.xx>
late Repository repo;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/mailmaprepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/mailmaprepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Mailmap', () {

View file

@ -9,21 +9,22 @@ void main() {
late Repository repo;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/mergerepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/mergerepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Merge', () {
group('analysis', () {
test('is up to date when no reference is provided', () {
final commit =
repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit;
final commit = repo.lookupCommit(
repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'],
);
final result = repo.mergeAnalysis(theirHead: commit.id);
expect(result[0], {GitMergeAnalysis.upToDate});
@ -34,8 +35,9 @@ void main() {
});
test('is up to date for provided ref', () {
final commit =
repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit;
final commit = repo.lookupCommit(
repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'],
);
final result = repo.mergeAnalysis(
theirHead: commit.id,
@ -48,18 +50,20 @@ void main() {
});
test('is fast forward', () {
final theirHead =
repo['6cbc22e509d72758ab4c8d9f287ea846b90c448b'] as Commit;
final ffCommit =
repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'] as Commit;
final ffBranch = repo.branches.create(
final theirHead = repo.lookupCommit(
repo['6cbc22e509d72758ab4c8d9f287ea846b90c448b'],
);
final ffCommit = repo.lookupCommit(
repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'],
);
final ffBranch = repo.createBranch(
name: 'ff-branch',
target: ffCommit,
);
final result = repo.mergeAnalysis(
theirHead: theirHead.id,
ourRef: ffBranch.name,
ourRef: 'refs/heads/${ffBranch.name}',
);
expect(
result[0],
@ -73,8 +77,9 @@ void main() {
});
test('is not fast forward and there is no conflicts', () {
final commit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final commit = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final result = repo.mergeAnalysis(theirHead: commit.id);
expect(result[0], {GitMergeAnalysis.normal});
@ -85,7 +90,7 @@ void main() {
});
test('writes conflicts to index', () {
final conflictBranch = repo.branches['conflict-branch'];
final conflictBranch = repo.lookupBranch('conflict-branch');
final index = repo.index;
final result = repo.mergeAnalysis(theirHead: conflictBranch.target);
@ -123,7 +128,7 @@ void main() {
});
test('successfully removes conflicts', () {
final conflictBranch = repo.branches['conflict-branch'];
final conflictBranch = repo.lookupBranch('conflict-branch');
final index = repo.index;
repo.merge(conflictBranch.target);
@ -149,7 +154,7 @@ master conflict edit
conflict branch edit
>>>>>>> conflict_file
""";
final conflictBranch = repo.branches['conflict-branch'];
final conflictBranch = repo.lookupBranch('conflict-branch');
final index = repo.index;
repo.merge(conflictBranch.target);
@ -171,10 +176,12 @@ conflict branch edit
group('merge commits', () {
test('successfully merges with default values', () {
final theirCommit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final ourCommit =
repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit;
final theirCommit = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final ourCommit = repo.lookupCommit(
repo['14905459d775f3f56a39ebc2ff081163f7da3529'],
);
final mergeIndex = repo.mergeCommits(
ourCommit: ourCommit,
@ -197,10 +204,12 @@ conflict branch edit
});
test('successfully merges with provided favor', () {
final theirCommit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final ourCommit =
repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit;
final theirCommit = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final ourCommit = repo.lookupCommit(
repo['14905459d775f3f56a39ebc2ff081163f7da3529'],
);
final mergeIndex = repo.mergeCommits(
ourCommit: ourCommit,
@ -215,10 +224,12 @@ conflict branch edit
});
test('successfully merges with provided merge and file flags', () {
final theirCommit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final ourCommit =
repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit;
final theirCommit = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final ourCommit = repo.lookupCommit(
repo['14905459d775f3f56a39ebc2ff081163f7da3529'],
);
final mergeIndex = repo.mergeCommits(
ourCommit: ourCommit,
@ -243,13 +254,15 @@ conflict branch edit
group('merge trees', () {
test('successfully merges with default values', () {
final theirCommit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final ourCommit =
repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit;
final baseCommit =
repo[repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha).sha]
as Commit;
final theirCommit = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final ourCommit = repo.lookupCommit(
repo['14905459d775f3f56a39ebc2ff081163f7da3529'],
);
final baseCommit = repo.lookupCommit(
repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha),
);
final theirTree = theirCommit.tree;
final ourTree = ourCommit.tree;
final ancestorTree = baseCommit.tree;
@ -281,13 +294,15 @@ conflict branch edit
});
test('successfully merges with provided favor', () {
final theirCommit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final ourCommit =
repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit;
final baseCommit =
repo[repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha).sha]
as Commit;
final theirCommit = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
final ourCommit = repo.lookupCommit(
repo['14905459d775f3f56a39ebc2ff081163f7da3529'],
);
final baseCommit = repo.lookupCommit(
repo.mergeBase(a: ourCommit.id.sha, b: theirCommit.id.sha),
);
final theirTree = theirCommit.tree;
final ourTree = ourCommit.tree;
final ancestorTree = baseCommit.tree;
@ -311,7 +326,9 @@ conflict branch edit
});
test('successfully cherry-picks commit', () {
final cherry = repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final cherry = repo.lookupCommit(
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'],
);
repo.cherryPick(cherry);
expect(repo.state, GitRepositoryState.cherrypick);
expect(repo.message, 'add another feature file\n');

View file

@ -19,14 +19,14 @@ void main() {
},
];
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Note', () {
@ -39,10 +39,7 @@ void main() {
expect(notes[i].id.sha, notesExpected[i]['id']);
expect(notes[i].message, notesExpected[i]['message']);
expect(notes[i].annotatedId.sha, notesExpected[i]['annotatedId']);
}
for (var note in notes) {
note.free();
notes[i].free();
}
});
@ -64,11 +61,11 @@ void main() {
final noteOid = repo.createNote(
author: signature,
committer: signature,
object: head.target,
annotatedId: head.target,
note: 'New note for HEAD',
force: true,
);
final noteBlob = repo[noteOid.sha] as Blob;
final noteBlob = repo.lookupBlob(noteOid);
expect(noteOid.sha, 'ffd6e2ceaf91c00ea6d29e2e897f906da720529f');
expect(noteBlob.content, 'New note for HEAD');
@ -81,15 +78,18 @@ void main() {
test('successfully removes note', () {
final signature = repo.defaultSignature;
final head = repo.head;
final note = repo.lookupNote(annotatedId: head.target);
note.remove(author: signature, committer: signature);
repo.deleteNote(
annotatedId: repo.head.target,
author: signature,
committer: signature,
);
expect(
() => repo.lookupNote(annotatedId: head.target),
throwsA(isA<LibGit2Error>()),
);
note.free();
head.free();
signature.free();
});

View file

@ -10,14 +10,14 @@ void main() {
const blobSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc';
const blobContent = 'Feature edit\n';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Odb', () {

View file

@ -11,14 +11,14 @@ void main() {
const biggerSha = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e9';
const lesserSha = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e7';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Oid', () {

View file

@ -7,14 +7,14 @@ void main() {
late Repository repo;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('PackBuilder', () {
@ -86,12 +86,14 @@ void main() {
test('successfully packs with provided packDelegate', () {
void packDelegate(PackBuilder packBuilder) {
for (var branchName in repo.branches.list()) {
final branch = repo.references['refs/heads/$branchName'];
for (var commit in repo.log(sha: branch.target.sha)) {
final branches = repo.branches;
for (var branch in branches) {
final ref = repo.lookupReference('refs/heads/${branch.name}');
for (var commit in repo.log(sha: ref.target.sha)) {
packBuilder.addRecursively(commit.id);
commit.free();
}
ref.free();
branch.free();
}
}

View file

@ -8,9 +8,9 @@ void main() {
late Directory tmpDir;
const oldBlob = '';
const newBlob = 'Feature edit\n';
late Oid oldBlobID;
late Oid newBlobID;
const path = 'feature_file';
const oldBlobSha = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391';
const newBlobSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc';
const blobPatch = """
diff --git a/feature_file b/feature_file
index e69de29..9c78c21 100644
@ -38,14 +38,16 @@ index e69de29..0000000
+++ /dev/null
""";
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
oldBlobID = repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'];
newBlobID = repo['9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'];
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Patch', () {
@ -90,8 +92,8 @@ index e69de29..0000000
});
test('successfully creates from blobs', () {
final a = repo[oldBlobSha] as Blob;
final b = repo[newBlobSha] as Blob;
final a = repo.lookupBlob(oldBlobID);
final b = repo.lookupBlob(newBlobID);
final patch = Patch.createFrom(
a: a,
b: b,
@ -105,7 +107,7 @@ index e69de29..0000000
});
test('successfully creates from one blob (add)', () {
final b = repo[newBlobSha] as Blob;
final b = repo.lookupBlob(newBlobID);
final patch = Patch.createFrom(
a: null,
b: b,
@ -119,7 +121,7 @@ index e69de29..0000000
});
test('successfully creates from one blob (delete)', () {
final a = repo[oldBlobSha] as Blob;
final a = repo.lookupBlob(oldBlobID);
final patch = Patch.createFrom(
a: a,
b: null,
@ -133,7 +135,7 @@ index e69de29..0000000
});
test('successfully creates from blob and buffer', () {
final a = repo[oldBlobSha] as Blob;
final a = repo.lookupBlob(oldBlobID);
final patch = Patch.createFrom(
a: a,
b: newBlob,
@ -147,7 +149,9 @@ index e69de29..0000000
});
test('throws when argument is not Blob or String', () {
final commit = repo['fc38877b2552ab554752d9a77e1f48f738cca79b'] as Commit;
final commit = repo.lookupCommit(
repo['fc38877b2552ab554752d9a77e1f48f738cca79b'],
);
expect(
() => Patch.createFrom(
a: commit,

View file

@ -12,21 +12,21 @@ void main() {
'14905459d775f3f56a39ebc2ff081163f7da3529',
];
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/mergerepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/mergerepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Rebase', () {
test('successfully performs rebase when there is no conflicts', () {
final signature = repo.defaultSignature;
final master = repo.references['refs/heads/master'];
final feature = repo.references['refs/heads/feature'];
final master = repo.lookupReference('refs/heads/master');
final feature = repo.lookupReference('refs/heads/feature');
repo.checkout(refName: feature.name);
expect(() => repo.index['.gitignore'], throwsA(isA<ArgumentError>()));
@ -60,9 +60,9 @@ void main() {
test('successfully performs rebase with provided upstream', () {
final signature = repo.defaultSignature;
final master = repo.references['refs/heads/master'];
final feature = repo.references['refs/heads/feature'];
final startCommit = repo[shas[1]] as Commit;
final master = repo.lookupReference('refs/heads/master');
final feature = repo.lookupReference('refs/heads/feature');
final startCommit = repo.lookupCommit(repo[shas[1]]);
repo.checkout(refName: feature.name);
expect(
@ -97,8 +97,8 @@ void main() {
test('stops when there is conflicts', () {
final signature = repo.defaultSignature;
final master = repo.references['refs/heads/master'];
final conflict = repo.references['refs/heads/conflict-branch'];
final master = repo.lookupReference('refs/heads/master');
final conflict = repo.lookupReference('refs/heads/conflict-branch');
repo.checkout(refName: conflict.name);
@ -124,8 +124,8 @@ void main() {
});
test('successfully aborts rebase in progress', () {
final master = repo.references['refs/heads/master'];
final conflict = repo.references['refs/heads/conflict-branch'];
final master = repo.lookupReference('refs/heads/master');
final conflict = repo.lookupReference('refs/heads/conflict-branch');
repo.checkout(refName: conflict.name);

View file

@ -9,20 +9,20 @@ void main() {
const lastCommit = '821ed6e80627b8769d170a293862f9fc60825226';
const newCommit = 'c68ff54aabf660fcdd9a2838d401583fe31249e3';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Reference', () {
test('returns a list', () {
expect(
repo.references.list,
repo.references,
[
'refs/heads/feature',
'refs/heads/master',
@ -38,7 +38,7 @@ void main() {
expect(head.type, ReferenceType.direct);
head.free();
final ref = repo.references['HEAD'];
final ref = repo.lookupReference('HEAD');
expect(ref.type, ReferenceType.symbolic);
ref.free();
});
@ -50,7 +50,7 @@ void main() {
});
test('returns SHA hex of symbolic reference', () {
final ref = repo.references['HEAD'];
final ref = repo.lookupReference('HEAD');
expect(ref.target.sha, lastCommit);
ref.free();
});
@ -62,7 +62,7 @@ void main() {
});
test('returns the short name', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/remotes/origin/master',
target: lastCommit,
);
@ -77,19 +77,19 @@ void main() {
});
test('checks if reference is a local branch', () {
final ref = repo.references['refs/heads/feature'];
final ref = repo.lookupReference('refs/heads/feature');
expect(ref.isBranch, true);
ref.free();
});
test('checks if reference is a note', () {
final ref = repo.references['refs/heads/master'];
final ref = repo.lookupReference('refs/heads/master');
expect(ref.isNote, false);
ref.free();
});
test('checks if reference is a remote branch', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/remotes/origin/master',
target: lastCommit,
);
@ -100,16 +100,16 @@ void main() {
});
test('checks if reference is a tag', () {
final ref = repo.references['refs/tags/v0.1'];
final ref = repo.lookupReference('refs/tags/v0.1');
expect(ref.isTag, true);
ref.free();
});
test('checks if reflog exists for the reference', () {
var ref = repo.references['refs/heads/master'];
var ref = repo.lookupReference('refs/heads/master');
expect(ref.hasLog, true);
ref = repo.references['refs/tags/v0.1'];
ref = repo.lookupReference('refs/tags/v0.1');
expect(ref.hasLog, false);
ref.free();
@ -117,43 +117,43 @@ void main() {
group('create direct', () {
test('successfully creates with Oid as target', () {
final ref = repo.references['refs/heads/master'];
final refFromOid = repo.references.create(
final ref = repo.lookupReference('refs/heads/master');
final refFromOid = repo.createReference(
name: 'refs/tags/from.oid',
target: ref.target,
);
expect(repo.references.list, contains('refs/tags/from.oid'));
expect(repo.references, contains('refs/tags/from.oid'));
refFromOid.free();
ref.free();
});
test('successfully creates with SHA hash as target', () {
final refFromHash = repo.references.create(
final refFromHash = repo.createReference(
name: 'refs/tags/from.hash',
target: lastCommit,
);
expect(repo.references.list, contains('refs/tags/from.hash'));
expect(repo.references, contains('refs/tags/from.hash'));
refFromHash.free();
});
test('successfully creates with short SHA hash as target', () {
final refFromHash = repo.references.create(
final refFromHash = repo.createReference(
name: 'refs/tags/from.short.hash',
target: '78b8bf',
);
expect(repo.references.list, contains('refs/tags/from.short.hash'));
expect(repo.references, contains('refs/tags/from.short.hash'));
refFromHash.free();
});
test('successfully creates with log message', () {
repo.setIdentity(name: 'name', email: 'email');
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/heads/log.message',
target: lastCommit,
logMessage: 'log message',
@ -172,7 +172,7 @@ void main() {
test('throws if target is not valid', () {
expect(
() => repo.references.create(
() => repo.createReference(
name: 'refs/tags/invalid',
target: '78b',
),
@ -182,7 +182,7 @@ void main() {
test('throws if name is not valid', () {
expect(
() => repo.references.create(
() => repo.createReference(
name: 'refs/tags/invalid~',
target: lastCommit,
),
@ -191,12 +191,12 @@ void main() {
});
test('successfully creates with force flag if name already exists', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/tags/test',
target: lastCommit,
);
final forceRef = repo.references.create(
final forceRef = repo.createReference(
name: 'refs/tags/test',
target: lastCommit,
force: true,
@ -209,13 +209,13 @@ void main() {
});
test('throws if name already exists', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/tags/test',
target: lastCommit,
);
expect(
() => repo.references.create(
() => repo.createReference(
name: 'refs/tags/test',
target: lastCommit,
),
@ -228,24 +228,24 @@ void main() {
group('create symbolic', () {
test('successfully creates with valid target', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/tags/symbolic',
target: 'refs/heads/master',
);
expect(repo.references.list, contains('refs/tags/symbolic'));
expect(repo.references, contains('refs/tags/symbolic'));
expect(ref.type, ReferenceType.symbolic);
ref.free();
});
test('successfully creates with force flag if name already exists', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/tags/test',
target: 'refs/heads/master',
);
final forceRef = repo.references.create(
final forceRef = repo.createReference(
name: 'refs/tags/test',
target: 'refs/heads/master',
force: true,
@ -259,13 +259,13 @@ void main() {
});
test('throws if name already exists', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/tags/exists',
target: 'refs/heads/master',
);
expect(
() => repo.references.create(
() => repo.createReference(
name: 'refs/tags/exists',
target: 'refs/heads/master',
),
@ -277,7 +277,7 @@ void main() {
test('throws if name is not valid', () {
expect(
() => repo.references.create(
() => repo.createReference(
name: 'refs/tags/invalid~',
target: 'refs/heads/master',
),
@ -287,7 +287,7 @@ void main() {
test('successfully creates with log message', () {
repo.setIdentity(name: 'name', email: 'email');
final ref = repo.references.create(
final ref = repo.createReference(
name: 'HEAD',
target: 'refs/heads/feature',
force: true,
@ -307,34 +307,34 @@ void main() {
});
test('successfully deletes reference', () {
final ref = repo.references.create(
final ref = repo.createReference(
name: 'refs/tags/test',
target: lastCommit,
);
expect(repo.references.list, contains('refs/tags/test'));
expect(repo.references, contains('refs/tags/test'));
ref.delete();
expect(repo.references.list, isNot(contains('refs/tags/test')));
repo.deleteReference('refs/tags/test');
expect(repo.references, isNot(contains('refs/tags/test')));
ref.free();
});
group('finds', () {
test('with provided name', () {
final ref = repo.references['refs/heads/master'];
final ref = repo.lookupReference('refs/heads/master');
expect(ref.target.sha, lastCommit);
ref.free();
});
test('throws when error occured', () {
expect(
() => repo.references['refs/heads/not/there'],
() => repo.lookupReference('refs/heads/not/there'),
throwsA(isA<LibGit2Error>()),
);
});
});
test('returns log for reference', () {
final ref = repo.references['refs/heads/master'];
final ref = repo.lookupReference('refs/heads/master');
final reflog = ref.log;
expect(reflog.last.message, 'commit (initial): init');
@ -344,7 +344,7 @@ void main() {
group('set target', () {
test('successfully sets with SHA hex', () {
final ref = repo.references['refs/heads/master'];
final ref = repo.lookupReference('refs/heads/master');
ref.setTarget(target: newCommit);
expect(ref.target.sha, newCommit);
@ -352,7 +352,7 @@ void main() {
});
test('successfully sets target with short SHA hex', () {
final ref = repo.references['refs/heads/master'];
final ref = repo.lookupReference('refs/heads/master');
ref.setTarget(target: newCommit.substring(0, 5));
expect(ref.target.sha, newCommit);
@ -360,7 +360,7 @@ void main() {
});
test('successfully sets symbolic target', () {
final ref = repo.references['HEAD'];
final ref = repo.lookupReference('HEAD');
expect(ref.target.sha, lastCommit);
ref.setTarget(target: 'refs/heads/feature');
@ -370,7 +370,7 @@ void main() {
});
test('successfully sets target with log message', () {
final ref = repo.references['HEAD'];
final ref = repo.lookupReference('HEAD');
expect(ref.target.sha, lastCommit);
repo.setIdentity(name: 'name', email: 'email');
@ -386,7 +386,7 @@ void main() {
});
test('throws on invalid target', () {
final ref = repo.references['HEAD'];
final ref = repo.lookupReference('HEAD');
expect(
() => ref.setTarget(target: 'refs/heads/invalid~'),
throwsA(isA<LibGit2Error>()),
@ -398,67 +398,56 @@ void main() {
group('rename', () {
test('successfully renames reference', () {
final ref = repo.references.create(
name: 'refs/tags/v1',
target: lastCommit,
repo.renameReference(
oldName: 'refs/tags/v0.1',
newName: 'refs/tags/renamed',
);
expect(ref.name, 'refs/tags/v1');
ref.rename(newName: 'refs/tags/v2');
expect(ref.name, 'refs/tags/v2');
ref.free();
expect(repo.references, contains('refs/tags/renamed'));
expect(repo.references, isNot(contains('refs/tags/v0.1')));
});
test('throws on invalid name', () {
final ref = repo.references.create(
name: 'refs/tags/v1',
target: lastCommit,
);
expect(
() => ref.rename(newName: 'refs/tags/invalid~'),
() => repo.renameReference(
oldName: 'refs/tags/v0.1',
newName: 'refs/tags/invalid~',
),
throwsA(isA<LibGit2Error>()),
);
ref.free();
});
test('throws if name already exists', () {
final ref1 = repo.references.create(
name: 'refs/tags/v1',
target: lastCommit,
);
final ref2 = repo.references.create(
name: 'refs/tags/v2',
target: lastCommit,
);
expect(
() => ref1.rename(newName: 'refs/tags/v2'),
() => repo.renameReference(
oldName: 'refs/tags/v0.1',
newName: 'refs/tags/v0.2',
),
throwsA(isA<LibGit2Error>()),
);
ref1.free();
ref2.free();
});
test('successfully renames with force flag set to true', () {
final ref1 = repo.references.create(
name: 'refs/tags/v1',
target: lastCommit,
final ref1 = repo.lookupReference('refs/tags/v0.1');
final ref2 = repo.lookupReference('refs/tags/v0.2');
expect(ref1.target.sha, '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8');
expect(ref2.target.sha, 'f0fdbf506397e9f58c59b88dfdd72778ec06cc0c');
expect(repo.references.length, 5);
repo.renameReference(
oldName: 'refs/tags/v0.1',
newName: 'refs/tags/v0.2',
force: true,
);
final ref2 = repo.references.create(
name: 'refs/tags/v2',
target: newCommit,
final updatedRef1 = repo.lookupReference('refs/tags/v0.2');
expect(
updatedRef1.target.sha,
'78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8',
);
expect(ref2.target.sha, newCommit);
ref1.rename(newName: 'refs/tags/v2', force: true);
expect(ref1.name, 'refs/tags/v2');
expect(repo.references, isNot(contains('refs/tags/v0.1')));
expect(repo.references.length, 4);
ref1.free();
ref2.free();
@ -466,9 +455,9 @@ void main() {
});
test('checks equality', () {
final ref1 = repo.references['refs/heads/master'];
final ref2 = repo.references['refs/heads/master'];
final ref3 = repo.references['refs/heads/feature'];
final ref1 = repo.lookupReference('refs/heads/master');
final ref2 = repo.lookupReference('refs/heads/master');
final ref3 = repo.lookupReference('refs/heads/feature');
expect(ref1 == ref2, true);
expect(ref1 != ref2, false);
@ -481,8 +470,8 @@ void main() {
});
test('successfully peels to non-tag object when no type is provided', () {
final ref = repo.references['refs/heads/master'];
final commit = repo[ref.target.sha] as Commit;
final ref = repo.lookupReference('refs/heads/master');
final commit = repo.lookupCommit(ref.target);
final peeled = ref.peel() as Commit;
expect(peeled.id, commit.id);
@ -493,8 +482,8 @@ void main() {
});
test('successfully peels to object of provided type', () {
final ref = repo.references['refs/heads/master'];
final commit = repo[ref.target.sha] as Commit;
final ref = repo.lookupReference('refs/heads/master');
final commit = repo.lookupCommit(ref.target);
final tree = commit.tree;
final peeledCommit = ref.peel(GitObject.commit) as Commit;
final peeledTree = ref.peel(GitObject.tree) as Tree;
@ -511,12 +500,12 @@ void main() {
test('successfully compresses references', () {
final packedRefsFile = File('${tmpDir.path}/.git/packed-refs');
expect(packedRefsFile.existsSync(), false);
final oldRefs = repo.references.list;
final oldRefs = repo.references;
repo.references.compress();
Reference.compress(repo);
expect(packedRefsFile.existsSync(), true);
final newRefs = repo.references.list;
final newRefs = repo.references;
expect(newRefs, oldRefs);
});
});

View file

@ -9,18 +9,18 @@ void main() {
late Reference head;
late Directory tmpDir;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
head = repo.head;
reflog = RefLog(head);
});
tearDown(() async {
tearDown(() {
reflog.free();
head.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('RefLog', () {

View file

@ -10,43 +10,61 @@ void main() {
late Remote remote;
final cloneDir = Directory('${Directory.systemTemp.path}/cloned');
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
if (await cloneDir.exists()) {
cloneDir.delete(recursive: true);
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
if (cloneDir.existsSync()) {
cloneDir.deleteSync(recursive: true);
}
originRepo = Repository.open(tmpDir.path);
clonedRepo = Repository.clone(
url: tmpDir.path,
localPath: cloneDir.path,
);
originRepo.branches['feature'].delete();
remote = clonedRepo.remotes['origin'];
originRepo.deleteBranch('feature');
remote = clonedRepo.lookupRemote('origin');
});
tearDown(() async {
tearDown(() {
remote.free();
originRepo.free();
clonedRepo.free();
await tmpDir.delete(recursive: true);
if (await cloneDir.exists()) {
cloneDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
if (cloneDir.existsSync()) {
cloneDir.deleteSync(recursive: true);
}
});
group('Remote', () {
test('fetch() does not prune branch by default', () {
remote.fetch();
expect(clonedRepo.branches.list(), contains('origin/feature'));
final branches = clonedRepo.branches;
expect(branches.any((branch) => branch.name == 'origin/feature'), true);
for (final branch in branches) {
branch.free();
}
});
test('fetch() successfully prunes branch with provided flag', () {
remote.fetch(prune: GitFetchPrune.prune);
expect(clonedRepo.branches.list(), isNot(contains('origin/feature')));
final branches = clonedRepo.branches;
expect(branches.any((branch) => branch.name == 'origin/feature'), false);
for (final branch in branches) {
branch.free();
}
});
test('fetch() does not prune branch with provided flag', () {
remote.fetch(prune: GitFetchPrune.noPrune);
expect(clonedRepo.branches.list(), contains('origin/feature'));
final branches = clonedRepo.branches;
expect(branches.any((branch) => branch.name == 'origin/feature'), true);
for (final branch in branches) {
branch.free();
}
});
test('prune() successfully prunes branches', () {
@ -58,11 +76,22 @@ void main() {
final callbacks = Callbacks(updateTips: updateTips);
remote.fetch(prune: GitFetchPrune.noPrune);
expect(clonedRepo.branches.list(), contains('origin/feature'));
var branches = clonedRepo.branches;
expect(branches.any((branch) => branch.name == 'origin/feature'), true);
for (final branch in branches) {
branch.free();
}
remote.prune(callbacks);
branches = clonedRepo.branches;
expect(pruned, contains('refs/remotes/origin/feature'));
expect(clonedRepo.branches.list(), isNot(contains('origin/feature')));
expect(branches.any((branch) => branch.name == 'origin/feature'), false);
for (final branch in branches) {
branch.free();
}
});
});
}

View file

@ -9,23 +9,23 @@ void main() {
const remoteName = 'origin';
const remoteUrl = 'git://github.com/SkinnyMind/libgit2dart.git';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Remote', () {
test('returns list of remotes', () {
expect(repo.remotes.list, ['origin']);
expect(repo.remotes, ['origin']);
});
test('successfully looks up remote for provided name', () {
final remote = repo.remotes['origin'];
final remote = repo.lookupRemote('origin');
expect(remote.name, remoteName);
expect(remote.url, remoteUrl);
@ -35,11 +35,11 @@ void main() {
});
test('throws when provided name for lookup is not found', () {
expect(() => repo.remotes['upstream'], throwsA(isA<LibGit2Error>()));
expect(() => repo.lookupRemote('upstream'), throwsA(isA<LibGit2Error>()));
});
test('successfully creates without fetchspec', () {
final remote = repo.remotes.create(name: 'upstream', url: remoteUrl);
final remote = repo.createRemote(name: 'upstream', url: remoteUrl);
expect(repo.remotes.length, 2);
expect(remote.name, 'upstream');
@ -51,7 +51,7 @@ void main() {
test('successfully creates with provided fetchspec', () {
const spec = '+refs/*:refs/*';
final remote = repo.remotes.create(
final remote = repo.createRemote(
name: 'upstream',
url: remoteUrl,
fetch: spec,
@ -67,23 +67,23 @@ void main() {
});
test('successfully deletes', () {
final remote = repo.remotes.create(name: 'upstream', url: remoteUrl);
final remote = repo.createRemote(name: 'upstream', url: remoteUrl);
expect(repo.remotes.length, 2);
repo.remotes.delete(remote.name);
repo.deleteRemote(remote.name);
expect(repo.remotes.length, 1);
remote.free();
});
test('successfully renames', () {
final remote = repo.remotes[remoteName];
final remote = repo.lookupRemote(remoteName);
final problems = repo.remotes.rename(remote: remoteName, newName: 'new');
final problems = repo.renameRemote(oldName: remoteName, newName: 'new');
expect(problems, isEmpty);
expect(remote.name, isNot('new'));
final newRemote = repo.remotes['new'];
final newRemote = repo.lookupRemote('new');
expect(newRemote.name, 'new');
newRemote.free();
@ -92,19 +92,19 @@ void main() {
test('throws when renaming with invalid names', () {
expect(
() => repo.remotes.rename(remote: '', newName: ''),
() => repo.renameRemote(oldName: '', newName: ''),
throwsA(isA<LibGit2Error>()),
);
});
test('successfully sets url', () {
final remote = repo.remotes[remoteName];
final remote = repo.lookupRemote(remoteName);
expect(remote.url, remoteUrl);
const newUrl = 'git://new/url.git';
repo.remotes.setUrl(remote: remoteName, url: newUrl);
Remote.setUrl(repo: repo, remote: remoteName, url: newUrl);
final newRemote = repo.remotes[remoteName];
final newRemote = repo.lookupRemote(remoteName);
expect(newRemote.url, newUrl);
newRemote.free();
@ -113,16 +113,16 @@ void main() {
test('throws when trying to set invalid url name', () {
expect(
() => repo.remotes.setUrl(remote: 'origin', url: ''),
() => Remote.setUrl(repo: repo, remote: 'origin', url: ''),
throwsA(isA<LibGit2Error>()),
);
});
test('successfully sets url for pushing', () {
const newUrl = 'git://new/url.git';
repo.remotes.setPushUrl(remote: remoteName, url: newUrl);
Remote.setPushUrl(repo: repo, remote: remoteName, url: newUrl);
final remote = repo.remotes[remoteName];
final remote = repo.lookupRemote(remoteName);
expect(remote.pushUrl, newUrl);
remote.free();
@ -130,13 +130,13 @@ void main() {
test('throws when trying to set invalid push url name', () {
expect(
() => repo.remotes.setPushUrl(remote: 'origin', url: ''),
() => Remote.setPushUrl(repo: repo, remote: 'origin', url: ''),
throwsA(isA<LibGit2Error>()),
);
});
test('returns refspec', () {
final remote = repo.remotes['origin'];
final remote = repo.lookupRemote('origin');
expect(remote.refspecCount, 1);
final refspec = remote.getRefspec(0);
@ -162,11 +162,12 @@ void main() {
});
test('successfully adds fetch refspec', () {
repo.remotes.addFetch(
Remote.addFetch(
repo: repo,
remote: 'origin',
refspec: '+refs/test/*:refs/test/remotes/*',
);
final remote = repo.remotes['origin'];
final remote = repo.lookupRemote('origin');
expect(remote.fetchRefspecs.length, 2);
expect(
remote.fetchRefspecs,
@ -180,11 +181,12 @@ void main() {
});
test('successfully adds push refspec', () {
repo.remotes.addPush(
Remote.addPush(
repo: repo,
remote: 'origin',
refspec: '+refs/test/*:refs/test/remotes/*',
);
final remote = repo.remotes['origin'];
final remote = repo.lookupRemote('origin');
expect(remote.pushRefspecs.length, 1);
expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']);
@ -192,11 +194,12 @@ void main() {
});
test('successfully returns remote repo\'s reference list', () {
repo.remotes.setUrl(
Remote.setUrl(
repo: repo,
remote: 'libgit2',
url: 'https://github.com/libgit2/TestGitRepository',
);
final remote = repo.remotes['libgit2'];
final remote = repo.lookupRemote('libgit2');
final refs = remote.ls();
expect(refs.first['local'], false);
@ -212,11 +215,12 @@ void main() {
});
test('successfully fetches data', () {
repo.remotes.setUrl(
Remote.setUrl(
repo: repo,
remote: 'libgit2',
url: 'https://github.com/libgit2/TestGitRepository',
);
final remote = repo.remotes['libgit2'];
final remote = repo.lookupRemote('libgit2');
final stats = remote.fetch();
@ -233,11 +237,12 @@ void main() {
test('successfully fetches data with provided transfer progress callback',
() {
repo.remotes.setUrl(
Remote.setUrl(
repo: repo,
remote: 'libgit2',
url: 'https://github.com/libgit2/TestGitRepository',
);
final remote = repo.remotes['libgit2'];
final remote = repo.lookupRemote('libgit2');
TransferProgress? callbackStats;
void tp(TransferProgress stats) => callbackStats = stats;
@ -263,11 +268,12 @@ Enumerating objects: 69, done.
Counting objects: 100% (1/1)\rCounting objects: 100% (1/1), done.
Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
""";
repo.remotes.setUrl(
Remote.setUrl(
repo: repo,
remote: 'libgit2',
url: 'https://github.com/libgit2/TestGitRepository',
);
final remote = repo.remotes['libgit2'];
final remote = repo.lookupRemote('libgit2');
var sidebandOutput = StringBuffer();
void sideband(String message) {
@ -283,11 +289,12 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
});
test('successfully fetches data with provided update tips callback', () {
repo.remotes.setUrl(
Remote.setUrl(
repo: repo,
remote: 'libgit2',
url: 'https://github.com/libgit2/TestGitRepository',
);
final remote = repo.remotes['libgit2'];
final remote = repo.lookupRemote('libgit2');
const tipsExpected = [
{
'refname': 'refs/tags/annotated_tag',
@ -323,21 +330,22 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
remote.free();
});
test('successfully pushes with update reference callback', () async {
test('successfully pushes with update reference callback', () {
final originDir =
Directory('${Directory.systemTemp.path}/origin_testrepo');
if (await originDir.exists()) {
await originDir.delete(recursive: true);
if (originDir.existsSync()) {
originDir.deleteSync(recursive: true);
}
await copyRepo(
originDir.createSync();
copyRepo(
from: Directory('test/assets/empty_bare.git/'),
to: await originDir.create(),
to: originDir,
);
final originRepo = Repository.open(originDir.path);
repo.remotes.create(name: 'local', url: originDir.path);
final remote = repo.remotes['local'];
repo.createRemote(name: 'local', url: originDir.path);
final remote = repo.lookupRemote('local');
var updateRefOutput = <String, String>{};
void updateRef(String refname, String message) {
@ -348,7 +356,7 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68
remote.push(refspecs: ['refs/heads/master'], callbacks: callbacks);
expect(
(originRepo[originRepo.head.target.sha] as Commit).id.sha,
originRepo.lookupCommit(originRepo.head.target).id.sha,
'821ed6e80627b8769d170a293862f9fc60825226',
);
expect(updateRefOutput, {'refs/heads/master': ''});

View file

@ -8,20 +8,19 @@ void main() {
late Directory tmpDir;
final cloneDir = Directory('${Directory.systemTemp.path}/cloned');
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
if (await cloneDir.exists()) {
if (cloneDir.existsSync()) {
cloneDir.delete(recursive: true);
}
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
if (await cloneDir.exists()) {
cloneDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
if (cloneDir.existsSync()) {
cloneDir.deleteSync(recursive: true);
}
});
@ -69,7 +68,7 @@ void main() {
test('successfully clones repository with provided remote callback', () {
Remote remote(Repository repo, String name, String url) =>
repo.remotes.create(name: 'test', url: tmpDir.path);
repo.createRemote(name: 'test', url: tmpDir.path);
final clonedRepo = Repository.clone(
url: tmpDir.path,
@ -79,15 +78,15 @@ void main() {
expect(clonedRepo.isEmpty, false);
expect(clonedRepo.isBare, false);
expect(clonedRepo.remotes.list, ['test']);
expect(clonedRepo.references.list, contains('refs/remotes/test/master'));
expect(clonedRepo.remotes, ['test']);
expect(clonedRepo.references, contains('refs/remotes/test/master'));
clonedRepo.free();
});
test('throws when cloning repository with invalid remote callback', () {
Remote remote(Repository repo, String name, String url) =>
repo.remotes.create(name: '', url: '');
repo.createRemote(name: '', url: '');
expect(
() => Repository.clone(

View file

@ -6,17 +6,17 @@ void main() {
late Repository repo;
final initDir = Directory('${Directory.systemTemp.path}/init_repo');
setUp(() async {
if (await initDir.exists()) {
await initDir.delete(recursive: true);
setUp(() {
if (initDir.existsSync()) {
initDir.deleteSync(recursive: true);
} else {
await initDir.create();
initDir.createSync();
}
});
tearDown(() async {
tearDown(() {
repo.free();
await initDir.delete(recursive: true);
initDir.deleteSync(recursive: true);
});
group('Repository.init', () {
test('successfully creates new bare repo at provided path', () {
@ -49,7 +49,7 @@ void main() {
File('${initDir.path}/.git/description').readAsStringSync(),
'test repo',
);
expect(repo.remotes['origin'].url, 'test.url');
expect(repo.lookupRemote('origin').url, 'test.url');
});
});
}

View file

@ -9,14 +9,14 @@ void main() {
const lastCommit = '821ed6e80627b8769d170a293862f9fc60825226';
const featureCommit = '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Repository', () {
@ -125,7 +125,7 @@ void main() {
test('successfully creates new blob', () {
final oid = repo.createBlob(newBlobContent);
final newBlob = repo[oid.sha] as Blob;
final newBlob = repo.lookupBlob(oid);
expect(newBlob, isA<Blob>());
@ -135,7 +135,7 @@ void main() {
test('successfully creates new blob from file at provided relative path',
() {
final oid = repo.createBlobFromWorkdir('feature_file');
final newBlob = repo[oid.sha] as Blob;
final newBlob = repo.lookupBlob(oid);
expect(newBlob, isA<Blob>());
@ -146,7 +146,7 @@ void main() {
final outsideFile =
File('${Directory.current.absolute.path}/test/blob_test.dart');
final oid = repo.createBlobFromDisk(outsideFile.path);
final newBlob = repo[oid.sha] as Blob;
final newBlob = repo.lookupBlob(oid);
expect(newBlob, isA<Blob>());
@ -161,11 +161,10 @@ void main() {
time: 1234,
);
const tagName = 'tag';
const target = 'f17d0d48eae3aa08cecf29128a35e310c97b3521';
final target = repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'];
const message = 'init tag\n';
final oid = Tag.create(
repo: repo,
final oid = repo.createTag(
tagName: tagName,
target: target,
targetType: GitObject.commit,
@ -173,7 +172,7 @@ void main() {
message: message,
);
final newTag = repo[oid.sha] as Tag;
final newTag = repo.lookupTag(oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Commit;
@ -181,7 +180,7 @@ void main() {
expect(newTag.name, tagName);
expect(newTag.message, message);
expect(tagger, signature);
expect(newTagTarget.id.sha, target);
expect(newTagTarget.id, target);
newTag.free();
newTagTarget.free();
@ -252,8 +251,8 @@ void main() {
});
test('checks if commit is a descendant of another commit', () {
final commit1 = repo['821ed6e8'] as Commit;
final commit2 = repo['78b8bf12'] as Commit;
final commit1 = repo.lookupCommit(repo['821ed6e8']);
final commit2 = repo.lookupCommit(repo['78b8bf12']);
expect(
repo.descendantOf(
@ -284,8 +283,8 @@ void main() {
});
test('returns number of ahead behind commits', () {
final commit1 = repo['821ed6e8'] as Commit;
final commit2 = repo['c68ff54a'] as Commit;
final commit1 = repo.lookupCommit(repo['821ed6e8']);
final commit2 = repo.lookupCommit(repo['c68ff54a']);
expect(
repo.aheadBehind(localSHA: commit1.id.sha, upstreamSHA: commit2.id.sha),

View file

@ -9,15 +9,15 @@ void main() {
late File file;
const sha = '6cbc22e509d72758ab4c8d9f287ea846b90c448b';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
file = File('${tmpDir.path}/feature_file');
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Reset', () {

View file

@ -9,14 +9,14 @@ void main() {
const headSHA = '821ed6e80627b8769d170a293862f9fc60825226';
const parentSHA = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('revParse', () {
@ -36,7 +36,7 @@ void main() {
});
test('.ext() returns commit and reference', () {
final masterRef = repo.references['refs/heads/master'];
final masterRef = repo.lookupReference('refs/heads/master');
var headParse = repo.revParseExt('master');
expect(headParse.object.id.sha, headSHA);
@ -46,7 +46,7 @@ void main() {
headParse.object.free();
headParse.reference?.free();
final featureRef = repo.references['refs/heads/feature'];
final featureRef = repo.lookupReference('refs/heads/feature');
headParse = repo.revParseExt('feature');
expect(

View file

@ -14,14 +14,14 @@ void main() {
'f17d0d48eae3aa08cecf29128a35e310c97b3521',
];
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('RevWalk', () {

View file

@ -8,8 +8,8 @@ void main() {
late Directory tmpDir;
late Signature stasher;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
stasher = Signature.create(
name: 'Stasher',
@ -17,10 +17,10 @@ void main() {
);
});
tearDown(() async {
tearDown(() {
stasher.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Stash', () {

View file

@ -10,14 +10,14 @@ void main() {
const submoduleUrl = 'https://github.com/libgit2/TestGitRepository';
const submoduleHeadSha = '49322bb17d3acc9146f98c97d078513228bbf3c0';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/submodulerepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/submodulerepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Submodule', () {

View file

@ -7,18 +7,19 @@ void main() {
late Repository repo;
late Tag tag;
late Directory tmpDir;
const tagSHA = 'f0fdbf506397e9f58c59b88dfdd72778ec06cc0c';
late Oid tagID;
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
tag = Tag.lookup(repo: repo, sha: tagSHA);
tagID = repo['f0fdbf506397e9f58c59b88dfdd72778ec06cc0c'];
tag = repo.lookupTag(tagID);
});
tearDown(() async {
tearDown(() {
tag.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Tag', () {
@ -36,7 +37,7 @@ void main() {
final target = tag.target as Commit;
final tagger = tag.tagger;
expect(tag.id.sha, tagSHA);
expect(tag.id, tagID);
expect(tag.name, 'v0.2');
expect(tag.message, 'annotated tag\n');
expect(target.message, 'add subdirectory file\n');
@ -53,11 +54,10 @@ void main() {
time: 1234,
);
const tagName = 'tag';
final target = 'f17d0d48eae3aa08cecf29128a35e310c97b3521';
final target = repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'];
const message = 'init tag\n';
final oid = Tag.create(
repo: repo,
final oid = repo.createTag(
tagName: tagName,
target: target,
targetType: GitObject.commit,
@ -65,7 +65,7 @@ void main() {
message: message,
);
final newTag = Tag.lookup(repo: repo, sha: oid.sha);
final newTag = repo.lookupTag(oid);
final tagger = newTag.tagger;
final newTagTarget = newTag.target as Commit;
@ -73,7 +73,7 @@ void main() {
expect(newTag.name, tagName);
expect(newTag.message, message);
expect(tagger, signature);
expect(newTagTarget.id.sha, target);
expect(newTagTarget.id, target);
newTag.free();
newTagTarget.free();
@ -87,7 +87,7 @@ void main() {
test('successfully deletes tag', () {
expect(repo.tags, ['v0.1', 'v0.2']);
tag.delete();
repo.deleteTag('v0.2');
expect(repo.tags, ['v0.1']);
});
});

View file

@ -7,19 +7,20 @@ void main() {
late Repository repo;
late Tree tree;
late Directory tmpDir;
const treeSHA = 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f';
const fileSHA = '1377554ebea6f98a2c748183bc5a96852af12ac2';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
tree = Tree.lookup(repo: repo, sha: treeSHA);
tree = repo.lookupTree(
repo['a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'],
);
});
tearDown(() async {
tearDown(() {
tree.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('Tree', () {
@ -70,7 +71,7 @@ void main() {
oid: fileOid,
filemode: GitFilemode.blob,
);
final newTree = Tree.lookup(repo: repo, sha: builder.write().sha);
final newTree = repo.lookupTree(builder.write());
final entry = newTree['filename'];
expect(newTree.length, 1);

View file

@ -7,18 +7,17 @@ void main() {
late Repository repo;
late Tree tree;
late Directory tmpDir;
const treeSHA = 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f';
setUp(() async {
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
setUp(() {
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
tree = Tree.lookup(repo: repo, sha: treeSHA);
tree = repo.lookupTree(repo['a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f']);
});
tearDown(() async {
tearDown(() {
tree.free();
repo.free();
await tmpDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
});
group('TreeBuilder', () {
@ -56,11 +55,13 @@ void main() {
expect(() => builder[entry.name], throwsA(isA<ArgumentError>()));
builder.add(
filename: entry.name, oid: entry.id, filemode: entry.filemode);
filename: entry.name,
oid: entry.id,
filemode: entry.filemode,
);
expect(builder[entry.name].name, entry.name);
builder.free();
entry.free();
});
test('successfully removes an entry', () {

View file

@ -7,91 +7,98 @@ void main() {
late Repository repo;
late Directory tmpDir;
final worktreeDir = Directory('${Directory.systemTemp.path}/worktree');
const worktreeName = 'worktree';
setUp(() async {
if (await worktreeDir.exists()) {
await worktreeDir.delete(recursive: true);
setUp(() {
if (worktreeDir.existsSync()) {
worktreeDir.deleteSync(recursive: true);
}
tmpDir = await setupRepo(Directory('test/assets/testrepo/'));
tmpDir = setupRepo(Directory('test/assets/testrepo/'));
repo = Repository.open(tmpDir.path);
});
tearDown(() async {
tearDown(() {
repo.free();
await tmpDir.delete(recursive: true);
if (await worktreeDir.exists()) {
await worktreeDir.delete(recursive: true);
tmpDir.deleteSync(recursive: true);
if (worktreeDir.existsSync()) {
worktreeDir.deleteSync(recursive: true);
}
});
group('Worktree', () {
test('successfully creates worktree at provided path', () {
const worktreeName = 'worktree';
expect(Worktree.list(repo), []);
expect(repo.worktrees, []);
final worktree = Worktree.create(
repo: repo,
final worktree = repo.createWorktree(
name: worktreeName,
path: worktreeDir.path,
);
final lookedupWorktree = repo.lookupWorktree(worktreeName);
final branches = repo.branches;
expect(Worktree.list(repo), [worktreeName]);
expect(repo.branches.list(), contains(worktreeName));
expect(repo.worktrees, [worktreeName]);
expect(branches.any((branch) => branch.name == worktreeName), true);
expect(worktree.name, worktreeName);
expect(lookedupWorktree.name, worktreeName);
expect(worktree.path, worktreeDir.path);
expect(lookedupWorktree.path, worktreeDir.path);
expect(File('${worktreeDir.path}/.git').existsSync(), true);
for (final branch in branches) {
branch.free();
}
lookedupWorktree.free();
worktree.free();
});
test(
'successfully creates worktree at provided path from provided reference',
() {
const worktreeName = 'worktree';
final head = repo.revParseSingle('HEAD');
final worktreeRef = repo.branches.create(name: 'v1', target: head);
final worktreeBranch = repo.branches['v1'];
expect(Worktree.list(repo), []);
final worktreeBranch = repo.createBranch(name: 'v1', target: head);
final ref = repo.lookupReference('refs/heads/v1');
expect(repo.worktrees, []);
final worktree = Worktree.create(
repo: repo,
final worktree = repo.createWorktree(
name: worktreeName,
path: worktreeDir.path,
ref: worktreeRef,
ref: ref,
);
final branches = repo.branches;
expect(Worktree.list(repo), [worktreeName]);
expect(repo.branches.list(), contains('v1'));
expect(repo.branches.list(), isNot(contains(worktreeName)));
expect(repo.worktrees, [worktreeName]);
expect(branches.any((branch) => branch.name == 'v1'), true);
expect(branches.any((branch) => branch.name == worktreeName), false);
expect(worktreeBranch.isCheckedOut, true);
worktreeDir.deleteSync(recursive: true);
worktree.prune();
expect(Worktree.list(repo), []);
expect(repo.worktrees, []);
expect(worktreeBranch.isCheckedOut, false);
expect(repo.branches.list(), contains('v1'));
expect(branches.any((branch) => branch.name == 'v1'), true);
for (final branch in branches) {
branch.free();
}
worktreeBranch.free();
worktreeRef.free();
ref.free();
head.free();
worktree.free();
});
test('successfully prunes worktree', () {
const worktreeName = 'worktree';
expect(Worktree.list(repo), []);
expect(repo.worktrees, []);
final worktree = Worktree.create(
repo: repo,
final worktree = repo.createWorktree(
name: worktreeName,
path: worktreeDir.path,
);
expect(Worktree.list(repo), [worktreeName]);
expect(repo.worktrees, [worktreeName]);
worktreeDir.deleteSync(recursive: true);
worktree.prune();
expect(Worktree.list(repo), []);
expect(repo.worktrees, []);
worktree.free();
});