feat(repository): add ability to get repository config

This commit is contained in:
Aleksey Kulikov 2021-08-12 20:00:25 +03:00
parent 0194d7c361
commit 594aca6474
7 changed files with 145 additions and 54 deletions

View file

@ -1,6 +1,14 @@
import 'dart:io';
import 'package:libgit2dart/libgit2dart.dart'; 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. // Open system + global config file.
final config = Config.open(); final config = Config.open();
@ -15,7 +23,7 @@ void main() {
// Open config file at provided path. // Open config file at provided path.
// Exception is thrown if file not found. // Exception is thrown if file not found.
try { try {
final repoConfig = Config.open(path: '.git/config'); final repoConfig = Config.open('$tmpDir/.git/config');
print('\nAll entries of repo config:'); print('\nAll entries of repo config:');
final entries = repoConfig.getEntries(); final entries = repoConfig.getEntries();
@ -49,4 +57,7 @@ void main() {
} catch (e) { } catch (e) {
print('\n$e'); print('\n$e');
} }
// Removing example repository.
await disposeRepo(tmpDir);
} }

View file

@ -10,21 +10,21 @@ import '../util.dart';
/// anything with it. /// anything with it.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Pointer<Pointer<git_config>> newConfig() { Pointer<git_config> newConfig() {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
final error = libgit2.git_config_new(out); final error = libgit2.git_config_new(out);
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
} }
return out;
} }
/// Create a new config instance containing a single on-disk file /// Create a new config instance containing a single on-disk file
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Pointer<Pointer<git_config>> open(String path) { Pointer<git_config> open(String path) {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
final pathC = path.toNativeUtf8().cast<Int8>(); final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_config_open_ondisk(out, pathC); final error = libgit2.git_config_open_ondisk(out, pathC);
@ -32,9 +32,9 @@ Pointer<Pointer<git_config>> open(String path) {
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
} }
return out;
} }
/// Open the global, XDG and system configuration files /// Open the global, XDG and system configuration files
@ -44,15 +44,15 @@ Pointer<Pointer<git_config>> open(String path) {
/// be used when accessing default config data outside a repository. /// be used when accessing default config data outside a repository.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Pointer<Pointer<git_config>> openDefault() { Pointer<git_config> openDefault() {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
final error = libgit2.git_config_open_default(out); final error = libgit2.git_config_open_default(out);
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
} }
return out;
} }
/// Locate the path to the global configuration file /// Locate the path to the global configuration file
@ -67,18 +67,18 @@ Pointer<Pointer<git_config>> openDefault() {
/// This method will not guess the path to the xdg compatible config file /// This method will not guess the path to the xdg compatible config file
/// (`.config/git/config`). /// (`.config/git/config`).
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws an error if file has not been found.
String findGlobal() { String findGlobal() {
final out = calloc<git_buf>(); final out = calloc<git_buf>(2);
final error = libgit2.git_config_find_global(out); final error = libgit2.git_config_find_global(out);
final path = out.ref.ptr.cast<Utf8>().toDartString(); final path = out.ref.ptr.cast<Utf8>().toDartString();
calloc.free(out); calloc.free(out);
if (error < 0) { if (error != 0) {
throw LibGit2Error(libgit2.git_error_last()); throw Error();
} } else {
return path; return path;
}
} }
/// Locate the path to the system configuration file /// Locate the path to the system configuration file
@ -94,9 +94,9 @@ String findSystem() {
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} } else {
return path; return path;
}
} }
/// Locate the path to the global xdg compatible configuration file /// Locate the path to the global xdg compatible configuration file
@ -113,9 +113,27 @@ String findXdg() {
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); 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<git_config> snapshot(Pointer<git_config> config) {
final out = calloc<Pointer<git_config>>();
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. /// Get the value of a config variable.
@ -131,9 +149,9 @@ String getValue(Pointer<git_config> cfg, String variable) {
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());
} } else {
return value.ref.value.cast<Utf8>().toDartString(); return value.ref.value.cast<Utf8>().toDartString();
}
} }
/// Set the value of a boolean config variable in the config file with the /// Set the value of a boolean config variable in the config file with the

