feat(branch): add bindings and api

This commit is contained in:
Aleksey Kulikov 2021-09-06 17:26:25 +03:00
parent 28c4eca573
commit 11dbb8195d
7 changed files with 503 additions and 1 deletions

View file

@ -0,0 +1,181 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:libgit2dart/libgit2dart.dart';
import 'libgit2_bindings.dart';
import 'reference.dart' as reference_bindings;
import '../error.dart';
import '../util.dart';
/// Return a list of branches.
///
/// Throws a [LibGit2Error] if error occured.
List<String> list(Pointer<git_repository> repo, int listFlags) {
final iterator = calloc<Pointer<git_branch_iterator>>();
final iteratorError = libgit2.git_branch_iterator_new(
iterator,
repo,
listFlags,
);
if (iteratorError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
var result = <String>[];
var error = 0;
while (error == 0) {
final reference = calloc<Pointer<git_reference>>();
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);
calloc.free(refType);
calloc.free(reference);
} else {
break;
}
}
libgit2.git_branch_iterator_free(iterator.value);
return result;
}
/// Lookup a branch by its name in a repository.
///
/// The generated reference must be freed by the user. The branch name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_reference> lookup(
Pointer<git_repository> repo,
String branchName,
int branchType,
) {
final out = calloc<Pointer<git_reference>>();
final branchNameC = branchName.toNativeUtf8().cast<Int8>();
final error = libgit2.git_branch_lookup(out, repo, branchNameC, branchType);
calloc.free(branchNameC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Create 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 by the user.
///
/// The branch name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_reference> create(
Pointer<git_repository> repo,
String branchName,
Pointer<git_commit> target,
bool force,
) {
final out = calloc<Pointer<git_reference>>();
final branchNameC = branchName.toNativeUtf8().cast<Int8>();
final forceC = force ? 1 : 0;
final error = libgit2.git_branch_create(
out,
repo,
branchNameC,
target,
forceC,
);
calloc.free(branchNameC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Delete 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(Pointer<git_reference> branch) {
final error = libgit2.git_branch_delete(branch);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
reference_bindings.free(branch);
}
}
/// Move/rename 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.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_reference> rename(
Pointer<git_reference> branch,
String newBranchName,
bool force,
) {
final out = calloc<Pointer<git_reference>>();
final newBranchNameC = newBranchName.toNativeUtf8().cast<Int8>();
final forceC = force ? 1 : 0;
final error = libgit2.git_branch_move(out, branch, newBranchNameC, forceC);
calloc.free(newBranchNameC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
reference_bindings.free(branch);
return out.value;
}
}
/// Determine if HEAD points to the given branch.
///
/// Throws a [LibGit2Error] if error occured.
bool isHead(Pointer<git_reference> branch) {
final result = libgit2.git_branch_is_head(branch);
if (result < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return result == 1 ? true : false;
}
}
/// Get the branch name.
///
/// Given a reference object, this will check that it really is a branch
/// (ie. it lives under "refs/heads/" or "refs/remotes/"), and return the branch part of it.
///
/// Throws a [LibGit2Error] if error occured.
String name(Pointer<git_reference> ref) {
final out = calloc<Pointer<Int8>>();
final error = libgit2.git_branch_name(out, ref);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
final result = out.value.cast<Utf8>().toDartString();
calloc.free(out);
return result;
}
}
/// Free the given reference to release memory.
void free(Pointer<git_reference> ref) => reference_bindings.free(ref);

133
lib/src/branch.dart Normal file
View file

@ -0,0 +1,133 @@
import 'dart:ffi';
import 'bindings/libgit2_bindings.dart';
import 'bindings/branch.dart' as bindings;
import 'bindings/reference.dart' as reference_bindings;
import 'commit.dart';
import 'reference.dart';
import 'repository.dart';
import 'oid.dart';
import 'git_types.dart';
import 'util.dart';
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.
///
/// Throws a [LibGit2Error] if error occured.
List<String> list() => bindings.list(_repoPointer, 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 => bindings.list(_repoPointer, 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 =>
bindings.list(_repoPointer, 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(
_repoPointer, reference_bindings.lookupDWIM(_repoPointer, branchName));
late final GitBranch type;
ref.isBranch ? type = GitBranch.local : GitBranch.remote;
ref.free();
return Branch(bindings.lookup(_repoPointer, branchName, type.value));
}
/// 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.
///
/// The branch name will be checked for validity.
///
/// Throws a [LibGit2Error] if error occured.
Reference create({
required String name,
required Commit target,
bool force = false,
}) {
final result = bindings.create(
_repoPointer,
name,
target.pointer,
force,
);
return Reference(_repoPointer, result);
}
}
class Branch {
/// Initializes a new instance of [Branch] class from provided pointer to
/// branch object in memory.
///
/// Should be freed with `free()` to release allocated memory.
Branch(this._branchPointer) {
libgit2.git_libgit2_init();
}
/// Pointer to memory address for allocated branch object.
late final Pointer<git_reference> _branchPointer;
/// Returns the OID pointed to by a branch.
///
/// Throws an exception if error occured.
Oid get target => Oid(reference_bindings.target(_branchPointer));
/// 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);
/// 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, newName, force));
}
/// Determines if HEAD points to the given branch.
///
/// Throws a [LibGit2Error] if error occured.
bool get isHead => bindings.isHead(_branchPointer);
/// Returns the branch name.
///
/// Given a reference object, this will check that it really is a branch
/// (ie. it lives under "refs/heads/" or "refs/remotes/"), and return the branch part of it.
///
/// Throws a [LibGit2Error] if error occured.
String get name => bindings.name(_branchPointer);
/// Releases memory allocated for branch object.
void free() => bindings.free(_branchPointer);
}

View file

@ -25,9 +25,11 @@ class Commit {
_commitPointer = bindings.lookup(repo.pointer, oid.pointer);
}
/// Pointer to memory address for allocated commit object.
late final Pointer<git_commit> _commitPointer;
/// Pointer to memory address for allocated commit object.
Pointer<git_commit> get pointer => _commitPointer;
/// Creates new commit in the repository.
///
/// Throws a [LibGit2Error] if error occured.

View file

@ -110,3 +110,17 @@ class GitRevParse {
int get value => _value;
}
/// Basic type of any Git branch.
class GitBranch {
const GitBranch._(this._value);
final int _value;
static const local = GitBranch._(1);
static const remote = GitBranch._(2);
static const all = GitBranch._(3);
int get value => _value;
}

View file

@ -456,4 +456,7 @@ class Repository {
message: message,
force: force);
}
/// Returns a [Branches] object.
Branches get branches => Branches(this);
}