From 6fe24dcb6533b149117c7885ae3a5b77f79ae1dd Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Fri, 14 Jan 2022 18:44:14 +0300 Subject: [PATCH] feat(index): add ability to create diff between two indexes (#34) --- lib/src/bindings/diff.dart | 69 +++++++++++++++++++++++++++++--------- lib/src/index.dart | 31 +++++++++++++++++ test/diff_test.dart | 54 +++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 15 deletions(-) diff --git a/lib/src/bindings/diff.dart b/lib/src/bindings/diff.dart index 282be76..415dc38 100644 --- a/lib/src/bindings/diff.dart +++ b/lib/src/bindings/diff.dart @@ -5,6 +5,42 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/error.dart'; import 'package:libgit2dart/src/util.dart'; +/// Create a diff with the difference between two index objects. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer indexToIndex({ + required Pointer repoPointer, + required Pointer oldIndexPointer, + required Pointer newIndexPointer, + required int flags, + required int contextLines, + required int interhunkLines, +}) { + final out = calloc>(); + final opts = _diffOptionsInit( + flags: flags, + contextLines: contextLines, + interhunkLines: interhunkLines, + ); + + final error = libgit2.git_diff_index_to_index( + out, + repoPointer, + oldIndexPointer, + newIndexPointer, + opts, + ); + + calloc.free(opts); + + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + /// Create a diff between the repository index and the workdir directory. Pointer indexToWorkdir({ required Pointer repoPointer, @@ -80,13 +116,14 @@ Pointer treeToWorkdir({ opts, ); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - calloc.free(opts); - return out.value; + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } } /// Create a diff between a tree and the working directory using index data to @@ -118,13 +155,14 @@ Pointer treeToWorkdirWithIndex({ opts, ); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - calloc.free(opts); - return out.value; + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } } /// Create a diff with the difference between two tree objects. @@ -153,13 +191,14 @@ Pointer treeToTree({ opts, ); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - calloc.free(opts); - return out.value; + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } } /// Query how many diff records are there in a diff. diff --git a/lib/src/index.dart b/lib/src/index.dart index f493e07..e4ebfc3 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -300,6 +300,37 @@ class Index with IterableMixin { void removeAll(List path) => bindings.removeAll(indexPointer: _indexPointer, pathspec: path); + /// Creates a diff with the difference between two index objects. + /// + /// [index] is the [Index] object to diff to. + /// + /// [flags] is a combination of [GitDiff] flags. Defaults to [GitDiff.normal]. + /// + /// [contextLines] is the number of unchanged lines that define the boundary + /// of a hunk (and to display before and after). Defaults to 3. + /// + /// [interhunkLines] is the maximum number of unchanged lines between hunk + /// boundaries before the hunks will be merged into one. Defaults to 0. + /// + /// Throws a [LibGit2Error] if error occured. + Diff diffToIndex({ + required Index index, + Set flags = const {GitDiff.normal}, + int contextLines = 3, + int interhunkLines = 0, + }) { + return Diff( + diff_bindings.indexToIndex( + repoPointer: bindings.owner(_indexPointer), + oldIndexPointer: _indexPointer, + newIndexPointer: index.pointer, + flags: flags.fold(0, (acc, e) => acc | e.value), + contextLines: contextLines, + interhunkLines: interhunkLines, + ), + ); + } + /// Creates a diff between the repository index and the workdir directory. /// /// [flags] is a combination of [GitDiff] flags. Defaults to [GitDiff.normal]. diff --git a/test/diff_test.dart b/test/diff_test.dart index 39c1e6d..78632a9 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -71,6 +71,21 @@ void main() { 'subdir/modified_file', ]; + const indexToIndex = [ + 'current_file', + 'file_deleted', + 'modified_file', + 'staged_changes', + 'staged_changes_file_deleted', + 'staged_changes_file_modified', + 'staged_new', + 'staged_new_file_deleted', + 'staged_new_file_modified', + 'subdir/current_file', + 'subdir/deleted_file', + 'subdir/modified_file', + ]; + const patchText = """ diff --git a/subdir/modified_file b/subdir/modified_file index e69de29..c217c63 100644 @@ -237,6 +252,45 @@ index e69de29..c217c63 100644 head.free(); }); + test( + 'throws when trying to diff between tree and workdir with index and ' + 'error occurs', () { + expect( + () => Diff.treeToWorkdirWithIndex( + repo: Repository(nullptr), + tree: null, + ), + throwsA(isA()), + ); + }); + + test('returns diff between index and index', () { + final index = repo.index; + final emptyIndex = Index.newInMemory(); + + final diff = index.diffToIndex(index: emptyIndex); + + expect(diff.length, 12); + for (var i = 0; i < diff.deltas.length; i++) { + expect(diff.deltas[i].newFile.path, indexToIndex[i]); + } + + index.free(); + emptyIndex.free(); + }); + + test('throws when trying to diff between index and index and error occurs', + () { + final index = repo.index; + + expect( + () => index.diffToIndex(index: Index(nullptr)), + throwsA(isA()), + ); + + index.free(); + }); + test('merges diffs', () { final head = repo.head; final commit = repo.lookupCommit(head.target);