mirror of
https://github.com/SkinnyMind/libgit2dart.git
synced 2025-05-05 04:39:07 -04:00
feat(commit): add ability to pass options to revert(...)
and revertTo(...)
API methods
- select parent to revert to for merge commits - merge options - checkout options
This commit is contained in:
parent
75687c469c
commit
35ee6284c1
3 changed files with 216 additions and 3 deletions
|
@ -384,8 +384,48 @@ Pointer<git_tree> tree(Pointer<git_commit> commit) {
|
||||||
void revert({
|
void revert({
|
||||||
required Pointer<git_repository> repoPointer,
|
required Pointer<git_repository> repoPointer,
|
||||||
required Pointer<git_commit> commitPointer,
|
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) {
|
if (error < 0) {
|
||||||
throw LibGit2Error(libgit2.git_error_last());
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
@ -403,11 +443,18 @@ Pointer<git_index> revertCommit({
|
||||||
required Pointer<git_commit> revertCommitPointer,
|
required Pointer<git_commit> revertCommitPointer,
|
||||||
required Pointer<git_commit> ourCommitPointer,
|
required Pointer<git_commit> ourCommitPointer,
|
||||||
required int mainline,
|
required int mainline,
|
||||||
|
int? mergeFavor,
|
||||||
|
int? mergeFlags,
|
||||||
|
int? mergeFileFlags,
|
||||||
}) {
|
}) {
|
||||||
final out = calloc<Pointer<git_index>>();
|
final out = calloc<Pointer<git_index>>();
|
||||||
final opts = calloc<git_merge_options>();
|
final opts = calloc<git_merge_options>();
|
||||||
libgit2.git_merge_options_init(opts, GIT_MERGE_OPTIONS_VERSION);
|
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(
|
final error = libgit2.git_revert_commit(
|
||||||
out,
|
out,
|
||||||
repoPointer,
|
repoPointer,
|
||||||
|
|
|
@ -189,23 +189,64 @@ class Commit extends Equatable {
|
||||||
|
|
||||||
/// Reverts commit, producing changes in the index and working directory.
|
/// 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.
|
/// 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(
|
bindings.revert(
|
||||||
repoPointer: bindings.owner(_commitPointer),
|
repoPointer: bindings.owner(_commitPointer),
|
||||||
commitPointer: _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
|
/// Reverts commit against provided [commit], producing an index that
|
||||||
/// reflects the result of the revert.
|
/// 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.
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
Index revertTo({
|
Index revertTo({
|
||||||
required Commit commit,
|
required Commit commit,
|
||||||
int mainline = 0,
|
int mainline = 0,
|
||||||
|
GitMergeFileFavor? mergeFavor,
|
||||||
|
Set<GitMergeFlag>? mergeFlags,
|
||||||
|
Set<GitMergeFileFlag>? mergeFileFlags,
|
||||||
}) {
|
}) {
|
||||||
return Index(
|
return Index(
|
||||||
bindings.revertCommit(
|
bindings.revertCommit(
|
||||||
|
@ -213,6 +254,9 @@ class Commit extends Equatable {
|
||||||
revertCommitPointer: _commitPointer,
|
revertCommitPointer: _commitPointer,
|
||||||
ourCommitPointer: commit.pointer,
|
ourCommitPointer: commit.pointer,
|
||||||
mainline: mainline,
|
mainline: mainline,
|
||||||
|
mergeFavor: mergeFavor?.value,
|
||||||
|
mergeFlags: mergeFlags?.fold(0, (acc, e) => acc! | e.value),
|
||||||
|
mergeFileFlags: mergeFileFlags?.fold(0, (acc, e) => acc! | e.value),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,109 @@ void main() {
|
||||||
expect(file.existsSync(), false);
|
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', () {
|
test('throws when trying to revert and error occurs', () {
|
||||||
expect(() => Commit(nullptr).revert(), throwsA(isA<LibGit2Error>()));
|
expect(() => Commit(nullptr).revert(), throwsA(isA<LibGit2Error>()));
|
||||||
});
|
});
|
||||||
|
@ -84,6 +187,25 @@ void main() {
|
||||||
expect(file.existsSync(), true);
|
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', () {
|
test('throws when trying to revert commit and error occurs', () {
|
||||||
final nullCommit = Commit(nullptr);
|
final nullCommit = Commit(nullptr);
|
||||||
expect(
|
expect(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue