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_foreach(repo.Handle, GitBranchType.GIT_BRANCH_LOCAL | GitBranchType.GIT_BRANCH_REMOTE, branchToCanoncialName) .Select(n => this[n]) .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. /// True to allow silent overwriting a potentially existing branch, false otherwise. /// A new . public virtual Branch Add(string name, Commit commit, bool allowOverwrite = false) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(commit, "commit"); using (Proxy.git_branch_create(repo.Handle, name, commit.Id, allowOverwrite)) {} return this[ShortToLocalName(name)]; } /// /// 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); } } /// /// Renames an existing local branch with a new name. /// /// 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) { 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)); } using (ReferenceSafeHandle referencePtr = repo.Refs.RetrieveReferencePtr(Reference.LocalBranchPrefix + branch.Name)) { using (ReferenceSafeHandle ref_out = Proxy.git_branch_move(referencePtr, newName, allowOverwrite)) { } } return this[newName]; } /// /// 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 static string branchToCanoncialName(IntPtr namePtr, GitBranchType branchType) { string shortName = Utf8Marshaler.FromNative(namePtr); switch (branchType) { case GitBranchType.GIT_BRANCH_LOCAL: return ShortToLocalName(shortName); case GitBranchType.GIT_BRANCH_REMOTE: return ShortToRemoteName(shortName); default: return shortName; } } private string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "Count = {0}", this.Count()); } } } }