From 6cda77675fa0e1d9cc8493a343168a5f9a8124a1 Mon Sep 17 00:00:00 2001
From: Adi Thakral <athakral@microsoft.com>
Date: Fri, 29 Jul 2022 16:02:03 -0700
Subject: [PATCH 1/2] Initial implementation for git_blame_buffer

---
 LibGit2Sharp/BlameHunkCollection.cs | 45 ++++++++++++++++++++++-------
 LibGit2Sharp/Core/NativeMethods.cs  |  7 +++++
 LibGit2Sharp/Core/Proxy.cs          | 21 ++++++++++++++
 3 files changed, 63 insertions(+), 10 deletions(-)

diff --git a/LibGit2Sharp/BlameHunkCollection.cs b/LibGit2Sharp/BlameHunkCollection.cs
index 869daf527..8783a44d7 100644
--- a/LibGit2Sharp/BlameHunkCollection.cs
+++ b/LibGit2Sharp/BlameHunkCollection.cs
@@ -11,10 +11,12 @@ namespace LibGit2Sharp
     /// <summary>
     /// The result of a blame operation.
     /// </summary>
-    public class BlameHunkCollection : IEnumerable<BlameHunk>
+    public class BlameHunkCollection : IEnumerable<BlameHunk>, IDisposable
     {
         private readonly IRepository repo;
         private readonly List<BlameHunk> hunks = new List<BlameHunk>();
+        private readonly RepositoryHandle repoHandle;
+        private readonly BlameHandle blameHandle;
 
         /// <summary>
         /// For easy mocking
@@ -49,15 +51,23 @@ internal unsafe BlameHunkCollection(Repository repo, RepositoryHandle repoHandle
                 }
             }
 
-            using (var blameHandle = Proxy.git_blame_file(repoHandle, path, rawopts))
-            {
-                var numHunks = NativeMethods.git_blame_get_hunk_count(blameHandle);
-                for (uint i = 0; i < numHunks; ++i)
-                {
-                    var rawHunk = Proxy.git_blame_get_hunk_byindex(blameHandle, i);
-                    hunks.Add(new BlameHunk(this.repo, rawHunk));
-                }
-            }
+            blameHandle = Proxy.git_blame_file(repoHandle, path, rawopts);
+
+            this.PopulateHunks();
+        }
+
+        private unsafe BlameHunkCollection(IRepository repo, RepositoryHandle repoHandle, BlameHandle reference, byte[] buffer)
+        {
+            this.repo = repo;
+            this.repoHandle = repoHandle;
+            this.blameHandle = Proxy.git_blame_buffer(repoHandle, reference, buffer);
+
+            this.PopulateHunks();
+        }
+
+        public BlameHunkCollection FromBuffer(byte[] buffer)
+        {
+            return new BlameHunkCollection(repo, repoHandle, this.blameHandle, buffer);
         }
 
         /// <summary>
@@ -100,5 +110,20 @@ IEnumerator IEnumerable.GetEnumerator()
         {
             return GetEnumerator();
         }
+
+        private unsafe void PopulateHunks()
+        {
+            var numHunks = NativeMethods.git_blame_get_hunk_count(blameHandle);
+            for (uint i = 0; i < numHunks; ++i)
+            {
+                var rawHunk = Proxy.git_blame_get_hunk_byindex(blameHandle, i);
+                hunks.Add(new BlameHunk(this.repo, rawHunk));
+            }
+        }
+
+        public void Dispose()
+        {
+            this.blameHandle.Dispose();
+        }
     }
 }
diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs
index f5d45f3cf..44ecc9599 100644
--- a/LibGit2Sharp/Core/NativeMethods.cs
+++ b/LibGit2Sharp/Core/NativeMethods.cs
@@ -190,6 +190,13 @@ internal static extern unsafe int git_blame_file(
             [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path,
             git_blame_options options);
 
+        [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern unsafe int git_blame_buffer(
+            out git_blame* blame,
+            git_blame* reference,
+            IntPtr buffer,
+            UIntPtr len);
+
         [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
         internal static extern unsafe void git_blame_free(git_blame* blame);
 
diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs
index 50cefc0df..050dfe305 100644
--- a/LibGit2Sharp/Core/Proxy.cs
+++ b/LibGit2Sharp/Core/Proxy.cs
@@ -35,6 +35,27 @@ public static unsafe BlameHandle git_blame_file(
             return NativeMethods.git_blame_get_hunk_byindex(blame, idx);
         }
 
+        public static unsafe BlameHandle git_blame_buffer(
+            RepositoryHandle repo,
+            BlameHandle reference,
+            byte[] buffer)
+        {
+            git_blame* ptr;
+            int res;
+
+            unsafe
+            {
+                fixed (byte* p = buffer)
+                {
+                    res = NativeMethods.git_blame_buffer(out ptr, reference, (IntPtr)p, (UIntPtr)buffer.Length);
+                }
+            }
+
+            Ensure.ZeroResult(res);
+
+            return new BlameHandle(ptr, true);
+        }
+
         #endregion
 
         #region git_blob_

From 797a8d0792006c6385b0535b1b2a1596f8aef4e4 Mon Sep 17 00:00:00 2001
From: Adi Thakral <athakral@microsoft.com>
Date: Fri, 28 Oct 2022 14:15:47 -0700
Subject: [PATCH 2/2] use the first blame ref for future calls

---
 LibGit2Sharp/BlameHunkCollection.cs | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/LibGit2Sharp/BlameHunkCollection.cs b/LibGit2Sharp/BlameHunkCollection.cs
index 8783a44d7..6176c3730 100644
--- a/LibGit2Sharp/BlameHunkCollection.cs
+++ b/LibGit2Sharp/BlameHunkCollection.cs
@@ -16,7 +16,8 @@ public class BlameHunkCollection : IEnumerable<BlameHunk>, IDisposable
         private readonly IRepository repo;
         private readonly List<BlameHunk> hunks = new List<BlameHunk>();
         private readonly RepositoryHandle repoHandle;
-        private readonly BlameHandle blameHandle;
+        private readonly BlameHandle referenceHandle;
+        private readonly bool shouldDisposeHandle;
 
         /// <summary>
         /// For easy mocking
@@ -51,23 +52,27 @@ internal unsafe BlameHunkCollection(Repository repo, RepositoryHandle repoHandle
                 }
             }
 
-            blameHandle = Proxy.git_blame_file(repoHandle, path, rawopts);
-
-            this.PopulateHunks();
+            referenceHandle = Proxy.git_blame_file(repoHandle, path, rawopts);
+            shouldDisposeHandle = true;
+            this.PopulateHunks(this.referenceHandle);
         }
 
         private unsafe BlameHunkCollection(IRepository repo, RepositoryHandle repoHandle, BlameHandle reference, byte[] buffer)
         {
             this.repo = repo;
             this.repoHandle = repoHandle;
-            this.blameHandle = Proxy.git_blame_buffer(repoHandle, reference, buffer);
+            this.referenceHandle = reference;
+            this.shouldDisposeHandle = false;
 
-            this.PopulateHunks();
+            using (var blameHandle = Proxy.git_blame_buffer(repoHandle, reference, buffer))
+            {
+                this.PopulateHunks(blameHandle);
+            }
         }
 
         public BlameHunkCollection FromBuffer(byte[] buffer)
         {
-            return new BlameHunkCollection(repo, repoHandle, this.blameHandle, buffer);
+            return new BlameHunkCollection(repo, repoHandle, this.referenceHandle, buffer);
         }
 
         /// <summary>
@@ -111,7 +116,7 @@ IEnumerator IEnumerable.GetEnumerator()
             return GetEnumerator();
         }
 
-        private unsafe void PopulateHunks()
+        private unsafe void PopulateHunks(BlameHandle blameHandle)
         {
             var numHunks = NativeMethods.git_blame_get_hunk_count(blameHandle);
             for (uint i = 0; i < numHunks; ++i)
@@ -123,7 +128,10 @@ private unsafe void PopulateHunks()
 
         public void Dispose()
         {
-            this.blameHandle.Dispose();
+            if (shouldDisposeHandle)
+            {
+                this.referenceHandle.Dispose();
+            }
         }
     }
 }