diff options
author | Jameson Miller <jamill@microsoft.com> | 2014-04-22 17:25:49 +0400 |
---|---|---|
committer | Jameson Miller <jamill@microsoft.com> | 2014-05-03 07:03:28 +0400 |
commit | 90155789aedd51bc8f2ccb122f48977bc0fccc66 (patch) | |
tree | 4ee1020c8e6b75552193894fbaa547d54275efad /LibGit2Sharp | |
parent | 81cb7603bd106ab6d9eefda97dbebe56039d35bb (diff) |
Update Checkout and Merge options
Checkout methods now use CheckoutOptions
Merge now takes several options:
- Option to specify what is checked out for file conflicts.
- Report CheckoutProgress and CheckoutNotify
- Option to specify MergeFileFavor
Updates for code review feedback
Diffstat (limited to 'LibGit2Sharp')
-rw-r--r-- | LibGit2Sharp/Branch.cs | 30 | ||||
-rw-r--r-- | LibGit2Sharp/CheckoutCallbacks.cs | 2 | ||||
-rw-r--r-- | LibGit2Sharp/CheckoutFileConflictStrategy.cs | 43 | ||||
-rw-r--r-- | LibGit2Sharp/CheckoutNotificationOptions.cs | 1 | ||||
-rw-r--r-- | LibGit2Sharp/CheckoutOptions.cs | 37 | ||||
-rw-r--r-- | LibGit2Sharp/CloneOptions.cs | 29 | ||||
-rw-r--r-- | LibGit2Sharp/Core/GitCheckoutOpts.cs | 21 | ||||
-rw-r--r-- | LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs | 98 | ||||
-rw-r--r-- | LibGit2Sharp/Core/GitMergeOpts.cs | 10 | ||||
-rw-r--r-- | LibGit2Sharp/IRepository.cs | 41 | ||||
-rw-r--r-- | LibGit2Sharp/LibGit2Sharp.csproj | 2 | ||||
-rw-r--r-- | LibGit2Sharp/MergeOptions.cs | 120 | ||||
-rw-r--r-- | LibGit2Sharp/Repository.cs | 230 | ||||
-rw-r--r-- | LibGit2Sharp/RepositoryExtensions.cs | 9 |
14 files changed, 548 insertions, 125 deletions
diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index e13c5c57..1e64b8b9 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -233,9 +233,37 @@ namespace LibGit2Sharp /// <param name="checkoutModifiers">Options controlling checkout behavior.</param> /// <param name="onCheckoutProgress">Callback method to report checkout progress updates through.</param> /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> + [Obsolete("This overload will be removed in the next release. Please use Branch.Checkout(CheckoutOptions, Signature) instead.")] public virtual void Checkout(CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotificationOptions) { - repo.Checkout(this, checkoutModifiers, onCheckoutProgress, checkoutNotificationOptions); + var options = new CheckoutOptions + { + CheckoutModifiers = checkoutModifiers, + OnCheckoutProgress = onCheckoutProgress, + }; + + if (checkoutNotificationOptions != null) + { + options.OnCheckoutNotify = checkoutNotificationOptions.CheckoutNotifyHandler; + options.CheckoutNotifyFlags = checkoutNotificationOptions.NotifyFlags; + } + + Checkout(options, null); + } + + /// <summary> + /// Checkout the tip commit of this <see cref="Branch"/> object with + /// <see cref="CheckoutOptions"/> parameter specifying checkout + /// behavior. If this commit is the current tip of the branch, will + /// checkout the named branch. Otherwise, will checkout the tip + /// commit as a detached HEAD. + /// </summary> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + public virtual void Checkout(CheckoutOptions options, Signature signature = null) + { + Ensure.ArgumentNotNull(options, "options"); + repo.Checkout(this, options, signature); } private Branch ResolveTrackedBranch() diff --git a/LibGit2Sharp/CheckoutCallbacks.cs b/LibGit2Sharp/CheckoutCallbacks.cs index fa8817b8..dc03846b 100644 --- a/LibGit2Sharp/CheckoutCallbacks.cs +++ b/LibGit2Sharp/CheckoutCallbacks.cs @@ -69,7 +69,7 @@ namespace LibGit2Sharp /// <param name="onCheckoutProgress"><see cref="CheckoutProgressHandler"/> that should be wrapped in the native callback.</param> /// <param name="onCheckoutNotify"><see cref="CheckoutNotifyHandler"/> delegate to call in response to checkout notification callback.</param> /// <returns>The delegate with signature matching the expected native callback.</returns> - internal static CheckoutCallbacks GenerateCheckoutCallbacks(CheckoutProgressHandler onCheckoutProgress, CheckoutNotifyHandler onCheckoutNotify) + internal static CheckoutCallbacks From(CheckoutProgressHandler onCheckoutProgress, CheckoutNotifyHandler onCheckoutNotify) { return new CheckoutCallbacks(onCheckoutProgress, onCheckoutNotify); } diff --git a/LibGit2Sharp/CheckoutFileConflictStrategy.cs b/LibGit2Sharp/CheckoutFileConflictStrategy.cs new file mode 100644 index 00000000..578ebe03 --- /dev/null +++ b/LibGit2Sharp/CheckoutFileConflictStrategy.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LibGit2Sharp +{ + /// <summary> + /// Enum specifying what content checkout should write to disk + /// for conflicts. + /// </summary> + public enum CheckoutFileConflictStrategy + { + /// <summary> + /// Use the default behavior for handling file conflicts. This is + /// controlled by the merge.conflictstyle config option, and is "Merge" + /// if no option is explicitly set. + /// </summary> + Normal, + + /// <summary> + /// For conflicting files, checkout the "ours" (stage 2) version of + /// the file from the index. + /// </summary> + Ours, + + /// <summary> + /// For conflicting files, checkout the "theirs" (stage 3) version of + /// the file from the index. + /// </summary> + Theirs, + + /// <summary> + /// Write normal merge files for conflicts. + /// </summary> + Merge, + + /// <summary> + /// Write diff3 formated files for conflicts. + /// </summary> + Diff3 + } +} diff --git a/LibGit2Sharp/CheckoutNotificationOptions.cs b/LibGit2Sharp/CheckoutNotificationOptions.cs index 97b5012c..c1d0a167 100644 --- a/LibGit2Sharp/CheckoutNotificationOptions.cs +++ b/LibGit2Sharp/CheckoutNotificationOptions.cs @@ -45,6 +45,7 @@ namespace LibGit2Sharp /// <summary> /// Class to specify options and callback on CheckoutNotifications. /// </summary> + [Obsolete("This class will be removed in the next release. Specify CheckoutNotification options through CheckoutOptions instead.")] public class CheckoutNotificationOptions { /// <summary> diff --git a/LibGit2Sharp/CheckoutOptions.cs b/LibGit2Sharp/CheckoutOptions.cs index 69fd0439..9e297cd4 100644 --- a/LibGit2Sharp/CheckoutOptions.cs +++ b/LibGit2Sharp/CheckoutOptions.cs @@ -1,11 +1,12 @@ -using LibGit2Sharp.Handlers; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; namespace LibGit2Sharp { /// <summary> /// Collection of parameters controlling Checkout behavior. /// </summary> - public sealed class CheckoutOptions + public sealed class CheckoutOptions : IConvertableToGitCheckoutOpts { /// <summary> /// Options controlling checkout behavior. @@ -13,13 +14,39 @@ namespace LibGit2Sharp public CheckoutModifiers CheckoutModifiers { get; set; } /// <summary> - /// Callback method to report checkout progress updates through. + /// The flags specifying what conditions are + /// reported through the OnCheckoutNotify delegate. /// </summary> + public CheckoutNotifyFlags CheckoutNotifyFlags { get; set; } + + /// <summary> + /// Delegate to be called during checkout for files that match + /// desired filter specified with the NotifyFlags property. + /// </summary> + public CheckoutNotifyHandler OnCheckoutNotify { get; set; } + + /// Delegate through which checkout will notify callers of + /// certain conditions. The conditions that are reported is + /// controlled with the CheckoutNotifyFlags property. public CheckoutProgressHandler OnCheckoutProgress { get; set; } + CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy + { + get + { + return CheckoutModifiers.HasFlag(CheckoutModifiers.Force) ? + CheckoutStrategy.GIT_CHECKOUT_FORCE : CheckoutStrategy.GIT_CHECKOUT_SAFE; + } + } + /// <summary> - /// Options to manage checkout notifications. + /// Generate a <see cref="CheckoutCallbacks"/> object with the delegates + /// hooked up to the native callbacks. /// </summary> - public CheckoutNotificationOptions CheckoutNotificationOptions { get; set; } + /// <returns></returns> + CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() + { + return CheckoutCallbacks.From(OnCheckoutProgress, OnCheckoutNotify); + } } } diff --git a/LibGit2Sharp/CloneOptions.cs b/LibGit2Sharp/CloneOptions.cs index 09e513f7..65b98bd0 100644 --- a/LibGit2Sharp/CloneOptions.cs +++ b/LibGit2Sharp/CloneOptions.cs @@ -1,11 +1,12 @@ -using LibGit2Sharp.Handlers; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; namespace LibGit2Sharp { /// <summary> /// Options to define clone behaviour /// </summary> - public sealed class CloneOptions + public sealed class CloneOptions : IConvertableToGitCheckoutOpts { /// <summary> /// Creates default <see cref="CloneOptions"/> for a non-bare clone @@ -40,5 +41,29 @@ namespace LibGit2Sharp /// Credentials to use for user/pass authentication /// </summary> public Credentials Credentials { get; set; } + + #region IConvertableToGitCheckoutOpts + + CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() + { + return CheckoutCallbacks.From(OnCheckoutProgress, null); + } + + CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy + { + get + { + return this.Checkout ? + CheckoutStrategy.GIT_CHECKOUT_SAFE_CREATE : + CheckoutStrategy.GIT_CHECKOUT_NONE; + } + } + + CheckoutNotifyFlags IConvertableToGitCheckoutOpts.CheckoutNotifyFlags + { + get { return CheckoutNotifyFlags.None; } + } + + #endregion } } diff --git a/LibGit2Sharp/Core/GitCheckoutOpts.cs b/LibGit2Sharp/Core/GitCheckoutOpts.cs index ca694395..214251d6 100644 --- a/LibGit2Sharp/Core/GitCheckoutOpts.cs +++ b/LibGit2Sharp/Core/GitCheckoutOpts.cs @@ -120,7 +120,7 @@ namespace LibGit2Sharp.Core IntPtr payload); [StructLayout(LayoutKind.Sequential)] - internal struct GitCheckoutOpts :IDisposable + internal struct GitCheckoutOpts { public uint version; @@ -146,15 +146,18 @@ namespace LibGit2Sharp.Core public IntPtr ancestor_label; public IntPtr our_label; public IntPtr their_label; + } + + /// <summary> + /// An inteface for objects that specify parameters from which a + /// GitCheckoutOpts struct can be populated. + /// </summary> + internal interface IConvertableToGitCheckoutOpts + { + CheckoutCallbacks GenerateCallbacks(); - public void Dispose() - { - if (paths == null) - { - return; - } + CheckoutStrategy CheckoutStrategy { get; } - paths.Dispose(); - } + CheckoutNotifyFlags CheckoutNotifyFlags { get; } } } diff --git a/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs b/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs new file mode 100644 index 00000000..b98a5cd9 --- /dev/null +++ b/LibGit2Sharp/Core/GitCheckoutOptsWrapper.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LibGit2Sharp.Core +{ + /// <summary> + /// A wrapper around the native GitCheckoutOpts structure. This class is responsible + /// for the managed objects that the native code points to. + /// </summary> + internal class GitCheckoutOptsWrapper : IDisposable + { + /// <summary> + /// Create wrapper around <see cref="GitCheckoutOpts"/> from <see cref="CheckoutOptions"/>. + /// </summary> + /// <param name="options">Options to create native GitCheckoutOpts structure from.</param> + /// <param name="paths">Paths to checkout.</param> + public GitCheckoutOptsWrapper(IConvertableToGitCheckoutOpts options, FilePath[] paths = null) + { + Callbacks = options.GenerateCallbacks(); + + if (paths != null) + { + PathArray = GitStrArrayIn.BuildFrom(paths); + } + + Options = new GitCheckoutOpts + { + version = 1, + checkout_strategy = options.CheckoutStrategy, + progress_cb = Callbacks.CheckoutProgressCallback, + notify_cb = Callbacks.CheckoutNotifyCallback, + notify_flags = options.CheckoutNotifyFlags, + paths = PathArray, + }; + } + + /// <summary> + /// Native struct to pass to libgit. + /// </summary> + public GitCheckoutOpts Options { get; set; } + + /// <summary> + /// The managed class mapping native callbacks into the + /// corresponding managed delegate. + /// </summary> + public CheckoutCallbacks Callbacks { get; private set; } + + /// <summary> + /// Keep the paths around so we can dispose them. + /// </summary> + private GitStrArrayIn PathArray; + + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + if (PathArray != null) + { + PathArray.Dispose(); + PathArray = null; + } + } + } + + /// <summary> + /// Method to translate from <see cref="CheckoutFileConflictStrategy"/> to <see cref="CheckoutStrategy"/> flags. + /// </summary> + internal static CheckoutStrategy CheckoutStrategyFromFileConflictStrategy(CheckoutFileConflictStrategy fileConflictStrategy) + { + CheckoutStrategy flags = default(CheckoutStrategy); + + switch (fileConflictStrategy) + { + case CheckoutFileConflictStrategy.Ours: + flags = CheckoutStrategy.GIT_CHECKOUT_USE_OURS; + break; + case CheckoutFileConflictStrategy.Theirs: + flags = CheckoutStrategy.GIT_CHECKOUT_USE_THEIRS; + break; + case CheckoutFileConflictStrategy.Merge: + flags = CheckoutStrategy.GIT_CHECKOUT_CONFLICT_STYLE_MERGE; + break; + case CheckoutFileConflictStrategy.Diff3: + flags = CheckoutStrategy.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; + break; + } + + return flags; + } + } +} diff --git a/LibGit2Sharp/Core/GitMergeOpts.cs b/LibGit2Sharp/Core/GitMergeOpts.cs index 36ccd45f..0c677817 100644 --- a/LibGit2Sharp/Core/GitMergeOpts.cs +++ b/LibGit2Sharp/Core/GitMergeOpts.cs @@ -30,7 +30,7 @@ namespace LibGit2Sharp.Core /// <summary> /// Flags for automerging content. /// </summary> - public GitMergeFileFavorFlags MergeFileFavorFlags; + public MergeFileFavor MergeFileFavorFlags; } /// <summary> @@ -84,12 +84,4 @@ namespace LibGit2Sharp.Core /// </summary> GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), } - - internal enum GitMergeFileFavorFlags - { - GIT_MERGE_FILE_FAVOR_NORMAL = 0, - GIT_MERGE_FILE_FAVOR_OURS = 1, - GIT_MERGE_FILE_FAVOR_THEIRS = 2, - GIT_MERGE_FILE_FAVOR_UNION = 3, - } } diff --git a/LibGit2Sharp/IRepository.cs b/LibGit2Sharp/IRepository.cs index 44e40d2a..28ce2989 100644 --- a/LibGit2Sharp/IRepository.cs +++ b/LibGit2Sharp/IRepository.cs @@ -83,6 +83,7 @@ namespace LibGit2Sharp /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> /// <param name="signature">Identity for use when updating the reflog.</param> /// <returns>The <see cref="Branch"/> that was checked out.</returns> + [Obsolete("This overload will be removed in the next release. Please use Repository.Checkout(Branch, CheckoutOptions, Signature) instead.")] Branch Checkout(Branch branch, CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotificationOptions, Signature signature = null); /// <summary> @@ -98,6 +99,7 @@ namespace LibGit2Sharp /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> /// <param name="signature">Identity for use when updating the reflog.</param> /// <returns>The <see cref="Branch"/> that was checked out.</returns> + [Obsolete("This overload will be removed in the next release. Please use Repository.Checkout(string, CheckoutOptions, Signature) instead.")] Branch Checkout(string committishOrBranchSpec, CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotificationOptions, Signature signature = null); /// <summary> @@ -112,9 +114,48 @@ namespace LibGit2Sharp /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> /// <param name="signature">Identity for use when updating the reflog.</param> /// <returns>The <see cref="Branch"/> that was checked out.</returns> + [Obsolete("This overload will be removed in the next release. Please use Repository.Checkout(Commit, CheckoutOptions, Signature) instead.")] Branch Checkout(Commit commit, CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotificationOptions, Signature signature = null); /// <summary> + /// Checkout the commit pointed at by the tip of the specified <see cref="Branch"/>. + /// <para> + /// If this commit is the current tip of the branch as it exists in the repository, the HEAD + /// will point to this branch. Otherwise, the HEAD will be detached, pointing at the commit sha. + /// </para> + /// </summary> + /// <param name="branch">The <see cref="Branch"/> to check out.</param> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + /// <returns>The <see cref="Branch"/> that was checked out.</returns> + Branch Checkout(Branch branch, CheckoutOptions options, Signature signature = null); + + /// <summary> + /// Checkout the specified branch, reference or SHA. + /// <para> + /// If the committishOrBranchSpec parameter resolves to a branch name, then the checked out HEAD will + /// will point to the branch. Otherwise, the HEAD will be detached, pointing at the commit sha. + /// </para> + /// </summary> + /// <param name="committishOrBranchSpec">A revparse spec for the commit or branch to checkout.</param> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + /// <returns>The <see cref="Branch"/> that was checked out.</returns> + Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature = null); + + /// <summary> + /// Checkout the specified <see cref="LibGit2Sharp.Commit"/>. + /// <para> + /// Will detach the HEAD and make it point to this commit sha. + /// </para> + /// </summary> + /// <param name="commit">The <see cref="LibGit2Sharp.Commit"/> to check out.</param> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + /// <returns>The <see cref="Branch"/> that was checked out.</returns> + Branch Checkout(Commit commit, CheckoutOptions options, Signature signature = null); + + /// <summary> /// Updates specifed paths in the index and working directory with the versions from the specified branch, reference, or SHA. /// <para> /// This method does not switch branches or update the current repository HEAD. diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 566e4005..e8261962 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -69,6 +69,7 @@ <Compile Include="BranchTrackingDetails.cs" /> <Compile Include="BranchUpdater.cs" /> <Compile Include="CheckoutCallbacks.cs" /> + <Compile Include="CheckoutFileConflictStrategy.cs" /> <Compile Include="CheckoutModifiers.cs" /> <Compile Include="CheckoutNotificationOptions.cs" /> <Compile Include="CheckoutOptions.cs" /> @@ -79,6 +80,7 @@ <Compile Include="CompareOptions.cs" /> <Compile Include="ContentChangeStats.cs" /> <Compile Include="Core\GitBuiltInFeatures.cs" /> + <Compile Include="Core\GitCheckoutOptsWrapper.cs" /> <Compile Include="Core\GitCredentialType.cs" /> <Compile Include="Core\Handles\PatchSafeHandle.cs" /> <Compile Include="Core\IntPtrExtensions.cs" /> diff --git a/LibGit2Sharp/MergeOptions.cs b/LibGit2Sharp/MergeOptions.cs index 06eb13ce..be25665e 100644 --- a/LibGit2Sharp/MergeOptions.cs +++ b/LibGit2Sharp/MergeOptions.cs @@ -1,22 +1,40 @@ - +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; + namespace LibGit2Sharp { /// <summary> /// Options controlling Merge behavior. /// </summary> - public sealed class MergeOptions + public sealed class MergeOptions : IConvertableToGitCheckoutOpts { /// <summary> /// Initializes a new instance of the <see cref="MergeOptions"/> class. - /// By default, a fast-forward merge will be performed if possible, and - /// if a merge commit is created, then it will be commited. + /// <para> + /// Default behavior: + /// A fast-forward merge will be performed if possible. + /// A merge commit will be committed, if one was created. + /// Merge will attempt to find renames. + /// </para> /// </summary> public MergeOptions() { CommitOnSuccess = true; + + FindRenames = true; + // TODO: libgit2 should provide reasonable defaults for these + // values, but it currently does not. + RenameThreshold = 50; + TargetLimit = 200; } /// <summary> + /// The Flags specifying what conditions are + /// reported through the OnCheckoutNotify delegate. + /// </summary> + public CheckoutNotifyFlags CheckoutNotifyFlags { get; set; } + + /// <summary> /// Commit the merge if the merge is successful and this is a non-fast-forward merge. /// If this is a fast-forward merge, then there is no merge commit and this option /// will not affect the merge. @@ -27,6 +45,63 @@ namespace LibGit2Sharp /// The type of merge to perform. /// </summary> public FastForwardStrategy FastForwardStrategy { get; set; } + + /// <summary> + /// How conflicting index entries should be written out during checkout. + /// </summary> + public CheckoutFileConflictStrategy FileConflictStrategy { get; set; } + + /// <summary> + /// Find renames. Default is true. + /// </summary> + public bool FindRenames { get; set; } + + /// <summary> + /// Similarity to consider a file renamed. + /// </summary> + public int RenameThreshold; + + /// <summary> + /// Maximum similarity sources to examine (overrides + /// 'merge.renameLimit' config (default 200) + /// </summary> + public int TargetLimit; + + /// <summary> + /// How to handle conflicts encountered during a merge. + /// </summary> + public MergeFileFavor MergeFileFavor { get; set; } + + /// <summary> + /// Delegate that the checkout will report progress through. + /// </summary> + public CheckoutProgressHandler OnCheckoutProgress { get; set; } + + /// <summary> + /// Delegate that checkout will notify callers of + /// certain conditions. The conditions that are reported is + /// controlled with the CheckoutNotifyFlags property. + /// </summary> + public CheckoutNotifyHandler OnCheckoutNotify { get; set; } + + #region IConvertableToGitCheckoutOpts + + CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() + { + return CheckoutCallbacks.From(OnCheckoutProgress, OnCheckoutNotify); + } + + CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy + { + get + { + return CheckoutStrategy.GIT_CHECKOUT_SAFE| + CheckoutStrategy.GIT_CHECKOUT_ALLOW_CONFLICTS | + GitCheckoutOptsWrapper.CheckoutStrategyFromFileConflictStrategy(FileConflictStrategy); + } + } + + #endregion } /// <summary> @@ -51,4 +126,41 @@ namespace LibGit2Sharp /// </summary> FastForwardOnly = 2, /* GIT_MERGE_FASTFORWARD_ONLY */ } + + /// <summary> + /// Enum specifying how merge should deal with conflicting regions + /// of the files. + /// </summary> + public enum MergeFileFavor + { + /// <summary> + /// When a region of a file is changed in both branches, a conflict + /// will be recorded in the index so that the checkout operation can produce + /// a merge file with conflict markers in the working directory. + /// This is the default. + /// </summary> + Normal = 0, + + /// <summary> + /// When a region of a file is changed in both branches, the file + /// created in the index will contain the "ours" side of any conflicting + /// region. The index will not record a conflict. + /// </summary> + Ours = 1, + + /// <summary> + /// When a region of a file is changed in both branches, the file + /// created in the index will contain the "theirs" side of any conflicting + /// region. The index will not record a conflict. + /// </summary> + Theirs = 2, + + /// <summary> + /// When a region of a file is changed in both branches, the file + /// created in the index will contain each unique line from each side, + /// which has the result of combining both files. The index will not + /// record a conflict. + /// </summary> + Union = 3, + } } diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index ce99a25e..36ffc279 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -549,36 +549,29 @@ namespace LibGit2Sharp { options = options ?? new CloneOptions(); - CheckoutCallbacks checkoutCallbacks = CheckoutCallbacks.GenerateCheckoutCallbacks( - options.OnCheckoutProgress, null); + using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options)) + { + var gitCheckoutOptions = checkoutOptionsWrapper.Options; - var callbacks = new RemoteCallbacks(null, options.OnTransferProgress, null, - options.Credentials); - GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks(); + var remoteCallbacks = new RemoteCallbacks(null, options.OnTransferProgress, null, options.Credentials); + var gitRemoteCallbacks = remoteCallbacks.GenerateCallbacks(); - var cloneOpts = new GitCloneOptions - { - Version = 1, - Bare = options.IsBare ? 1 : 0, - CheckoutOpts = + var cloneOpts = new GitCloneOptions { - version = 1, - progress_cb = - checkoutCallbacks.CheckoutProgressCallback, - checkout_strategy = options.Checkout - ? CheckoutStrategy.GIT_CHECKOUT_SAFE_CREATE - : CheckoutStrategy.GIT_CHECKOUT_NONE - }, - RemoteCallbacks = gitCallbacks, - }; + Version = 1, + Bare = options.IsBare ? 1 : 0, + CheckoutOpts = gitCheckoutOptions, + RemoteCallbacks = gitRemoteCallbacks, + }; - FilePath repoPath; - using (RepositorySafeHandle repo = Proxy.git_clone(sourceUrl, workdirPath, ref cloneOpts)) - { - repoPath = Proxy.git_repository_path(repo); - } + FilePath repoPath; + using (RepositorySafeHandle repo = Proxy.git_clone(sourceUrl, workdirPath, ref cloneOpts)) + { + repoPath = Proxy.git_repository_path(repo); + } - return repoPath.Native; + return repoPath.Native; + } } /// <summary> @@ -605,9 +598,39 @@ namespace LibGit2Sharp /// <param name="checkoutNotifications"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> /// <param name="signature">Identity for use when updating the reflog.</param> /// <returns>The <see cref="Branch"/> that was checked out.</returns> + [Obsolete("This overload will be removed in the next release. Please use Repository.Checkout(string, CheckoutOptions, Signature) instead.")] public Branch Checkout(string committishOrBranchSpec, CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotifications, Signature signature = null) { + var options = new CheckoutOptions() + { + CheckoutModifiers = checkoutModifiers, + OnCheckoutProgress = onCheckoutProgress + }; + + if (checkoutNotifications != null) + { + options.OnCheckoutNotify = checkoutNotifications.CheckoutNotifyHandler; + options.CheckoutNotifyFlags = checkoutNotifications.NotifyFlags; + } + + return Checkout(committishOrBranchSpec, options, signature); + } + + /// <summary> + /// Checkout the specified <see cref="Branch"/>, reference or SHA. + /// <para> + /// If the committishOrBranchSpec parameter resolves to a branch name, then the checked out HEAD will + /// will point to the branch. Otherwise, the HEAD will be detached, pointing at the commit sha. + /// </para> + /// </summary> + /// <param name="committishOrBranchSpec">A revparse spec for the commit or branch to checkout.</param> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + /// <returns>The <see cref="Branch"/> that was checked out.</returns> + public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature = null) + { Ensure.ArgumentNotNullOrEmptyString(committishOrBranchSpec, "committishOrBranchSpec"); + Ensure.ArgumentNotNull(options, "options"); var handles = Proxy.git_revparse_ext(Handle, committishOrBranchSpec); if (handles == null) @@ -626,7 +649,7 @@ namespace LibGit2Sharp if (reference.IsLocalBranch()) { Branch branch = Branches[reference.CanonicalName]; - return Checkout(branch, checkoutModifiers, onCheckoutProgress, checkoutNotifications); + return Checkout(branch, options, signature); } } @@ -640,7 +663,7 @@ namespace LibGit2Sharp } Commit commit = obj.DereferenceToCommit(true); - Checkout(commit.Tree, checkoutModifiers, onCheckoutProgress, checkoutNotifications, commit.Id.Sha, committishOrBranchSpec, signature); + Checkout(commit.Tree, options, commit.Id.Sha, committishOrBranchSpec, signature); return Head; } @@ -656,10 +679,40 @@ namespace LibGit2Sharp /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> /// <param name="signature">Identity for use when updating the reflog.</param> /// <returns>The <see cref="Branch"/> that was checked out.</returns> + [Obsolete("This overload will be removed in the next release. Please use Repository.Checkout(Branch, CheckoutOptions, Signature) instead.")] public Branch Checkout(Branch branch, CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotificationOptions, Signature signature = null) { Ensure.ArgumentNotNull(branch, "branch"); + var options = new CheckoutOptions + { + CheckoutModifiers = checkoutModifiers, + OnCheckoutProgress = onCheckoutProgress, + }; + + if (checkoutNotificationOptions != null) + { + options.OnCheckoutNotify = checkoutNotificationOptions.CheckoutNotifyHandler; + options.CheckoutNotifyFlags = checkoutNotificationOptions.NotifyFlags; + } + + return Checkout(branch, options, signature); + } + + /// <summary> + /// Checkout the tip commit of the specified <see cref="Branch"/> object. If this commit is the + /// current tip of the branch, will checkout the named branch. Otherwise, will checkout the tip commit + /// as a detached HEAD. + /// </summary> + /// <param name="branch">The <see cref="Branch"/> to check out.</param> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + /// <returns>The <see cref="Branch"/> that was checked out.</returns> + public Branch Checkout(Branch branch, CheckoutOptions options, Signature signature = null) + { + Ensure.ArgumentNotNull(branch, "branch"); + Ensure.ArgumentNotNull(options, "options"); + // Make sure this is not an unborn branch. if (branch.Tip == null) { @@ -672,11 +725,11 @@ namespace LibGit2Sharp string.Equals(Refs[branch.CanonicalName].TargetIdentifier, branch.Tip.Id.Sha, StringComparison.OrdinalIgnoreCase)) { - Checkout(branch.Tip.Tree, checkoutModifiers, onCheckoutProgress, checkoutNotificationOptions, branch.CanonicalName, branch.Name, signature); + Checkout(branch.Tip.Tree, options, branch.CanonicalName, branch.Name, signature); } else { - Checkout(branch.Tip.Tree, checkoutModifiers, onCheckoutProgress, checkoutNotificationOptions, branch.Tip.Id.Sha, branch.Name, signature); + Checkout(branch.Tip.Tree, options, branch.Tip.Id.Sha, branch.Name, signature); } return Head; @@ -694,9 +747,43 @@ namespace LibGit2Sharp /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> /// <param name="signature">Identity for use when updating the reflog.</param> /// <returns>The <see cref="Branch"/> that was checked out.</returns> + [Obsolete("This overload will be removed in the next release. Please use Repository.Checkout(Commit, CheckoutOptions, Signature) instead.")] public Branch Checkout(Commit commit, CheckoutModifiers checkoutModifiers, CheckoutProgressHandler onCheckoutProgress, CheckoutNotificationOptions checkoutNotificationOptions, Signature signature = null) { - Checkout(commit.Tree, checkoutModifiers, onCheckoutProgress, checkoutNotificationOptions, commit.Id.Sha, commit.Id.Sha, signature); + + var options = new CheckoutOptions + { + CheckoutModifiers = checkoutModifiers, + OnCheckoutProgress = onCheckoutProgress, + }; + + if (checkoutNotificationOptions != null) + { + options.OnCheckoutNotify = checkoutNotificationOptions.CheckoutNotifyHandler; + options.CheckoutNotifyFlags = checkoutNotificationOptions.NotifyFlags; + } + + Checkout(commit.Tree, options, commit.Id.Sha, commit.Id.Sha, signature); + + return Head; + } + + /// <summary> + /// Checkout the specified <see cref="LibGit2Sharp.Commit"/>. + /// <para> + /// Will detach the HEAD and make it point to this commit sha. + /// </para> + /// </summary> + /// <param name="commit">The <see cref="LibGit2Sharp.Commit"/> to check out.</param> + /// <param name="options"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> + /// <param name="signature">Identity for use when updating the reflog.</param> + /// <returns>The <see cref="Branch"/> that was checked out.</returns> + public Branch Checkout(Commit commit, CheckoutOptions options, Signature signature = null) + { + Ensure.ArgumentNotNull(commit, "commit"); + Ensure.ArgumentNotNull(options, "options"); + + Checkout(commit.Tree, options, commit.Id.Sha, commit.Id.Sha, signature); return Head; } @@ -706,29 +793,18 @@ namespace LibGit2Sharp /// to already be in the form of a canonical branch name or a commit ID. /// </summary> /// <param name="tree">The <see cref="Tree"/> to checkout.</param> - /// <param name="checkoutModifiers"><see cref="CheckoutModifiers"/> controlling checkout behavior.</param> - /// <param name="onCheckoutProgress"><see cref="CheckoutProgressHandler"/> that checkout progress is reported through.</param> - /// <param name="checkoutNotificationOptions"><see cref="CheckoutNotificationOptions"/> to manage checkout notifications.</param> + /// <param name="checkoutOptions"><see cref="CheckoutOptions"/> controlling checkout behavior.</param> /// <param name="headTarget">Target for the new HEAD.</param> /// <param name="refLogHeadSpec">The spec which will be written as target in the reflog.</param> /// <param name="signature">Identity for use when updating the reflog.</param> private void Checkout( Tree tree, - CheckoutModifiers checkoutModifiers, - CheckoutProgressHandler onCheckoutProgress, - CheckoutNotificationOptions checkoutNotificationOptions, + CheckoutOptions checkoutOptions, string headTarget, string refLogHeadSpec, Signature signature) { var previousHeadName = Info.IsHeadDetached ? Head.Tip.Sha : Head.Name; - var opts = new CheckoutOptions - { - CheckoutModifiers = checkoutModifiers, - OnCheckoutProgress = onCheckoutProgress, - CheckoutNotificationOptions = checkoutNotificationOptions - }; - - CheckoutTree(tree, null, opts); + CheckoutTree(tree, null, checkoutOptions); Refs.UpdateTarget("HEAD", headTarget, signature, string.Format( @@ -745,37 +821,14 @@ namespace LibGit2Sharp private void CheckoutTree( Tree tree, IList<string> paths, - CheckoutOptions opts) + IConvertableToGitCheckoutOpts opts) { - CheckoutNotifyHandler onCheckoutNotify = opts.CheckoutNotificationOptions != null ? opts.CheckoutNotificationOptions.CheckoutNotifyHandler : null; - CheckoutNotifyFlags checkoutNotifyFlags = opts.CheckoutNotificationOptions != null ? opts.CheckoutNotificationOptions.NotifyFlags : default(CheckoutNotifyFlags); - CheckoutCallbacks checkoutCallbacks = CheckoutCallbacks.GenerateCheckoutCallbacks(opts.OnCheckoutProgress, onCheckoutNotify); - - GitStrArrayIn strArray = (paths != null && paths.Count > 0) ? GitStrArrayIn.BuildFrom(ToFilePaths(paths)) : null; - - var options = new GitCheckoutOpts - { - version = 1, - checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_SAFE, - progress_cb = checkoutCallbacks.CheckoutProgressCallback, - notify_cb = checkoutCallbacks.CheckoutNotifyCallback, - notify_flags = checkoutNotifyFlags, - paths = strArray - }; - try + using(GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(opts, ToFilePaths(paths))) { - if (opts.CheckoutModifiers.HasFlag(CheckoutModifiers.Force)) - { - options.checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_FORCE; - } - + var options = checkoutOptionsWrapper.Options; Proxy.git_checkout_tree(Handle, tree.Id, ref options); } - finally - { - options.Dispose(); - } } /// <summary> @@ -977,14 +1030,7 @@ namespace LibGit2Sharp | CheckoutStrategy.GIT_CHECKOUT_ALLOW_CONFLICTS, }; - try - { - Proxy.git_checkout_index(Handle, new NullGitObjectSafeHandle(), ref options); - } - finally - { - options.Dispose(); - } + Proxy.git_checkout_index(Handle, new NullGitObjectSafeHandle(), ref options); } private void CleanupDisposableDependencies() @@ -1168,7 +1214,7 @@ namespace LibGit2Sharp throw new LibGit2SharpException("Unable to perform Fast-Forward merge with mith multiple merge heads."); } - mergeResult = FastForwardMerge(mergeHeads[0], merger); + mergeResult = FastForwardMerge(mergeHeads[0], merger, options); } else if (mergeAnalysis.HasFlag(GitMergeAnalysis.GIT_MERGE_ANALYSIS_NORMAL)) { @@ -1184,7 +1230,7 @@ namespace LibGit2Sharp throw new LibGit2SharpException("Unable to perform Fast-Forward merge with mith multiple merge heads."); } - mergeResult = FastForwardMerge(mergeHeads[0], merger); + mergeResult = FastForwardMerge(mergeHeads[0], merger, options); } else { @@ -1226,15 +1272,20 @@ namespace LibGit2Sharp var mergeOptions = new GitMergeOpts { - Version = 1 + Version = 1, + MergeFileFavorFlags = options.MergeFileFavor, + MergeTreeFlags = options.FindRenames ? GitMergeTreeFlags.GIT_MERGE_TREE_FIND_RENAMES : + GitMergeTreeFlags.GIT_MERGE_TREE_NORMAL, + RenameThreshold = (uint) options.RenameThreshold, + TargetLimit = (uint) options.TargetLimit, }; - var checkoutOpts = new GitCheckoutOpts + using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options)) { - version = 1 - }; + var checkoutOpts = checkoutOptionsWrapper.Options; - Proxy.git_merge(Handle, mergeHeads, mergeOptions, checkoutOpts); + Proxy.git_merge(Handle, mergeHeads, mergeOptions, checkoutOpts); + } if (Index.IsFullyMerged) { @@ -1260,18 +1311,15 @@ namespace LibGit2Sharp /// </summary> /// <param name="mergeHead">The merge head handle to fast-forward merge.</param> /// <param name="merger">The <see cref="Signature"/> of who is performing the merge.</param> + /// <param name="options">Options controlling merge behavior.</param> /// <returns>The <see cref="MergeResult"/> of the merge.</returns> - private MergeResult FastForwardMerge(GitMergeHeadHandle mergeHead, Signature merger) + private MergeResult FastForwardMerge(GitMergeHeadHandle mergeHead, Signature merger, MergeOptions options) { ObjectId id = Proxy.git_merge_head_id(mergeHead); Commit fastForwardCommit = (Commit) Lookup(id, ObjectType.Commit); Ensure.GitObjectIsNotNull(fastForwardCommit, id.Sha); - var checkoutOpts = new CheckoutOptions - { - CheckoutModifiers = CheckoutModifiers.None, - }; - CheckoutTree(fastForwardCommit.Tree, null, checkoutOpts); + CheckoutTree(fastForwardCommit.Tree, null, options); var reference = Refs.Head.ResolveToDirectReference(); diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index 8e859072..43907b76 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -254,7 +254,8 @@ namespace LibGit2Sharp /// <returns>The <see cref="Branch"/> that was checked out.</returns> public static Branch Checkout(this IRepository repository, string commitOrBranchSpec, Signature signature = null) { - return repository.Checkout(commitOrBranchSpec, CheckoutModifiers.None, null, null, signature); + CheckoutOptions options = new CheckoutOptions(); + return repository.Checkout(commitOrBranchSpec, options, signature); } /// <summary> @@ -270,7 +271,8 @@ namespace LibGit2Sharp /// <returns>The <see cref="Branch"/> that was checked out.</returns> public static Branch Checkout(this IRepository repository, Branch branch, Signature signature = null) { - return repository.Checkout(branch, CheckoutModifiers.None, null, null, signature); + CheckoutOptions options = new CheckoutOptions(); + return repository.Checkout(branch, options, signature); } /// <summary> @@ -285,7 +287,8 @@ namespace LibGit2Sharp /// <returns>The <see cref="Branch"/> that was checked out.</returns> public static Branch Checkout(this IRepository repository, Commit commit, Signature signature = null) { - return repository.Checkout(commit, CheckoutModifiers.None, null, null, signature); + CheckoutOptions options = new CheckoutOptions(); + return repository.Checkout(commit, options, signature); } internal static string BuildRelativePathFrom(this Repository repo, string path) |