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