feat(repository): add ability to lookup different types of git objects with []

This commit is contained in:
Aleksey Kulikov 2021-09-04 12:04:28 +03:00
parent f19a34a768
commit 2cf974c624
14 changed files with 124 additions and 67 deletions

View file

@ -3,13 +3,23 @@ import 'bindings/libgit2_bindings.dart';
import 'bindings/blob.dart' as bindings; import 'bindings/blob.dart' as bindings;
import 'oid.dart'; import 'oid.dart';
import 'repository.dart'; import 'repository.dart';
import 'util.dart';
class Blob { class Blob {
/// Initializes a new instance of [Blob] class from provided /// Initializes a new instance of [Blob] class from provided pointer to
/// [Repository] and [Oid] objects. /// blob object in memory.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed with `free()` to release allocated memory.
Blob.lookup(Repository repo, Oid oid) { Blob(this._blobPointer) {
libgit2.git_libgit2_init();
}
/// Initializes a new instance of [Blob] class from provided
/// [Repository] object and [sha] hex string.
///
/// Should be freed with `free()` to release allocated memory.
Blob.lookup(Repository repo, String sha) {
final oid = Oid.fromSHA(repo, sha);
_blobPointer = bindings.lookup(repo.pointer, oid.pointer); _blobPointer = bindings.lookup(repo.pointer, oid.pointer);
} }

View file

@ -16,11 +16,12 @@ class Commit {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
} }
/// Initializes a new instance of [Commit] class from provided [Repository] /// Initializes a new instance of [Commit] class from provided [Repository] object
/// and [Oid] objects. /// and [sha] hex string.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed with `free()` to release allocated memory.
Commit.lookup(Repository repo, Oid oid) { Commit.lookup(Repository repo, String sha) {
final oid = Oid.fromSHA(repo, sha);
_commitPointer = bindings.lookup(repo.pointer, oid.pointer); _commitPointer = bindings.lookup(repo.pointer, oid.pointer);
} }
@ -40,8 +41,7 @@ class Commit {
String? updateRef, String? updateRef,
String? messageEncoding, String? messageEncoding,
}) { }) {
final treeOid = Oid.fromSHA(repo, treeSHA); final tree = Tree.lookup(repo, treeSHA);
final tree = Tree.lookup(repo, treeOid);
final result = Oid(bindings.create( final result = Oid(bindings.create(
repo.pointer, repo.pointer,

View file

@ -63,33 +63,33 @@ class GitSort {
} }
/// Basic type (loose or packed) of any Git object. /// Basic type (loose or packed) of any Git object.
class GitObjectType { class GitObject {
const GitObjectType._(this._value); const GitObject._(this._value);
final int _value; final int _value;
/// Object can be any of the following. /// Object can be any of the following.
static const any = GitObjectType._(-2); static const any = GitObject._(-2);
/// Object is invalid. /// Object is invalid.
static const invalid = GitObjectType._(-1); static const invalid = GitObject._(-1);
/// A commit object. /// A commit object.
static const commit = GitObjectType._(1); static const commit = GitObject._(1);
/// A tree (directory listing) object. /// A tree (directory listing) object.
static const tree = GitObjectType._(2); static const tree = GitObject._(2);
/// A file revision object. /// A file revision object.
static const blob = GitObjectType._(3); static const blob = GitObject._(3);
/// An annotated tag object. /// An annotated tag object.
static const tag = GitObjectType._(4); static const tag = GitObject._(4);
/// A delta, base is given by an offset. /// A delta, base is given by an offset.
static const offsetDelta = GitObjectType._(6); static const offsetDelta = GitObject._(6);
/// A delta, base is given by object id. /// A delta, base is given by object id.
static const refDelta = GitObjectType._(7); static const refDelta = GitObject._(7);
int get value => _value; int get value => _value;
} }

View file

@ -93,21 +93,21 @@ class Index {
/// Updates the contents of an existing index object in memory by reading from the /// Updates the contents of an existing index object in memory by reading from the
/// specified tree. /// specified tree.
void readTree(Object target) { void readTree(Object target) {
late final Oid oid;
late final Tree tree; late final Tree tree;
if (target is Oid) { if (target is Oid) {
final repo = Repository(bindings.owner(_indexPointer)); final repo = Repository(bindings.owner(_indexPointer));
tree = Tree.lookup(repo, target); tree = Tree.lookup(repo, target.sha);
} else if (target is Tree) { } else if (target is Tree) {
tree = target; tree = target;
} else if (isValidShaHex(target as String)) { } else if (target is String) {
final repo = Repository(bindings.owner(_indexPointer)); final repo = Repository(bindings.owner(_indexPointer));
oid = Oid.fromSHA(repo, target); tree = Tree.lookup(repo, target);
tree = Tree.lookup(repo, oid);
} else { } else {
throw ArgumentError.value( throw ArgumentError.value(
'$target should be either Oid object, SHA hex string or Tree object'); '$target should be either Oid object, SHA hex string or Tree object');
} }
bindings.readTree(_indexPointer, tree.pointer); bindings.readTree(_indexPointer, tree.pointer);
tree.free(); tree.free();
} }

View file

@ -15,14 +15,20 @@ class Oid {
/// in the ODB of [repository] with provided hexadecimal [sha] string that is 40 characters /// in the ODB of [repository] with provided hexadecimal [sha] string that is 40 characters
/// long or shorter. /// long or shorter.
/// ///
/// Throws [ArgumentError] if provided [sha] hex string is not valid.
///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Oid.fromSHA(Repository repository, String sha) { Oid.fromSHA(Repository repository, String sha) {
if (sha.length == 40) { if (isValidShaHex(sha)) {
_oidPointer = bindings.fromSHA(sha); if (sha.length == 40) {
_oidPointer = bindings.fromSHA(sha);
} else {
final odb = repository.odb;
_oidPointer = odb.existsPrefix(bindings.fromStrN(sha), sha.length);
odb.free();
}
} else { } else {
final odb = repository.odb; throw ArgumentError.value('$sha is not a valid sha hex string');
_oidPointer = odb.existsPrefix(bindings.fromStrN(sha), sha.length);
odb.free();
} }
} }

View file

@ -4,6 +4,7 @@ import 'package:libgit2dart/libgit2dart.dart';
import 'bindings/libgit2_bindings.dart'; import 'bindings/libgit2_bindings.dart';
import 'bindings/repository.dart' as bindings; import 'bindings/repository.dart' as bindings;
import 'bindings/merge.dart' as merge_bindings; import 'bindings/merge.dart' as merge_bindings;
import 'bindings/object.dart' as object_bindings;
import 'commit.dart'; import 'commit.dart';
import 'config.dart'; import 'config.dart';
import 'index.dart'; import 'index.dart';
@ -273,8 +274,8 @@ class Repository {
late final Oid oid; late final Oid oid;
late final bool isDirect; late final bool isDirect;
if (target.runtimeType == Oid) { if (target is Oid) {
oid = target as Oid; oid = target;
isDirect = true; isDirect = true;
} else if (isValidShaHex(target as String)) { } else if (isValidShaHex(target as String)) {
oid = Oid.fromSHA(this, target); oid = Oid.fromSHA(this, target);
@ -314,18 +315,39 @@ class Repository {
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Odb get odb => Odb(bindings.odb(_repoPointer)); Odb get odb => Odb(bindings.odb(_repoPointer));
/// Looksup [Commit] for provided [sha] hex string. /// Looksup git object (commit, tree, blob, tag) for provided [sha] hex string.
/// ///
/// Throws [ArgumentError] if provided [sha] is not valid sha hex string. /// Returned object should be explicitly downcasted to one of four of git object types.
Commit operator [](String sha) { ///
late final Oid oid; /// ```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(this, sha);
final object = object_bindings.lookup(
_repoPointer,
oid.pointer,
GitObject.any.value,
);
final type = object_bindings.type(object);
if (isValidShaHex(sha)) { if (type == GitObject.commit.value) {
oid = Oid.fromSHA(this, sha); 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 { } else {
throw ArgumentError.value('$sha is not a valid sha hex string'); throw ArgumentError.value(
'$sha should be pointing to either commit, tree, blob or a tag');
} }
return Commit.lookup(this, oid);
} }
/// Returns the list of commits starting from provided [oid]. /// Returns the list of commits starting from provided [oid].
@ -415,7 +437,7 @@ class Repository {
Oid createTag({ Oid createTag({
required String tagName, required String tagName,
required Oid target, required Oid target,
required GitObjectType targetType, required GitObject targetType,
required Signature tagger, required Signature tagger,
required String message, required String message,
bool force = false, bool force = false,

View file

@ -8,13 +8,23 @@ import 'oid.dart';
import 'repository.dart'; import 'repository.dart';
import 'signature.dart'; import 'signature.dart';
import 'git_types.dart'; import 'git_types.dart';
import 'util.dart';
class Tag { class Tag {
/// Initializes a new instance of [Tag] class from provided /// Initializes a new instance of [Tag] class from provided pointer to
/// [Repository] and [Oid] objects. /// tag object in memory.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed with `free()` to release allocated memory.
Tag.lookup(Repository repo, Oid oid) { Tag(this._tagPointer) {
libgit2.git_libgit2_init();
}
/// Initializes a new instance of [Tag] class from provided
/// [Repository] object and [sha] hex string.
///
/// Should be freed with `free()` to release allocated memory.
Tag.lookup(Repository repo, String sha) {
final oid = Oid.fromSHA(repo, sha);
_tagPointer = bindings.lookup(repo.pointer, oid.pointer); _tagPointer = bindings.lookup(repo.pointer, oid.pointer);
} }
@ -39,7 +49,7 @@ class Tag {
required Repository repository, required Repository repository,
required String tagName, required String tagName,
required Oid target, required Oid target,
required GitObjectType targetType, required GitObject targetType,
required Signature tagger, required Signature tagger,
required String message, required String message,
bool force = false, bool force = false,

View file

@ -7,11 +7,20 @@ import 'git_types.dart';
import 'util.dart'; import 'util.dart';
class Tree { class Tree {
/// Initializes a new instance of [Tree] class from provided /// Initializes a new instance of [Tree] class from provided pointer to
/// [Repository] and [Oid] objects. /// tree object in memory.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed with `free()` to release allocated memory.
Tree.lookup(Repository repo, Oid oid) { Tree(this._treePointer) {
libgit2.git_libgit2_init();
}
/// Initializes a new instance of [Tree] class from provided
/// [Repository] object and [sha] hex string.
///
/// Should be freed with `free()` to release allocated memory.
Tree.lookup(Repository repo, String sha) {
final oid = Oid.fromSHA(repo, sha);
_treePointer = bindings.lookup(repo.pointer, oid.pointer); _treePointer = bindings.lookup(repo.pointer, oid.pointer);
} }

View file

@ -21,7 +21,7 @@ void main() {
to: await Directory(tmpDir).create(), to: await Directory(tmpDir).create(),
); );
repo = Repository.open(tmpDir); repo = Repository.open(tmpDir);
blob = Blob.lookup(repo, Oid.fromSHA(repo, blobSHA)); blob = Blob.lookup(repo, blobSHA);
}); });
tearDown(() async { tearDown(() async {
@ -44,7 +44,7 @@ void main() {
test('successfully creates new blob', () { test('successfully creates new blob', () {
final oid = Blob.create(repo, newBlobContent); final oid = Blob.create(repo, newBlobContent);
final newBlob = Blob.lookup(repo, oid); final newBlob = Blob.lookup(repo, oid.sha);
expect(newBlob.id.sha, '18fdaeef018e57a92bcad2d4a35b577f34089af6'); expect(newBlob.id.sha, '18fdaeef018e57a92bcad2d4a35b577f34089af6');
expect(newBlob.isBinary, false); expect(newBlob.isBinary, false);
@ -57,7 +57,7 @@ void main() {
test('successfully creates new blob from file at provided relative path', test('successfully creates new blob from file at provided relative path',
() { () {
final oid = Blob.createFromWorkdir(repo, 'feature_file'); final oid = Blob.createFromWorkdir(repo, 'feature_file');
final newBlob = Blob.lookup(repo, oid); final newBlob = Blob.lookup(repo, oid.sha);
expect(newBlob.id.sha, blobSHA); expect(newBlob.id.sha, blobSHA);
expect(newBlob.isBinary, false); expect(newBlob.isBinary, false);
@ -89,7 +89,7 @@ void main() {
final outsideFile = final outsideFile =
File('${Directory.current.absolute.path}/test/blob_test.dart'); File('${Directory.current.absolute.path}/test/blob_test.dart');
final oid = Blob.createFromDisk(repo, outsideFile.path); final oid = Blob.createFromDisk(repo, outsideFile.path);
final newBlob = Blob.lookup(repo, oid); final newBlob = Blob.lookup(repo, oid.sha);
expect(newBlob, isA<Blob>()); expect(newBlob, isA<Blob>());
expect(newBlob.isBinary, false); expect(newBlob.isBinary, false);

View file

@ -45,13 +45,13 @@ void main() {
group('Commit', () { group('Commit', () {
test('successfully returns when 40 char sha hex is provided', () { test('successfully returns when 40 char sha hex is provided', () {
final commit = repo[mergeCommit]; final commit = repo[mergeCommit] as Commit;
expect(commit, isA<Commit>()); expect(commit, isA<Commit>());
commit.free(); commit.free();
}); });
test('successfully returns when sha hex is short', () { test('successfully returns when sha hex is short', () {
final commit = repo[mergeCommit.substring(0, 5)]; final commit = repo[mergeCommit.substring(0, 5)] as Commit;
expect(commit, isA<Commit>()); expect(commit, isA<Commit>());
commit.free(); commit.free();
}); });
@ -66,7 +66,7 @@ void main() {
parents: [mergeCommit], parents: [mergeCommit],
); );
final commit = repo[oid.sha]; final commit = repo[oid.sha] as Commit;
expect(commit.id.sha, oid.sha); expect(commit.id.sha, oid.sha);
expect(commit.message, message); expect(commit.message, message);
@ -91,7 +91,7 @@ void main() {
parents: [], parents: [],
); );
final commit = repo[oid.sha]; final commit = repo[oid.sha] as Commit;
expect(commit.id.sha, oid.sha); expect(commit.id.sha, oid.sha);
expect(commit.message, message); expect(commit.message, message);
@ -115,7 +115,7 @@ void main() {
parents: [mergeCommit, 'fc38877b2552ab554752d9a77e1f48f738cca79b'], parents: [mergeCommit, 'fc38877b2552ab554752d9a77e1f48f738cca79b'],
); );
final commit = repo[oid.sha]; final commit = repo[oid.sha] as Commit;
expect(commit.id.sha, oid.sha); expect(commit.id.sha, oid.sha);
expect(commit.message, message); expect(commit.message, message);
@ -141,7 +141,7 @@ void main() {
parents: [mergeCommit], parents: [mergeCommit],
); );
final commit = repo[oid.sha]; final commit = repo[oid.sha] as Commit;
expect(commit.id.sha, oid.sha); expect(commit.id.sha, oid.sha);
expect(commit.message, message); expect(commit.message, message);

View file

@ -275,7 +275,7 @@ void main() {
test('successfully creates new blob', () { test('successfully creates new blob', () {
final oid = repo.createBlob(newBlobContent); final oid = repo.createBlob(newBlobContent);
final newBlob = Blob.lookup(repo, oid); final newBlob = repo[oid.sha] as Blob;
expect(newBlob, isA<Blob>()); expect(newBlob, isA<Blob>());
@ -286,7 +286,7 @@ void main() {
'successfully creates new blob from file at provided relative path', 'successfully creates new blob from file at provided relative path',
() { () {
final oid = repo.createBlobFromWorkdir('feature_file'); final oid = repo.createBlobFromWorkdir('feature_file');
final newBlob = Blob.lookup(repo, oid); final newBlob = repo[oid.sha] as Blob;
expect(newBlob, isA<Blob>()); expect(newBlob, isA<Blob>());
@ -297,7 +297,7 @@ void main() {
final outsideFile = final outsideFile =
File('${Directory.current.absolute.path}/test/blob_test.dart'); File('${Directory.current.absolute.path}/test/blob_test.dart');
final oid = repo.createBlobFromDisk(outsideFile.path); final oid = repo.createBlobFromDisk(outsideFile.path);
final newBlob = Blob.lookup(repo, oid); final newBlob = repo[oid.sha] as Blob;
expect(newBlob, isA<Blob>()); expect(newBlob, isA<Blob>());
@ -320,12 +320,12 @@ void main() {
repository: repo, repository: repo,
tagName: tagName, tagName: tagName,
target: target, target: target,
targetType: GitObjectType.commit, targetType: GitObject.commit,
tagger: signature, tagger: signature,
message: message, message: message,
); );
final newTag = Tag.lookup(repo, oid); final newTag = repo[oid.sha] as Tag;
final tagger = newTag.tagger; final tagger = newTag.tagger;
final newTagTarget = newTag.target; final newTagTarget = newTag.target;

View file

@ -19,7 +19,7 @@ void main() {
to: await Directory(tmpDir).create(), to: await Directory(tmpDir).create(),
); );
repo = Repository.open(tmpDir); repo = Repository.open(tmpDir);
tag = Tag.lookup(repo, Oid.fromSHA(repo, tagSHA)); tag = Tag.lookup(repo, tagSHA);
}); });
tearDown(() async { tearDown(() async {
@ -68,12 +68,12 @@ void main() {
repository: repo, repository: repo,
tagName: tagName, tagName: tagName,
target: target, target: target,
targetType: GitObjectType.commit, targetType: GitObject.commit,
tagger: signature, tagger: signature,
message: message, message: message,
); );
final newTag = Tag.lookup(repo, oid); final newTag = Tag.lookup(repo, oid.sha);
final tagger = newTag.tagger; final tagger = newTag.tagger;
final newTagTarget = newTag.target; final newTagTarget = newTag.target;

View file

@ -20,7 +20,7 @@ void main() {
to: await Directory(tmpDir).create(), to: await Directory(tmpDir).create(),
); );
repo = Repository.open(tmpDir); repo = Repository.open(tmpDir);
tree = Tree.lookup(repo, Oid.fromSHA(repo, treeSHA)); tree = Tree.lookup(repo, treeSHA);
}); });
tearDown(() async { tearDown(() async {
@ -73,7 +73,7 @@ void main() {
final builder = TreeBuilder(repo); final builder = TreeBuilder(repo);
builder.add('filename', fileOid, GitFilemode.blob); builder.add('filename', fileOid, GitFilemode.blob);
final newTree = Tree.lookup(repo, builder.write()); final newTree = Tree.lookup(repo, builder.write().sha);
final entry = newTree['filename']; final entry = newTree['filename'];
expect(newTree.length, 1); expect(newTree.length, 1);

View file

@ -19,7 +19,7 @@ void main() {
to: await Directory(tmpDir).create(), to: await Directory(tmpDir).create(),
); );
repo = Repository.open(tmpDir); repo = Repository.open(tmpDir);
tree = Tree.lookup(repo, Oid.fromSHA(repo, treeSHA)); tree = Tree.lookup(repo, treeSHA);
}); });
tearDown(() async { tearDown(() async {