Compare commits

...

22 commits

Author SHA1 Message Date
Aleksey Kulikov
b66662f33c chore: v1.2.2 2023-02-03 12:54:44 +03:00
Aleksey Kulikov
e1ca7e81a2 chore: remove coverage badge 2023-02-02 16:20:28 +03:00
Aleksey Kulikov
7c6060b02c
test(credentials): enable some of the previously disabled tests for Linux (#82) 2023-02-02 16:15:24 +03:00
Aleksey Kulikov
ed67a4e307
fix: lookup package in correct location of Dart/Flutter cached packages (#81) 2023-02-02 15:29:31 +03:00
Aleksey Kulikov
675872c3ef chore: v1.2.1 2022-12-24 19:09:16 +03:00
Aleksey Kulikov
fa946c5ce3
fix: use default location of Flutter's '.pub_cache' folder (#79) 2022-12-24 19:01:37 +03:00
Aleksey Kulikov
34d492a9b6 docs: update README with troubleshooting instructions for Windows 2022-11-22 15:27:25 +03:00
Aleksey Kulikov
ef9390d08b
chore: bump minimum dart and flutter versions (#76) 2022-09-16 16:45:31 +03:00
Dillon Nys
d55742d6d7
chore: Lookup library in system path (#75) 2022-08-10 19:43:28 +03:00
Aleksey Kulikov
07e83a1f41 chore: v1.2.0 2022-07-15 13:23:22 +03:00
Aleksey Kulikov
a41fc55099
feat: upgrade libgit2 to 1.5.0 (#74) 2022-07-15 13:10:42 +03:00
Aleksey Kulikov
016370625b style: stricter linting 2022-07-05 18:00:35 +03:00
Aleksey Kulikov
4a36922129
feat(reset): add ability to pass checkout options to reset(...) API method (#72) 2022-06-23 18:37:27 +03:00
Aleksey Kulikov
5f829dd1ca
feat(worktree): add ability to pass options to prune(...) API method (#71) 2022-06-18 11:54:46 +03:00
Aleksey Kulikov
2daadaa9a4
style: annotate declarations for internal use (#70) 2022-06-18 11:17:14 +03:00
Aleksey Kulikov
d71e00947e
feat(merge): add ability to pass options to fileFromIndex(...) API method (#69) 2022-06-17 15:40:19 +03:00
Aleksey Kulikov
aa073c531e
feat(index): add ability to pass options to addAll(...) API method (#68) 2022-06-17 14:52:46 +03:00
Aleksey Kulikov
d113af44b5
feat(commit): add ability to pass options to revert(...) and revertTo(...) API methods (#67)
- select parent to revert to for merge commits
- merge options
- checkout options
2022-06-17 12:43:54 +03:00
Aleksey Kulikov
75687c469c chore: upgrade dependencies 2022-06-17 10:16:43 +03:00
Aleksey Kulikov
aa74c46a12 chore: v1.1.2 2022-06-09 18:02:49 +03:00
Aleksey Kulikov
6be34fe9a0
fix: lookup library in Flutter's .pub_cache folder (#66) 2022-06-09 17:56:27 +03:00
Aleksey Kulikov
48e2240c73
feat(revwalk): add ability to limit number of commits to walk (#65) 2022-06-09 17:15:55 +03:00
57 changed files with 798 additions and 157 deletions

View file

@ -1,3 +1,43 @@
## 1.2.2
- fix: lookup package in correct location of Dart/Flutter cached packages
## 1.2.1
- fix: use default location of Flutter's '.pub_cache' folder
- docs: update README with troubleshooting instructions for Windows
- chore: bump minimum dart and flutter versions
- chore: Lookup library in system path (thanks @dnys1)
## 1.2.0
- feat: upgrade libgit2 to 1.5.0
- feat: add ability to pass checkout options to `reset(...)` API method
- feat: add ability to pass options to `prune(...)` Worktree API method
- feat: add ability to pass options to `Merge.fileFromIndex(...)` API method
- feat: add ability to pass options to `addAll(...)` Index API method
- feat: add ability to pass options to `revert(...)` and `revertTo(...)` Commit API methods:
- select parent to revert to for merge commits
- merge options
- checkout options
- chore: upgrade dependencies
## 1.1.2
- fix: lookup library in Flutter's .pub_cache folder
- feat: add ability to limit number of commits to walk in revision walk
## 1.1.1
- fix: lookup library in correct locations

View file

@ -1,7 +1,5 @@
# libgit2dart
![Coverage](coverage_badge.svg)
**Dart bindings to libgit2**
libgit2dart package provides ability to use [libgit2](https://github.com/libgit2/libgit2) in Dart/Flutter.
@ -665,6 +663,8 @@ Fork libgit2dart, improve libgit2dart, send a pull request.
### Troubleshooting
#### Linux:
If you are developing on Linux using non-Debian based distrib you might encounter these errors:
- Failed to load dynamic library: libpcre.so.3: cannot open shared object file: No such file or directory
@ -679,6 +679,16 @@ sudo ln -s /usr/lib64/libpcre.so /usr/lib64/libpcre.so.3
sudo ln -s /usr/lib64/libpcreposix.so /usr/lib64/libpcreposix.so.3
```
#### Windows:
If you are developing on Windows you might encounter:
- Failed to load dynamic library: error code 126
That happens because libgit2 dynamic library bundled with libgit2dart package is precompiled with ssh support, and it fails to find the `libssh2.dll`.
To fix that error you should [build](https://github.com/libssh2/libssh2/blob/master/docs/INSTALL_CMAKE.md) libssh2, and place resulting `libssh2.dll` somewhere in system path (e.g. "Windows\System32").
### Ffigen
To generate bindings with ffigen use (adjust paths to yours):
@ -693,7 +703,6 @@ To run all tests and generate coverage report make sure to have activated packag
```sh
$ dart pub global activate coverage
$ dart pub global activate flutter_coverage_badge
```
And run:

View file

@ -4,6 +4,7 @@ analyzer:
language:
strict-casts: true
strict-raw-types: true
strict-inference: true
exclude:
- lib/src/bindings/libgit2_bindings.dart

View file

@ -5,7 +5,6 @@ import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/libgit2.dart';
import 'package:libgit2dart/src/util.dart';
import 'package:path/path.dart' as path;
import 'package:pub_cache/pub_cache.dart';
/// Copies prebuilt libgit2 library from package in '.pub_cache' into correct
/// directory for [platform].
@ -23,28 +22,25 @@ Future<void> copyLibrary(String platform) async {
);
}
} else {
String? checkCache(PubCache pubCache) =>
pubCache.getLatestVersion('libgit2dart')?.resolve()?.location.path;
final libPath = checkCache(PubCache()) ??
checkCache(
PubCache(
Directory(
path.join(Platform.environment['FLUTTER_ROOT']!, '.pub-cache'),
),
),
);
final libPath = checkCache();
final libName = getLibName();
stdout.writeln('Copying libgit2 for $platform');
if (libPath == null) {
stdout.writeln(
"Couldn't find libgit2dart package.\n"
"Make sure to run 'dart pub get' to resolve dependencies.",
);
} else {
final destination = path.join(libDir, platform);
Directory(destination).createSync(recursive: true);
File(path.join(libPath!, platform, libName)).copySync(
File(path.join(libPath, platform, libName)).copySync(
path.join(destination, libName),
);
stdout.writeln('Done! libgit2 for $platform is now available!');
}
}
}
class CleanCommand extends Command<void> {
@ -57,8 +53,10 @@ class CleanCommand extends Command<void> {
@override
void run() {
stdout.writeln('Cleaning...');
if (Directory(libDir).existsSync()) {
Directory(libDir).deleteSync(recursive: true);
}
}
}
void main(List<String> args) {

View file

@ -1,2 +1,2 @@
#!/bin/bash
dart test --coverage=coverage --test-randomize-ordering-seed random && dart pub global run coverage:format_coverage --lcov --check-ignore --in=coverage --out=coverage/lcov.info --report-on=lib && genhtml coverage/lcov.info -o coverage/ && dart pub global run flutter_coverage_badge
dart test --coverage=coverage --test-randomize-ordering-seed random && dart pub global run coverage:format_coverage --lcov --check-ignore --in=coverage --out=coverage/lcov.info --report-on=lib && genhtml coverage/lcov.info -o coverage/

View file

@ -1,20 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="102" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="a">
<rect width="102" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#a)">
<path fill="#555" d="M0 0h59v20H0z"/>
<path fill="#44cc11" d="M59 0h43v20H59z"/>
<path fill="url(#b)" d="M0 0h102v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
<text x="305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">coverage</text>
<text x="305" y="140" transform="scale(.1)" textLength="490">coverage</text>
<text x="795" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">100%</text>
<text x="795" y="140" transform="scale(.1)" textLength="330">100%</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1 KiB

View file

@ -84,6 +84,7 @@ class AnnotatedCommit extends Equatable {
/// Pointer to pointer to memory address for allocated commit object.
///
/// Note: For internal use.
@internal
Pointer<git_annotated_commit> get pointer => _annotatedCommitPointer;
/// Commit oid that the given annotated commit refers to.

View file

@ -384,8 +384,48 @@ Pointer<git_tree> tree(Pointer<git_commit> commit) {
void revert({
required Pointer<git_repository> repoPointer,
required Pointer<git_commit> commitPointer,
required int mainline,
int? mergeFavor,
int? mergeFlags,
int? mergeFileFlags,
int? checkoutStrategy,
String? checkoutDirectory,
List<String>? checkoutPaths,
}) {
final error = libgit2.git_revert(repoPointer, commitPointer, nullptr);
final opts = calloc<git_revert_options>();
libgit2.git_revert_options_init(opts, GIT_REVERT_OPTIONS_VERSION);
opts.ref.mainline = mainline;
if (mergeFavor != null) opts.ref.merge_opts.file_favor = mergeFavor;
if (mergeFlags != null) opts.ref.merge_opts.flags = mergeFlags;
if (mergeFileFlags != null) opts.ref.merge_opts.file_flags = mergeFileFlags;
if (checkoutStrategy != null) {
opts.ref.checkout_opts.checkout_strategy = checkoutStrategy;
}
if (checkoutDirectory != null) {
opts.ref.checkout_opts.target_directory = checkoutDirectory.toChar();
}
var pathPointers = <Pointer<Char>>[];
Pointer<Pointer<Char>> strArray = nullptr;
if (checkoutPaths != null) {
pathPointers = checkoutPaths.map((e) => e.toChar()).toList();
strArray = calloc(checkoutPaths.length);
for (var i = 0; i < checkoutPaths.length; i++) {
strArray[i] = pathPointers[i];
}
opts.ref.checkout_opts.paths.strings = strArray;
opts.ref.checkout_opts.paths.count = checkoutPaths.length;
}
final error = libgit2.git_revert(repoPointer, commitPointer, opts);
for (final p in pathPointers) {
calloc.free(p);
}
calloc.free(strArray);
calloc.free(opts);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
@ -403,11 +443,18 @@ Pointer<git_index> revertCommit({
required Pointer<git_commit> revertCommitPointer,
required Pointer<git_commit> ourCommitPointer,
required int mainline,
int? mergeFavor,
int? mergeFlags,
int? mergeFileFlags,
}) {
final out = calloc<Pointer<git_index>>();
final opts = calloc<git_merge_options>();
libgit2.git_merge_options_init(opts, GIT_MERGE_OPTIONS_VERSION);
if (mergeFavor != null) opts.ref.file_favor = mergeFavor;
if (mergeFlags != null) opts.ref.flags = mergeFlags;
if (mergeFileFlags != null) opts.ref.file_flags = mergeFileFlags;
final error = libgit2.git_revert_commit(
out,
repoPointer,

View file

@ -292,6 +292,7 @@ void addFromBuffer({
void addAll({
required Pointer<git_index> indexPointer,
required List<String> pathspec,
required int flags,
}) {
final pathspecC = calloc<git_strarray>();
final pathPointers = pathspec.map((e) => e.toChar()).toList();
@ -307,7 +308,7 @@ void addAll({
final error = libgit2.git_index_add_all(
indexPointer,
pathspecC,
0,
flags,
nullptr,
nullptr,
);

View file

@ -2217,6 +2217,23 @@ class Libgit2 {
int Function(
ffi.Pointer<ffi.Int>, ffi.Pointer<ffi.Int>, ffi.Pointer<ffi.Int>)>();
/// Return the prerelease state of the libgit2 library currently being
/// used. For nightly builds during active development, this will be
/// "alpha". Releases may have a "beta" or release candidate ("rc1",
/// "rc2", etc) prerelease. For a final release, this function returns
/// NULL.
///
/// @return the name of the prerelease state or NULL
ffi.Pointer<ffi.Char> git_libgit2_prerelease() {
return _git_libgit2_prerelease();
}
late final _git_libgit2_prereleasePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
'git_libgit2_prerelease');
late final _git_libgit2_prerelease =
_git_libgit2_prereleasePtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
/// Query compile time options for libgit2.
///
/// @return A combination of GIT_FEATURE_* values.
@ -9459,8 +9476,8 @@ class Libgit2 {
/// See `git_tag_create()` for rules about valid names.
///
/// Note that if the move succeeds, the old reference object will not
/// + be valid anymore, and should be freed immediately by the user using
/// + `git_reference_free()`.
/// be valid anymore, and should be freed immediately by the user using
/// `git_reference_free()`.
///
/// @param out New reference object for the updated name.
///
@ -11868,7 +11885,7 @@ class Libgit2 {
/// completes, resolve any conflicts and prepare a commit.
///
/// For compatibility with git, the repository is put into a merging
/// state. Once the commit is done (or if the uses wishes to abort),
/// state. Once the commit is done (or if the user wishes to abort),
/// you should clear this state by calling
/// `git_repository_state_cleanup()`.
///
@ -15441,7 +15458,7 @@ class Libgit2 {
/// global configuration file.
///
/// This method will not guess the path to the xdg compatible
/// config file (.config/git/config).
/// config file (`.config/git/config`).
///
/// @param out Pointer to a user-allocated git_buf in which to store the path
/// @return 0 if a global configuration file has been found. Its path will be stored in `out`.
@ -15488,8 +15505,8 @@ class Libgit2 {
/// Locate the path to the system configuration file
///
/// If /etc/gitconfig doesn't exist, it will look for
/// %PROGRAMFILES%\Git\etc\gitconfig.
/// If `/etc/gitconfig` doesn't exist, it will look for
/// `%PROGRAMFILES%\Git\etc\gitconfig`.
///
/// @param out Pointer to a user-allocated git_buf in which to store the path
/// @return 0 if a system configuration file has been
@ -15510,7 +15527,7 @@ class Libgit2 {
/// Locate the path to the configuration file in ProgramData
///
/// Look for the file in %PROGRAMDATA%\Git\config used by portable git.
/// Look for the file in `%PROGRAMDATA%\Git\config` used by portable git.
///
/// @param out Pointer to a user-allocated git_buf in which to store the path
/// @return 0 if a ProgramData configuration file has been
@ -16083,8 +16100,8 @@ class Libgit2 {
/// Return the current entry and advance the iterator
///
/// The pointers returned by this function are valid until the iterator
/// is freed.
/// The pointers returned by this function are valid until the next call
/// to `git_config_next` or until the iterator is freed.
///
/// @param entry pointer to store the entry
/// @param iter the iterator
@ -16922,7 +16939,8 @@ class Libgit2 {
late final _git_error_clear =
_git_error_clearPtr.asFunction<void Function()>();
/// Set the error message string for this thread.
/// Set the error message string for this thread, using `printf`-style
/// formatting.
///
/// This function is public so that custom ODB backends and the like can
/// relay an error message through libgit2. Most regular users of libgit2
@ -16935,7 +16953,31 @@ class Libgit2 {
///
/// @param error_class One of the `git_error_t` enum above describing the
/// general subsystem that is responsible for the error.
/// @param string The formatted error message to keep
/// @param fmt The `printf`-style format string; subsequent arguments must
/// be the arguments for the format string.
void git_error_set(
int error_class,
ffi.Pointer<ffi.Char> fmt,
) {
return _git_error_set(
error_class,
fmt,
);
}
late final _git_error_setPtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(ffi.Int, ffi.Pointer<ffi.Char>)>>('git_error_set');
late final _git_error_set =
_git_error_setPtr.asFunction<void Function(int, ffi.Pointer<ffi.Char>)>();
/// Set the error message string for this thread. This function is like
/// `git_error_set` but takes a static string instead of a `printf`-style
/// format.
///
/// @param error_class One of the `git_error_t` enum above describing the
/// general subsystem that is responsible for the error.
/// @param string The error message to keep
/// @return 0 on success or -1 on failure
int git_error_set_str(
int error_class,
@ -30687,7 +30729,7 @@ abstract class git_error_t {
static const int GIT_ERROR_FILESYSTEM = 30;
static const int GIT_ERROR_PATCH = 31;
static const int GIT_ERROR_WORKTREE = 32;
static const int GIT_ERROR_SHA1 = 33;
static const int GIT_ERROR_SHA = 33;
static const int GIT_ERROR_HTTP = 34;
static const int GIT_ERROR_INTERNAL = 35;
}
@ -31200,12 +31242,15 @@ class git_status_options extends ffi.Struct {
external int version;
/// The `show` value is one of the `git_status_show_t` constants that
/// control which files to scan and in what order.
/// control which files to scan and in what order. The default is
/// `GIT_STATUS_SHOW_INDEX_AND_WORKDIR`.
@ffi.Int32()
external int show1;
/// The `flags` value is an OR'ed combination of the
/// `git_status_opt_t` values above.
/// `git_status_opt_t` values above. The default is
/// `GIT_STATUS_OPT_DEFAULTS`, which matches git's default
/// behavior.
@ffi.UnsignedInt()
external int flags;
@ -32606,6 +32651,8 @@ const int GITERR_WORKTREE = 32;
const int GITERR_SHA1 = 33;
const int GIT_ERROR_SHA1 = 33;
const int GIT_IDXENTRY_NAMEMASK = 4095;
const int GIT_IDXENTRY_STAGEMASK = 12288;
@ -32710,14 +32757,16 @@ const int GIT_CREDTYPE_SSH_MEMORY = 64;
const int GIT_EMAIL_CREATE_OPTIONS_VERSION = 1;
const String LIBGIT2_VERSION = '1.4.3';
const String LIBGIT2_VERSION = '1.5.0';
const int LIBGIT2_VER_MAJOR = 1;
const int LIBGIT2_VER_MINOR = 4;
const int LIBGIT2_VER_MINOR = 5;
const int LIBGIT2_VER_REVISION = 3;
const int LIBGIT2_VER_REVISION = 0;
const int LIBGIT2_VER_PATCH = 0;
const String LIBGIT2_SOVERSION = '1.4';
const int LIBGIT2_VER_PRERELEASE = 0;
const String LIBGIT2_SOVERSION = '1.5';

View file

@ -186,30 +186,39 @@ String mergeFile({
libgit2.git_merge_file_input_init(theirsC, GIT_MERGE_FILE_INPUT_VERSION);
ancestorC.ref.ptr = ancestor.toChar();
ancestorC.ref.size = ancestor.length;
Pointer<Char> ancestorLabelC = nullptr;
oursC.ref.ptr = ours.toChar();
oursC.ref.size = ours.length;
Pointer<Char> oursLabelC = nullptr;
theirsC.ref.ptr = theirs.toChar();
theirsC.ref.size = theirs.length;
Pointer<Char> theirsLabelC = nullptr;
final opts = calloc<git_merge_file_options>();
libgit2.git_merge_file_options_init(opts, GIT_MERGE_FILE_OPTIONS_VERSION);
opts.ref.favor = favor;
opts.ref.flags = flags;
if (ancestorLabel.isNotEmpty) {
opts.ref.ancestor_label = ancestorLabel.toChar();
ancestorLabelC = ancestorLabel.toChar();
opts.ref.ancestor_label = ancestorLabelC;
}
if (oursLabel.isNotEmpty) {
opts.ref.our_label = oursLabel.toChar();
oursLabelC = oursLabel.toChar();
opts.ref.our_label = oursLabelC;
}
if (theirsLabel.isNotEmpty) {
opts.ref.their_label = theirsLabel.toChar();
theirsLabelC = theirsLabel.toChar();
opts.ref.their_label = theirsLabelC;
}
libgit2.git_merge_file(out, ancestorC, oursC, theirsC, opts);
calloc.free(ancestorC);
calloc.free(ancestorLabelC);
calloc.free(oursC);
calloc.free(oursLabelC);
calloc.free(theirsC);
calloc.free(theirsLabelC);
calloc.free(opts);
final result = out.ref.ptr.toDartString(length: out.ref.len);
@ -226,17 +235,43 @@ String mergeFile({
String mergeFileFromIndex({
required Pointer<git_repository> repoPointer,
required Pointer<git_index_entry>? ancestorPointer,
required String ancestorLabel,
required Pointer<git_index_entry> oursPointer,
required String oursLabel,
required Pointer<git_index_entry> theirsPointer,
required String theirsLabel,
required int favor,
required int flags,
}) {
final out = calloc<git_merge_file_result>();
final opts = calloc<git_merge_file_options>();
Pointer<Char> ancestorLabelC = nullptr;
Pointer<Char> oursLabelC = nullptr;
Pointer<Char> theirsLabelC = nullptr;
libgit2.git_merge_file_options_init(opts, GIT_MERGE_FILE_OPTIONS_VERSION);
opts.ref.favor = favor;
opts.ref.flags = flags;
if (ancestorLabel.isNotEmpty) {
ancestorLabelC = ancestorLabel.toChar();
opts.ref.ancestor_label = ancestorLabelC;
}
if (oursLabel.isNotEmpty) {
oursLabelC = oursLabel.toChar();
opts.ref.our_label = oursLabelC;
}
if (theirsLabel.isNotEmpty) {
theirsLabelC = theirsLabel.toChar();
opts.ref.their_label = theirsLabelC;
}
final error = libgit2.git_merge_file_from_index(
out,
repoPointer,
ancestorPointer ?? nullptr,
oursPointer,
theirsPointer,
nullptr,
opts,
);
late final String result;
@ -244,6 +279,10 @@ String mergeFileFromIndex({
result = out.ref.ptr.toDartString(length: out.ref.len);
}
calloc.free(ancestorLabelC);
calloc.free(oursLabelC);
calloc.free(theirsLabelC);
calloc.free(opts);
calloc.free(out);
if (error < 0) {

View file

@ -17,13 +17,48 @@ import 'package:libgit2dart/src/util.dart';
/// HARD reset will trigger a MIXED reset and the working directory will be
/// replaced with the content of the index. (Untracked and ignored files will
/// be left alone, however.)
///
/// Throws a [LibGit2Error] if error occured.
void reset({
required Pointer<git_repository> repoPointer,
required Pointer<git_object> targetPointer,
required int resetType,
required Pointer<git_checkout_options> checkoutOptsPointer,
int? strategy,
String? checkoutDirectory,
List<String>? pathspec,
}) {
libgit2.git_reset(repoPointer, targetPointer, resetType, checkoutOptsPointer);
final opts = calloc<git_checkout_options>();
libgit2.git_checkout_options_init(opts, GIT_CHECKOUT_OPTIONS_VERSION);
if (strategy != null) {
opts.ref.checkout_strategy = strategy;
}
if (checkoutDirectory != null) {
opts.ref.target_directory = checkoutDirectory.toChar();
}
var pathPointers = <Pointer<Char>>[];
Pointer<Pointer<Char>> strArray = nullptr;
if (pathspec != null) {
pathPointers = pathspec.map((e) => e.toChar()).toList();
strArray = calloc(pathspec.length);
for (var i = 0; i < pathspec.length; i++) {
strArray[i] = pathPointers[i];
}
opts.ref.paths.strings = strArray;
opts.ref.paths.count = pathspec.length;
}
final error = libgit2.git_reset(repoPointer, targetPointer, resetType, opts);
for (final p in pathPointers) {
calloc.free(p);
}
calloc.free(strArray);
calloc.free(opts);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
}
/// Updates some entries in the index from the target commit tree.

View file

@ -145,11 +145,12 @@ void pushRange({
List<Pointer<git_commit>> walk({
required Pointer<git_repository> repoPointer,
required Pointer<git_revwalk> walkerPointer,
required int limit,
}) {
final result = <Pointer<git_commit>>[];
var error = 0;
while (error == 0) {
void next() {
final oid = calloc<git_oid>();
error = libgit2.git_revwalk_next(oid, walkerPointer);
if (error == 0) {
@ -158,10 +159,21 @@ List<Pointer<git_commit>> walk({
oidPointer: oid,
);
result.add(commit);
} else {
break;
}
calloc.free(oid);
} else {
calloc.free(oid);
return;
}
}
if (limit == 0) {
while (error == 0) {
next();
}
} else {
for (var i = 0; i < limit; i++) {
next();
}
}
return result;

View file

@ -96,8 +96,16 @@ bool isPrunable(Pointer<git_worktree> wt) {
/// Prune working tree.
///
/// Prune the working tree, that is remove the git data structures on disk.
void prune(Pointer<git_worktree> wt) {
libgit2.git_worktree_prune(wt, nullptr);
void prune({required Pointer<git_worktree> worktreePointer, int? flags}) {
final opts = calloc<git_worktree_prune_options>();
libgit2.git_worktree_prune_options_init(
opts,
GIT_WORKTREE_PRUNE_OPTIONS_VERSION,
);
if (flags != null) opts.ref.flags = flags;
libgit2.git_worktree_prune(worktreePointer, opts);
}
/// List names of linked working trees.

View file

@ -12,6 +12,7 @@ class Blob extends Equatable {
/// blob object in memory.
///
/// Note: For internal use. Use [Blob.lookup] instead.
@internal
Blob(this._blobPointer) {
_finalizer.attach(this, _blobPointer, detach: this);
}
@ -30,6 +31,7 @@ class Blob extends Equatable {
/// Pointer to memory address for allocated blob object.
///
/// Note: For internal use.
@internal
Pointer<git_blob> get pointer => _blobPointer;
/// Creates a new blob from a [content] string and writes it to ODB.

View file

@ -15,6 +15,7 @@ class Branch extends Equatable {
/// Note: For internal use. Instead, use one of:
/// - [Branch.create]
/// - [Branch.lookup]
@internal
Branch(this._branchPointer) {
_finalizer.attach(this, _branchPointer, detach: this);
}
@ -74,6 +75,7 @@ class Branch extends Equatable {
/// Pointer to memory address for allocated branch object.
///
/// Note: For internal use.
@internal
Pointer<git_reference> get pointer => _branchPointer;
/// Returns a list of branches that can be found in a [repo]sitory for

View file

@ -13,6 +13,7 @@ class Commit extends Equatable {
/// commit object in memory.
///
/// Note: For internal use. Use [Commit.lookup] instead.
@internal
Commit(this._commitPointer) {
_finalizer.attach(this, _commitPointer, detach: this);
}
@ -31,6 +32,7 @@ class Commit extends Equatable {
/// Pointer to memory address for allocated commit object.
///
/// Note: For internal use.
@internal
Pointer<git_commit> get pointer => _commitPointer;
/// Creates new commit in the [repo]sitory.
@ -189,23 +191,64 @@ class Commit extends Equatable {
/// Reverts commit, producing changes in the index and working directory.
///
/// [mainline] is parent of the commit if it is a merge (i.e. 1, 2, etc.).
///
/// [mergeFavor] is one of the optional [GitMergeFileFavor] flags for
/// handling conflicting content.
///
/// [mergeFlags] is optional combination of [GitMergeFlag] flags.
///
/// [mergeFileFlags] is optional combination of [GitMergeFileFlag] flags.
///
/// [checkoutStrategy] is optional combination of [GitCheckout] flags.
///
/// [checkoutDirectory] is optional alternative checkout path to workdir.
///
/// [checkoutPaths] is optional list of files to checkout (by default all
/// paths are processed).
///
/// Throws a [LibGit2Error] if error occured.
void revert() {
void revert({
int mainline = 0,
GitMergeFileFavor? mergeFavor,
Set<GitMergeFlag>? mergeFlags,
Set<GitMergeFileFlag>? mergeFileFlags,
Set<GitCheckout>? checkoutStrategy,
String? checkoutDirectory,
List<String>? checkoutPaths,
}) {
bindings.revert(
repoPointer: bindings.owner(_commitPointer),
commitPointer: _commitPointer,
mainline: mainline,
mergeFavor: mergeFavor?.value,
mergeFlags: mergeFlags?.fold(0, (acc, e) => acc! | e.value),
mergeFileFlags: mergeFileFlags?.fold(0, (acc, e) => acc! | e.value),
checkoutStrategy: checkoutStrategy?.fold(0, (acc, e) => acc! | e.value),
checkoutDirectory: checkoutDirectory,
checkoutPaths: checkoutPaths,
);
}
/// Reverts commit against provided [commit], producing an index that
/// reflects the result of the revert.
///
/// [mainline] is parent of the commit if it is a merge (i.e. 1, 2).
/// [mainline] is parent of the commit if it is a merge (i.e. 1, 2, etc.).
///
/// [mergeFavor] is one of the optional [GitMergeFileFavor] flags for
/// handling conflicting content.
///
/// [mergeFlags] is optional combination of [GitMergeFlag] flags.
///
/// [mergeFileFlags] is optional combination of [GitMergeFileFlag] flags.
///
/// Throws a [LibGit2Error] if error occured.
Index revertTo({
required Commit commit,
int mainline = 0,
GitMergeFileFavor? mergeFavor,
Set<GitMergeFlag>? mergeFlags,
Set<GitMergeFileFlag>? mergeFileFlags,
}) {
return Index(
bindings.revertCommit(
@ -213,6 +256,9 @@ class Commit extends Equatable {
revertCommitPointer: _commitPointer,
ourCommitPointer: commit.pointer,
mainline: mainline,
mergeFavor: mergeFavor?.value,
mergeFlags: mergeFlags?.fold(0, (acc, e) => acc! | e.value),
mergeFileFlags: mergeFileFlags?.fold(0, (acc, e) => acc! | e.value),
),
);
}

View file

@ -20,6 +20,7 @@ class Config with IterableMixin<ConfigEntry> {
/// - [Config.system]
/// - [Config.global]
/// - [Config.xdg]
@internal
Config(this._configPointer) {
_finalizer.attach(this, _configPointer, detach: this);
}

View file

@ -21,6 +21,7 @@ class Diff extends Equatable {
/// - [Diff.treeToWorkdirWithIndex]
/// - [Diff.treeToTree]
/// - [Diff.parse]
@internal
Diff(this._diffPointer) {
_finalizer.attach(this, _diffPointer, detach: this);
}
@ -276,6 +277,7 @@ class Diff extends Equatable {
/// Pointer to memory address for allocated diff object.
///
/// Note: For internal use.
@internal
Pointer<git_diff> get pointer => _diffPointer;
/// How many diff records are there in a diff.
@ -471,6 +473,9 @@ final _finalizer = Finalizer<Pointer<git_diff>>(
class DiffDelta extends Equatable {
/// Initializes a new instance of [DiffDelta] class from provided
/// pointer to diff delta object in memory.
///
/// Note: For internal use.
@internal
const DiffDelta(this._diffDeltaPointer);
/// Pointer to memory address for allocated diff delta object.
@ -578,6 +583,7 @@ class DiffStats {
/// pointer to diff stats object in memory.
///
/// Note: For internal use.
@internal
DiffStats(this._diffStatsPointer) {
_statsFinalizer.attach(this, _diffStatsPointer, detach: this);
}

View file

@ -3,9 +3,12 @@
import 'dart:ffi';
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/extensions.dart';
import 'package:meta/meta.dart';
/// Details of the last error that occurred.
class LibGit2Error {
/// Note: For internal use.
@internal
LibGit2Error(this._errorPointer);
final Pointer<git_error> _errorPointer;

View file

@ -1173,3 +1173,37 @@ enum GitBlobFilter {
const GitBlobFilter(this.value);
final int value;
}
/// Flags for APIs that add files matching pathspec.
enum GitIndexAddOption {
defaults(0),
/// Skip the checking of ignore rules.
force(1),
/// Disable glob expansion and force exact matching of files in working
/// directory.
disablePathspecMatch(2),
/// Check that each entry in the pathspec is an exact match to a filename on
/// disk is either not ignored or already in the index.
checkPathspec(4);
const GitIndexAddOption(this.value);
final int value;
}
/// Flags to alter working tree pruning behavior.
enum GitWorktree {
/// Prune working tree even if working tree is valid.
pruneValid(1),
/// Prune working tree even if it is locked.
pruneLocked(2),
/// Prune checked out working tree.
pruneWorkingTree(4);
const GitWorktree(this.value);
final int value;
}

View file

@ -13,6 +13,7 @@ class Index with IterableMixin<IndexEntry> {
/// pointer to index object in memory.
///
/// Note: For internal use.
@internal
Index(this._indexPointer) {
_finalizer.attach(this, _indexPointer, detach: this);
}
@ -31,6 +32,7 @@ class Index with IterableMixin<IndexEntry> {
/// Pointer to memory address for allocated index object.
///
/// Note: For internal use.
@internal
Pointer<git_index> get pointer => _indexPointer;
/// Full path to the index file on disk.
@ -215,9 +217,18 @@ class Index with IterableMixin<IndexEntry> {
/// that matches will be added to the index (either updating an existing
/// entry or adding a new entry).
///
/// [flags] is optional combination of [GitIndexAddOption] flags.
///
/// Throws a [LibGit2Error] if error occured.
void addAll(List<String> pathspec) {
bindings.addAll(indexPointer: _indexPointer, pathspec: pathspec);
void addAll(
List<String> pathspec, {
Set<GitIndexAddOption> flags = const {GitIndexAddOption.defaults},
}) {
bindings.addAll(
indexPointer: _indexPointer,
pathspec: pathspec,
flags: flags.fold(0, (acc, e) => acc | e.value),
);
}
/// Updates all index entries to match the working directory.
@ -330,6 +341,7 @@ class IndexEntry extends Equatable {
/// Initializes a new instance of [IndexEntry] class.
///
/// Note: For internal use.
@internal
const IndexEntry(this._indexEntryPointer);
final Pointer<git_index_entry> _indexEntryPointer;
@ -337,6 +349,7 @@ class IndexEntry extends Equatable {
/// Pointer to memory address for allocated index entry object.
///
/// Note: For internal use.
@internal
Pointer<git_index_entry> get pointer => _indexEntryPointer;
/// [Oid] of the index entry.
@ -378,6 +391,7 @@ class ConflictEntry {
/// Initializes a new instance of [ConflictEntry] class.
///
/// Note: For internal use.
@internal
const ConflictEntry(
this._indexPointer,
this._path,

View file

@ -265,18 +265,43 @@ class Merge {
/// given common [ancestor] as the baseline, producing a string that reflects
/// the merge result containing possible conflicts.
///
/// [ancestorLabel] is optional label for the ancestor file side of the
/// conflict which will be prepended to labels in diff3-format merge files.
///
/// [oursLabel] is optional label for our file side of the conflict which
/// will be prepended to labels in merge files.
///
/// [theirsLabel] is optional label for their file side of the conflict which
/// will be prepended to labels in merge files.
///
/// [favor] is one of the [GitMergeFileFavor] flags for handling conflicting
/// content. Defaults to [GitMergeFileFavor.normal].
///
/// [flags] is a combination of [GitMergeFileFlag] flags. Defaults to
/// [GitMergeFileFlag.defaults].
///
/// Throws a [LibGit2Error] if error occured.
static String fileFromIndex({
required Repository repo,
required IndexEntry? ancestor,
String ancestorLabel = '',
required IndexEntry ours,
String oursLabel = '',
required IndexEntry theirs,
String theirsLabel = '',
GitMergeFileFavor favor = GitMergeFileFavor.normal,
Set<GitMergeFileFlag> flags = const {GitMergeFileFlag.defaults},
}) {
return bindings.mergeFileFromIndex(
repoPointer: repo.pointer,
ancestorPointer: ancestor?.pointer,
ancestorLabel: ancestorLabel,
oursPointer: ours.pointer,
oursLabel: oursLabel,
theirsPointer: theirs.pointer,
theirsLabel: theirsLabel,
favor: favor.value,
flags: flags.fold(0, (acc, e) => acc | e.value),
);
}

View file

@ -11,6 +11,7 @@ class Note extends Equatable {
/// pointer to note and annotatedOid objects in memory.
///
/// Note: For internal use. Use [Note.lookup] instead.
@internal
Note(this._notePointer, this._annotatedOidPointer) {
_finalizer.attach(this, _notePointer, detach: this);
}

View file

@ -12,6 +12,7 @@ class Odb extends Equatable {
/// pointer to Odb object in memory.
///
/// Note: For internal use.
@internal
Odb(this._odbPointer) {
_finalizer.attach(this, _odbPointer, detach: this);
}
@ -32,6 +33,7 @@ class Odb extends Equatable {
/// Pointer to memory address for allocated oid object.
///
/// Note: For internal use.
@internal
Pointer<git_odb> get pointer => _odbPointer;
/// Adds an on-disk alternate to an existing Object DB.

View file

@ -14,6 +14,7 @@ class Oid extends Equatable {
/// pointer to Oid object in memory.
///
/// Note: For internal use. Use [Oid.fromSHA] instead.
@internal
Oid(this._oidPointer);
/// Initializes a new instance of [Oid] class by determining if an object can
@ -41,6 +42,9 @@ class Oid extends Equatable {
/// Initializes a new instance of [Oid] class from provided raw git_oid
/// structure.
///
/// Note: For internal use.
@internal
Oid.fromRaw(git_oid raw) {
_oidPointer = bindings.fromRaw(raw.id);
}
@ -50,6 +54,7 @@ class Oid extends Equatable {
/// Pointer to memory address for allocated oid object.
///
/// Note: For internal use.
@internal
Pointer<git_oid> get pointer => _oidPointer;
/// Hexadecimal SHA string.

View file

@ -2,6 +2,7 @@ import 'dart:ffi';
import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/bindings/packbuilder.dart' as bindings;
import 'package:meta/meta.dart';
class PackBuilder {
/// Initializes a new instance of [PackBuilder] class.
@ -9,6 +10,7 @@ class PackBuilder {
/// Throws a [LibGit2Error] if error occured.
///
/// Note: For internal use.
@internal
PackBuilder(Repository repo) {
_packbuilderPointer = bindings.init(repo.pointer);
_finalizer.attach(this, _packbuilderPointer, detach: this);

View file

@ -18,6 +18,7 @@ class Patch extends Equatable {
/// - [Patch.fromBlobAndBuffer]
/// - [Patch.fromBuffers]
/// - [Patch.fromDiff]
@internal
Patch(this._patchPointer) {
_finalizer.attach(this, _patchPointer, detach: this);
}

View file

@ -17,6 +17,7 @@ class Reference extends Equatable {
/// Note: For internal use. Instead, use one of:
/// - [Reference.create]
/// - [Reference.lookup]
@internal
Reference(this._refPointer) {
_finalizer.attach(this, _refPointer, detach: this);
}
@ -87,6 +88,7 @@ class Reference extends Equatable {
/// Pointer to memory address for allocated reference object.
///
/// Note: For internal use.
@internal
Pointer<git_reference> get pointer => _refPointer;
/// Deletes an existing reference with provided [name].

View file

@ -11,6 +11,7 @@ class Refspec extends Equatable {
/// from provided pointer to refspec object in memory.
///
/// Note: For internal use.
@internal
const Refspec(this._refspecPointer);
/// Pointer to memory address for allocated refspec object.

View file

@ -327,6 +327,7 @@ class TransferProgress {
/// pointer to transfer progress object in memory.
///
/// Note: For internal use.
@internal
const TransferProgress(this._transferProgressPointer);
/// Pointer to memory address for allocated transfer progress object.

View file

@ -23,6 +23,7 @@ class Repository extends Equatable {
/// - [Repository.init]
/// - [Repository.open]
/// - [Repository.clone]
@internal
Repository(Pointer<git_repository> pointer) {
_repoPointer = pointer;
_finalizer.attach(this, _repoPointer, detach: this);
@ -161,6 +162,7 @@ class Repository extends Equatable {
/// Pointer to memory address for allocated repository object.
///
/// Note: For internal use.
@internal
Pointer<git_repository> get pointer => _repoPointer;
/// Looks for a git repository and return its path. The lookup start from
@ -555,8 +557,23 @@ class Repository extends Equatable {
///
/// [resetType] is one of the [GitReset] flags.
///
/// [strategy], [checkoutDirectory] and [pathspec] are optional checkout
/// options to be used for a HARD reset.
///
/// [strategy] is optional combination of [GitCheckout] flags.
///
/// [checkoutDirectory] is optional alternative checkout path to workdir.
///
/// [pathspec] is optional list of files to checkout.
///
/// Throws a [LibGit2Error] if error occured.
void reset({required Oid oid, required GitReset resetType}) {
void reset({
required Oid oid,
required GitReset resetType,
Set<GitCheckout>? strategy,
String? checkoutDirectory,
List<String>? pathspec,
}) {
final object = object_bindings.lookup(
repoPointer: _repoPointer,
oidPointer: oid.pointer,
@ -567,7 +584,9 @@ class Repository extends Equatable {
repoPointer: _repoPointer,
targetPointer: object,
resetType: resetType.value,
checkoutOptsPointer: nullptr,
strategy: strategy?.fold(0, (acc, e) => acc! | e.value),
checkoutDirectory: checkoutDirectory,
pathspec: pathspec,
);
object_bindings.free(object);

View file

@ -2,6 +2,7 @@ import 'dart:ffi';
import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/bindings/revwalk.dart' as bindings;
import 'package:meta/meta.dart';
class RevWalk {
/// Initializes a new instance of the [RevWalk] class.
@ -15,15 +16,20 @@ class RevWalk {
/// Pointer to memory address for allocated [RevWalk] object.
///
/// Note: For internal use.
@internal
Pointer<git_revwalk> get pointer => _revWalkPointer;
/// Returns the list of commits from the revision walk.
///
/// [limit] is optional number of commits to walk (by default walks through
/// all of the commits pushed onto the walker).
///
/// Default sorting is reverse chronological order (default in git).
List<Commit> walk() {
List<Commit> walk({int limit = 0}) {
final pointers = bindings.walk(
repoPointer: bindings.repository(_revWalkPointer),
walkerPointer: _revWalkPointer,
limit: limit,
);
return pointers.map((e) => Commit(e)).toList();

View file

@ -16,6 +16,7 @@ class Signature extends Equatable {
/// Note: For internal use. Instead, use one of:
/// - [Signature.create]
/// - [Signature.defaultSignature]
@internal
Signature(Pointer<git_signature> pointer) {
_signaturePointer = bindings.duplicate(pointer);
_finalizer.attach(this, _signaturePointer, detach: this);
@ -62,6 +63,7 @@ class Signature extends Equatable {
/// Pointer to memory address for allocated signature object.
///
/// Note: For internal use.
@internal
Pointer<git_signature> get pointer => _signaturePointer;
/// Full name of the author.

View file

@ -7,6 +7,9 @@ import 'package:meta/meta.dart';
class Stash extends Equatable {
/// Initializes a new instance of [Stash] class from provided stash [index],
/// [message] and [oid].
///
/// Note: For internal use. Use [Stash.create] instead to create stash.
@internal
const Stash({
required this.index,
required this.message,

View file

@ -13,6 +13,7 @@ class Tag extends Equatable {
/// tag object in memory.
///
/// Note: For internal use. Use [Tag.lookup] instead.
@internal
Tag(this._tagPointer) {
_finalizer.attach(this, _tagPointer, detach: this);
}

View file

@ -12,6 +12,7 @@ class Tree extends Equatable {
/// tree object in memory.
///
/// Note: For internal use. Use [Tree.lookup] instead.
@internal
Tree(this._treePointer) {
_finalizer.attach(this, _treePointer, detach: this);
}
@ -30,6 +31,7 @@ class Tree extends Equatable {
/// Pointer to memory address for allocated tree object.
///
/// Note: For internal use.
@internal
Pointer<git_tree> get pointer => _treePointer;
/// List with tree entries of a tree.
@ -113,6 +115,7 @@ class TreeEntry extends Equatable {
/// tree entry object in memory.
///
/// Note: For internal use.
@internal
const TreeEntry(this._treeEntryPointer);
/// Initializes a new instance of [TreeEntry] class from provided pointer to

View file

@ -1,13 +1,15 @@
// coverage:ignore-file
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
import 'package:libgit2dart/src/bindings/libgit2_opts_bindings.dart';
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
const libgit2Version = '1.4.3';
const libgit2Version = '1.5.0';
final libDir = path.join('.dart_tool', 'libgit2');
String getLibName() {
@ -24,6 +26,20 @@ String getLibName() {
return 'libgit2-$libgit2Version.$ext';
}
/// Returns location of the most recent verison of the libgit2dart package
/// contained in the cache.
String? checkCache() {
final cache = json.decode(
Process.runSync('dart', ['pub', 'cache', 'list']).stdout as String,
) as Map<String, dynamic>;
final packages = cache['packages'] as Map<String, dynamic>;
final libPackages = packages['libgit2dart'] as Map<String, dynamic>?;
final versions = libPackages?.keys.map((e) => Version.parse(e)).toList();
final latestVersion = libPackages?[Version.primary(versions!).toString()]
as Map<String, dynamic>?;
return latestVersion?['location'] as String?;
}
/// Checks if [File]/[Link] exists for [path].
bool _doesFileExist(String path) {
return File(path).existsSync() || Link(path).existsSync();
@ -52,6 +68,25 @@ String? _resolveLibPath(String name) {
libPath = path.join(path.dirname(Platform.resolvedExecutable), 'lib', name);
if (_doesFileExist(libPath)) return libPath;
// If lib is installed in system dir.
if (Platform.isMacOS || Platform.isLinux) {
final paths = [
'/usr/local/lib/libgit2.$libgit2Version.dylib',
'/usr/local/lib/libgit2.so.$libgit2Version',
'/usr/lib64/libgit2.so.$libgit2Version'
];
for (final path in paths) {
if (_doesFileExist(path)) return path;
}
}
// If lib is in '.pub_cache' folder.
final cachedLocation = checkCache();
if (cachedLocation != null) {
libPath = path.join(cachedLocation, Platform.operatingSystem, name);
if (_doesFileExist(libPath)) return libPath;
}
return null;
}

View file

@ -77,10 +77,15 @@ class Worktree extends Equatable {
/// Throws a [LibGit2Error] if error occured.
bool get isPrunable => bindings.isPrunable(_worktreePointer);
/// Prunes working tree.
/// Prunes working tree, that is removes the git data structures on disk.
///
/// Prune the working tree, that is remove the git data structures on disk.
void prune() => bindings.prune(_worktreePointer);
/// [flags] is optional combination of [GitWorktree] flags.
void prune([Set<GitWorktree>? flags]) {
bindings.prune(
worktreePointer: _worktreePointer,
flags: flags?.fold(0, (acc, e) => acc! | e.value),
);
}
/// Whether worktree is valid.
///

View file

@ -20,6 +20,6 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
# List of absolute paths to libraries that should be bundled with the plugin
set(libgit2dart_bundled_libraries
"${CMAKE_CURRENT_SOURCE_DIR}/libgit2-1.4.3.so"
"${CMAKE_CURRENT_SOURCE_DIR}/libgit2-1.5.0.so"
PARENT_SCOPE
)

Binary file not shown.

BIN
linux/libgit2-1.5.0.so Normal file

Binary file not shown.

Binary file not shown.

BIN
macos/libgit2-1.5.0.dylib Normal file

Binary file not shown.

View file

@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'libgit2dart'
s.version = '1.1.1'
s.version = '1.2.2'
s.summary = 'Dart bindings to libgit2.'
s.description = <<-DESC
Dart bindings to libgit2.
@ -15,7 +15,7 @@ Dart bindings to libgit2.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'
s.vendored_libraries = 'libgit2-1.4.3.dylib'
s.vendored_libraries = 'libgit2-1.5.0.dylib'
s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }

View file

@ -2,24 +2,24 @@ name: libgit2dart
description: Dart bindings to libgit2, provides ability to use libgit2 library in Dart and Flutter.
version: 1.1.1
version: 1.2.2
homepage: https://github.com/SkinnyMind/libgit2dart
environment:
sdk: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"
sdk: ">=2.18.0 <3.0.0"
flutter: ">=3.3.0"
dependencies:
args: ^2.3.0
equatable: ^2.0.3
ffi: ^1.1.2
ffi: ^2.0.0
meta: ^1.7.0
path: ^1.8.1
pub_cache: ^0.3.1
pub_semver: ^2.1.3
dev_dependencies:
ffigen: ^5.0.0
ffigen: ^6.0.1
lints: ^2.0.0
test: ^1.20.0

View file

@ -67,6 +67,109 @@ void main() {
expect(file.existsSync(), false);
});
test('reverts merge commit to provided parent', () {
const masterContents = 'master contents';
final file = File(p.join(repo.workdir, 'another_feature_file'))
..createSync()
..writeAsStringSync(masterContents);
repo.index.add('another_feature_file');
repo.index.write();
// Creating commit on 'master' branch with file contents conflicting to
// 'feature' branch.
final masterTip = Commit.create(
repo: repo,
updateRef: 'HEAD',
message: 'master commit\n',
author: author,
committer: committer,
tree: Tree.lookup(repo: repo, oid: repo.index.writeTree()),
parents: [Commit.lookup(repo: repo, oid: repo.head.target)],
);
// Switching to 'feature' branch.
Checkout.reference(repo: repo, name: 'refs/heads/feature');
repo.setHead('refs/heads/feature');
file.writeAsStringSync('feature contents');
repo.index.add('another_feature_file');
repo.index.write();
// Creating commit on 'feature' branch with file contents conflicting to
// 'master' branch.
final featureTip = Commit.create(
repo: repo,
updateRef: 'HEAD',
message: 'feature commit\n',
author: author,
committer: committer,
tree: Tree.lookup(repo: repo, oid: repo.index.writeTree()),
parents: [Commit.lookup(repo: repo, oid: repo.head.target)],
);
// Merging master branch.
Merge.commit(
repo: repo,
commit: AnnotatedCommit.lookup(
repo: repo,
oid: Oid.fromSHA(repo: repo, sha: masterTip.sha),
),
);
expect(repo.index.hasConflicts, true);
// "Resolving" conflict.
repo.index.updateAll(['another_feature_file']);
repo.index.write();
repo.stateCleanup();
// Creating merge commit.
final mergeOid = Commit.create(
repo: repo,
updateRef: 'HEAD',
message: 'merge commit\n',
author: author,
committer: committer,
tree: Tree.lookup(repo: repo, oid: repo.index.writeTree()),
parents: [
Commit.lookup(repo: repo, oid: featureTip),
Commit.lookup(repo: repo, oid: masterTip),
],
);
final mergeCommit = Commit.lookup(repo: repo, oid: mergeOid);
mergeCommit.revert(mainline: 2);
expect(file.readAsStringSync(), masterContents);
});
test('reverts commit with provided merge options and checkout options', () {
final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']);
final file = File(p.join(repo.workdir, 'dir', 'dir_file.txt'));
expect(repo.index.find('dir/dir_file.txt'), true);
expect(file.existsSync(), true);
commit.revert(
mergeFavor: GitMergeFileFavor.ours,
mergeFlags: {GitMergeFlag.noRecursive, GitMergeFlag.skipREUC},
mergeFileFlags: {
GitMergeFileFlag.ignoreWhitespace,
GitMergeFileFlag.styleZdiff3
},
checkoutStrategy: {
GitCheckout.force,
GitCheckout.conflictStyleMerge,
},
checkoutDirectory: repo.workdir,
checkoutPaths: ['dir/dir_file.txt'],
);
expect(repo.index.find('dir/dir_file.txt'), false);
expect(file.existsSync(), false);
});
test('throws when trying to revert and error occurs', () {
expect(() => Commit(nullptr).revert(), throwsA(isA<LibGit2Error>()));
});
@ -84,6 +187,25 @@ void main() {
expect(file.existsSync(), true);
});
test('reverts commit to provided commit with provided merge options', () {
final file = File(p.join(repo.workdir, 'dir', 'dir_file.txt'));
expect(repo.index.find('dir/dir_file.txt'), true);
expect(file.existsSync(), true);
final from = Commit.lookup(repo: repo, oid: repo['821ed6e']);
final revertIndex = from.revertTo(
commit: Commit.lookup(repo: repo, oid: repo['78b8bf1']),
mergeFavor: GitMergeFileFavor.ours,
mergeFlags: {GitMergeFlag.noRecursive, GitMergeFlag.skipREUC},
mergeFileFlags: {
GitMergeFileFlag.ignoreWhitespace,
GitMergeFileFlag.styleZdiff3
},
);
expect(revertIndex.find('dir/dir_file.txt'), false);
expect(file.existsSync(), true);
});
test('throws when trying to revert commit and error occurs', () {
final nullCommit = Commit(nullptr);
expect(

View file

@ -83,10 +83,7 @@ void main() {
cloneDir.deleteSync(recursive: true);
});
test(
testOn: '!linux',
'clones repository with provided keypair',
() {
test('clones repository with provided keypair', () {
final cloneDir = Directory.systemTemp.createTempSync('clone');
final keypair = Keypair(
username: 'git',
@ -107,8 +104,7 @@ void main() {
if (Platform.isLinux || Platform.isMacOS) {
cloneDir.deleteSync(recursive: true);
}
},
);
});
test('throws when no credentials is provided', () {
final cloneDir = Directory.systemTemp.createTempSync('clone');
@ -189,10 +185,7 @@ void main() {
cloneDir.deleteSync(recursive: true);
});
test(
testOn: '!linux',
'clones repository with provided keypair from memory',
() {
test('clones repository with provided keypair from memory', () {
final cloneDir = Directory.systemTemp.createTempSync('clone');
final pubKey = File(p.join('test', 'assets', 'keys', 'id_rsa.pub'))
.readAsStringSync();
@ -217,8 +210,7 @@ void main() {
if (Platform.isLinux || Platform.isMacOS) {
cloneDir.deleteSync(recursive: true);
}
},
);
});
test('throws when provided keypair from memory is incorrect', () {
final cloneDir = Directory.systemTemp.createTempSync('clone');

View file

@ -551,4 +551,25 @@ void main() {
expect(actual, expected);
});
});
test('GitIndexAddOption returns correct values', () {
const expected = {
GitIndexAddOption.defaults: 0,
GitIndexAddOption.force: 1,
GitIndexAddOption.disablePathspecMatch: 2,
GitIndexAddOption.checkPathspec: 4,
};
final actual = {for (final e in GitIndexAddOption.values) e: e.value};
expect(actual, expected);
});
test('GitWorktree returns correct values', () {
const expected = {
GitWorktree.pruneValid: 1,
GitWorktree.pruneLocked: 2,
GitWorktree.pruneWorkingTree: 4,
};
final actual = {for (final e in GitWorktree.values) e: e.value};
expect(actual, expected);
});
}

View file

@ -175,7 +175,10 @@ void main() {
group('addAll()', () {
test('adds with provided pathspec', () {
index.clear();
index.addAll(['file', 'feature_file']);
index.addAll(
['file', 'feature_file'],
flags: {GitIndexAddOption.checkPathspec, GitIndexAddOption.force},
);
expect(index.length, 2);
expect(index['file'].oid.sha, fileSha);

View file

@ -168,31 +168,38 @@ Another feature edit
expect(diff, diffExpected);
});
test('merges with provided merge flags and file flags', () {
test('merges with provided options', () {
const diffExpected = """
\<<<<<<< conflict_file
master conflict edit
\<<<<<<< ours
Feature edit on feature branch
||||||| ancestor
Feature edit
=======
conflict branch edit
>>>>>>> conflict_file
Another feature edit
>>>>>>> theirs
""";
Checkout.reference(repo: repo, name: 'refs/heads/feature');
repo.setHead('refs/heads/feature');
Merge.commit(
repo: repo,
commit: AnnotatedCommit.lookup(
repo: repo,
oid: Branch.lookup(repo: repo, name: 'conflict-branch').target,
oid: Branch.lookup(repo: repo, name: 'ancestor-conflict').target,
),
mergeFlags: {GitMergeFlag.noRecursive},
fileFlags: {GitMergeFileFlag.ignoreWhitespaceEOL},
);
final conflictedFile = repo.index.conflicts['conflict_file']!;
final conflictedFile = repo.index.conflicts['feature_file']!;
final diff = Merge.fileFromIndex(
repo: repo,
ancestor: null,
ancestor: conflictedFile.ancestor,
ancestorLabel: 'ancestor',
ours: conflictedFile.our!,
oursLabel: 'ours',
theirs: conflictedFile.their!,
theirsLabel: 'theirs',
flags: {GitMergeFileFlag.styleDiff3},
);
expect(diff, diffExpected);

View file

@ -278,7 +278,7 @@ void main() {
expect(refs.first.localId, null);
expect(refs.first.name, 'HEAD');
expect(refs.first.symRef, 'refs/heads/master');
expect((refs.first.oid).sha, '49322bb17d3acc9146f98c97d078513228bbf3c0');
expect(refs.first.oid.sha, '49322bb17d3acc9146f98c97d078513228bbf3c0');
expect(refs.first.toString(), contains('RemoteReference{'));
expect(refs.first, remote.ls().first);
});

View file

@ -50,6 +50,34 @@ void main() {
expect(diff.deltas.length, 1);
});
test('resets with provided checkout options', () {
expect(file.readAsStringSync(), 'Feature edit\n');
repo.reset(
oid: repo[sha],
resetType: GitReset.hard,
strategy: {GitCheckout.conflictStyleZdiff3},
pathspec: ['feature_file'],
);
expect(file.readAsStringSync(), isEmpty);
});
test(
'throws when trying to reset and error occurs',
testOn: '!windows',
() {
expect(
() => repo.reset(
oid: repo[sha],
resetType: GitReset.hard,
checkoutDirectory: '',
),
throwsA(isA<LibGit2Error>()),
);
},
);
group('resetDefault', () {
test('updates entry in the index', () {
file.writeAsStringSync('new edit');

View file

@ -50,6 +50,16 @@ void main() {
}
});
test('walks only number of commits provided with limit', () {
final walker = RevWalk(repo);
walker.push(repo[log.first]);
final commits = walker.walk(limit: 1);
expect(commits.length, 1);
expect(commits[0].oid.sha, log[0]);
});
test('returns list of commits with reverse sorting', () {
final walker = RevWalk(repo);

View file

@ -151,6 +151,22 @@ void main() {
expect(repo.worktrees, <String>[]);
});
test('prunes worktree with provided flags', () {
expect(repo.worktrees, <String>[]);
final worktree = Worktree.create(
repo: repo,
name: worktreeName,
path: worktreeDir.path,
);
expect(repo.worktrees, [worktreeName]);
expect(worktree.isPrunable, false);
expect(worktree.isValid, true);
worktree.prune({GitWorktree.pruneValid});
expect(repo.worktrees, <String>[]);
});
test('throws when trying get list of worktrees and error occurs', () {
expect(
() => Worktree.list(Repository(nullptr)),

View file

@ -19,6 +19,6 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
# List of absolute paths to libraries that should be bundled with the plugin
set(libgit2dart_bundled_libraries
"${CMAKE_CURRENT_SOURCE_DIR}/libgit2-1.4.3.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/libgit2-1.5.0.dll"
PARENT_SCOPE
)