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_LOCAL | GitBranchType.GIT_BRANCH_REMOTE)
.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.
/// The target commit.
/// Identity used for updating the reflog
/// Message added to the reflog. If null, the default is "branch: Created from [sha]".
/// True to allow silent overwriting a potentially existing branch, false otherwise.
/// A new .
public virtual Branch Add(string name, Commit commit, Signature signature, string logMessage = null, bool allowOverwrite = false)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(commit, "commit");
if (logMessage == null)
{
logMessage = "branch: Created from " + commit.Id;
}
using (Proxy.git_branch_create(repo.Handle, name, commit.Id, allowOverwrite, signature.OrDefault(repo.Config), logMessage)) {}
var branch = this[ShortToLocalName(name)];
return branch;
}
///
/// Create a new local branch with the specified name, using the default reflog message
///
/// 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 = false)
{
return Add(name, commit, null, null, allowOverwrite);
}
///
/// Deletes the specified branch.
///
/// The branch to delete.
public virtual void Remove(Branch branch)
{
Ensure.ArgumentNotNull(branch, "branch");
using (ReferenceSafeHandle referencePtr = repo.Refs.RetrieveReferencePtr(branch.CanonicalName))
{
Proxy.git_branch_delete(referencePtr);
}
}
///
/// Rename an existing local branch
///
/// The current local branch.
/// The new name the existing branch should bear.
/// Identity used for updating the reflog
/// Message added to the reflog. If null, the default is "branch: renamed [old] to [new]".
/// True to allow silent overwriting a potentially existing branch, false otherwise.
/// A new .
public virtual Branch Move(Branch branch, string newName, Signature signature, string logMessage = null, bool allowOverwrite = false)
{
Ensure.ArgumentNotNull(branch, "branch");
Ensure.ArgumentNotNullOrEmptyString(newName, "newName");
if (branch.IsRemote)
{
throw new LibGit2SharpException(
string.Format(CultureInfo.InvariantCulture,
"Cannot rename branch '{0}'. It's a remote tracking branch.", branch.Name));
}
if (logMessage == null)
{
logMessage = string.Format(CultureInfo.InvariantCulture,
"branch: renamed {0} to {1}", branch.CanonicalName, Reference.LocalBranchPrefix + newName);
}
using (ReferenceSafeHandle referencePtr = repo.Refs.RetrieveReferencePtr(Reference.LocalBranchPrefix + branch.Name))
{
using (Proxy.git_branch_move(referencePtr, newName, allowOverwrite, signature.OrDefault(repo.Config), logMessage))
{
}
}
var newBranch = this[newName];
return newBranch;
}
///
/// Rename an existing local branch, using the default reflog message
///
/// 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 Move(Branch branch, string newName, bool allowOverwrite = false)
{
return Move(branch, newName, null, null, allowOverwrite);
}
///
/// 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.Name];
}
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());
}
}
}
}