feat(rebase)!: add more bindings and API methods

This commit is contained in:
Aleksey Kulikov 2021-12-22 19:07:57 +03:00
parent fe570a6990
commit ec7d272807
3 changed files with 171 additions and 18 deletions

View file

@ -50,11 +50,45 @@ Pointer<git_rebase> init({
} }
} }
/// Opens an existing rebase that was previously started by either an
/// invocation of [init] or by another client.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_rebase> open(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_rebase>>();
final opts = calloc<git_rebase_options>();
libgit2.git_rebase_options_init(opts, GIT_REBASE_OPTIONS_VERSION);
final error = libgit2.git_rebase_open(out, repo, opts);
if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Gets the count of rebase operations that are to be applied. /// Gets the count of rebase operations that are to be applied.
int operationsCount(Pointer<git_rebase> rebase) { int operationsCount(Pointer<git_rebase> rebase) {
return libgit2.git_rebase_operation_entrycount(rebase); return libgit2.git_rebase_operation_entrycount(rebase);
} }
/// Gets the rebase operation specified by the given index.
Pointer<git_rebase_operation> getOperationByIndex({
required Pointer<git_rebase> rebase,
required int index,
}) {
return libgit2.git_rebase_operation_byindex(rebase, index);
}
/// Gets the index of the rebase operation that is currently being applied. If
/// the first operation has not yet been applied (because you have called [init]
/// but not yet [next]) then this returns `-1`.
int currentOperation(Pointer<git_rebase> rebase) {
return libgit2.git_rebase_operation_current(rebase);
}
/// Performs the next rebase operation and returns the information about it. /// 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 /// 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 /// GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and
@ -75,7 +109,7 @@ Pointer<git_rebase_operation> next(Pointer<git_rebase> rebase) {
} }
/// Commits the current patch. You must have resolved any conflicts that were /// Commits the current patch. You must have resolved any conflicts that were
/// introduced during the patch application from the `next()` invocation. /// introduced during the patch application from the [next] invocation.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
void commit({ void commit({
@ -113,5 +147,24 @@ void finish(Pointer<git_rebase> rebase) =>
/// working directory to their state before rebase began. /// working directory to their state before rebase began.
void abort(Pointer<git_rebase> rebase) => libgit2.git_rebase_abort(rebase); void abort(Pointer<git_rebase> rebase) => libgit2.git_rebase_abort(rebase);
/// Gets the original HEAD id for merge rebases.
Pointer<git_oid> origHeadOid(Pointer<git_rebase> rebase) =>
libgit2.git_rebase_orig_head_id(rebase);
/// Gets the original HEAD ref name for merge rebases.
String origHeadName(Pointer<git_rebase> rebase) {
final result = libgit2.git_rebase_orig_head_name(rebase);
return result == nullptr ? '' : result.cast<Utf8>().toDartString();
}
/// Gets the onto id for merge rebases.
Pointer<git_oid> ontoOid(Pointer<git_rebase> rebase) =>
libgit2.git_rebase_onto_id(rebase);
/// Gets the onto ref name for merge rebases.
String ontoName(Pointer<git_rebase> rebase) {
return libgit2.git_rebase_onto_name(rebase).cast<Utf8>().toDartString();
}
/// Free memory allocated for rebase object. /// Free memory allocated for rebase object.
void free(Pointer<git_rebase> rebase) => libgit2.git_rebase_free(rebase); void free(Pointer<git_rebase> rebase) => libgit2.git_rebase_free(rebase);

View file

@ -34,12 +34,62 @@ class Rebase {
); );
} }
/// Opens an existing rebase that was previously started by either an
/// invocation of [Rebase.init] or by another client.
///
/// **IMPORTANT**: Should be freed to release allocated memory.
///
/// Throws a [LibGit2Error] if error occured.
Rebase.open(Repository repo) {
_rebasePointer = bindings.open(repo.pointer);
}
/// Pointer to memory address for allocated rebase object. /// Pointer to memory address for allocated rebase object.
late final Pointer<git_rebase> _rebasePointer; late final Pointer<git_rebase> _rebasePointer;
/// Number of rebase operations that are to be applied. /// List of operations that are to be applied.
int get operationsCount { List<RebaseOperation> get operations {
return bindings.operationsCount(_rebasePointer); final result = <RebaseOperation>[];
final operationsCount = bindings.operationsCount(_rebasePointer);
for (var i = 0; i < operationsCount; i++) {
final operation = bindings.getOperationByIndex(
rebase: _rebasePointer,
index: i,
);
result.add(RebaseOperation(operation));
}
return result;
}
/// Index of the rebase operation that is currently being applied. If the
/// first operation has not yet been applied (because you have called
/// [Rebase.init] but not yet [next]) then this returns `-1`.
int get currentOperation {
return bindings.currentOperation(_rebasePointer);
}
/// Original HEAD oid for merge rebases.
Oid get origHeadOid {
return Oid.fromRaw(bindings.origHeadOid(_rebasePointer).ref);
}
/// Original HEAD ref name for merge rebases.
///
/// Returns empty string if no information available.
String get origHeadName {
return bindings.origHeadName(_rebasePointer);
}
/// Onto oid for merge rebases.
Oid get ontoOid {
return Oid.fromRaw(bindings.ontoOid(_rebasePointer).ref);
}
/// Onto ref name for merge rebases.
String get ontoName {
return bindings.ontoName(_rebasePointer);
} }
/// Performs the next rebase operation and returns the information about it. /// Performs the next rebase operation and returns the information about it.

View file

@ -32,9 +32,15 @@ void main() {
time: 1234, time: 1234,
); );
final master = repo.lookupReference('refs/heads/master'); final master = repo.lookupReference('refs/heads/master');
final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final branchHead = AnnotatedCommit.fromReference(
repo: repo,
reference: master,
);
final feature = repo.lookupReference('refs/heads/feature'); final feature = repo.lookupReference('refs/heads/feature');
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target); final ontoHead = AnnotatedCommit.fromReference(
repo: repo,
reference: feature,
);
repo.checkout(refName: feature.name); repo.checkout(refName: feature.name);
expect(() => repo.index['.gitignore'], throwsA(isA<ArgumentError>())); expect(() => repo.index['.gitignore'], throwsA(isA<ArgumentError>()));
@ -45,10 +51,15 @@ void main() {
onto: ontoHead, onto: ontoHead,
); );
final operationsCount = rebase.operationsCount; expect(rebase.origHeadOid, master.target);
expect(operationsCount, 3); expect(rebase.origHeadName, 'refs/heads/master');
expect(rebase.ontoOid, feature.target);
expect(rebase.ontoName, 'feature');
for (var i = 0; i < operationsCount; i++) { final operations = rebase.operations;
expect(operations.length, 3);
for (var i = 0; i < operations.length; i++) {
final operation = rebase.next(); final operation = rebase.next();
expect(operation.type, GitRebaseOperation.pick); expect(operation.type, GitRebaseOperation.pick);
expect(operation.oid.sha, shas[i]); expect(operation.oid.sha, shas[i]);
@ -86,10 +97,18 @@ void main() {
onto: ontoHead, onto: ontoHead,
); );
final operationsCount = rebase.operationsCount; expect(
expect(operationsCount, 3); rebase.origHeadOid.sha,
'14905459d775f3f56a39ebc2ff081163f7da3529',
);
expect(rebase.origHeadName, 'refs/heads/master');
expect(rebase.ontoOid, feature.target);
expect(rebase.ontoName, '2ee89b2f7124b8e4632bc6a20774a90b795245e4');
for (var i = 0; i < operationsCount; i++) { final operations = rebase.operations;
expect(operations.length, 3);
for (var i = 0; i < operations.length; i++) {
final operation = rebase.next(); final operation = rebase.next();
expect(operation.type, GitRebaseOperation.pick); expect(operation.type, GitRebaseOperation.pick);
expect(operation.oid.sha, shas[i]); expect(operation.oid.sha, shas[i]);
@ -130,11 +149,19 @@ void main() {
upstream: upstream, upstream: upstream,
); );
final operationsCount = rebase.operationsCount; expect(rebase.origHeadOid, master.target);
expect(operationsCount, 1); expect(rebase.origHeadName, '');
expect(rebase.ontoOid.sha, shas[1]);
expect(rebase.ontoName, '821ed6e80627b8769d170a293862f9fc60825226');
for (var i = 0; i < operationsCount; i++) { final operations = rebase.operations;
expect(operations.length, 1);
for (final operation in operations) {
expect(rebase.currentOperation, -1);
expect(operation.type, GitRebaseOperation.pick);
rebase.next(); rebase.next();
expect(rebase.currentOperation, 0);
rebase.commit(committer: signature); rebase.commit(committer: signature);
} }
@ -173,7 +200,7 @@ void main() {
branch: branchHead, branch: branchHead,
onto: ontoHead, onto: ontoHead,
); );
expect(rebase.operationsCount, 1); expect(rebase.operations.length, 1);
rebase.next(); rebase.next();
expect(repo.status['conflict_file'], {GitStatus.conflicted}); expect(repo.status['conflict_file'], {GitStatus.conflicted});
@ -210,7 +237,7 @@ void main() {
branch: branchHead, branch: branchHead,
onto: ontoHead, onto: ontoHead,
); );
expect(rebase.operationsCount, 1); expect(rebase.operations.length, 1);
rebase.next(); // repo now have conflicts rebase.next(); // repo now have conflicts
expect(() => rebase.next(), throwsA(isA<LibGit2Error>())); expect(() => rebase.next(), throwsA(isA<LibGit2Error>()));
@ -236,7 +263,7 @@ void main() {
branch: branchHead, branch: branchHead,
onto: ontoHead, onto: ontoHead,
); );
expect(rebase.operationsCount, 1); expect(rebase.operations.length, 1);
rebase.next(); rebase.next();
expect(repo.status['conflict_file'], {GitStatus.conflicted}); expect(repo.status['conflict_file'], {GitStatus.conflicted});
@ -252,5 +279,28 @@ void main() {
conflict.free(); conflict.free();
master.free(); master.free();
}); });
test('opens an existing rebase', () {
final feature = repo.lookupReference('refs/heads/feature');
final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target);
final rebase = Rebase.init(
repo: repo,
onto: ontoHead,
);
expect(rebase.operations.length, 3);
final openRebase = Rebase.open(repo);
expect(openRebase.operations.length, 3);
openRebase.free();
rebase.free();
ontoHead.free();
feature.free();
});
test('throws when trying to open an existing rebase but there is none', () {
expect(() => Rebase.open(repo), throwsA(isA<LibGit2Error>()));
});
}); });
} }