diff --git a/lib/src/bindings/blame.dart b/lib/src/bindings/blame.dart index f63807a..2600e40 100644 --- a/lib/src/bindings/blame.dart +++ b/lib/src/bindings/blame.dart @@ -58,6 +58,38 @@ Pointer file({ } } +/// Get blame data for a file that has been modified in memory. The [reference] +/// parameter is a pre-calculated blame for the in-odb history of the file. +/// This means that once a file blame is completed (which can be expensive), +/// updating the buffer blame is very fast. +/// +/// Lines that differ between the buffer and the committed version are marked +/// as having a zero OID for their finalCommitOid. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer buffer({ + required Pointer reference, + required String buffer, +}) { + final out = calloc>(); + final bufferC = buffer.toNativeUtf8().cast(); + final error = libgit2.git_blame_buffer( + out, + reference, + bufferC, + buffer.length, + ); + + calloc.free(bufferC); + + if (error < 0) { + calloc.free(out); + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + /// Gets the number of hunks that exist in the blame structure. int hunkCount(Pointer blame) { return libgit2.git_blame_get_hunk_count(blame); diff --git a/lib/src/blame.dart b/lib/src/blame.dart index d9cfd4d..ad0a60b 100644 --- a/lib/src/blame.dart +++ b/lib/src/blame.dart @@ -59,6 +59,23 @@ class Blame with IterableMixin { ); } + /// Returns blame data for a file that has been modified in memory. The + /// [reference] parameter is a pre-calculated blame for the in-odb history of + /// the file. + /// This means that once a file blame is completed (which can be expensive), + /// updating the buffer blame is very fast. + /// + /// Lines that differ between the buffer and the committed version are marked + /// as having a zero Oid for their finalCommitOid. + /// + /// Throws a [LibGit2Error] if error occured. + Blame.buffer({required Blame reference, required String buffer}) { + _blamePointer = bindings.buffer( + reference: reference._blamePointer, + buffer: buffer, + ); + } + /// Pointer to memory address for allocated blame object. late final Pointer _blamePointer; @@ -117,8 +134,11 @@ class BlameHunk { /// Author of [finalCommitOid]. If [GitBlameFlag.useMailmap] has been /// specified, it will contain the canonical real name and email address. - Signature get finalCommitter => - Signature(_blameHunkPointer.ref.final_signature); + Signature? get finalCommitter { + return _blameHunkPointer.ref.final_signature == nullptr + ? null + : Signature(_blameHunkPointer.ref.final_signature); + } /// [Oid] of the commit where this line was last changed. Oid get finalCommitOid => Oid.fromRaw(_blameHunkPointer.ref.final_commit_id); @@ -129,8 +149,11 @@ class BlameHunk { /// Author of [originCommitOid]. If [GitBlameFlag.useMailmap] has been /// specified, it will contain the canonical real name and email address. - Signature get originCommitter => - Signature(_blameHunkPointer.ref.orig_signature); + Signature? get originCommitter { + return _blameHunkPointer.ref.orig_signature == nullptr + ? null + : Signature(_blameHunkPointer.ref.orig_signature); + } /// [Oid] of the commit where this hunk was found. This will usually be the /// same as [finalCommitOid], except when diff --git a/test/blame_test.dart b/test/blame_test.dart index 2293af1..0a4aef6 100644 --- a/test/blame_test.dart +++ b/test/blame_test.dart @@ -88,6 +88,31 @@ void main() { expect(() => repo.blame(path: 'invalid'), throwsA(isA())); }); + test('returns blame for buffer', () { + final blame = repo.blame(path: 'feature_file'); + expect(blame.length, 2); + + final bufferBlame = Blame.buffer(reference: blame, buffer: ' '); + final blameHunk = bufferBlame.first; + expect(bufferBlame.length, 1); + expect(blameHunk.originCommitOid.sha, '0' * 40); + expect(blameHunk.originCommitter, null); + expect(blameHunk.finalCommitOid.sha, '0' * 40); + expect(blameHunk.finalCommitter, null); + + bufferBlame.free(); + blame.free(); + }); + + test('throws when trying to get blame for empty buffer', () { + final blame = repo.blame(path: 'feature_file'); + expect( + () => Blame.buffer(reference: blame, buffer: ''), + throwsA(isA()), + ); + blame.free(); + }); + test( 'successfully gets the blame for provided file with ' 'minMatchCharacters set', () {