feat(rebase): add bindings and api

This commit is contained in:
Aleksey Kulikov 2021-10-05 11:25:45 +03:00
parent 52707dcc63
commit 2ce419d7c4
5 changed files with 476 additions and 0 deletions

View file

@ -23,6 +23,7 @@ export 'src/callbacks.dart';
export 'src/credentials.dart';
export 'src/blame.dart';
export 'src/note.dart';
export 'src/rebase.dart';
export 'src/features.dart';
export 'src/error.dart';
export 'src/git_types.dart';

View file

@ -0,0 +1,129 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'libgit2_bindings.dart';
import '../error.dart';
import '../util.dart';
/// Initializes a rebase operation to rebase the changes in [branchPointer] relative
/// to [upstreamPointer] onto [ontoPointer] another branch. To begin the rebase process, call
/// `next()`. When you have finished with this object, call `free()`.
///
/// [branchPointer] is the terminal commit to rebase, or null to rebase the current branch.
///
/// [upstreamPointer] is the commit to begin rebasing from, or null to rebase all
/// reachable commits.
///
/// [ontoPointer] is the branch to rebase onto, or null to rebase onto the given upstream.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_rebase> init({
required Pointer<git_repository> repoPointer,
required Pointer<git_annotated_commit>? branchPointer,
required Pointer<git_annotated_commit>? upstreamPointer,
required Pointer<git_annotated_commit>? ontoPointer,
}) {
final out = calloc<Pointer<git_rebase>>();
final opts = calloc<git_rebase_options>();
final optsError = libgit2.git_rebase_options_init(
opts,
GIT_REBASE_OPTIONS_VERSION,
);
if (optsError < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
final error = libgit2.git_rebase_init(
out,
repoPointer,
branchPointer ?? nullptr,
upstreamPointer ?? nullptr,
ontoPointer ?? nullptr,
opts,
);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Gets the count of rebase operations that are to be applied.
int operationsCount(Pointer<git_rebase> rebase) {
return libgit2.git_rebase_operation_entrycount(rebase);
}
/// Performs the next rebase operation and returns the information about it.
/// If the operation is one that applies a patch (which is any operation except
/// GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and
/// working directory will be updated with the changes. If there are conflicts,
/// you will need to address those before committing the changes.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_rebase_operation> next(Pointer<git_rebase> rebase) {
final operation = calloc<Pointer<git_rebase_operation>>();
final error = libgit2.git_rebase_next(operation, rebase);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return operation.value;
}
}
/// Commits the current patch. You must have resolved any conflicts that were
/// introduced during the patch application from the `next()` invocation.
///
/// Throws a [LibGit2Error] if error occured.
void commit({
required Pointer<git_rebase> rebasePointer,
required Pointer<git_signature>? authorPointer,
required Pointer<git_signature> committerPointer,
required String? message,
}) {
final id = calloc<git_oid>();
final messageC = message?.toNativeUtf8().cast<Int8>() ?? nullptr;
final error = libgit2.git_rebase_commit(
id,
rebasePointer,
authorPointer ?? nullptr,
committerPointer,
nullptr,
messageC,
);
calloc.free(messageC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
}
/// Finishes a rebase that is currently in progress once all patches have been applied.
///
/// Throws a [LibGit2Error] if error occured.
void finish(Pointer<git_rebase> rebase) {
final error = libgit2.git_rebase_finish(rebase, nullptr);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
}
/// Aborts a rebase that is currently in progress, resetting the repository and working
/// directory to their state before rebase began.
///
/// Throws a [LibGit2Error] if error occured.
void abort(Pointer<git_rebase> rebase) {
final error = libgit2.git_rebase_abort(rebase);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
}
/// Free memory allocated for rebase object.
void free(Pointer<git_rebase> rebase) => libgit2.git_rebase_free(rebase);

View file

@ -1453,3 +1453,48 @@ class GitBlameFlag {
@override
String toString() => 'GitBlameFlag.$_name';
}
/// Type of rebase operation in-progress after calling rebase's `next()`.
class GitRebaseOperation {
const GitRebaseOperation._(this._value, this._name);
final int _value;
final String _name;
/// The given commit is to be cherry-picked. The client should commit
/// the changes and continue if there are no conflicts.
static const pick = GitRebaseOperation._(0, 'pick');
/// The given commit is to be cherry-picked, but the client should prompt
/// the user to provide an updated commit message.
static const reword = GitRebaseOperation._(1, 'reword');
/// The given commit is to be cherry-picked, but the client should stop
/// to allow the user to edit the changes before committing them.
static const edit = GitRebaseOperation._(2, 'edit');
/// The given commit is to be squashed into the previous commit. The
/// commit message will be merged with the previous message.
static const squash = GitRebaseOperation._(3, 'squash');
/// The given commit is to be squashed into the previous commit. The
/// commit message from this commit will be discarded.
static const fixup = GitRebaseOperation._(4, 'fixup');
/// No commit will be cherry-picked. The client should run the given
/// command and (if successful) continue.
static const exec = GitRebaseOperation._(5, 'exec');
static const List<GitRebaseOperation> values = [
pick,
reword,
edit,
squash,
fixup,
exec,
];
int get value => _value;
@override
String toString() => 'GitRebaseOperation.$_name';
}

148
lib/src/rebase.dart Normal file
View file

@ -0,0 +1,148 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'bindings/libgit2_bindings.dart';
import 'bindings/rebase.dart' as bindings;
import 'bindings/commit.dart' as commit_bindings;
import 'git_types.dart';
import 'oid.dart';
import 'repository.dart';
import 'signature.dart';
class Rebase {
/// Initializes a new instance of the [Rebase] class by initializing a
/// rebase operation to rebase the changes in [branch] relative to [upstream]
/// [onto] another branch. To begin the rebase process, call `next()`.
/// When you have finished with this object, call `free()`.
///
/// [branch] is the terminal commit to rebase, default is to rebase the current branch.
///
/// [upstream] is the commit to begin rebasing from, default is to rebase all
/// reachable commits.
///
/// [onto] is the branch to rebase onto, default is to rebase onto the given [upstream]
/// (throws if [upstream] is not provided).
///
/// Throws a [LibGit2Error] if error occured.
Rebase.init({
required Repository repo,
Oid? branch,
Oid? upstream,
Oid? onto,
}) {
Pointer<git_annotated_commit>? _branch, _upstream, _onto;
if (branch != null) {
_branch = commit_bindings
.annotatedLookup(
repoPointer: repo.pointer,
oidPointer: branch.pointer,
)
.value;
}
if (upstream != null) {
_upstream = commit_bindings
.annotatedLookup(
repoPointer: repo.pointer,
oidPointer: upstream.pointer,
)
.value;
}
if (onto != null) {
_onto = commit_bindings
.annotatedLookup(
repoPointer: repo.pointer,
oidPointer: onto.pointer,
)
.value;
}
_rebasePointer = bindings.init(
repoPointer: repo.pointer,
branchPointer: _branch,
upstreamPointer: _upstream,
ontoPointer: _onto,
);
}
/// Pointer to memory address for allocated rebase object.
late final Pointer<git_rebase> _rebasePointer;
/// The count of rebase operations that are to be applied.
int get operationsCount {
return bindings.operationsCount(_rebasePointer);
}
/// Performs the next rebase operation and returns the information about it.
/// If the operation is one that applies a patch (which is any operation except
/// [GitRebaseOperation.exec]) then the patch will be applied and the index and
/// working directory will be updated with the changes. If there are conflicts,
/// you will need to address those before committing the changes.
///
/// Throws a [LibGit2Error] if error occured.
RebaseOperation next() {
return RebaseOperation(bindings.next(_rebasePointer));
}
/// Commits the current patch. You must have resolved any conflicts that were
/// introduced during the patch application from the `next()` invocation.
///
/// Throws a [LibGit2Error] if error occured.
void commit({
required Signature committer,
String? message,
Signature? author,
}) {
bindings.commit(
rebasePointer: _rebasePointer,
authorPointer: author?.pointer,
committerPointer: committer.pointer,
message: message,
);
}
/// Finishes a rebase that is currently in progress once all patches have been applied.
///
/// Throws a [LibGit2Error] if error occured.
void finish() => bindings.finish(_rebasePointer);
/// Aborts a rebase that is currently in progress, resetting the repository and working
/// directory to their state before rebase began.
///
/// Throws a [LibGit2Error] if error occured.
void abort() => bindings.abort(_rebasePointer);
/// Releases memory allocated for rebase object.
void free() => bindings.free(_rebasePointer);
}
class RebaseOperation {
/// Initializes a new instance of the [RebaseOperation] class from
/// provided pointer to rebase operation object in memory.
const RebaseOperation(this._rebaseOperationPointer);
/// Pointer to memory address for allocated rebase operation object.
final Pointer<git_rebase_operation> _rebaseOperationPointer;
/// Returns the type of rebase operation.
GitRebaseOperation get type {
late final GitRebaseOperation result;
for (var operation in GitRebaseOperation.values) {
if (_rebaseOperationPointer.ref.type == operation.value) {
result = operation;
break;
}
}
return result;
}
/// The commit ID being cherry-picked. This will be populated for
/// all operations except those of type [GitRebaseOperation.exec].
Oid get id => Oid.fromRaw(_rebaseOperationPointer.ref.id);
/// The executable the user has requested be run. This will only
/// be populated for operations of type [GitRebaseOperation.exec].
String get exec {
return _rebaseOperationPointer.ref.exec == nullptr
? ''
: _rebaseOperationPointer.ref.exec.cast<Utf8>().toDartString();
}
}