using System; using System.Collections.Generic; using System.Globalization; using System.IO; using LibGit2Sharp.Core; using LibGit2Sharp.Handlers; namespace LibGit2Sharp { /// /// Provides helper overloads to a . /// public static class RepositoryExtensions { /// /// Try to lookup an object by its sha or a reference name. /// /// /// The being looked up. /// The revparse spec for the object to lookup. /// public static T Lookup(this IRepository repository, string objectish) where T : GitObject { EnsureNoGitLink(); if (typeof (T) == typeof (GitObject)) { return (T)repository.Lookup(objectish); } return (T)repository.Lookup(objectish, GitObject.TypeToKindMap[typeof(T)]); } /// /// Try to lookup an object by its . /// /// /// The being looked up. /// The id. /// public static T Lookup(this IRepository repository, ObjectId id) where T : GitObject { EnsureNoGitLink(); if (typeof(T) == typeof(GitObject)) { return (T)repository.Lookup(id); } return (T)repository.Lookup(id, GitObject.TypeToKindMap[typeof(T)]); } private static void EnsureNoGitLink() where T : GitObject { if (typeof(T) != typeof(GitLink)) { return; } throw new ArgumentException("A GitObject of type 'GitLink' cannot be looked up."); } /// /// Creates a lightweight tag with the specified name. This tag will point at the commit pointed at by the . /// /// The being worked with. /// The name of the tag to create. public static Tag ApplyTag(this IRepository repository, string tagName) { return ApplyTag(repository, tagName, repository.Head.CanonicalName); } /// /// Creates a lightweight tag with the specified name. This tag will point at the . /// /// The being worked with. /// The name of the tag to create. /// The revparse spec for the target object. public static Tag ApplyTag(this IRepository repository, string tagName, string objectish) { return repository.Tags.Add(tagName, objectish); } /// /// Creates an annotated tag with the specified name. This tag will point at the commit pointed at by the . /// /// The being worked with. /// The name of the tag to create. /// The identity of the creator of this tag. /// The annotation message. public static Tag ApplyTag(this IRepository repository, string tagName, Signature tagger, string message) { return ApplyTag(repository, tagName, repository.Head.CanonicalName, tagger, message); } /// /// Creates an annotated tag with the specified name. This tag will point at the . /// /// The being worked with. /// The name of the tag to create. /// The revparse spec for the target object. /// The identity of the creator of this tag. /// The annotation message. public static Tag ApplyTag(this IRepository repository, string tagName, string objectish, Signature tagger, string message) { return repository.Tags.Add(tagName, objectish, tagger, message); } /// /// Creates a branch with the specified name. This branch will point at the commit pointed at by the . /// /// The being worked with. /// The name of the branch to create. public static Branch CreateBranch(this IRepository repository, string branchName) { return CreateBranch(repository, branchName, repository.Head.Tip); } /// /// Creates a branch with the specified name. This branch will point at . /// /// The being worked with. /// The name of the branch to create. /// The commit which should be pointed at by the Branch. public static Branch CreateBranch(this IRepository repository, string branchName, Commit target) { return repository.Branches.Add(branchName, target); } /// /// Creates a branch with the specified name. This branch will point at the commit pointed at by the . /// /// The being worked with. /// The name of the branch to create. /// The revparse spec for the target commit. public static Branch CreateBranch(this IRepository repository, string branchName, string committish) { return repository.Branches.Add(branchName, committish); } /// /// Sets the current /> to the specified commit and optionally resets the and /// the content of the working tree to match. /// /// The being worked with. /// Flavor of reset operation to perform. /// A revparse spec for the target commit object. public static void Reset(this IRepository repository, ResetOptions resetOptions, string committish = "HEAD") { Ensure.ArgumentNotNullOrEmptyString(committish, "committish"); Commit commit = LookUpCommit(repository, committish); repository.Reset(resetOptions, commit); } /// /// Replaces entries in the with entries from the specified commit. /// /// The being worked with. /// A revparse spec for the target commit object. /// The list of paths (either files or directories) that should be considered. /// /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// public static void Reset(this IRepository repository, string committish = "HEAD", IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) { if (repository.Info.IsBare) { throw new BareRepositoryException("Reset is not allowed in a bare repository"); } Ensure.ArgumentNotNullOrEmptyString(committish, "committish"); Commit commit = LookUpCommit(repository, committish); repository.Reset(commit, paths, explicitPathsOptions); } private static Commit LookUpCommit(IRepository repository, string committish) { GitObject obj = repository.Lookup(committish); Ensure.GitObjectIsNotNull(obj, committish); return obj.DereferenceToCommit(true); } /// /// Stores the content of the as a new into the repository. /// The tip of the will be used as the parent of this new Commit. /// Once the commit is created, the will move forward to point at it. /// Both the Author and Committer will be guessed from the Git configuration. An exception will be raised if no configuration is reachable. /// /// The being worked with. /// The description of why a change was made to the repository. /// True to amend the current pointed at by , false otherwise. /// The generated . public static Commit Commit(this IRepository repository, string message, bool amendPreviousCommit = false) { Signature author = repository.Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now, true); return repository.Commit(message, author, amendPreviousCommit); } /// /// Stores the content of the as a new into the repository. /// The tip of the will be used as the parent of this new Commit. /// Once the commit is created, the will move forward to point at it. /// The Committer will be guessed from the Git configuration. An exception will be raised if no configuration is reachable. /// /// The being worked with. /// The of who made the change. /// The description of why a change was made to the repository. /// True to amend the current pointed at by , false otherwise. /// The generated . public static Commit Commit(this IRepository repository, string message, Signature author, bool amendPreviousCommit = false) { Signature committer = repository.Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now, true); return repository.Commit(message, author, committer, amendPreviousCommit); } /// /// Fetch from the specified remote. /// /// The being worked with. /// The name of the to fetch from. /// Optional parameter indicating what tags to download. /// Progress callback. Corresponds to libgit2 progress callback. /// Completion callback. Corresponds to libgit2 completion callback. /// UpdateTips callback. Corresponds to libgit2 update_tips callback. /// Callback method that transfer progress will be reported through. /// Reports the client's state regarding the received and processed (bytes, objects) from the server. /// Credentials to use for username/password authentication. public static void Fetch(this IRepository repository, string remoteName, TagFetchMode tagFetchMode = TagFetchMode.Auto, ProgressHandler onProgress = null, CompletionHandler onCompletion = null, UpdateTipsHandler onUpdateTips = null, TransferProgressHandler onTransferProgress = null, Credentials credentials = null) { Ensure.ArgumentNotNull(repository, "repository"); Ensure.ArgumentNotNullOrEmptyString(remoteName, "remoteName"); Remote remote = repository.Network.Remotes.RemoteForName(remoteName, true); repository.Network.Fetch(remote, tagFetchMode, onProgress, onCompletion, onUpdateTips, onTransferProgress, credentials); } /// /// Checkout the specified , reference or SHA. /// /// The being worked with. /// A revparse spec for the commit or branch to checkout. /// The that was checked out. public static Branch Checkout(this IRepository repository, string commitOrBranchSpec) { return repository.Checkout(commitOrBranchSpec, CheckoutOptions.None, null); } /// /// Checkout the specified . /// /// The being worked with. /// The to check out. /// The that was checked out. public static Branch Checkout(this IRepository repository, Branch branch) { return repository.Checkout(branch, CheckoutOptions.None, null); } internal static string BuildRelativePathFrom(this Repository repo, string path) { //TODO: To be removed when libgit2 natively implements this if (!Path.IsPathRooted(path)) { return path; } string normalizedPath = Path.GetFullPath(path); if (!repo.PathStartsWith(normalizedPath, repo.Info.WorkingDirectory)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to process file '{0}'. This file is not located under the working directory of the repository ('{1}').", normalizedPath, repo.Info.WorkingDirectory)); } return normalizedPath.Substring(repo.Info.WorkingDirectory.Length); } } }