From 594aca64741e2873fee004f7bce520449b1a88fa Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Thu, 12 Aug 2021 20:00:25 +0300 Subject: [PATCH] feat(repository): add ability to get repository config --- example/config_example.dart | 15 +++++++-- lib/src/bindings/config.dart | 58 +++++++++++++++++++++----------- lib/src/bindings/repository.dart | 38 +++++++++++++++++++++ lib/src/config.dart | 57 ++++++++++++++----------------- lib/src/repository.dart | 21 ++++++++++++ test/config_test.dart | 2 +- test/repository_test.dart | 8 +++++ 7 files changed, 145 insertions(+), 54 deletions(-) diff --git a/example/config_example.dart b/example/config_example.dart index 079b996..05f2c17 100644 --- a/example/config_example.dart +++ b/example/config_example.dart @@ -1,6 +1,14 @@ +import 'dart:io'; + import 'package:libgit2dart/libgit2dart.dart'; -void main() { +import 'helpers.dart'; + +void main() async { + // Preparing example repository. + final tmpDir = '${Directory.systemTemp.path}/example_repo/'; + await prepareRepo(tmpDir); + // Open system + global config file. final config = Config.open(); @@ -15,7 +23,7 @@ void main() { // Open config file at provided path. // Exception is thrown if file not found. try { - final repoConfig = Config.open(path: '.git/config'); + final repoConfig = Config.open('$tmpDir/.git/config'); print('\nAll entries of repo config:'); final entries = repoConfig.getEntries(); @@ -49,4 +57,7 @@ void main() { } catch (e) { print('\n$e'); } + + // Removing example repository. + await disposeRepo(tmpDir); } diff --git a/lib/src/bindings/config.dart b/lib/src/bindings/config.dart index e35725c..0c4d57e 100644 --- a/lib/src/bindings/config.dart +++ b/lib/src/bindings/config.dart @@ -10,21 +10,21 @@ import '../util.dart'; /// anything with it. /// /// Throws a [LibGit2Error] if error occured. -Pointer> newConfig() { +Pointer newConfig() { final out = calloc>(); final error = libgit2.git_config_new(out); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; } - - return out; } /// Create a new config instance containing a single on-disk file /// /// Throws a [LibGit2Error] if error occured. -Pointer> open(String path) { +Pointer open(String path) { final out = calloc>(); final pathC = path.toNativeUtf8().cast(); final error = libgit2.git_config_open_ondisk(out, pathC); @@ -32,9 +32,9 @@ Pointer> open(String path) { if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; } - - return out; } /// Open the global, XDG and system configuration files @@ -44,15 +44,15 @@ Pointer> open(String path) { /// be used when accessing default config data outside a repository. /// /// Throws a [LibGit2Error] if error occured. -Pointer> openDefault() { +Pointer openDefault() { final out = calloc>(); final error = libgit2.git_config_open_default(out); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; } - - return out; } /// Locate the path to the global configuration file @@ -67,18 +67,18 @@ Pointer> openDefault() { /// This method will not guess the path to the xdg compatible config file /// (`.config/git/config`). /// -/// Throws a [LibGit2Error] if error occured. +/// Throws an error if file has not been found. String findGlobal() { - final out = calloc(); + final out = calloc(2); final error = libgit2.git_config_find_global(out); final path = out.ref.ptr.cast().toDartString(); calloc.free(out); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); + if (error != 0) { + throw Error(); + } else { + return path; } - - return path; } /// Locate the path to the system configuration file @@ -94,9 +94,9 @@ String findSystem() { if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); + } else { + return path; } - - return path; } /// Locate the path to the global xdg compatible configuration file @@ -113,9 +113,27 @@ String findXdg() { if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); + } else { + return path; } +} - return path; +/// Create a snapshot of the configuration. +/// +/// Create a snapshot of the current state of a configuration, which allows you to look +/// into a consistent view of the configuration for looking up complex values +/// (e.g. a remote, submodule). +/// +/// Throws a [LibGit2Error] if error occured. +Pointer snapshot(Pointer config) { + final out = calloc>(); + final error = libgit2.git_config_snapshot(out, config); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } } /// Get the value of a config variable. @@ -131,9 +149,9 @@ String getValue(Pointer cfg, String variable) { if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); + } else { + return value.ref.value.cast().toDartString(); } - - return value.ref.value.cast().toDartString(); } /// Set the value of a boolean config variable in the config file with the diff --git a/lib/src/bindings/repository.dart b/lib/src/bindings/repository.dart index 45b9ae4..80ef458 100644 --- a/lib/src/bindings/repository.dart +++ b/lib/src/bindings/repository.dart @@ -192,6 +192,44 @@ Map identity(Pointer repo) { return identity; } +/// Get the configuration file for this repository. +/// +/// If a configuration file has not been set, the default config set for the repository +/// will be returned, including global and system configurations (if they are available). +/// +/// The configuration file must be freed once it's no longer being used by the user. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer config(Pointer repo) { + final out = calloc>(); + final error = libgit2.git_repository_config(out, repo); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Get a snapshot of the repository's configuration. +/// +/// Convenience function to take a snapshot from the repository's configuration. +/// The contents of this snapshot will not change, even if the underlying config files are modified. +/// +/// The configuration file must be freed once it's no longer being used by the user. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer configSnapshot(Pointer repo) { + final out = calloc>(); + final error = libgit2.git_repository_config_snapshot(out, repo); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + /// Get the Index file for this repository. /// /// If a custom index has not been set, the default index for the repository diff --git a/lib/src/config.dart b/lib/src/config.dart index 5f5973f..79ec674 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -7,9 +7,13 @@ import 'util.dart'; /// [Config] provides management of global configuration options /// (system, global, XDG, excluding repository config) class Config { - Config(); + /// Initializes a new instance of [Config] class from provided + /// pointer to config object in memory. + Config(this._configPointer) { + libgit2.git_libgit2_init(); + } - /// Initializes a new instance of [Config] class. + /// Initializes a new instance of [Config] class from provided [path]. /// /// If [path] isn't provided, opens global, XDG and system config files. /// @@ -17,14 +21,14 @@ class Config { /// Git config file following the default Git config syntax (see man git-config). /// /// [Config] object should be closed with [free] function to release allocated memory. - Config.open({this.path}) { + Config.open([String? path]) { libgit2.git_libgit2_init(); if (path == null) { - _configPointer = bindings.openDefault().value; + _configPointer = bindings.openDefault(); } else { - if (File(path!).existsSync()) { - _configPointer = bindings.open(path!).value; + if (File(path).existsSync()) { + _configPointer = bindings.open(path); } else { throw Exception('File not found'); } @@ -39,30 +43,20 @@ class Config { Config.system() { libgit2.git_libgit2_init(); - try { - final systemPath = bindings.findSystem(); - _configPointer = bindings.open(systemPath).value; - } catch (e) { - _configPointer = nullptr; - rethrow; - } + final systemPath = bindings.findSystem(); + _configPointer = bindings.open(systemPath); } /// Initializes a new instance of [Config] class. /// /// Opens the global configuration file. /// - /// Throws a [LibGit2Error] if error occured. + /// Throws an error if file has not been found. Config.global() { libgit2.git_libgit2_init(); - try { - final globalPath = bindings.findGlobal(); - _configPointer = bindings.open(globalPath).value; - } catch (e) { - _configPointer = nullptr; - rethrow; - } + final globalPath = bindings.findGlobal(); + _configPointer = bindings.open(globalPath); } /// Initializes a new instance of [Config] class. @@ -73,21 +67,22 @@ class Config { Config.xdg() { libgit2.git_libgit2_init(); - try { - final xdgPath = bindings.findXdg(); - _configPointer = bindings.open(xdgPath).value; - } catch (e) { - _configPointer = nullptr; - rethrow; - } + final xdgPath = bindings.findXdg(); + _configPointer = bindings.open(xdgPath); } - /// Path to on-disk config file provided by user. - String? path; - /// Pointer to memory address for allocated config object. late final Pointer _configPointer; + /// Create a snapshot of the configuration. + /// + /// Create a snapshot of the current state of a configuration, which allows you to look + /// into a consistent view of the configuration for looking up complex values + /// (e.g. a remote, submodule). + /// + /// Throws a [LibGit2Error] if error occured. + Config get snapshot => Config(bindings.snapshot(_configPointer)); + /// Returns map of all the config variables and their values. Map getEntries() { return bindings.getEntries(_configPointer); diff --git a/lib/src/repository.dart b/lib/src/repository.dart index dc6fc9b..e43c9f7 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -1,4 +1,5 @@ import 'dart:ffi'; +import 'config.dart'; import 'index.dart'; import 'odb.dart'; import 'oid.dart'; @@ -188,6 +189,26 @@ class Repository { libgit2.git_libgit2_shutdown(); } + /// Returns the configuration file for this repository. + /// + /// If a configuration file has not been set, the default config set for the repository + /// will be returned, including global and system configurations (if they are available). + /// + /// The configuration file must be freed once it's no longer being used by the user. + /// + /// Throws a [LibGit2Error] if error occured. + Config get config => Config(bindings.config(_repoPointer)); + + /// Returns a snapshot of the repository's configuration. + /// + /// Convenience function to take a snapshot from the repository's configuration. + /// The contents of this snapshot will not change, even if the underlying config files are modified. + /// + /// The configuration file must be freed once it's no longer being used by the user. + /// + /// Throws a [LibGit2Error] if error occured. + Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer)); + /// Returns [Reference] object pointing to repository head. /// /// Must be freed once it's no longer being used. diff --git a/test/config_test.dart b/test/config_test.dart index 0043308..9fe02b0 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -22,7 +22,7 @@ void main() { group('Config', () { setUp(() { File('$tmpDir/$configFileName').writeAsStringSync(contents); - config = Config.open(path: '$tmpDir/$configFileName'); + config = Config.open('$tmpDir/$configFileName'); }); tearDown(() { diff --git a/test/repository_test.dart b/test/repository_test.dart index ce16cc0..beff60c 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -141,6 +141,14 @@ void main() { await Directory(tmpDir).delete(recursive: true); }); + test('returns config for repository', () { + final config = repo.config; + expect(config.getValue('remote.origin.url'), + 'git://github.com/SkinnyMind/libgit2dart.git'); + + config.free(); + }); + test('returns empty string when there is no namespace', () { expect(repo.namespace, isEmpty); });