mirror of
https://github.com/SkinnyMind/libgit2dart.git
synced 2025-05-05 04:39:07 -04:00
feat(diff): add more bindings and api methods
This commit is contained in:
parent
6c1735d67d
commit
c1959cfde9
3 changed files with 238 additions and 29 deletions
|
@ -298,6 +298,24 @@ Pointer<git_buf> addToBuf({
|
||||||
return bufferPointer;
|
return bufferPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Counter for hunk number being applied.
|
||||||
|
///
|
||||||
|
/// **IMPORTANT**: make sure to reset it to 0 before using since it's a global
|
||||||
|
/// variable.
|
||||||
|
int _counter = 0;
|
||||||
|
|
||||||
|
/// When applying a patch, callback that will be made per hunk.
|
||||||
|
int _hunkCb(Pointer<git_diff_hunk> hunk, Pointer<Void> payload) {
|
||||||
|
final index = payload.cast<Int32>().value;
|
||||||
|
if (_counter == index) {
|
||||||
|
_counter++;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
_counter++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply a diff to the given repository, making changes directly in the
|
/// Apply a diff to the given repository, making changes directly in the
|
||||||
/// working directory, the index, or both.
|
/// working directory, the index, or both.
|
||||||
///
|
///
|
||||||
|
@ -305,6 +323,7 @@ Pointer<git_buf> addToBuf({
|
||||||
bool apply({
|
bool apply({
|
||||||
required Pointer<git_repository> repoPointer,
|
required Pointer<git_repository> repoPointer,
|
||||||
required Pointer<git_diff> diffPointer,
|
required Pointer<git_diff> diffPointer,
|
||||||
|
int? hunkIndex,
|
||||||
required int location,
|
required int location,
|
||||||
bool check = false,
|
bool check = false,
|
||||||
}) {
|
}) {
|
||||||
|
@ -313,8 +332,18 @@ bool apply({
|
||||||
if (check) {
|
if (check) {
|
||||||
opts.ref.flags |= git_apply_flags_t.GIT_APPLY_CHECK;
|
opts.ref.flags |= git_apply_flags_t.GIT_APPLY_CHECK;
|
||||||
}
|
}
|
||||||
|
Pointer<Int32> payload = nullptr;
|
||||||
|
if (hunkIndex != null) {
|
||||||
|
_counter = 0;
|
||||||
|
const except = -1;
|
||||||
|
final git_apply_hunk_cb callback = Pointer.fromFunction(_hunkCb, except);
|
||||||
|
payload = calloc<Int32>()..value = hunkIndex;
|
||||||
|
opts.ref.payload = payload.cast();
|
||||||
|
opts.ref.hunk_cb = callback;
|
||||||
|
}
|
||||||
final error = libgit2.git_apply(repoPointer, diffPointer, location, opts);
|
final error = libgit2.git_apply(repoPointer, diffPointer, location, opts);
|
||||||
|
|
||||||
|
calloc.free(payload);
|
||||||
calloc.free(opts);
|
calloc.free(opts);
|
||||||
|
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
|
@ -324,6 +353,46 @@ bool apply({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply a diff to a tree, and return the resulting image as an index.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
Pointer<git_index> applyToTree({
|
||||||
|
required Pointer<git_repository> repoPointer,
|
||||||
|
required Pointer<git_tree> treePointer,
|
||||||
|
required Pointer<git_diff> diffPointer,
|
||||||
|
int? hunkIndex,
|
||||||
|
}) {
|
||||||
|
final out = calloc<Pointer<git_index>>();
|
||||||
|
final opts = calloc<git_apply_options>();
|
||||||
|
libgit2.git_apply_options_init(opts, GIT_APPLY_OPTIONS_VERSION);
|
||||||
|
Pointer<Int32> payload = nullptr;
|
||||||
|
if (hunkIndex != null) {
|
||||||
|
_counter = 0;
|
||||||
|
const except = -1;
|
||||||
|
final git_apply_hunk_cb callback = Pointer.fromFunction(_hunkCb, except);
|
||||||
|
payload = calloc<Int32>()..value = hunkIndex;
|
||||||
|
opts.ref.payload = payload.cast();
|
||||||
|
opts.ref.hunk_cb = callback;
|
||||||
|
}
|
||||||
|
final error = libgit2.git_apply_to_tree(
|
||||||
|
out,
|
||||||
|
repoPointer,
|
||||||
|
treePointer,
|
||||||
|
diffPointer,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
|
||||||
|
calloc.free(payload);
|
||||||
|
calloc.free(opts);
|
||||||
|
|
||||||
|
if (error < 0) {
|
||||||
|
calloc.free(out);
|
||||||
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
} else {
|
||||||
|
return out.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Free a previously allocated diff stats.
|
/// Free a previously allocated diff stats.
|
||||||
void statsFree(Pointer<git_diff_stats> stats) =>
|
void statsFree(Pointer<git_diff_stats> stats) =>
|
||||||
libgit2.git_diff_stats_free(stats);
|
libgit2.git_diff_stats_free(stats);
|
||||||
|
|
|
@ -1362,27 +1362,56 @@ class Repository {
|
||||||
/// [location] (directly in the working directory (default), the index or
|
/// [location] (directly in the working directory (default), the index or
|
||||||
/// both).
|
/// both).
|
||||||
///
|
///
|
||||||
|
/// [hunkIndex] is optional index of the hunk to apply.
|
||||||
|
///
|
||||||
/// Throws a [LibGit2Error] if error occured.
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
void apply({
|
void apply({
|
||||||
required Diff diff,
|
required Diff diff,
|
||||||
|
int? hunkIndex,
|
||||||
GitApplyLocation location = GitApplyLocation.workdir,
|
GitApplyLocation location = GitApplyLocation.workdir,
|
||||||
}) {
|
}) {
|
||||||
diff_bindings.apply(
|
diff_bindings.apply(
|
||||||
repoPointer: _repoPointer,
|
repoPointer: _repoPointer,
|
||||||
diffPointer: diff.pointer,
|
diffPointer: diff.pointer,
|
||||||
|
hunkIndex: hunkIndex,
|
||||||
location: location.value,
|
location: location.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the [diff] to the [tree], and returns the resulting image as an
|
||||||
|
/// index.
|
||||||
|
///
|
||||||
|
/// [hunkIndex] is optional index of the hunk to apply.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
Index applyToTree({
|
||||||
|
required Diff diff,
|
||||||
|
required Tree tree,
|
||||||
|
int? hunkIndex,
|
||||||
|
}) {
|
||||||
|
return Index(
|
||||||
|
diff_bindings.applyToTree(
|
||||||
|
repoPointer: _repoPointer,
|
||||||
|
diffPointer: diff.pointer,
|
||||||
|
treePointer: tree.pointer,
|
||||||
|
hunkIndex: hunkIndex,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the [diff] will apply to provided [location] (the working
|
/// Checks if the [diff] will apply to provided [location] (the working
|
||||||
/// directory (default), the index or both).
|
/// directory (default), the index or both).
|
||||||
|
///
|
||||||
|
/// [hunkIndex] is optional index of the hunk to apply.
|
||||||
bool applies({
|
bool applies({
|
||||||
required Diff diff,
|
required Diff diff,
|
||||||
|
int? hunkIndex,
|
||||||
GitApplyLocation location = GitApplyLocation.workdir,
|
GitApplyLocation location = GitApplyLocation.workdir,
|
||||||
}) {
|
}) {
|
||||||
return diff_bindings.apply(
|
return diff_bindings.apply(
|
||||||
repoPointer: _repoPointer,
|
repoPointer: _repoPointer,
|
||||||
diffPointer: diff.pointer,
|
diffPointer: diff.pointer,
|
||||||
|
hunkIndex: hunkIndex,
|
||||||
location: location.value,
|
location: location.value,
|
||||||
check: true,
|
check: true,
|
||||||
);
|
);
|
||||||
|
|
|
@ -242,13 +242,44 @@ index e69de29..c217c63 100644
|
||||||
diff.free();
|
diff.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('apply', () {
|
||||||
test('checks if diff can be applied to repository', () {
|
test('checks if diff can be applied to repository', () {
|
||||||
final diff1 = repo.diff();
|
final diff1 = repo.diff();
|
||||||
expect(repo.applies(diff: diff1, location: GitApplyLocation.both), false);
|
expect(
|
||||||
|
repo.applies(diff: diff1, location: GitApplyLocation.both),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
final diff2 = Diff.parse(patchText);
|
final diff2 = Diff.parse(patchText);
|
||||||
repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
|
repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
|
||||||
expect(repo.applies(diff: diff2, location: GitApplyLocation.both), true);
|
expect(
|
||||||
|
repo.applies(diff: diff2, location: GitApplyLocation.both),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
diff1.free();
|
||||||
|
diff2.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('checks if hunk with provided index can be applied to repository',
|
||||||
|
() {
|
||||||
|
final diff1 = repo.diff();
|
||||||
|
expect(
|
||||||
|
repo.applies(diff: diff1, location: GitApplyLocation.both),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final diff2 = Diff.parse(patchText);
|
||||||
|
final hunk = diff2.patches.first.hunks.first;
|
||||||
|
repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
|
||||||
|
expect(
|
||||||
|
repo.applies(
|
||||||
|
diff: diff2,
|
||||||
|
hunkIndex: hunk.index,
|
||||||
|
location: GitApplyLocation.both,
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
diff1.free();
|
diff1.free();
|
||||||
diff2.free();
|
diff2.free();
|
||||||
|
@ -283,6 +314,86 @@ index e69de29..c217c63 100644
|
||||||
diff.free();
|
diff.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('successfully applies hunk with provided index to repository', () {
|
||||||
|
final diff = Diff.parse(patchText);
|
||||||
|
final hunk = diff.patches.first.hunks.first;
|
||||||
|
final file = File('${tmpDir.path}/subdir/modified_file');
|
||||||
|
|
||||||
|
repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
|
||||||
|
expect(file.readAsStringSync(), '');
|
||||||
|
|
||||||
|
repo.apply(diff: diff, hunkIndex: hunk.index);
|
||||||
|
expect(file.readAsStringSync(), 'Modified content\n');
|
||||||
|
|
||||||
|
diff.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('successfully applies diff to tree', () {
|
||||||
|
final diff = Diff.parse(patchText);
|
||||||
|
|
||||||
|
repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
|
||||||
|
final head = repo.head;
|
||||||
|
final commit = repo.lookupCommit(head.target);
|
||||||
|
final tree = commit.tree;
|
||||||
|
|
||||||
|
final oldIndex = repo.index;
|
||||||
|
final oldBlob = repo.lookupBlob(oldIndex['subdir/modified_file'].oid);
|
||||||
|
expect(oldBlob.content, '');
|
||||||
|
|
||||||
|
final newIndex = repo.applyToTree(diff: diff, tree: tree);
|
||||||
|
final newBlob = repo.lookupBlob(newIndex['subdir/modified_file'].oid);
|
||||||
|
expect(newBlob.content, 'Modified content\n');
|
||||||
|
|
||||||
|
oldBlob.free();
|
||||||
|
newBlob.free();
|
||||||
|
oldIndex.free();
|
||||||
|
newIndex.free();
|
||||||
|
tree.free();
|
||||||
|
commit.free();
|
||||||
|
head.free();
|
||||||
|
diff.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('successfully applies hunk with provided index to tree', () {
|
||||||
|
final diff = Diff.parse(patchText);
|
||||||
|
final hunk = diff.patches.first.hunks.first;
|
||||||
|
|
||||||
|
repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
|
||||||
|
final head = repo.head;
|
||||||
|
final commit = repo.lookupCommit(head.target);
|
||||||
|
final tree = commit.tree;
|
||||||
|
|
||||||
|
final oldIndex = repo.index;
|
||||||
|
final oldBlob = repo.lookupBlob(oldIndex['subdir/modified_file'].oid);
|
||||||
|
expect(oldBlob.content, '');
|
||||||
|
|
||||||
|
final newIndex = repo.applyToTree(
|
||||||
|
diff: diff,
|
||||||
|
tree: tree,
|
||||||
|
hunkIndex: hunk.index,
|
||||||
|
);
|
||||||
|
final newBlob = repo.lookupBlob(newIndex['subdir/modified_file'].oid);
|
||||||
|
expect(newBlob.content, 'Modified content\n');
|
||||||
|
|
||||||
|
oldBlob.free();
|
||||||
|
newBlob.free();
|
||||||
|
oldIndex.free();
|
||||||
|
newIndex.free();
|
||||||
|
tree.free();
|
||||||
|
commit.free();
|
||||||
|
head.free();
|
||||||
|
diff.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when trying to apply diff to tree and error occurs', () {
|
||||||
|
final diff = Diff.parse(patchText);
|
||||||
|
expect(
|
||||||
|
() => repo.applyToTree(diff: diff, tree: Tree(nullptr)),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('successfully finds similar entries', () {
|
test('successfully finds similar entries', () {
|
||||||
final index = repo.index;
|
final index = repo.index;
|
||||||
final head = repo.head;
|
final head = repo.head;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue