using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;
namespace LibGit2Sharp
{
///
/// The collection of Branches in a
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class BranchCollection : IEnumerable
{
internal readonly Repository repo;
///
/// Needed for mocking purposes.
///
protected BranchCollection()
{ }
///
/// Initializes a new instance of the class.
///
/// The repo.
internal BranchCollection(Repository repo)
{
this.repo = repo;
}
///
/// Gets the with the specified name.
///
public virtual Branch this[string name]
{
get
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
if (LooksLikeABranchName(name))
{
return BuildFromReferenceName(name);
}
Branch branch = BuildFromReferenceName(ShortToLocalName(name));
if (branch != null)
{
return branch;
}
branch = BuildFromReferenceName(ShortToRemoteName(name));
if (branch != null)
{
return branch;
}
return BuildFromReferenceName(ShortToRefName(name));
}
}
private static string ShortToLocalName(string name)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", Reference.LocalBranchPrefix, name);
}
private static string ShortToRemoteName(string name)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", Reference.RemoteTrackingBranchPrefix, name);
}
private static string ShortToRefName(string name)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", "refs/", name);
}
private Branch BuildFromReferenceName(string canonicalName)
{
var reference = repo.Refs.Resolve(canonicalName);
return reference == null ? null : new Branch(repo, reference, canonicalName);
}
#region IEnumerable Members
///
/// Returns an enumerator that iterates through the collection.
///
/// An object that can be used to iterate through the collection.
public virtual IEnumerator GetEnumerator()
{
return Proxy.git_branch_iterator(repo, GitBranchType.GIT_BRANCH_ALL)
.ToList()
.GetEnumerator();
}
///
/// Returns an enumerator that iterates through the collection.
///
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
///
/// Create a new local branch with the specified name
///
/// The name of the branch.
/// Revparse spec for the target commit.
/// A new .
public virtual Branch Add(string name, string committish)
{
return Add(name, committish, false);
}
///
/// Create a new local branch with the specified name
///
/// The name of the branch.
/// The target commit.
/// A new .
public virtual Branch Add(string name, Commit commit)
{
return Add(name, commit, false);
}
///
/// Create a new local branch with the specified name
///
/// The name of the branch.
/// The target commit.
/// True to allow silent overwriting a potentially existing branch, false otherwise.
/// A new .
public virtual Branch Add(string name, Commit commit, bool allowOverwrite)
{
Ensure.ArgumentNotNull(commit, "commit");
return Add(name, commit.Sha, allowOverwrite);
}
///
/// Create a new local branch with the specified name
///
/// The name of the branch.
/// Revparse spec for the target commit.
/// True to allow silent overwriting a potentially existing branch, false otherwise.
/// A new .
public virtual Branch Add(string name, string committish, bool allowOverwrite)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNullOrEmptyString(committish, "committish");
using (Proxy.git_branch_create_from_annotated(repo.Handle, name, committish, allowOverwrite))
{ }
var branch = this[ShortToLocalName(name)];
return branch;
}
///
/// Deletes the branch with the specified name.
///
/// The name of the branch to delete.
public virtual void Remove(string name)
{
Remove(name, false);
}
///
/// Deletes the branch with the specified name.
///
/// The name of the branch to delete.
/// True if the provided is the name of a remote branch, false otherwise.
public virtual void Remove(string name, bool isRemote)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
string branchName = isRemote ? Reference.RemoteTrackingBranchPrefix + name : name;
Branch branch = this[branchName];
if (branch == null)
{
return;
}
Remove(branch);
}
///
/// Deletes the specified branch.
///
/// The branch to delete.
public virtual void Remove(Branch branch)
{
Ensure.ArgumentNotNull(branch, "branch");
using (ReferenceHandle referencePtr = repo.Refs.RetrieveReferencePtr(branch.CanonicalName))
{
Proxy.git_branch_delete(referencePtr);
}
}
///
/// Rename an existing local branch, using the default reflog message
///
/// The current branch name.
/// The new name the existing branch should bear.
/// A new .
public virtual Branch Rename(string currentName, string newName)
{
return Rename(currentName, newName, false);
}
///
/// Rename an existing local branch, using the default reflog message
///
/// The current branch name.
/// The new name the existing branch should bear.
/// True to allow silent overwriting a potentially existing branch, false otherwise.
/// A new .
public virtual Branch Rename(string currentName, string newName, bool allowOverwrite)
{
Ensure.ArgumentNotNullOrEmptyString(currentName, "currentName");
Ensure.ArgumentNotNullOrEmptyString(newName, "newName");
Branch branch = this[currentName];
if (branch == null)
{
throw new LibGit2SharpException("No branch named '{0}' exists in the repository.");
}
return Rename(branch, newName, allowOverwrite);
}
///
/// Rename an existing local branch
///
/// The current local branch.
/// The new name the existing branch should bear.
/// A new .
public virtual Branch Rename(Branch branch, string newName)
{
return Rename(branch, newName, false);
}
///
/// Rename an existing local branch
///
/// The current local branch.
/// The new name the existing branch should bear.
/// True to allow silent overwriting a potentially existing branch, false otherwise.
/// A new .
public virtual Branch Rename(Branch branch, string newName, bool allowOverwrite)
{
Ensure.ArgumentNotNull(branch, "branch");
Ensure.ArgumentNotNullOrEmptyString(newName, "newName");
if (branch.IsRemote)
{
throw new LibGit2SharpException("Cannot rename branch '{0}'. It's a remote tracking branch.",
branch.FriendlyName);
}
using (ReferenceHandle referencePtr = repo.Refs.RetrieveReferencePtr(Reference.LocalBranchPrefix + branch.FriendlyName))
{
using (Proxy.git_branch_move(referencePtr, newName, allowOverwrite))
{ }
}
var newBranch = this[newName];
return newBranch;
}
///
/// Update properties of a branch.
///
/// The branch to update.
/// Delegate to perform updates on the branch.
/// The updated branch.
public virtual Branch Update(Branch branch, params Action[] actions)
{
var updater = new BranchUpdater(repo, branch);
foreach (Action action in actions)
{
action(updater);
}
return this[branch.FriendlyName];
}
private static bool LooksLikeABranchName(string referenceName)
{
return referenceName == "HEAD" ||
referenceName.LooksLikeLocalBranch() ||
referenceName.LooksLikeRemoteTrackingBranch();
}
private string DebuggerDisplay
{
get { return string.Format(CultureInfo.InvariantCulture, "Count = {0}", this.Count()); }
}
}
}