From 2accdd53f574bfff1a260305afb0cdebf2ba8025 Mon Sep 17 00:00:00 2001 From: Georgios Diamantopoulos Date: Tue, 13 Sep 2022 17:29:04 +0300 Subject: [PATCH] allow reading patch from diff file and applying it to a repository --- .../PatchEntryChangesFixture.cs | 43 ++++++++++++++++--- LibGit2Sharp/Core/GitDiffApplyOpts.cs | 21 +++++++++ LibGit2Sharp/Core/NativeMethods.cs | 13 ++++++ LibGit2Sharp/Core/Proxy.cs | 22 ++++++++++ LibGit2Sharp/DeltaApplyStatus.cs | 23 ++++++++++ LibGit2Sharp/Patch.cs | 10 +++++ LibGit2Sharp/PatchApplyOptions.cs | 37 ++++++++++++++++ LibGit2Sharp/RepositoryExtensions.cs | 33 ++++++++++++++ 8 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 LibGit2Sharp/Core/GitDiffApplyOpts.cs create mode 100644 LibGit2Sharp/DeltaApplyStatus.cs create mode 100644 LibGit2Sharp/PatchApplyOptions.cs diff --git a/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs b/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs index dc2552a10..4aeeebf07 100644 --- a/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs +++ b/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs @@ -1,10 +1,5 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using LibGit2Sharp.Tests.TestHelpers; +using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -40,5 +35,41 @@ public void PatchEntryBasics() } } } + + [Fact] + public void ReadPatch() + { + var path = SandboxStandardTestRepoGitDir(); + string file = "numbers.txt"; + + // The repo + using (var repo = new Repository(path)) + { + Tree rootCommitTree = repo.Lookup("f8d44d7").Tree; + Tree commitTreeWithUpdatedFile = repo.Lookup("ec9e401").Tree; + + // Create patch by diffing + using (var patch = repo.Diff.Compare(rootCommitTree, commitTreeWithUpdatedFile)) + { + string patchContent = patch.Content; + + var parsedPatch = Patch.FromPatchContent(patchContent); + + Assert.Equal(patch.Content, parsedPatch.Content); + + PatchEntryChanges entryChanges = parsedPatch[file]; + Assert.Equal(2, entryChanges.LinesAdded); + Assert.Equal(1, entryChanges.LinesDeleted); + Assert.Equal(187, entryChanges.Patch.Length); + // Smoke test + Assert.Equal(Mode.NonExecutableFile, entryChanges.Mode); + Assert.Equal(new ObjectId("4625a36000000000000000000000000000000000"), entryChanges.Oid); + Assert.Equal(ChangeKind.Modified, entryChanges.Status); + Assert.Equal(file, entryChanges.OldPath); + Assert.Equal(Mode.NonExecutableFile, entryChanges.OldMode); + Assert.Equal(new ObjectId("7909961000000000000000000000000000000000"), entryChanges.OldOid); + } + } + } } } diff --git a/LibGit2Sharp/Core/GitDiffApplyOpts.cs b/LibGit2Sharp/Core/GitDiffApplyOpts.cs new file mode 100644 index 000000000..1fb21edb6 --- /dev/null +++ b/LibGit2Sharp/Core/GitDiffApplyOpts.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int git_apply_delta_cb(IntPtr delta, IntPtr payload); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int git_apply_hunk_cb(IntPtr hunk, IntPtr payload); + + [StructLayout(LayoutKind.Sequential)] + internal class GitDiffApplyOpts + { + public uint Version = 1; + public git_apply_delta_cb DeltaCallback; + public git_apply_hunk_cb HunkCallback; + public IntPtr Payload; + public uint Flags = 0; + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index f5d45f3cf..e3711261e 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -176,6 +176,13 @@ internal static extern int git_error_set_str( [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_error_set_oom(); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_apply( + git_repository* repo, + git_diff* diff, + PatchApplyLocation location, + GitDiffApplyOpts options); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UInt32 git_blame_get_hunk_count(git_blame* blame); @@ -710,6 +717,12 @@ internal static extern unsafe int git_diff_find_similar( [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_diff_delta* git_diff_get_delta(git_diff* diff, UIntPtr idx); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_diff_from_buffer(out git_diff* diff, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] + string content, + UIntPtr len); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_filter_register( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 50cefc0df..8e7702409 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -17,6 +17,21 @@ internal class Proxy internal static readonly bool isOSXArm64 = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + #region git_apply_ + + public static unsafe void git_apply( + RepositoryHandle repo, + DiffHandle diff, + PatchApplyLocation location, + GitDiffApplyOpts options) + { + int res = NativeMethods.git_apply(repo, diff, location, options ?? new GitDiffApplyOpts()); + + Ensure.ZeroResult(res); + } + + #endregion + #region git_blame_ public static unsafe BlameHandle git_blame_file( @@ -854,6 +869,13 @@ public static unsafe int git_diff_num_deltas(DiffHandle diff) return NativeMethods.git_diff_get_delta(diff, (UIntPtr)idx); } + public static unsafe DiffHandle git_diff_from_buffer(string content, UIntPtr len) + { + int res = NativeMethods.git_diff_from_buffer(out var diff, content, len); + Ensure.ZeroResult(res); + return new DiffHandle(diff, true); + } + #endregion #region git_error_ diff --git a/LibGit2Sharp/DeltaApplyStatus.cs b/LibGit2Sharp/DeltaApplyStatus.cs new file mode 100644 index 000000000..665431cfe --- /dev/null +++ b/LibGit2Sharp/DeltaApplyStatus.cs @@ -0,0 +1,23 @@ +namespace LibGit2Sharp +{ + /// + /// What to do with a delta. + /// + public enum DeltaApplyStatus + { + /// + /// Apply the delta. + /// + Apply, + + /// + /// Do not apply the delta and abort. + /// + Abort, + + /// + /// Do not apply the delta and continue. + /// + Skip + } +} diff --git a/LibGit2Sharp/Patch.cs b/LibGit2Sharp/Patch.cs index 50157eb32..e9876eec4 100644 --- a/LibGit2Sharp/Patch.cs +++ b/LibGit2Sharp/Patch.cs @@ -47,6 +47,16 @@ internal unsafe Patch(DiffHandle diff) } } + /// + /// Instantiate a patch from its content. + /// + /// The patch content + /// The Patch instance + public static Patch FromPatchContent(string content) + { + return new Patch(Proxy.git_diff_from_buffer(content, (UIntPtr)content.Length)); + } + private unsafe void AddFileChange(git_diff_delta* delta) { var treeEntryChanges = new TreeEntryChanges(delta); diff --git a/LibGit2Sharp/PatchApplyOptions.cs b/LibGit2Sharp/PatchApplyOptions.cs new file mode 100644 index 000000000..27b98a25f --- /dev/null +++ b/LibGit2Sharp/PatchApplyOptions.cs @@ -0,0 +1,37 @@ +namespace LibGit2Sharp +{ + /// + /// The options to be used for patch application. + /// + public sealed class PatchApplyOptions + { + /// + /// The location to apply (workdir, index or both) + /// + public PatchApplyLocation Location { get; set; } + } + + /// + /// Possible application locations for applying a patch. + /// + public enum PatchApplyLocation + { + /// + /// Apply the patch to the workdir, leaving the index untouched. + /// This is the equivalent of `git apply` with no location argument. + /// + Workdir = 0, + + /// + /// Apply the patch to the index, leaving the working directory + /// untouched. This is the equivalent of `git apply --cached`. + /// + Index = 1, + + /// + /// Apply the patch to both the working directory and the index. + /// This is the equivalent of `git apply --index`. + /// + Both = 2 + } +} diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index edeb6b93f..92540573d 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { @@ -470,5 +471,37 @@ public static string Describe(this IRepository repository, Commit commit) { return repository.Describe(commit, new DescribeOptions()); } + + /// + /// Applies a patch to a repository. + /// + /// The being worked with. + /// The diff to be applied. + /// The options to use. + public static void ApplyPatch(this Repository repository, string patchContent, PatchApplyOptions applyOptions = null) + { + if (applyOptions == null) + { + applyOptions = new PatchApplyOptions(); + } + + using (var diff = Proxy.git_diff_from_buffer(patchContent, (UIntPtr) patchContent.Length)) + { + Proxy.git_apply(repository.Handle, diff, applyOptions.Location, null); + } + } + + /// + /// Applies a patch file to a repository. + /// + /// The being worked with. + /// The path to the patch file + /// The options to use. + public static void ApplyPatchFile(this Repository repository, string patchFilePath, PatchApplyOptions options = null) + { + string content = File.ReadAllText(patchFilePath); + + ApplyPatch(repository, content, options); + } } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy