From cd9f38c2bd0a6e00f727515cd2d67fcaae19ba09 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Thu, 16 Sep 2021 18:56:53 +0300 Subject: [PATCH] feat(diff): add ability to apply diff --- lib/src/bindings/diff.dart | 26 ++++++++++++++++++++++++++ lib/src/git_types.dart | 26 ++++++++++++++++++++++++++ lib/src/repository.dart | 21 +++++++++++++++++++++ test/diff_test.dart | 16 ++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/lib/src/bindings/diff.dart b/lib/src/bindings/diff.dart index 53a13d0..980ddc8 100644 --- a/lib/src/bindings/diff.dart +++ b/lib/src/bindings/diff.dart @@ -267,6 +267,32 @@ Pointer addToBuf(Pointer patch, Pointer buffer) { } } +/// Apply a diff to the given repository, making changes directly in the working directory, +/// the index, or both. +/// +/// Throws a [LibGit2Error] if error occured. +bool apply( + Pointer repo, + Pointer diff, + int location, [ + bool check = false, +]) { + final opts = calloc(); + libgit2.git_apply_options_init(opts, GIT_APPLY_OPTIONS_VERSION); + if (check) { + opts.ref.flags = git_apply_flags_t.GIT_APPLY_CHECK; + } + final error = libgit2.git_apply(repo, diff, location, opts); + + calloc.free(opts); + + if (error < 0) { + return check ? false : throw LibGit2Error(libgit2.git_error_last()); + } else { + return true; + } +} + /// Free a previously allocated diff stats. void statsFree(Pointer stats) => libgit2.git_diff_stats_free(stats); diff --git a/lib/src/git_types.dart b/lib/src/git_types.dart index b7f9675..74c301c 100644 --- a/lib/src/git_types.dart +++ b/lib/src/git_types.dart @@ -1046,3 +1046,29 @@ class GitDiffLine { @override String toString() => 'GitDiffLine.$_name'; } + +/// Possible application locations for `apply()` +class GitApplyLocation { + const GitApplyLocation._(this._value, this._name); + final int _value; + final String _name; + + /// Apply the patch to the workdir, leaving the index untouched. + /// This is the equivalent of `git apply` with no location argument. + static const workdir = GitApplyLocation._(0, 'workdir'); + + /// Apply the patch to the index, leaving the working directory + /// untouched. This is the equivalent of `git apply --cached`. + static const index = GitApplyLocation._(1, 'index'); + + /// Apply the patch to both the working directory and the index. + /// This is the equivalent of `git apply --index`. + static const both = GitApplyLocation._(2, 'both'); + + static const List values = [workdir, index, both]; + + int get value => _value; + + @override + String toString() => 'GitApplyLocation.$_name'; +} diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 2ed521a..4d6ce1f 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -798,4 +798,25 @@ class Repository { }) { return a.diff(newBlob: b, oldAsPath: aPath, newAsPath: bPath); } + + /// Applies the [diff] to the given repository, making changes directly in the working directory. + /// + /// Throws a [LibGit2Error] if error occured. + void apply(Diff diff) { + diff_bindings.apply( + _repoPointer, + diff.pointer, + GitApplyLocation.workdir.value, + ); + } + + /// Checks if the [diff] will apply to HEAD. + bool applies(Diff diff) { + return diff_bindings.apply( + _repoPointer, + diff.pointer, + GitApplyLocation.index.value, + true, + ); + } } diff --git a/test/diff_test.dart b/test/diff_test.dart index 0e5497d..848ca72 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -194,6 +194,22 @@ index e69de29..c217c63 100644 diff.free(); }); + test( + 'checks if diff can be applied to repository and successfully applies it', + () { + final diff = Diff.parse(patchText); + final file = File('${tmpDir}subdir/modified_file'); + + repo.reset('a763aa560953e7cfb87ccbc2f536d665aa4dff22', GitReset.hard); + expect(file.readAsStringSync(), ''); + + expect(repo.applies(diff), true); + repo.apply(diff); + expect(file.readAsStringSync(), 'Modified content\n'); + + diff.free(); + }); + test('successfully creates patch from entry index in diff', () { final diff = Diff.parse(patchText); final patch = Patch.fromDiff(diff, 0);