View file

@ -192,6 +192,44 @@ Map<String, String> identity(Pointer<git_repository> repo) {
return identity; 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<git_config> config(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_config>>();
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<git_config> configSnapshot(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_config>>();
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. /// Get the Index file for this repository.
/// ///
/// If a custom index has not been set, the default index for the repository /// If a custom index has not been set, the default index for the repository

View file

@ -7,9 +7,13 @@ import 'util.dart';
/// [Config] provides management of global configuration options /// [Config] provides management of global configuration options
/// (system, global, XDG, excluding repository config) /// (system, global, XDG, excluding repository config)
class 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. /// 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). /// 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] object should be closed with [free] function to release allocated memory.
Config.open({this.path}) { Config.open([String? path]) {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
if (path == null) { if (path == null) {
_configPointer = bindings.openDefault().value; _configPointer = bindings.openDefault();
} else { } else {
if (File(path!).existsSync()) { if (File(path).existsSync()) {
_configPointer = bindings.open(path!).value; _configPointer = bindings.open(path);
} else { } else {
throw Exception('File not found'); throw Exception('File not found');
} }
@ -39,30 +43,20 @@ class Config {
Config.system() { Config.system() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
try {
final systemPath = bindings.findSystem(); final systemPath = bindings.findSystem();
_configPointer = bindings.open(systemPath).value; _configPointer = bindings.open(systemPath);
} catch (e) {
_configPointer = nullptr;
rethrow;
}
} }
/// Initializes a new instance of [Config] class. /// Initializes a new instance of [Config] class.
/// ///
/// Opens the global configuration file. /// Opens the global configuration file.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws an error if file has not been found.
Config.global() { Config.global() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
try {
final globalPath = bindings.findGlobal(); final globalPath = bindings.findGlobal();
_configPointer = bindings.open(globalPath).value; _configPointer = bindings.open(globalPath);
} catch (e) {
_configPointer = nullptr;
rethrow;
}
} }
/// Initializes a new instance of [Config] class. /// Initializes a new instance of [Config] class.
@ -73,21 +67,22 @@ class Config {
Config.xdg() { Config.xdg() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
try {
final xdgPath = bindings.findXdg(); final xdgPath = bindings.findXdg();
_configPointer = bindings.open(xdgPath).value; _configPointer = bindings.open(xdgPath);
} catch (e) {
_configPointer = nullptr;
rethrow;
} }
}
/// Path to on-disk config file provided by user.
String? path;
/// Pointer to memory address for allocated config object. /// Pointer to memory address for allocated config object.
late final Pointer<git_config> _configPointer; late final Pointer<git_config> _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. /// Returns map of all the config variables and their values.
Map<String, String> getEntries() { Map<String, String> getEntries() {
return bindings.getEntries(_configPointer); return bindings.getEntries(_configPointer);

View file

@ -1,4 +1,5 @@
import 'dart:ffi'; import 'dart:ffi';
import 'config.dart';
import 'index.dart'; import 'index.dart';
import 'odb.dart'; import 'odb.dart';
import 'oid.dart'; import 'oid.dart';
@ -188,6 +189,26 @@ class Repository {
libgit2.git_libgit2_shutdown(); 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. /// Returns [Reference] object pointing to repository head.
/// ///
/// Must be freed once it's no longer being used. /// Must be freed once it's no longer being used.

View file

@ -22,7 +22,7 @@ void main() {
group('Config', () { group('Config', () {
setUp(() { setUp(() {
File('$tmpDir/$configFileName').writeAsStringSync(contents); File('$tmpDir/$configFileName').writeAsStringSync(contents);
config = Config.open(path: '$tmpDir/$configFileName'); config = Config.open('$tmpDir/$configFileName');
}); });
tearDown(() { tearDown(() {

View file

@ -141,6 +141,14 @@ void main() {
await Directory(tmpDir).delete(recursive: true); 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', () { test('returns empty string when there is no namespace', () {
expect(repo.namespace, isEmpty); expect(repo.namespace, isEmpty);
}); });