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