feat(stash): add bindings and api

This commit is contained in:
Aleksey Kulikov 2021-09-21 13:18:11 +03:00
parent 7b8dfcc1af
commit 3cb55817ad
7 changed files with 386 additions and 13 deletions

View file

@ -19,10 +19,10 @@ void head(
String? directory,
List<String>? paths,
) {
final initOptions = _initOptions(strategy, directory, paths);
final optsC = initOptions[0];
final pathPointers = initOptions[1];
final strArray = initOptions[2];
final initOpts = initOptions(strategy, directory, paths);
final optsC = initOpts[0];
final pathPointers = initOpts[1];
final strArray = initOpts[2];
final error = libgit2.git_checkout_head(repo, optsC);
@ -47,10 +47,10 @@ void index(
String? directory,
List<String>? paths,
) {
final initOptions = _initOptions(strategy, directory, paths);
final optsC = initOptions[0];
final pathPointers = initOptions[1];
final strArray = initOptions[2];
final initOpts = initOptions(strategy, directory, paths);
final optsC = initOpts[0];
final pathPointers = initOpts[1];
final strArray = initOpts[2];
final error = libgit2.git_checkout_index(repo, nullptr, optsC);
@ -77,10 +77,10 @@ void tree(
String? directory,
List<String>? paths,
) {
final initOptions = _initOptions(strategy, directory, paths);
final optsC = initOptions[0];
final pathPointers = initOptions[1];
final strArray = initOptions[2];
final initOpts = initOptions(strategy, directory, paths);
final optsC = initOpts[0];
final pathPointers = initOpts[1];
final strArray = initOpts[2];
final error = libgit2.git_checkout_tree(repo, treeish, optsC);
@ -96,7 +96,7 @@ void tree(
}
}
List<dynamic> _initOptions(
List<dynamic> initOptions(
int strategy,
String? directory,
List<String>? paths,

148
lib/src/bindings/stash.dart Normal file
View file

@ -0,0 +1,148 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import '../error.dart';
import '../stash.dart';
import 'checkout.dart' as checkout_bindings;
import 'libgit2_bindings.dart';
import '../util.dart';
/// Save the local modifications to a new stash.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_oid> stash(
Pointer<git_repository> repo,
Pointer<git_signature> stasher,
String message,
int flags,
) {
final out = calloc<git_oid>();
final messageC =
message.isNotEmpty ? message.toNativeUtf8().cast<Int8>() : nullptr;
final error = libgit2.git_stash_save(out, repo, stasher, messageC, flags);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out;
}
}
/// Apply a single stashed state from the stash list.
///
/// Throws a [LibGit2Error] if error occured.
void apply(
Pointer<git_repository> repo,
int index,
int flags,
int strategy,
String? directory,
List<String>? paths,
) {
final options =
calloc<git_stash_apply_options>(sizeOf<git_stash_apply_options>());
final optionsError = libgit2.git_stash_apply_options_init(
options, GIT_STASH_APPLY_OPTIONS_VERSION);
if (optionsError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
final checkoutOptions =
checkout_bindings.initOptions(strategy, directory, paths);
final optsC = checkoutOptions[0];
final pathPointers = checkoutOptions[1];
final strArray = checkoutOptions[2];
options.ref.flags = flags;
options.ref.checkout_options = (optsC as Pointer<git_checkout_options>).ref;
final error = libgit2.git_stash_apply(repo, index, options);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
for (var p in pathPointers) {
calloc.free(p);
}
calloc.free(strArray);
calloc.free(optsC);
calloc.free(options);
}
/// Remove a single stashed state from the stash list.
///
/// Throws a [LibGit2Error] if error occured.
void drop(Pointer<git_repository> repo, int index) {
libgit2.git_stash_drop(repo, index);
}
/// Apply a single stashed state from the stash list and remove it from the list if successful.
///
/// Throws a [LibGit2Error] if error occured.
void pop(
Pointer<git_repository> repo,
int index,
int flags,
int strategy,
String? directory,
List<String>? paths,
) {
final options =
calloc<git_stash_apply_options>(sizeOf<git_stash_apply_options>());
final optionsError = libgit2.git_stash_apply_options_init(
options, GIT_STASH_APPLY_OPTIONS_VERSION);
if (optionsError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
final checkoutOptions =
checkout_bindings.initOptions(strategy, directory, paths);
final optsC = checkoutOptions[0];
final pathPointers = checkoutOptions[1];
final strArray = checkoutOptions[2];
options.ref.flags = flags;
options.ref.checkout_options = (optsC as Pointer<git_checkout_options>).ref;
final error = libgit2.git_stash_pop(repo, index, options);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
for (var p in pathPointers) {
calloc.free(p);
}
calloc.free(strArray);
calloc.free(optsC);
calloc.free(options);
}
var _stashList = <Stash>[];
/// Loop over all the stashed states.
List<Stash> list(Pointer<git_repository> repo) {
const except = -1;
_stashList.clear();
git_stash_cb callBack = Pointer.fromFunction(_stashCb, except);
libgit2.git_stash_foreach(repo, callBack, nullptr);
return _stashList;
}
int _stashCb(
int index,
Pointer<Int8> message,
Pointer<git_oid> oid,
Pointer<Void> payload,
) {
_stashList.add(Stash(
index: index,
message: message.cast<Utf8>().toDartString(),
oid: oid,
));
return 0;
}

View file

@ -1120,3 +1120,57 @@ class GitConfigLevel {
@override
String toString() => 'GitConfigLevel.$_name';
}
/// Stash flags.
class GitStash {
const GitStash._(this._value, this._name);
final int _value;
final String _name;
/// No option, default.
static const defaults = GitStash._(0, 'defaults');
/// All changes already added to the index are left intact in
/// the working directory.
static const keepIndex = GitStash._(1, 'keepIndex');
/// All untracked files are also stashed and then cleaned up
/// from the working directory.
static const includeUntracked = GitStash._(2, 'includeUntracked');
/// All ignored files are also stashed and then cleaned up from
/// the working directory.
static const includeIgnored = GitStash._(4, 'includeIgnored');
static const List<GitStash> values = [
defaults,
keepIndex,
includeUntracked,
includeIgnored,
];
int get value => _value;
@override
String toString() => 'GitStash.$_name';
}
/// Stash application flags.
class GitStashApply {
const GitStashApply._(this._value, this._name);
final int _value;
final String _name;
static const defaults = GitStashApply._(0, 'defaults');
/// Try to reinstate not only the working tree's changes,
/// but also the index's changes.
static const reinstateIndex = GitStashApply._(1, 'reinstateIndex');
static const List<GitStashApply> values = [defaults, reinstateIndex];
int get value => _value;
@override
String toString() => 'GitStashApply.$_name';
}

View file

@ -10,6 +10,7 @@ import 'bindings/commit.dart' as commit_bindings;
import 'bindings/checkout.dart' as checkout_bindings;
import 'bindings/reset.dart' as reset_bindings;
import 'bindings/diff.dart' as diff_bindings;
import 'bindings/stash.dart' as stash_bindings;
import 'branch.dart';
import 'commit.dart';
import 'config.dart';
@ -837,4 +838,77 @@ class Repository {
true,
);
}
/// Saves the local modifications to a new stash.
///
/// Throws a [LibGit2Error] if error occured.
Oid stash({
required Signature stasher,
String message = '',
bool keepIndex = false,
bool includeUntracked = false,
bool includeIgnored = false,
}) {
int flags = 0;
if (keepIndex) flags |= GitStash.keepIndex.value;
if (includeUntracked) flags |= GitStash.includeUntracked.value;
if (includeIgnored) flags |= GitStash.includeIgnored.value;
return Oid(stash_bindings.stash(
_repoPointer,
stasher.pointer,
message,
flags,
));
}
/// Applies a single stashed state from the stash list.
///
/// Throws a [LibGit2Error] if error occured.
void stashApply({
int index = 0,
bool reinstateIndex = false,
Set<GitCheckout> strategy = const {
GitCheckout.safe,
GitCheckout.recreateMissing
},
String? directory,
List<String>? paths,
}) {
int flags = reinstateIndex ? GitStashApply.reinstateIndex.value : 0;
final int strat =
strategy.fold(0, (previousValue, e) => previousValue | e.value);
stash_bindings.apply(_repoPointer, index, flags, strat, directory, paths);
}
/// Removes a single stashed state from the stash list.
///
/// Throws a [LibGit2Error] if error occured.
void stashDrop([int index = 0]) => stash_bindings.drop(_repoPointer, index);
/// Applies a single stashed state from the stash list and remove it from the list if successful.
///
/// Throws a [LibGit2Error] if error occured.
void stashPop({
int index = 0,
bool reinstateIndex = false,
Set<GitCheckout> strategy = const {
GitCheckout.safe,
GitCheckout.recreateMissing
},
String? directory,
List<String>? paths,
}) {
int flags = reinstateIndex ? GitStashApply.reinstateIndex.value : 0;
final int strat =
strategy.fold(0, (previousValue, e) => previousValue | e.value);
stash_bindings.pop(_repoPointer, index, flags, strat, directory, paths);
}
/// Returns list of all the stashed states, first being the most recent.
List<Stash> get stashList {
return stash_bindings.list(_repoPointer);
}
}

28
lib/src/stash.dart Normal file
View file

@ -0,0 +1,28 @@
import 'dart:ffi';
import 'bindings/libgit2_bindings.dart';
import 'oid.dart';
class Stash {
/// Initializes a new instance of [Stash] class.
Stash({
required this.index,
required this.message,
required Pointer<git_oid> oid,
}) {
this.oid = Oid(oid);
}
/// The position within the stash list.
final int index;
/// The stash message.
final String message;
/// The commit oid of the stashed state.
late final Oid oid;
@override
String toString() {
return 'Stash{index: $index, message: $message, sha: ${oid.sha}}';
}
}