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);
}
}
}