diff --git a/Lib/NativeBinaries/amd64/git2-9bbc8f3.dll b/Lib/NativeBinaries/amd64/git2-9bbc8f3.dll deleted file mode 100644 index f162e6c60..000000000 Binary files a/Lib/NativeBinaries/amd64/git2-9bbc8f3.dll and /dev/null differ diff --git a/Lib/NativeBinaries/amd64/git2-b2d0243.dll b/Lib/NativeBinaries/amd64/git2-b2d0243.dll new file mode 100644 index 000000000..0d7c8f8dd Binary files /dev/null and b/Lib/NativeBinaries/amd64/git2-b2d0243.dll differ diff --git a/Lib/NativeBinaries/amd64/git2-9bbc8f3.pdb b/Lib/NativeBinaries/amd64/git2-b2d0243.pdb similarity index 51% rename from Lib/NativeBinaries/amd64/git2-9bbc8f3.pdb rename to Lib/NativeBinaries/amd64/git2-b2d0243.pdb index 43f4171fc..1835fa726 100644 Binary files a/Lib/NativeBinaries/amd64/git2-9bbc8f3.pdb and b/Lib/NativeBinaries/amd64/git2-b2d0243.pdb differ diff --git a/Lib/NativeBinaries/x86/git2-9bbc8f3.dll b/Lib/NativeBinaries/x86/git2-9bbc8f3.dll deleted file mode 100644 index db04bb5b2..000000000 Binary files a/Lib/NativeBinaries/x86/git2-9bbc8f3.dll and /dev/null differ diff --git a/Lib/NativeBinaries/x86/git2-b2d0243.dll b/Lib/NativeBinaries/x86/git2-b2d0243.dll new file mode 100644 index 000000000..cbabc3d6d Binary files /dev/null and b/Lib/NativeBinaries/x86/git2-b2d0243.dll differ diff --git a/Lib/NativeBinaries/x86/git2-9bbc8f3.pdb b/Lib/NativeBinaries/x86/git2-b2d0243.pdb similarity index 52% rename from Lib/NativeBinaries/x86/git2-9bbc8f3.pdb rename to Lib/NativeBinaries/x86/git2-b2d0243.pdb index 89c4624fc..c8b831dd9 100644 Binary files a/Lib/NativeBinaries/x86/git2-9bbc8f3.pdb and b/Lib/NativeBinaries/x86/git2-b2d0243.pdb differ diff --git a/LibGit2Sharp.Tests/StashFixture.cs b/LibGit2Sharp.Tests/StashFixture.cs index c9f94064f..228cc3e44 100644 --- a/LibGit2Sharp.Tests/StashFixture.cs +++ b/LibGit2Sharp.Tests/StashFixture.cs @@ -176,6 +176,106 @@ public void CanStashAndKeepIndex() } } + [Fact] + public void CanStashAndApplyWithOptions() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "staged_file_path.txt"; + Touch(repo.Info.WorkingDirectory, filename, "I'm staged\n"); + repo.Stage(filename); + + repo.Stashes.Add(stasher, "This stash with default options"); + Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Apply(0)); + + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(filename)); + Assert.Equal(1, repo.Stashes.Count()); + + repo.Stage(filename); + + repo.Stashes.Add(stasher, "This stash with default options"); + Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Apply(0, StashApplyModifiers.ReinstateIndex)); + + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); + Assert.Equal(2, repo.Stashes.Count()); + } + } + + [Fact] + public void CanStashAndPop() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "staged_file_path.txt"; + const string contents = "I'm staged\n"; + string contentsNew = "I'm staged" + Environment.NewLine; + Touch(repo.Info.WorkingDirectory, filename, contents); + repo.Stage(filename); + + repo.Stashes.Add(stasher, "This stash with default options"); + Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Pop(0)); + + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(filename)); + Assert.Equal(0, repo.Stashes.Count()); + Assert.Equal(contentsNew, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename))); + } + } + + [Fact] + public void StashReportsConflictsWhenReinstated() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "staged_file_path.txt"; + const string originalContents = "I'm pre-stash."; + const string filename2 = "unstaged_file_path.txt"; + const string newContents = "I'm post-stash."; + + Touch(repo.Info.WorkingDirectory, filename, originalContents); + repo.Stage(filename); + Touch(repo.Info.WorkingDirectory, filename2, originalContents); + + repo.Stashes.Add(stasher, "This stash with default options"); + + Touch(repo.Info.WorkingDirectory, filename, newContents); + repo.Stage(filename); + Touch(repo.Info.WorkingDirectory, filename2, newContents); + + Assert.Equal(StashApplyStatus.Conflicts, repo.Stashes.Pop(0, StashApplyModifiers.ReinstateIndex)); + Assert.Equal(1, repo.Stashes.Count()); + Assert.Equal(originalContents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename))); + Assert.Equal(originalContents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename2))); + + } + } + + [Fact] + public void StashReportsExistingInWorkDir() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + var stasher = Constants.Signature; + + const string filename = "unstaged_file_path.txt"; + Touch(repo.Info.WorkingDirectory, filename, "I'm unstaged\n"); + + repo.Stashes.Add(stasher, "This stash with default options", StashModifiers.IncludeUntracked); + Touch(repo.Info.WorkingDirectory, filename, "I'm another unstaged\n"); + + Assert.Equal(StashApplyStatus.UntrackedExist, repo.Stashes.Pop(0)); + } + } + [Fact] public void CanStashIgnoredFiles() { diff --git a/LibGit2Sharp/Core/GitCheckoutOpts.cs b/LibGit2Sharp/Core/GitCheckoutOpts.cs index 4416aa601..3424094be 100644 --- a/LibGit2Sharp/Core/GitCheckoutOpts.cs +++ b/LibGit2Sharp/Core/GitCheckoutOpts.cs @@ -158,6 +158,7 @@ internal struct GitCheckoutOpts public GitStrArray paths; public IntPtr baseline; + public IntPtr baseline_index; public IntPtr target_directory; public IntPtr ancestor_label; diff --git a/LibGit2Sharp/Core/GitMergeOpts.cs b/LibGit2Sharp/Core/GitMergeOpts.cs index a2ebe979d..7ed690fea 100644 --- a/LibGit2Sharp/Core/GitMergeOpts.cs +++ b/LibGit2Sharp/Core/GitMergeOpts.cs @@ -31,6 +31,11 @@ internal struct GitMergeOpts /// Flags for automerging content. /// public MergeFileFavor MergeFileFavorFlags; + + /// + /// Flags to use for file merging. + /// + public GitMergeFileFlags FileFlags; } /// @@ -105,4 +110,53 @@ internal enum GitMergeTreeFlags /// GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), } + + [Flags] + internal enum GitMergeFileFlags + { + /// + /// No options. + /// + GIT_MERGE_FILE_DEFAULT = 0, + + /// + /// Creates standard conflicted merge files. + /// + GIT_MERGE_FILE_STYLE_MERGE = (1 << 0), + + /// + /// Creates diff3 style files. + /// + GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1), + + /// + /// Condenses non-alphanumeric regions for simplified diff files. + /// + GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2), + + /// + /// Ignores all whitespace. + /// + GIT_MERGE_FILE_IGNORE_WHITESPACE = (1 << 3), + + /// + /// Ignores changes in amount of whitespace. + /// + GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = (1 << 4), + + /// + /// Ignores whitespace at the end of the line. + /// + GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = (1 << 5), + + /// + /// Uses the 'patience' diff algorithm. + /// + GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6), + + /// + /// Take extra time to find the minimal diff. + /// + GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7), + } } diff --git a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs index 724c6c414..b816cae9c 100644 --- a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs +++ b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs @@ -8,9 +8,11 @@ internal class GitSmartSubtransportRegistration { public IntPtr SubtransportCallback; public uint Rpc; + public IntPtr Param; public delegate int create_callback( out IntPtr subtransport, - IntPtr transport); + IntPtr transport, + IntPtr param); } } diff --git a/LibGit2Sharp/Core/NativeDllName.cs b/LibGit2Sharp/Core/NativeDllName.cs index 01f148a91..9d4d1a4bd 100644 --- a/LibGit2Sharp/Core/NativeDllName.cs +++ b/LibGit2Sharp/Core/NativeDllName.cs @@ -2,6 +2,6 @@ namespace LibGit2Sharp.Core { internal static class NativeDllName { - public const string Name = "git2-9bbc8f3"; + public const string Name = "git2-b2d0243"; } } diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index a8fb1eacf..699eed39e 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -726,7 +726,7 @@ internal static extern int git_note_remove( [DllImport(libgit2)] internal static extern int git_note_default_ref( - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] out string notes_ref, + GitBuf buf, RepositorySafeHandle repo); internal delegate int git_note_foreach_cb( @@ -1325,6 +1325,20 @@ internal static extern int git_stash_foreach( [DllImport(libgit2)] internal static extern int git_stash_drop(RepositorySafeHandle repo, UIntPtr index); + [DllImport(libgit2)] + internal static extern int git_stash_apply( + RepositorySafeHandle repo, + UIntPtr index, + ref GitCheckoutOpts opts, + StashApplyModifiers flags); + + [DllImport(libgit2)] + internal static extern int git_stash_pop( + RepositorySafeHandle repo, + UIntPtr index, + ref GitCheckoutOpts opts, + StashApplyModifiers flags); + [DllImport(libgit2)] internal static extern int git_status_file( out FileStatus statusflags, diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index ab2f7eadc..443903921 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -1298,12 +1298,12 @@ public static ObjectId git_note_create( public static string git_note_default_ref(RepositorySafeHandle repo) { using (ThreadAffinity()) + using (var buf = new GitBuf()) { - string notes_ref; - int res = NativeMethods.git_note_default_ref(out notes_ref, repo); + int res = NativeMethods.git_note_default_ref(buf, repo); Ensure.ZeroResult(res); - return notes_ref; + return LaxUtf8Marshaler.FromNative(buf.ptr); } } @@ -2719,7 +2719,47 @@ public static void git_stash_drop(RepositorySafeHandle repo, int index) using (ThreadAffinity()) { int res = NativeMethods.git_stash_drop(repo, (UIntPtr) index); - Ensure.BooleanResult(res); + Ensure.ZeroResult(res); + } + } + + private static StashApplyStatus get_stash_status(int res) + { + if (res == (int)GitErrorCode.MergeConflict) + { + return StashApplyStatus.Conflicts; + } + + if (res == (int)GitErrorCode.Exists) + { + return StashApplyStatus.UntrackedExist; + } + + Ensure.ZeroResult(res); + return StashApplyStatus.Applied; + } + + public static StashApplyStatus git_stash_apply( + RepositorySafeHandle repo, + int index, + ref GitCheckoutOpts opts, + StashApplyModifiers flags) + { + using (ThreadAffinity()) + { + return get_stash_status(NativeMethods.git_stash_apply(repo, (UIntPtr)index, ref opts, flags)); + } + } + + public static StashApplyStatus git_stash_pop( + RepositorySafeHandle repo, + int index, + ref GitCheckoutOpts opts, + StashApplyModifiers flags) + { + using (ThreadAffinity()) + { + return get_stash_status(NativeMethods.git_stash_pop(repo, (UIntPtr)index, ref opts, flags)); } } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 3ca4a3892..194a1fedb 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -134,6 +134,7 @@ + diff --git a/LibGit2Sharp/SmartSubtransportRegistration.cs b/LibGit2Sharp/SmartSubtransportRegistration.cs index df7c1cb4b..8247b023c 100644 --- a/LibGit2Sharp/SmartSubtransportRegistration.cs +++ b/LibGit2Sharp/SmartSubtransportRegistration.cs @@ -80,7 +80,8 @@ private static class EntryPoints private static int Subtransport( out IntPtr subtransport, - IntPtr transport) + IntPtr transport, + IntPtr payload) { subtransport = IntPtr.Zero; diff --git a/LibGit2Sharp/StashApplyStatus.cs b/LibGit2Sharp/StashApplyStatus.cs new file mode 100644 index 000000000..86fa6bff6 --- /dev/null +++ b/LibGit2Sharp/StashApplyStatus.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// The status of what happened as a result of a stash application. + /// + public enum StashApplyStatus + { + /// + /// The changes were successfully stashed. + /// + Applied, + + /// + /// The stash application resulted in conflicts. + /// + Conflicts, + + /// + /// The stash application was not applied due to existing + /// untracked files that would be overwritten by the stash + /// contents. + /// + UntrackedExist, + } +} diff --git a/LibGit2Sharp/StashCollection.cs b/LibGit2Sharp/StashCollection.cs index ffe137a5b..77c674cdd 100644 --- a/LibGit2Sharp/StashCollection.cs +++ b/LibGit2Sharp/StashCollection.cs @@ -114,6 +114,36 @@ public virtual void Remove(int index) Proxy.git_stash_drop(repo.Handle, index); } + /// + /// Applies a single stashed state from the stash list + /// + /// the index of the stash to remove (0 being the most recent one). + /// the options to use for checking out the stash. + /// the flags to use for applying the changes. + public virtual StashApplyStatus Apply(int index, StashApplyModifiers flags = StashApplyModifiers.Default, CheckoutOptions options = null) + { + using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options ?? new CheckoutOptions())) + { + var opts = checkoutOptionsWrapper.Options; + return Proxy.git_stash_apply(repo.Handle, index, ref opts, flags); + } + } + + /// + /// Pops a single stashed state from the stash list + /// + /// the index of the stash to remove (0 being the most recent one). + /// the options to use for checking out the stash. + /// the flags to use for applying the changes. + public virtual StashApplyStatus Pop(int index, StashApplyModifiers flags = StashApplyModifiers.Default, CheckoutOptions options = null) + { + using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options ?? new CheckoutOptions())) + { + var opts = checkoutOptionsWrapper.Options; + return Proxy.git_stash_pop(repo.Handle, index, ref opts, flags); + } + } + private string DebuggerDisplay { get diff --git a/LibGit2Sharp/StashModifiers.cs b/LibGit2Sharp/StashModifiers.cs index b0e6d41ff..1f2f66be7 100644 --- a/LibGit2Sharp/StashModifiers.cs +++ b/LibGit2Sharp/StashModifiers.cs @@ -31,4 +31,23 @@ public enum StashModifiers /// IncludeIgnored = (1 << 2), } + + /// + /// Options controlling Stash applying behavior. + /// + [Flags] + public enum StashApplyModifiers + { + /// + /// Default. Reinstante working directory stashed + /// changes. + /// + Default = 0, + + /// + /// Reinstate both index and working directory stashed + /// changes. + /// + ReinstateIndex = (1 << 0), + } } diff --git a/LibGit2Sharp/libgit2_hash.txt b/LibGit2Sharp/libgit2_hash.txt index 1f04184d9..985448b76 100644 --- a/LibGit2Sharp/libgit2_hash.txt +++ b/LibGit2Sharp/libgit2_hash.txt @@ -1 +1 @@ -9bbc8f350b80a5a6e94651ec667cf9e5d545b317 +b2d02433141ddc4c914a6b0e0027f6c58fc11cb6 diff --git a/libgit2 b/libgit2 index 9bbc8f350..47f374002 160000 --- a/libgit2 +++ b/libgit2 @@ -1 +1 @@ -Subproject commit 9bbc8f350b80a5a6e94651ec667cf9e5d545b317 +Subproject commit 47f37400253210f483d84fb9c2ecf44fb5986849 diff --git a/nuget.package/build/LibGit2Sharp.props b/nuget.package/build/LibGit2Sharp.props index b12792b74..3c028bc9e 100644 --- a/nuget.package/build/LibGit2Sharp.props +++ b/nuget.package/build/LibGit2Sharp.props @@ -1,20 +1,20 @@  - - NativeBinaries\amd64\git2-9bbc8f3.dll + + NativeBinaries\amd64\git2-b2d0243.dll PreserveNewest - - NativeBinaries\amd64\git2-9bbc8f3.pdb + + NativeBinaries\amd64\git2-b2d0243.pdb PreserveNewest - - NativeBinaries\x86\git2-9bbc8f3.dll + + NativeBinaries\x86\git2-b2d0243.dll PreserveNewest - - NativeBinaries\x86\git2-9bbc8f3.pdb + + NativeBinaries\x86\git2-b2d0243.pdb PreserveNewest