From f63808b4f835b744b4ba601d9127ce40d6920d6e Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Sat, 4 Sep 2021 14:50:34 +0300 Subject: [PATCH] feat(reference): add ability to peel reference until object of specified type is found --- lib/src/bindings/reference.dart | 19 +++++++++++++++++ lib/src/reference.dart | 36 +++++++++++++++++++++++++++++++++ lib/src/tag.dart | 7 +++++++ test/reference_test.dart | 29 ++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/lib/src/bindings/reference.dart b/lib/src/bindings/reference.dart index 9d3e87f..a7c1a78 100644 --- a/lib/src/bindings/reference.dart +++ b/lib/src/bindings/reference.dart @@ -395,6 +395,25 @@ bool compare(Pointer ref1, Pointer ref2) { return result == 0 ? true : false; } +/// Recursively peel reference until object of the specified type is found. +/// +/// The retrieved peeled object is owned by the repository and should be closed to release memory. +/// +/// If you pass GIT_OBJECT_ANY as the target type, then the object will be peeled until a +/// non-tag object is met. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer peel(Pointer ref, int type) { + final out = calloc>(); + final error = libgit2.git_reference_peel(out, ref, type); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + /// Ensure the reference name is well-formed. /// /// Valid reference names must follow one of two patterns: diff --git a/lib/src/reference.dart b/lib/src/reference.dart index 02f6419..1bd4fd0 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -1,10 +1,17 @@ import 'dart:ffi'; +import 'package:libgit2dart/libgit2dart.dart'; + import 'bindings/libgit2_bindings.dart'; import 'bindings/reference.dart' as bindings; +import 'bindings/object.dart' as object_bindings; +import 'blob.dart'; +import 'commit.dart'; import 'oid.dart'; import 'reflog.dart'; import 'git_types.dart'; import 'repository.dart'; +import 'tag.dart'; +import 'tree.dart'; import 'util.dart'; class References { @@ -169,6 +176,35 @@ class Reference { } } + /// Recursively peel reference until object of the specified [type] is found. + /// + /// The retrieved peeled object is owned by the repository and should be closed to release memory. + /// + /// If no [type] is provided, then the object will be peeled until a non-tag object is met. + /// + /// Returned object should be explicitly downcasted to one of four of git object types. + /// + /// ```dart + /// final commit = ref.peel(GitObject.commit) as Commit; + /// final tree = ref.peel(GitObject.tree) as Tree; + /// ``` + /// + /// Throws a [LibGit2Error] if error occured. + Object peel([GitObject type = GitObject.any]) { + final object = bindings.peel(_refPointer, type.value); + final objectType = object_bindings.type(object); + + if (objectType == GitObject.commit.value) { + return Commit(object.cast()); + } else if (objectType == GitObject.tree.value) { + return Tree(object.cast()); + } else if (objectType == GitObject.blob.value) { + return Blob(object.cast()); + } else { + return Tag(object.cast()); + } + } + /// Returns the full name of a reference. String get name => bindings.name(_refPointer); diff --git a/lib/src/tag.dart b/lib/src/tag.dart index 8de67b1..38841de 100644 --- a/lib/src/tag.dart +++ b/lib/src/tag.dart @@ -81,6 +81,13 @@ class Tag { /// /// Returned object should be explicitly downcasted to one of four of git object types. /// + /// ```dart + /// final commit = tag.target as Commit; + /// final tree = tag.target as Tree; + /// final blob = tag.target as Blob; + /// final tag = tag.target as Tag; + /// ``` + /// /// Throws a [LibGit2Error] if error occured. Object get target { final type = bindings.targetType(_tagPointer); diff --git a/test/reference_test.dart b/test/reference_test.dart index 34f8bf0..bc0b61c 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:libgit2dart/src/git_types.dart'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'helpers/util.dart'; @@ -485,5 +486,33 @@ void main() { ref2.free(); ref3.free(); }); + + 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 peeled = ref.peel() as Commit; + + expect(peeled.id, commit.id); + + peeled.free(); + commit.free(); + ref.free(); + }); + + test('successfully peels to object of provided type', () { + final ref = repo.references['refs/heads/master']; + final commit = repo[ref.target.sha] as Commit; + final tree = repo[commit.tree.sha] as Tree; + final peeledCommit = ref.peel(GitObject.commit) as Commit; + final peeledTree = ref.peel(GitObject.tree) as Tree; + + expect(peeledCommit.id, commit.id); + expect(peeledTree.id, tree.id); + + peeledCommit.free(); + commit.free(); + tree.free(); + ref.free(); + }); }); }