using System; using LibGit2Sharp.Core; using System.Globalization; using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { /// /// Exposes properties of a branch that can be updated. /// public class BranchUpdater { private readonly Repository repo; private readonly Branch branch; /// /// Needed for mocking purposes. /// protected BranchUpdater() { } internal BranchUpdater(Repository repo, Branch branch) { Ensure.ArgumentNotNull(repo, "repo"); Ensure.ArgumentNotNull(branch, "branch"); this.repo = repo; this.branch = branch; } /// /// Sets the upstream information for the branch. /// /// Passing null or string.Empty will unset the upstream. /// /// /// The upstream branch name is with respect to the current repository. /// So, passing "refs/remotes/origin/master" will set the current branch /// to track "refs/heads/master" on the origin. Passing in /// "refs/heads/master" will result in the branch tracking the local /// master branch. /// /// public virtual string TrackedBranch { set { if (string.IsNullOrEmpty(value)) { UnsetUpstream(); return; } SetUpstream(value); } } /// /// Set the upstream branch for this branch. /// /// To track the "master" branch on the "origin" remote, set the /// property to "origin" and the /// property to "refs/heads/master". /// /// public virtual string UpstreamBranch { set { SetUpstreamBranch(value); } } /// /// Set the upstream remote for this branch. /// /// To track the "master" branch on the "origin" remote, set the /// property to "origin" and the /// property to "refs/heads/master". /// /// public virtual string Remote { set { SetUpstreamRemote(value); } } private void UnsetUpstream() { SetUpstreamRemote(string.Empty); SetUpstreamBranch(string.Empty); } /// /// Set the upstream information for the current branch. /// /// The upstream branch name is with respect to the current repository. /// So, passing "refs/remotes/origin/master" will set the current branch /// to track "refs/heads/master" on the origin. Passing in /// "refs/heads/master" will result in the branch tracking the local /// master branch. /// /// /// The remote branch to track (e.g. refs/remotes/origin/master). private void SetUpstream(string upstreamBranchName) { if (branch.IsRemote) { throw new LibGit2SharpException("Cannot set upstream branch on a remote branch."); } string remoteName; string branchName; GetUpstreamInformation(upstreamBranchName, out remoteName, out branchName); SetUpstreamRemote(remoteName); SetUpstreamBranch(branchName); } /// /// Set the upstream merge branch for the local branch. /// /// The merge branch in the upstream remote's namespace. private void SetUpstreamBranch(string mergeBranchName) { string configKey = string.Format("branch.{0}.merge", branch.Name); if (string.IsNullOrEmpty(mergeBranchName)) { repo.Config.Unset(configKey); } else { repo.Config.Set(configKey, mergeBranchName); } } /// /// Set the upstream remote for the local branch. /// /// The name of the remote to set as the upstream branch. private void SetUpstreamRemote(string remoteName) { string configKey = string.Format("branch.{0}.remote", branch.Name); if (string.IsNullOrEmpty(remoteName)) { repo.Config.Unset(configKey); } else { if (!remoteName.Equals(".", StringComparison.Ordinal)) { // Verify that remote exists. repo.Network.Remotes.RemoteForName(remoteName); } repo.Config.Set(configKey, remoteName); } } /// /// Get the upstream remote and merge branch name from a Canonical branch name. /// This will return the remote name (or ".") if a local branch for the remote name. /// /// The canonical branch name to parse. /// The name of the corresponding remote the branch belongs to /// or "." if it is a local branch. /// The name of the upstream branch to merge into. private void GetUpstreamInformation(string canonicalName, out string remoteName, out string mergeBranchName) { remoteName = null; mergeBranchName = null; if (canonicalName.LooksLikeLocalBranch()) { remoteName = "."; mergeBranchName = canonicalName; } else if (canonicalName.LooksLikeRemoteTrackingBranch()) { remoteName = Proxy.git_branch_remote_name(repo.Handle, canonicalName); Remote remote = repo.Network.Remotes.RemoteForName(remoteName); mergeBranchName = remote.FetchSpecTransformToSource(canonicalName); } else { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "'{0}' does not look like a valid canonical branch name.", canonicalName)); } } } }