using System;
using System.Globalization;
using LibGit2Sharp.Core;
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(CultureInfo.InvariantCulture, "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(CultureInfo.InvariantCulture, "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, true);
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));
}
}
}
}