From b603a567b2ca26a7c6fade3c0245bddd56b71638 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Mon, 9 Aug 2021 13:14:24 +0300 Subject: [PATCH] feat(repository): add ability to set head --- lib/src/repository.dart | 25 ++++++++++++++++----- test/repository_test.dart | 47 ++++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/lib/src/repository.dart b/lib/src/repository.dart index a5c8fb3..a119645 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; import 'odb.dart'; +import 'oid.dart'; import 'reference.dart'; import 'bindings/libgit2_bindings.dart'; import 'bindings/repository.dart' as bindings; @@ -77,11 +78,11 @@ class Repository { return bindings.isHeadDetached(_repoPointer); } - /// Makes the repository HEAD point to the specified reference. + /// Makes the repository HEAD point to the specified reference or commit. /// - /// If the provided [reference] points to a Tree or a Blob, the HEAD is unaltered. + /// If the provided [target] points to a Tree or a Blob, the HEAD is unaltered. /// - /// If the provided [reference] points to a branch, the HEAD will point to that branch, + /// If the provided [target] points to a branch, the HEAD will point to that branch, /// staying attached, or become attached if it isn't yet. /// /// If the branch doesn't exist yet, the HEAD will be attached to an unborn branch. @@ -89,8 +90,22 @@ class Repository { /// Otherwise, the HEAD will be detached and will directly point to the Commit. /// /// Throws a [LibGit2Error] if error occured. - void setHead(String reference) { - bindings.setHead(_repoPointer, reference); + void setHead(String target) { + late final Oid oid; + + if (isValidShaHex(target)) { + if (target.length == 40) { + oid = Oid.fromSHA(target); + } else { + final shortOid = Oid.fromSHAn(target); + final odb = this.odb; + oid = Oid(odb.existsPrefix(shortOid.pointer, target.length)); + odb.free(); + } + bindings.setHeadDetached(_repoPointer, oid.pointer); + } else { + bindings.setHead(_repoPointer, target); + } } /// Checks if the current branch is unborn. diff --git a/test/repository_test.dart b/test/repository_test.dart index 92e2c65..c825abd 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -50,15 +50,6 @@ void main() { test('returns empty string as path of the working directory', () { expect(repo.workdir, ''); }); - - group('setHead', () { - test('throws when reference doesn\'t exist', () { - expect( - () => repo.setHead('refs/tags/doesnt/exist'), - throwsA(isA()), - ); - }); - }); }); group('standard', () { @@ -130,9 +121,12 @@ void main() { }); group('testrepo', () { + const lastCommit = '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'; + const featureCommit = '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'; + final tmpDir = '${Directory.systemTemp.path}/testrepo/'; - setUpAll(() async { + setUp(() async { if (await Directory(tmpDir).exists()) { await Directory(tmpDir).delete(recursive: true); } @@ -143,7 +137,7 @@ void main() { repo = Repository.open(tmpDir); }); - tearDownAll(() async { + tearDown(() async { repo.free(); await Directory(tmpDir).delete(recursive: true); }); @@ -159,6 +153,37 @@ void main() { repo.setNamespace(null); expect(repo.namespace, ''); }); + + group('setHead', () { + test('successfully sets head when target is reference', () { + expect(repo.head.name, 'refs/heads/master'); + expect(repo.head.target.sha, lastCommit); + repo.setHead('refs/heads/feature'); + expect(repo.head.name, 'refs/heads/feature'); + expect(repo.head.target.sha, featureCommit); + }); + + test('successfully sets head when target is sha hex', () { + expect(repo.head.target.sha, lastCommit); + repo.setHead(featureCommit); + expect(repo.head.target.sha, featureCommit); + expect(repo.isHeadDetached, true); + }); + + test('successfully sets head when target is short sha hex', () { + expect(repo.head.target.sha, lastCommit); + repo.setHead(featureCommit.substring(0, 5)); + expect(repo.head.target.sha, featureCommit); + expect(repo.isHeadDetached, true); + }); + + test('successfully attaches to an unborn branch', () { + expect(repo.head.name, 'refs/heads/master'); + expect(repo.isBranchUnborn, false); + repo.setHead('refs/heads/not.there'); + expect(repo.isBranchUnborn, true); + }); + }); }); }); }