From 1f0201d259ea3ee55aa75dded2d623540668ff10 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Mon, 9 Aug 2021 11:56:37 +0300 Subject: [PATCH] refactor(reference): remove duplication --- example/reference_example.dart | 5 +- example/repository_example.dart | 1 - lib/libgit2dart.dart | 1 + lib/src/reference.dart | 105 ++++++++++++----------- lib/src/repository.dart | 97 +-------------------- test/reference_test.dart | 146 +++++++++++++++++++------------- 6 files changed, 147 insertions(+), 208 deletions(-) diff --git a/example/reference_example.dart b/example/reference_example.dart index 40ffd38..1855d6f 100644 --- a/example/reference_example.dart +++ b/example/reference_example.dart @@ -4,7 +4,10 @@ import 'package:libgit2dart/libgit2dart.dart'; void main() { final repo = Repository.open(Directory.current.path); - final ref = repo.getReference('refs/heads/master'); + + print('Repository references: ${Reference.list(repo)}'); + + final ref = Reference.get(repo, 'refs/heads/master'); print('Reference SHA hex: ${ref.target.sha}'); print('Is reference a local branch: ${ref.isBranch}'); diff --git a/example/repository_example.dart b/example/repository_example.dart index 780aa1a..5bcc579 100644 --- a/example/repository_example.dart +++ b/example/repository_example.dart @@ -11,7 +11,6 @@ void main() { print('Is repository bare: ${repo.isBare}'); print('Is repository empty: ${repo.isEmpty}'); print('Is head detached: ${repo.isHeadDetached}'); - print('Repository refs: ${repo.references}'); // free() should be called on object to free memory when done. repo.free(); diff --git a/lib/libgit2dart.dart b/lib/libgit2dart.dart index ab62a5a..a87cafa 100644 --- a/lib/libgit2dart.dart +++ b/lib/libgit2dart.dart @@ -1,3 +1,4 @@ export 'src/repository.dart'; export 'src/config.dart'; export 'src/error.dart'; +export 'src/reference.dart'; diff --git a/lib/src/reference.dart b/lib/src/reference.dart index 338c172..55124b1 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -5,6 +5,7 @@ import 'bindings/repository.dart' as repo_bindings; import 'odb.dart'; import 'oid.dart'; import 'reflog.dart'; +import 'repository.dart'; import 'util.dart'; enum ReferenceType { direct, symbolic } @@ -16,9 +17,9 @@ class Reference { libgit2.git_libgit2_init(); } - /// Initializes a new instance of the [Reference] class by creating a new direct reference. + /// Initializes a new instance of the [Reference] class by creating a new reference. /// - /// The direct reference will be created in the repository and written to the disk. + /// The reference will be created in the repository and written to the disk. /// The generated [Reference] object must be freed by the user. /// /// Valid reference names must follow one of two patterns: @@ -28,79 +29,81 @@ class Reference { /// Names prefixed with "refs/" can be almost anything. You must avoid the characters /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have /// special meaning to revparse. - /// /// Throws a [LibGit2Error] if a reference already exists with the given name /// unless force is true, in which case it will be overwritten. /// /// The message for the reflog will be ignored if the reference does not belong in the /// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog. - Reference.createDirect({ - required Pointer repo, + Reference.create({ + required Repository repository, required String name, - required Pointer oid, - required bool force, + required Object target, + bool force = false, String? logMessage, }) { - _refPointer = bindings.createDirect(repo, name, oid, force, logMessage); - } + late final Oid oid; + late final bool isDirect; - /// Initializes a new instance of the [Reference] class by creating a new symbolic reference. - /// - /// A symbolic reference is a reference name that refers to another reference name. - /// If the other name moves, the symbolic name will move, too. As a simple example, - /// the "HEAD" reference might refer to "refs/heads/master" while on the "master" branch - /// of a repository. - /// - /// The symbolic reference will be created in the repository and written to the disk. - /// The generated reference object must be freed by the user. - /// - /// Valid reference names must follow one of two patterns: - /// - /// Top-level names must contain only capital letters and underscores, and must begin and end - /// with a letter. (e.g. "HEAD", "ORIG_HEAD"). - /// Names prefixed with "refs/" can be almost anything. You must avoid the characters - /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have special - /// meaning to revparse. - /// This function will throw an [LibGit2Error] if a reference already exists with the given - /// name unless force is true, in which case it will be overwritten. - /// - /// The message for the reflog will be ignored if the reference does not belong in the standard - /// set (HEAD, branches and remote-tracking branches) and it does not have a reflog. - Reference.createSymbolic({ - required Pointer repo, - required String name, - required String target, - required bool force, - String? logMessage, - }) { - _refPointer = - bindings.createSymbolic(repo, name, target, force, logMessage); + if (target.runtimeType == Oid) { + oid = target as Oid; + isDirect = true; + } else if (isValidShaHex(target as String)) { + if (target.length == 40) { + oid = Oid.fromSHA(target); + } else { + final shortOid = Oid.fromSHAn(target); + final odb = repository.odb; + oid = Oid(odb.existsPrefix(shortOid.pointer, target.length)); + odb.free(); + } + isDirect = true; + } else { + isDirect = false; + } + + if (isDirect) { + _refPointer = bindings.createDirect( + repository.pointer, + name, + oid.pointer, + force, + logMessage, + ); + } else { + _refPointer = bindings.createSymbolic( + repository.pointer, + name, + target as String, + force, + logMessage, + ); + } } /// Initializes a new instance of the [Reference] class by - /// lookingup a reference by [name] in a repository. + /// lookingup a reference by [name] in a [repository]. /// /// Should be freed with `free()` to release allocated memory. /// /// The name will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. - Reference.lookup(Pointer repo, String name) { + Reference.get(Repository repository, String name) { libgit2.git_libgit2_init(); - _refPointer = bindings.lookup(repo, name); + _refPointer = bindings.lookup(repository.pointer, name); } /// Initializes a new instance of the [Reference] class by - /// lookingup a reference by DWIMing it's short [name] in a repository. + /// lookingup a reference by DWIMing it's short [name] in a [repository]. /// /// Should be freed with `free()` to release allocated memory. /// /// The name will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. - Reference.lookupDWIM(Pointer repo, String name) { + Reference.getDWIM(Repository repository, String name) { libgit2.git_libgit2_init(); - _refPointer = bindings.lookupDWIM(repo, name); + _refPointer = bindings.lookupDWIM(repository.pointer, name); } /// Pointer to memory address for allocated reference object. @@ -201,18 +204,18 @@ class Reference { _refPointer = bindings.rename(_refPointer, newName, force, logMessage); } - /// Returns a list with all the references that can be found in a repository. + /// Returns a list with all the references that can be found in a [repository]. /// /// Throws a [LibGit2Error] if error occured. - static List list(Pointer repo) { - return bindings.list(repo); + static List list(Repository repository) { + return bindings.list(repository.pointer); } /// Checks if a reflog exists for the specified reference [name]. /// /// Throws a [LibGit2Error] if error occured. - static bool hasLog(Pointer repo, String name) { - return bindings.hasLog(repo, name); + static bool hasLog(Repository repository, String name) { + return bindings.hasLog(repository.pointer, name); } /// Returns a list with entries of reference log. diff --git a/lib/src/repository.dart b/lib/src/repository.dart index cf557b1..a5c8fb3 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -1,6 +1,5 @@ import 'dart:ffi'; import 'odb.dart'; -import 'oid.dart'; import 'reference.dart'; import 'bindings/libgit2_bindings.dart'; import 'bindings/repository.dart' as bindings; @@ -22,9 +21,11 @@ class Repository { _repoPointer = bindings.open(path); } - /// Pointer to memory address for allocated repository object. late final Pointer _repoPointer; + /// Pointer to memory address for allocated repository object. + Pointer get pointer => _repoPointer; + /// Returns path to the `.git` folder for normal repositories /// or path to the repository itself for bare repositories. String get path => bindings.path(_repoPointer); @@ -157,101 +158,9 @@ class Repository { libgit2.git_libgit2_shutdown(); } - /// Creates a new reference. - /// - /// The reference will be created in the repository and written to the disk. - /// The generated [Reference] object must be freed by the user. - /// - /// Valid reference names must follow one of two patterns: - /// - /// Top-level names must contain only capital letters and underscores, and must begin and end - /// with a letter. (e.g. "HEAD", "ORIG_HEAD"). - /// Names prefixed with "refs/" can be almost anything. You must avoid the characters - /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have - /// special meaning to revparse. - /// Throws a [LibGit2Error] if a reference already exists with the given name - /// unless force is true, in which case it will be overwritten. - /// - /// The message for the reflog will be ignored if the reference does not belong in the - /// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog. - Reference createReference({ - required String name, - required Object target, - bool force = false, - String? logMessage, - }) { - late final Oid oid; - late final bool isDirect; - - if (target.runtimeType == Oid) { - oid = target as Oid; - isDirect = true; - } else if (isValidShaHex(target as String)) { - 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(); - } - isDirect = true; - } else { - isDirect = false; - } - - if (isDirect) { - return Reference.createDirect( - repo: _repoPointer, - name: name, - oid: oid.pointer, - force: force, - logMessage: logMessage, - ); - } else { - return Reference.createSymbolic( - repo: _repoPointer, - name: name, - target: target as String, - force: force, - logMessage: logMessage, - ); - } - } - /// Returns [Reference] object pointing to repository head. Reference get head => Reference(bindings.head(_repoPointer)); - /// Returns [Reference] object by lookingup a [name] in repository. - /// - /// Throws a [LibGit2Error] if error occured. - Reference getReference(String name) => Reference.lookup(_repoPointer, name); - - /// Returns [Reference] object by lookingup a short [name] in repository. - /// - /// Throws a [LibGit2Error] if error occured. - Reference getReferenceDWIM(String name) => - Reference.lookupDWIM(_repoPointer, name); - - /// Checks if a reflog exists for the specified reference [name]. - /// - /// Throws a [LibGit2Error] if error occured. - bool referenceHasLog(String name) => Reference.hasLog(_repoPointer, name); - - /// Returns a map with all the references names and corresponding SHA hashes - /// that can be found in a repository. - Map get references { - var refMap = {}; - final refList = Reference.list(_repoPointer); - for (var ref in refList) { - final r = getReference(ref); - refMap[ref] = r.target.sha; - r.free(); - } - - return refMap; - } - /// Returns [Odb] for this repository. /// /// ODB Object must be freed once it's no longer being used. diff --git a/test/reference_test.dart b/test/reference_test.dart index 927e196..ff01705 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -34,43 +34,47 @@ void main() { group('.createDirect()', () { test('successfully creates with Oid as target', () { - final ref = repo.getReference('refs/heads/master'); - final refFromOid = repo.createReference( + final ref = Reference.get(repo, 'refs/heads/master'); + final refFromOid = Reference.create( + repository: repo, name: 'refs/tags/from.oid', target: ref.target, ); - expect(repo.references, contains('refs/tags/from.oid')); + expect(Reference.list(repo), contains('refs/tags/from.oid')); refFromOid.free(); ref.free(); }); test('successfully creates with SHA hash as target', () { - final refFromHash = repo.createReference( + final refFromHash = Reference.create( + repository: repo, name: 'refs/tags/from.hash', target: lastCommit, ); - expect(repo.references, contains('refs/tags/from.hash')); + expect(Reference.list(repo), contains('refs/tags/from.hash')); refFromHash.free(); }); test('successfully creates with short SHA hash as target', () { - final refFromHash = repo.createReference( + final refFromHash = Reference.create( + repository: repo, name: 'refs/tags/from.short.hash', target: '78b8bf', ); - expect(repo.references, contains('refs/tags/from.short.hash')); + expect(Reference.list(repo), contains('refs/tags/from.short.hash')); refFromHash.free(); }); test('successfully creates with log message', () { repo.setIdentity(name: 'name', email: 'email'); - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/heads/log.message', target: lastCommit, logMessage: 'log message', @@ -89,7 +93,8 @@ void main() { test('throws if target is not valid', () { expect( - () => repo.createReference( + () => Reference.create( + repository: repo, name: 'refs/tags/invalid', target: '78b', ), @@ -99,7 +104,8 @@ void main() { test('throws if name is not valid', () { expect( - () => repo.createReference( + () => Reference.create( + repository: repo, name: 'refs/tags/invalid~', target: lastCommit, ), @@ -108,12 +114,14 @@ void main() { }); test('successfully creates with force flag if name already exists', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/test', target: lastCommit, ); - final forceRef = repo.createReference( + final forceRef = Reference.create( + repository: repo, name: 'refs/tags/test', target: lastCommit, force: true, @@ -126,13 +134,15 @@ void main() { }); test('throws if name already exists', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/test', target: lastCommit, ); expect( - () => repo.createReference( + () => Reference.create( + repository: repo, name: 'refs/tags/test', target: lastCommit, ), @@ -145,24 +155,27 @@ void main() { group('.createSymbolic()', () { test('successfully creates with valid target', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/symbolic', target: 'refs/heads/master', ); - expect(repo.references, contains('refs/tags/symbolic')); + expect(Reference.list(repo), contains('refs/tags/symbolic')); expect(ref.type, ReferenceType.symbolic); ref.free(); }); test('successfully creates with force flag if name already exists', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/test', target: 'refs/heads/master', ); - final forceRef = repo.createReference( + final forceRef = Reference.create( + repository: repo, name: 'refs/tags/test', target: 'refs/heads/master', force: true, @@ -176,13 +189,15 @@ void main() { }); test('throws if name already exists', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/exists', target: 'refs/heads/master', ); expect( - () => repo.createReference( + () => Reference.create( + repository: repo, name: 'refs/tags/exists', target: 'refs/heads/master', ), @@ -194,7 +209,8 @@ void main() { test('throws if name is not valid', () { expect( - () => repo.createReference( + () => Reference.create( + repository: repo, name: 'refs/tags/invalid~', target: 'refs/heads/master', ), @@ -204,7 +220,8 @@ void main() { test('successfully creates with log message', () { repo.setIdentity(name: 'name', email: 'email'); - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'HEAD', target: 'refs/heads/feature', force: true, @@ -224,14 +241,15 @@ void main() { }); test('successfully deletes reference', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/test', target: lastCommit, ); - expect(repo.references, contains('refs/tags/test')); + expect(Reference.list(repo), contains('refs/tags/test')); ref.delete(); - expect(repo.references, isNot(contains('refs/tags/test'))); + expect(Reference.list(repo), isNot(contains('refs/tags/test'))); ref.free(); }); @@ -239,7 +257,7 @@ void main() { expect(repo.head.type, ReferenceType.direct); repo.head.free(); - final ref = repo.getReference('HEAD'); + final ref = Reference.get(repo, 'HEAD'); expect(ref.type, ReferenceType.symbolic); ref.free(); }); @@ -250,7 +268,7 @@ void main() { }); test('returns SHA hex of symbolic reference', () { - final ref = repo.getReference('HEAD'); + final ref = Reference.get(repo, 'HEAD'); expect(ref.target.sha, lastCommit); ref.free(); }); @@ -261,7 +279,8 @@ void main() { }); test('returns the short name', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/remotes/origin/master', target: lastCommit, ); @@ -275,34 +294,31 @@ void main() { test('returns a map with all the references of repository', () { expect( - repo.references, - { - 'refs/heads/feature': '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4', - 'refs/heads/master': '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8', - 'refs/tags/v0.1': '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8', - }, + Reference.list(repo), + ['refs/heads/feature', 'refs/heads/master', 'refs/tags/v0.1'], ); }); test('checks if reflog exists for the reference', () { - expect(repo.referenceHasLog('refs/heads/master'), true); - expect(repo.referenceHasLog('refs/tags/v0.1'), false); + expect(Reference.hasLog(repo, 'refs/heads/master'), true); + expect(Reference.hasLog(repo, 'refs/tags/v0.1'), false); }); test('checks if reference is a local branch', () { - final ref = repo.getReference('refs/heads/feature'); + final ref = Reference.get(repo, 'refs/heads/feature'); expect(ref.isBranch, true); ref.free(); }); test('checks if reference is a note', () { - final ref = repo.getReference('refs/heads/master'); + final ref = Reference.get(repo, 'refs/heads/master'); expect(ref.isNote, false); ref.free(); }); test('checks if reference is a remote branch', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/remotes/origin/master', target: lastCommit, ); @@ -313,21 +329,21 @@ void main() { }); test('checks if reference is a tag', () { - final ref = repo.getReference('refs/tags/v0.1'); + final ref = Reference.get(repo, 'refs/tags/v0.1'); expect(ref.isTag, true); ref.free(); }); group('.lookup()', () { test('finds a reference with provided name', () { - final ref = repo.getReference('refs/heads/master'); + final ref = Reference.get(repo, 'refs/heads/master'); expect(ref.target.sha, lastCommit); ref.free(); }); test('throws when error occured', () { expect( - () => repo.getReference('refs/heads/not/there'), + () => Reference.get(repo, 'refs/heads/not/there'), throwsA(isA()), ); }); @@ -335,28 +351,30 @@ void main() { group('.lookupDWIM()', () { test('finds a reference with provided name', () { - final remoteRef = repo.createReference( + final remoteRef = Reference.create( + repository: repo, name: 'refs/remotes/origin/master', target: lastCommit, ); expect(remoteRef.shorthand, 'origin/master'); - final tagRef = repo.createReference( + final tagRef = Reference.create( + repository: repo, name: 'refs/tags/v1', target: lastCommit, ); expect(tagRef.shorthand, 'v1'); - var ref = repo.getReferenceDWIM('refs/heads/master'); + var ref = Reference.getDWIM(repo, 'refs/heads/master'); expect(ref.name, 'refs/heads/master'); - ref = repo.getReferenceDWIM('master'); + ref = Reference.getDWIM(repo, 'master'); expect(ref.name, 'refs/heads/master'); - ref = repo.getReferenceDWIM('origin/master'); + ref = Reference.getDWIM(repo, 'origin/master'); expect(ref.name, 'refs/remotes/origin/master'); - ref = repo.getReferenceDWIM('v1'); + ref = Reference.getDWIM(repo, 'v1'); expect(ref.name, 'refs/tags/v1'); remoteRef.free(); @@ -366,21 +384,21 @@ void main() { test('throws when error occured', () { expect( - () => repo.getReferenceDWIM('refs/heads/not/there'), + () => Reference.getDWIM(repo, 'refs/heads/not/there'), throwsA(isA()), ); }); }); test('returns log for reference', () { - final ref = repo.getReference('refs/heads/master'); + final ref = Reference.get(repo, 'refs/heads/master'); expect(ref.log.last.message, 'commit (initial): init'); ref.free(); }); group('.setTarget()', () { test('successfully sets target with SHA hex', () { - final ref = repo.getReference('refs/heads/master'); + final ref = Reference.get(repo, 'refs/heads/master'); ref.setTarget(newCommit); expect(ref.target.sha, newCommit); @@ -388,7 +406,7 @@ void main() { }); test('successfully sets target with short SHA hex', () { - final ref = repo.getReference('refs/heads/master'); + final ref = Reference.get(repo, 'refs/heads/master'); ref.setTarget(newCommit.substring(0, 5)); expect(ref.target.sha, newCommit); @@ -396,7 +414,7 @@ void main() { }); test('successfully sets symbolic target', () { - final ref = repo.getReference('HEAD'); + final ref = Reference.get(repo, 'HEAD'); expect(ref.target.sha, lastCommit); ref.setTarget('refs/heads/feature'); @@ -406,7 +424,7 @@ void main() { }); test('successfully sets target with log message', () { - final ref = repo.getReference('HEAD'); + final ref = Reference.get(repo, 'HEAD'); expect(ref.target.sha, lastCommit); repo.setIdentity(name: 'name', email: 'email'); @@ -420,7 +438,7 @@ void main() { }); test('throws on invalid target', () { - final ref = repo.getReference('HEAD'); + final ref = Reference.get(repo, 'HEAD'); expect( () => ref.setTarget('refs/heads/invalid~'), throwsA(isA()), @@ -432,7 +450,8 @@ void main() { group('.rename()', () { test('successfully renames reference', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/v1', target: lastCommit, ); @@ -445,7 +464,8 @@ void main() { }); test('throws on invalid name', () { - final ref = repo.createReference( + final ref = Reference.create( + repository: repo, name: 'refs/tags/v1', target: lastCommit, ); @@ -459,12 +479,14 @@ void main() { }); test('throws if name already exists', () { - final ref1 = repo.createReference( + final ref1 = Reference.create( + repository: repo, name: 'refs/tags/v1', target: lastCommit, ); - final ref2 = repo.createReference( + final ref2 = Reference.create( + repository: repo, name: 'refs/tags/v2', target: lastCommit, ); @@ -479,12 +501,14 @@ void main() { }); test('successfully renames with force flag set to true', () { - final ref1 = repo.createReference( + final ref1 = Reference.create( + repository: repo, name: 'refs/tags/v1', target: lastCommit, ); - final ref2 = repo.createReference( + final ref2 = Reference.create( + repository: repo, name: 'refs/tags/v2', target: newCommit, );