diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 25e652522..f382ef7a2 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -1,5 +1,7 @@  + + net472;netcoreapp3.1;net6.0 diff --git a/LibGit2Sharp/Commands/Pull.cs b/LibGit2Sharp/Commands/Pull.cs index bee1bbbda..aab844755 100644 --- a/LibGit2Sharp/Commands/Pull.cs +++ b/LibGit2Sharp/Commands/Pull.cs @@ -15,13 +15,11 @@ public static partial class Commands /// The repository. /// The signature to use for the merge. /// The options for fetch and merging. - public static MergeResult Pull(Repository repository, Signature merger, PullOptions options) + public static MergeResult Pull(Repository repository, Signature merger, PullOptions options = null) { Ensure.ArgumentNotNull(repository, "repository"); - Ensure.ArgumentNotNull(merger, "merger"); - options = options ?? new PullOptions(); Branch currentBranch = repository.Head; if (!currentBranch.IsTracking) @@ -34,7 +32,24 @@ public static MergeResult Pull(Repository repository, Signature merger, PullOpti throw new LibGit2SharpException("No upstream remote for the current branch."); } - Commands.Fetch(repository, currentBranch.RemoteName, new string[0], options.FetchOptions, null); + return Pull(repository, currentBranch.RemoteName, merger, options); + } + + /// + /// Fetch changes from the configured upstream remote and branch into the branch pointed at by HEAD. + /// + /// The repository. + /// The remote name or repository path. + /// The signature to use for the merge. + /// The options for fetch and merging. + public static MergeResult Pull(Repository repository, string remoteNameOrPath, Signature merger, PullOptions options = null) + { + Ensure.ArgumentNotNull(repository, "repository"); + Ensure.ArgumentNotNull(merger, "merger"); + + + options = options ?? new PullOptions(); + Commands.Fetch(repository, remoteNameOrPath, new string[0], options.FetchOptions, null); return repository.MergeFetchedRefs(merger, options.MergeOptions); } } diff --git a/LibGit2Sharp/ContentChangeLine.cs b/LibGit2Sharp/ContentChangeLine.cs new file mode 100644 index 000000000..90c3f95c0 --- /dev/null +++ b/LibGit2Sharp/ContentChangeLine.cs @@ -0,0 +1,18 @@ +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + public class ContentChangeLine + { + public int OldLineNo; + public int NewLineNo; + public int NumLines; + + internal ContentChangeLine(GitDiffLine line) + { + OldLineNo = line.OldLineNo; + NewLineNo = line.NewLineNo; + NumLines = line.NumLines; + } + } +} diff --git a/LibGit2Sharp/ContentChanges.cs b/LibGit2Sharp/ContentChanges.cs index c4628f919..dec88db79 100644 --- a/LibGit2Sharp/ContentChanges.cs +++ b/LibGit2Sharp/ContentChanges.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -11,9 +12,10 @@ namespace LibGit2Sharp /// Holds the changes between two s. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class ContentChanges + public class ContentChanges : IEnumerable { private readonly StringBuilder patchBuilder = new StringBuilder(); + private readonly List lines = new List(); /// /// Needed for mocking purposes. @@ -106,17 +108,6 @@ private unsafe int LineCallback(git_diff_delta* delta, GitDiffHunk hunk, GitDiff switch (line.lineOrigin) { case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION: - AddedLines.Add(new Line(line.NewLineNo, decodedContent)); - LinesAdded++; - prefix = Encoding.ASCII.GetString(new[] { (byte)line.lineOrigin }); - break; - - case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION: - DeletedLines.Add(new Line(line.OldLineNo, decodedContent)); - LinesDeleted++; - prefix = Encoding.ASCII.GetString(new[] { (byte)line.lineOrigin }); - break; - case GitDiffLineOrigin.GIT_DIFF_LINE_CONTEXT: prefix = Encoding.ASCII.GetString(new[] { (byte)line.lineOrigin }); break; @@ -126,11 +117,40 @@ private unsafe int LineCallback(git_diff_delta* delta, GitDiffHunk hunk, GitDiff break; } + AppendGitDiffLine(line, decodedContent); AppendToPatch(prefix); AppendToPatch(decodedContent); return 0; } + internal void AppendGitDiffLine(GitDiffLine line, string patch) + { + switch (line.lineOrigin) + { + case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION: + AddedLines.Add(new Line(line.NewLineNo, patch)); + LinesAdded++; + lines.Add(new ContentChangeLine(line)); + break; + + case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION: + DeletedLines.Add(new Line(line.OldLineNo, patch)); + LinesDeleted++; + lines.Add(new ContentChangeLine(line)); + break; + } + } + + public IEnumerator GetEnumerator() + { + return lines.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return lines.GetEnumerator(); + } + private string DebuggerDisplay { get diff --git a/LibGit2Sharp/Core/GitFetchOptions.cs b/LibGit2Sharp/Core/GitFetchOptions.cs index 3f0baa2c2..ec924cfe6 100644 --- a/LibGit2Sharp/Core/GitFetchOptions.cs +++ b/LibGit2Sharp/Core/GitFetchOptions.cs @@ -11,6 +11,7 @@ internal class GitFetchOptions public bool UpdateFetchHead = true; public TagFetchMode download_tags; public GitProxyOptions ProxyOptions; + public RemoteFollowRedirects FollowRedirects; public GitStrArrayManaged CustomHeaders; } } diff --git a/LibGit2Sharp/Core/GitPushOptions.cs b/LibGit2Sharp/Core/GitPushOptions.cs index f733534d2..a832a5f3d 100644 --- a/LibGit2Sharp/Core/GitPushOptions.cs +++ b/LibGit2Sharp/Core/GitPushOptions.cs @@ -9,6 +9,7 @@ internal class GitPushOptions public int PackbuilderDegreeOfParallelism; public GitRemoteCallbacks RemoteCallbacks; public GitProxyOptions ProxyOptions; + public RemoteFollowRedirects FollowRedirects; public GitStrArrayManaged CustomHeaders; } } diff --git a/LibGit2Sharp/Core/GitStatusOptions.cs b/LibGit2Sharp/Core/GitStatusOptions.cs index d577cefe6..d634fd20b 100644 --- a/LibGit2Sharp/Core/GitStatusOptions.cs +++ b/LibGit2Sharp/Core/GitStatusOptions.cs @@ -15,6 +15,8 @@ internal class GitStatusOptions : IDisposable public IntPtr Baseline = IntPtr.Zero; + public ushort RenameThreshold = 50; + public void Dispose() { PathSpec.Dispose(); diff --git a/LibGit2Sharp/Core/GitWorktree.cs b/LibGit2Sharp/Core/GitWorktree.cs index c71cb16c0..669038f68 100644 --- a/LibGit2Sharp/Core/GitWorktree.cs +++ b/LibGit2Sharp/Core/GitWorktree.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; -using System.Text; namespace LibGit2Sharp.Core { @@ -37,6 +35,8 @@ internal class git_worktree_add_options public int locked; public IntPtr @ref = IntPtr.Zero; + + public GitCheckoutOpts checkout_options; } [StructLayout(LayoutKind.Sequential)] diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index f5d45f3cf..972bf069f 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -66,7 +66,7 @@ private static string GetGlobalSettingsNativeLibraryPath() return Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension()); } -#if NETSTANDARD +#if NETSTANDARD || NET47_OR_GREATER private static bool TryUseNativeLibrary() => false; #else private static bool TryUseNativeLibrary() diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 50cefc0df..a365a739b 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -2378,8 +2378,9 @@ public static unsafe RemoteHandle git_remote_lookup(RepositoryHandle repo, strin git_remote* handle; int res = NativeMethods.git_remote_lookup(out handle, repo, name); - if (res == (int)GitErrorCode.NotFound && !throwsIfNotFound) + if (!throwsIfNotFound) { + if (res == (int)GitErrorCode.NotFound || res == (int)GitErrorCode.InvalidSpecification) return null; } diff --git a/LibGit2Sharp/Core/RemoteFollowRedirects.cs b/LibGit2Sharp/Core/RemoteFollowRedirects.cs new file mode 100644 index 000000000..387d41a75 --- /dev/null +++ b/LibGit2Sharp/Core/RemoteFollowRedirects.cs @@ -0,0 +1,10 @@ +namespace LibGit2Sharp.Core +{ + internal enum RemoteFollowRedirects + { + Unspecified = 0, + None = 1, + Initial = 2, + All = 4 + } +} diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 57c81cdfb..d15108bf7 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -1,7 +1,9 @@  + + - netstandard2.0;netcoreapp3.1 + netstandard2.1;netcoreapp3.1;net472;net50;net60 true LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .NET LibGit2Sharp contributors @@ -16,6 +18,7 @@ ..\libgit2sharp.snk square-logo.png App_Readme/LICENSE.md + false true @@ -30,9 +33,13 @@ - - - + + + + + + + diff --git a/LibGit2Sharp/Patch.cs b/LibGit2Sharp/Patch.cs index 50157eb32..6da72d144 100644 --- a/LibGit2Sharp/Patch.cs +++ b/LibGit2Sharp/Patch.cs @@ -76,19 +76,17 @@ private unsafe int PrintCallBack(git_diff_delta* delta, GitDiffHunk hunk, GitDif case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION: linesAdded++; - currentChange.LinesAdded++; - currentChange.AddedLines.Add(new Line(line.NewLineNo, patchPart)); prefix = "+"; break; case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION: linesDeleted++; - currentChange.LinesDeleted++; - currentChange.DeletedLines.Add(new Line(line.OldLineNo, patchPart)); prefix = "-"; break; } + currentChange.AppendGitDiffLine(line, patchPart); + string formattedOutput = string.Concat(prefix, patchPart); fullPatchBuilder.Append(formattedOutput); diff --git a/LibGit2Sharp/Rebase.cs b/LibGit2Sharp/Rebase.cs index 00dc3f267..9d753ddb5 100644 --- a/LibGit2Sharp/Rebase.cs +++ b/LibGit2Sharp/Rebase.cs @@ -69,6 +69,56 @@ unsafe AnnotatedCommitHandle AnnotatedCommitHandleFromRefHandle(ReferenceHandle Proxy.git_annotated_commit_from_ref(this.repository.Handle, refHandle); } + ReferenceHandle RefHandleFrom(Branch b) => + (b == null) ? null : this.repository.Refs.RetrieveReferencePtr(b.CanonicalName); + + ReferenceHandle RefHandleFrom(string name) => + (name == null) ? null : this.repository.Refs.RetrieveReferencePtr(name); + + class RebaseReferenceGuard : IDisposable + { + Reference rf; + Action disposeFn; + + public RebaseReferenceGuard(Reference rf_, Action disposeFn_) + { + rf = rf_; + disposeFn = disposeFn_; + } + + public string CanonicalName => rf.CanonicalName; + + public void Dispose() + { + disposeFn(rf); + } + } + + RebaseReferenceGuard ReferenceFrom(Commit c) + { + if (c == null) + return null; + + var rfname = $"refs/rebase/{c.Sha}"; + var rf = this.repository.Refs.Resolve(rfname); + rf = rf ?? this.repository.Refs.Add(rfname, c.Id); + + return new RebaseReferenceGuard(rf, r => { try { this.repository.Refs.Remove(r); } catch { }}); + } + + public virtual RebaseResult Start(Commit annotated, Commit upstream, Commit onto, Identity committer, RebaseOptions options) + { + Ensure.ArgumentNotNull(upstream, "upstream"); + + // TODO: Rebase does not work with just references, is that a bug or design feature? + using (var annotatedRef = ReferenceFrom(annotated)) + using (var upstreamRef = ReferenceFrom(upstream)) + using (var ontoRef = ReferenceFrom(onto)) + using (ReferenceHandle annotatedRefPtr = RefHandleFrom(annotatedRef?.CanonicalName)) + using (ReferenceHandle upstreamRefPtr = RefHandleFrom(upstreamRef?.CanonicalName)) + using (ReferenceHandle ontoRefPtr = RefHandleFrom(ontoRef?.CanonicalName)) + return Start(annotatedRefPtr, upstreamRefPtr, ontoRefPtr, committer, options); + } /// /// Start a rebase operation. /// @@ -82,6 +132,16 @@ public virtual RebaseResult Start(Branch branch, Branch upstream, Branch onto, I { Ensure.ArgumentNotNull(upstream, "upstream"); + using (ReferenceHandle branchRefPtr = RefHandleFrom(branch)) + using (ReferenceHandle upstreamRefPtr = RefHandleFrom(upstream)) + using (ReferenceHandle ontoRefPtr = RefHandleFrom(onto)) + return Start(branchRefPtr, upstreamRefPtr, ontoRefPtr, committer, options); + } + + internal virtual RebaseResult Start(ReferenceHandle annotatedRefPtr, ReferenceHandle upstreamRefPtr, ReferenceHandle ontoRefPtr, Identity committer, RebaseOptions options) + { + Ensure.ArgumentNotNull(upstreamRefPtr, "upstream"); + options = options ?? new RebaseOptions(); EnsureNonBareRepo(); @@ -92,13 +152,6 @@ public virtual RebaseResult Start(Branch branch, Branch upstream, Branch onto, I this.repository.Info.CurrentOperation); } - Func RefHandleFromBranch = (Branch b) => - { - return (b == null) ? - null : - this.repository.Refs.RetrieveReferencePtr(b.CanonicalName); - }; - using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options)) { GitRebaseOptions gitRebaseOptions = new GitRebaseOptions() @@ -107,10 +160,7 @@ public virtual RebaseResult Start(Branch branch, Branch upstream, Branch onto, I checkout_options = checkoutOptionsWrapper.Options, }; - using (ReferenceHandle branchRefPtr = RefHandleFromBranch(branch)) - using (ReferenceHandle upstreamRefPtr = RefHandleFromBranch(upstream)) - using (ReferenceHandle ontoRefPtr = RefHandleFromBranch(onto)) - using (AnnotatedCommitHandle annotatedBranchCommitHandle = AnnotatedCommitHandleFromRefHandle(branchRefPtr)) + using (AnnotatedCommitHandle annotatedBranchCommitHandle = AnnotatedCommitHandleFromRefHandle(annotatedRefPtr)) using (AnnotatedCommitHandle upstreamRefAnnotatedCommitHandle = AnnotatedCommitHandleFromRefHandle(upstreamRefPtr)) using (AnnotatedCommitHandle ontoRefAnnotatedCommitHandle = AnnotatedCommitHandleFromRefHandle(ontoRefPtr)) using (RebaseHandle rebaseOperationHandle = Proxy.git_rebase_init(this.repository.Handle, @@ -119,6 +169,8 @@ public virtual RebaseResult Start(Branch branch, Branch upstream, Branch onto, I ontoRefAnnotatedCommitHandle, gitRebaseOptions)) { + this.repository.Submodules.UpdateAll(options.SubmoduleUpdateOptions); + RebaseResult rebaseResult = RebaseOperationImpl.Run(rebaseOperationHandle, this.repository, committer, diff --git a/LibGit2Sharp/RebaseOptions.cs b/LibGit2Sharp/RebaseOptions.cs index 62cb6cbdb..301b9099e 100644 --- a/LibGit2Sharp/RebaseOptions.cs +++ b/LibGit2Sharp/RebaseOptions.cs @@ -41,6 +41,11 @@ public sealed class RebaseOptions : IConvertableToGitCheckoutOpts /// public CheckoutFileConflictStrategy FileConflictStrategy { get; set; } + /// + /// Submodule update options passed to submodule updates on rebase step. + /// + public SubmoduleUpdateOptions SubmoduleUpdateOptions { get; set; } + CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() { return CheckoutCallbacks.From(OnCheckoutProgress, OnCheckoutNotify); diff --git a/LibGit2Sharp/SubmoduleCollection.cs b/LibGit2Sharp/SubmoduleCollection.cs index fc508107a..dd66400e2 100644 --- a/LibGit2Sharp/SubmoduleCollection.cs +++ b/LibGit2Sharp/SubmoduleCollection.cs @@ -70,6 +70,15 @@ public virtual void Init(string name, bool overwrite) } } + + public virtual void UpdateAll(SubmoduleUpdateOptions options) + { + options = options ?? new SubmoduleUpdateOptions(); + + foreach (var sm in this) + Update(sm.Name, options); + } + /// /// Update specified submodule. /// diff --git a/LibGit2Sharp/WorktreeCollection.cs b/LibGit2Sharp/WorktreeCollection.cs index 9822e882c..42d459b11 100644 --- a/LibGit2Sharp/WorktreeCollection.cs +++ b/LibGit2Sharp/WorktreeCollection.cs @@ -1,12 +1,10 @@ -using LibGit2Sharp.Core; -using LibGit2Sharp.Core.Handles; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using System.Text; +using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { @@ -48,7 +46,7 @@ public virtual Worktree this[string name] } /// - /// + /// /// /// /// @@ -57,7 +55,7 @@ public virtual Worktree this[string name] /// public virtual Worktree Add(string committishOrBranchSpec, string name, string path, bool isLocked) { - if(string.Equals(committishOrBranchSpec, name)) + if (string.Equals(committishOrBranchSpec, name)) { // Proxy.git_worktree_add() creates a new branch of name = name, so if we want to checkout a given branch then the 'name' cannot be the same as the target branch return null; @@ -66,7 +64,8 @@ public virtual Worktree Add(string committishOrBranchSpec, string name, string p git_worktree_add_options options = new git_worktree_add_options { version = 1, - locked = Convert.ToInt32(isLocked) + locked = Convert.ToInt32(isLocked), + checkout_options = new GitCheckoutOpts { version = 1 } }; using (var handle = Proxy.git_worktree_add(repo.Handle, name, path, options)) @@ -83,13 +82,13 @@ public virtual Worktree Add(string committishOrBranchSpec, string name, string p } } - - return this[name]; + + return this[name]; } /// - /// + /// /// /// /// @@ -99,7 +98,8 @@ public virtual Worktree Add(string name, string path, bool isLocked) git_worktree_add_options options = new git_worktree_add_options { version = 1, - locked = Convert.ToInt32(isLocked) + locked = Convert.ToInt32(isLocked), + checkout_options = new GitCheckoutOpts { version = 1 } }; using (var handle = Proxy.git_worktree_add(repo.Handle, name, path, options)) @@ -112,7 +112,7 @@ public virtual Worktree Add(string name, string path, bool isLocked) } /// - /// + /// /// /// /// @@ -122,7 +122,7 @@ public virtual bool Prune(Worktree worktree) } /// - /// + /// /// /// /// diff --git a/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj b/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj index 3bca18b34..ec1eb34d1 100644 --- a/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj +++ b/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj @@ -1,5 +1,7 @@  + + Exe net472 diff --git a/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj b/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj index 0596f203c..b34a9b64c 100644 --- a/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj +++ b/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj @@ -1,5 +1,7 @@  + + Exe net472