feat(describe): add bindings and api

This commit is contained in:
Aleksey Kulikov 2021-10-06 16:02:25 +03:00
parent c88b75b0fd
commit caac6b2fd2
6 changed files with 409 additions and 4 deletions

View file

@ -0,0 +1,158 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'libgit2_bindings.dart';
import '../error.dart';
import '../util.dart';
/// Describe a commit.
///
/// Perform the describe operation on the given committish object.
///
/// Returned object should be freed with `describeResultFree()` once no longer needed.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_describe_result> commit({
required Pointer<git_commit> commitPointer,
int? maxCandidatesTags,
int? describeStrategy,
String? pattern,
bool? onlyFollowFirstParent,
bool? showCommitOidAsFallback,
}) {
final out = calloc<Pointer<git_describe_result>>();
final opts = _initOpts(
maxCandidatesTags: maxCandidatesTags,
describeStrategy: describeStrategy,
pattern: pattern,
onlyFollowFirstParent: onlyFollowFirstParent,
showCommitOidAsFallback: showCommitOidAsFallback,
);
final error = libgit2.git_describe_commit(out, commitPointer.cast(), opts);
calloc.free(opts);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Describe a commit.
///
/// Perform the describe operation on the current commit and the worktree.
/// After peforming describe on HEAD, a status is run and the description is
/// considered to be dirty if there are.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_describe_result> workdir({
required Pointer<git_repository> repo,
int? maxCandidatesTags,
int? describeStrategy,
String? pattern,
bool? onlyFollowFirstParent,
bool? showCommitOidAsFallback,
}) {
final out = calloc<Pointer<git_describe_result>>();
final opts = _initOpts(
maxCandidatesTags: maxCandidatesTags,
describeStrategy: describeStrategy,
pattern: pattern,
onlyFollowFirstParent: onlyFollowFirstParent,
showCommitOidAsFallback: showCommitOidAsFallback,
);
final error = libgit2.git_describe_workdir(out, repo, opts);
calloc.free(opts);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Print the describe result to a buffer.
///
/// Throws a [LibGit2Error] if error occured.
String format({
required Pointer<git_describe_result> describeResultPointer,
int? abbreviatedSize,
bool? alwaysUseLongFormat,
String? dirtySuffix,
}) {
final out = calloc<git_buf>(sizeOf<git_buf>());
final opts = calloc<git_describe_format_options>();
final optsError = libgit2.git_describe_format_options_init(
opts,
GIT_DESCRIBE_FORMAT_OPTIONS_VERSION,
);
if (optsError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
if (abbreviatedSize != null) {
opts.ref.abbreviated_size = abbreviatedSize;
}
if (alwaysUseLongFormat != null) {
opts.ref.always_use_long_format = alwaysUseLongFormat ? 1 : 0;
}
if (dirtySuffix != null) {
opts.ref.dirty_suffix = dirtySuffix.toNativeUtf8().cast<Int8>();
}
final error = libgit2.git_describe_format(out, describeResultPointer, opts);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
final result = out.ref.ptr.cast<Utf8>().toDartString();
calloc.free(out);
return result;
}
}
/// Free the describe result.
void describeResultFree(Pointer<git_describe_result> result) {
libgit2.git_describe_result_free(result);
}
/// Initialize git_describe_options structure.
Pointer<git_describe_options> _initOpts({
int? maxCandidatesTags,
int? describeStrategy,
String? pattern,
bool? onlyFollowFirstParent,
bool? showCommitOidAsFallback,
}) {
final opts = calloc<git_describe_options>();
final error = libgit2.git_describe_options_init(
opts,
GIT_DESCRIBE_OPTIONS_VERSION,
);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
if (maxCandidatesTags != null) {
opts.ref.max_candidates_tags = maxCandidatesTags;
}
if (describeStrategy != null) {
opts.ref.describe_strategy = describeStrategy;
}
if (pattern != null) {
opts.ref.pattern = pattern.toNativeUtf8().cast<Int8>();
}
if (onlyFollowFirstParent != null) {
opts.ref.only_follow_first_parent = onlyFollowFirstParent ? 1 : 0;
}
if (showCommitOidAsFallback != null) {
opts.ref.show_commit_oid_as_fallback = showCommitOidAsFallback ? 1 : 0;
}
return opts;
}

View file

@ -1498,3 +1498,30 @@ class GitRebaseOperation {
@override
String toString() => 'GitRebaseOperation.$_name';
}
/// Reference lookup strategy.
///
/// These behave like the --tags and --all options to git-describe,
/// namely they say to look for any reference in either refs/tags/ or
/// refs/ respectively.
class GitDescribeStrategy {
const GitDescribeStrategy._(this._value, this._name);
final int _value;
final String _name;
/// Only match annotated tags.
static const defaultStrategy = GitDescribeStrategy._(0, 'defaultStrategy');
/// Match everything under `refs/tags/` (includes lightweight tags).
static const tags = GitDescribeStrategy._(1, 'tags');
/// Match everything under `refs/` (includes branches).
static const all = GitDescribeStrategy._(2, 'all');
static const List<GitDescribeStrategy> values = [defaultStrategy, tags, all];
int get value => _value;
@override
String toString() => 'GitDescribeStrategy.$_name';
}

View file

@ -13,6 +13,7 @@ import 'bindings/diff.dart' as diff_bindings;
import 'bindings/stash.dart' as stash_bindings;
import 'bindings/attr.dart' as attr_bindings;
import 'bindings/graph.dart' as graph_bindings;
import 'bindings/describe.dart' as describe_bindings;
import 'branch.dart';
import 'commit.dart';
import 'config.dart';
@ -530,6 +531,11 @@ class Repository {
force: force);
}
/// Returns a list with all the tags in the repository.
///
/// Throws a [LibGit2Error] if error occured.
List<String> get tags => Tag.list(this);
/// Returns a [Branches] object.
Branches get branches => Branches(this);
@ -1218,4 +1224,77 @@ class Repository {
upstreamPointer: upstream.pointer,
);
}
/// Describes a commit or the current worktree.
///
/// [maxCandidatesTags] is the number of candidate tags to consider. Increasing above 10 will
/// take slightly longer but may produce a more accurate result. A value of 0 will cause
/// only exact matches to be output. Default is 10.
///
/// [describeStrategy] is reference lookup strategy that is one of [GitDescribeStrategy].
/// Default matches only annotated tags.
///
/// [pattern] is pattern to use for tags matching, excluding the "refs/tags/" prefix.
///
/// [onlyFollowFirstParent] checks whether or not to follow only the first parent
/// commit upon seeing a merge commit.
///
/// [showCommitOidAsFallback] determines if full id of the commit should be shown
/// if no matching tag or reference is found.
///
/// [abbreviatedSize] is the minimum number of hexadecimal digits to show for abbreviated
/// object names. A value of 0 will suppress long format, only showing the closest tag.
/// Default is 7.
///
/// [alwaysUseLongFormat] determines if he long format (the nearest tag, the number of
/// commits, and the abbrevated commit name) should be used even when the commit matches
/// the tag.
///
/// [dirtySuffix] is a string to append if the working tree is dirty.
///
/// Throws a [LibGit2Error] if error occured.
String describe({
Commit? commit,
int? maxCandidatesTags,
GitDescribeStrategy? describeStrategy,
String? pattern,
bool? onlyFollowFirstParent,
bool? showCommitOidAsFallback,
int? abbreviatedSize,
bool? alwaysUseLongFormat,
String? dirtySuffix,
}) {
late final Pointer<git_describe_result> describeResult;
if (commit != null) {
describeResult = describe_bindings.commit(
commitPointer: commit.pointer,
maxCandidatesTags: maxCandidatesTags,
describeStrategy: describeStrategy?.value,
pattern: pattern,
onlyFollowFirstParent: onlyFollowFirstParent,
showCommitOidAsFallback: showCommitOidAsFallback,
);
} else {
describeResult = describe_bindings.workdir(
repo: _repoPointer,
maxCandidatesTags: maxCandidatesTags,
describeStrategy: describeStrategy?.value,
pattern: pattern,
onlyFollowFirstParent: onlyFollowFirstParent,
showCommitOidAsFallback: showCommitOidAsFallback,
);
}
final result = describe_bindings.format(
describeResultPointer: describeResult,
abbreviatedSize: abbreviatedSize,
alwaysUseLongFormat: alwaysUseLongFormat,
dirtySuffix: dirtySuffix,
);
describe_bindings.describeResultFree(describeResult);
return result;
}
}