Files
git-tfs/GitTfs/Commands/Subtree.cs
Philippe Miossec e9d97a9cea Refactor branch option to be more consistent
Now, we have 3 options for the `clone` and `fetch` commands:
--branches=none: Ignore branches and merge changesets, fetching only the clone tfs path
--branches=auto:(default) Manage merged changesets and initialize the merged branches
--branches=all: Manage merged changesets and initialize all the branches during the clone

+ clean and improve the doc
2016-02-17 00:00:33 +01:00

225 lines
8.0 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using NDesk.Options;
using Sep.Git.Tfs.Core;
using StructureMap;
namespace Sep.Git.Tfs.Commands
{
[Pluggable("subtree")]
[Description("subtree [add|pull|split] [options] [remote | ( [tfs-url] [repository-path] )]")]
[RequiresValidGitRepository]
public class Subtree : GitTfsCommand
{
private readonly TextWriter _stdout;
private readonly Fetch _fetch;
private readonly QuickFetch _quickFetch;
private readonly Globals _globals;
private readonly RemoteOptions _remoteOptions;
private string Prefix;
private bool Squash = false;
public OptionSet OptionSet
{
get
{
return new OptionSet
{
{ "p|prefix=",
v => Prefix = v},
{ "squash",
v => Squash = v != null},
// { "r|revision=",
// v => RevisionToFetch = Convert.ToInt32(v) },
}
.Merge(_fetch.OptionSet);
}
}
public Subtree(TextWriter stdout, Fetch fetch, QuickFetch quickFetch, Globals globals, RemoteOptions remoteOptions)
{
this._stdout = stdout;
this._fetch = fetch;
this._quickFetch = quickFetch;
this._globals = globals;
this._remoteOptions = remoteOptions;
}
public int Run(IList<string> args)
{
string command = args.FirstOrDefault() ?? "";
_stdout.WriteLine("executing subtree " + command);
if (string.IsNullOrEmpty(Prefix))
{
_stdout.WriteLine("Prefix must be specified, use -p or -prefix");
return GitTfsExitCodes.InvalidArguments;
}
switch (command.ToLower())
{
case "add":
return DoAdd(args.ElementAtOrDefault(1) ?? "", args.ElementAtOrDefault(2) ?? "");
case "pull":
return DoPull(args.ElementAtOrDefault(1));
case "split":
return DoSplit();
default:
_stdout.WriteLine("Expected one of [add, pull, split]");
return GitTfsExitCodes.InvalidArguments;
}
}
public int DoAdd(string tfsUrl, string tfsRepositoryPath)
{
if (File.Exists(Prefix) || Directory.Exists(Prefix))
{
_stdout.WriteLine(string.Format("Directory {0} already exists", Prefix));
return GitTfsExitCodes.InvalidArguments;
}
var fetch = Squash ? this._quickFetch : this._fetch;
IGitTfsRemote owner = null;
string ownerId = _globals.RemoteId;
if (!string.IsNullOrEmpty(ownerId))
{
//check for the specified remote
owner = _globals.Repository.HasRemote(_globals.RemoteId) ? _globals.Repository.ReadTfsRemote(_globals.RemoteId) : null;
if (owner != null && !string.IsNullOrEmpty(owner.TfsRepositoryPath))
{
owner = null;
ownerId = null;
}
}
if(string.IsNullOrEmpty(ownerId))
{
//check for any remote that has no TfsRepositoryPath
owner = _globals.Repository.ReadAllTfsRemotes().FirstOrDefault(x => string.IsNullOrEmpty(x.TfsRepositoryPath) && !x.IsSubtree);
}
if (owner == null)
{
owner = _globals.Repository.CreateTfsRemote(new RemoteInfo
{
Id = ownerId ?? GitTfsConstants.DefaultRepositoryId,
Url = tfsUrl,
Repository = null,
RemoteOptions = _remoteOptions
});
_stdout.WriteLine("-> new owning remote " + owner.Id);
}
else
{
ownerId = owner.Id;
_stdout.WriteLine("Attaching subtree to owning remote " + owner.Id);
}
//create a remote for the new subtree
string remoteId = string.Format(GitTfsConstants.RemoteSubtreeFormat, owner.Id, Prefix);
IGitTfsRemote remote = _globals.Repository.HasRemote(remoteId) ?
_globals.Repository.ReadTfsRemote(remoteId) :
_globals.Repository.CreateTfsRemote(new RemoteInfo
{
Id = remoteId,
Url = tfsUrl,
Repository = tfsRepositoryPath,
RemoteOptions = _remoteOptions,
});
_stdout.WriteLine("-> new remote " + remote.Id);
fetch.BranchStrategy = BranchStrategy.None;
int result = fetch.Run(remote.Id);
if (result == GitTfsExitCodes.OK)
{
var p = Prefix.Replace(" ", "\\ ");
int latest = Math.Max(owner.MaxChangesetId, remote.MaxChangesetId);
string msg = string.Format(GitTfsConstants.TfsCommitInfoFormat, owner.TfsUrl, owner.TfsRepositoryPath, latest);
msg = string.Format(@"Add '{0}/' from commit '{1}'
{2}", Prefix, remote.MaxCommitHash, msg);
_globals.Repository.CommandNoisy(
"subtree", "add",
"--prefix=" + p,
string.Format("-m {0}", msg),
remote.RemoteRef);
//update the owner remote to point at the commit where the newly created subtree was merged.
var commit = _globals.Repository.GetCurrentCommit();
owner.UpdateTfsHead(commit, latest);
result = GitTfsExitCodes.OK;
}
return result;
}
public int DoPull(string remoteId)
{
ValidatePrefix();
remoteId = remoteId ?? string.Format(GitTfsConstants.RemoteSubtreeFormat, _globals.RemoteId ?? GitTfsConstants.DefaultRepositoryId, Prefix);
IGitTfsRemote remote = _globals.Repository.ReadTfsRemote(remoteId);
int result = this._fetch.Run(remote.Id);
if (result == GitTfsExitCodes.OK)
{
var p = Prefix.Replace(" ", "\\ ");
_globals.Repository.CommandNoisy("subtree", "merge", "--prefix=" + p, remote.RemoteRef);
result = GitTfsExitCodes.OK;
}
return result;
}
public int DoSplit()
{
ValidatePrefix();
var p = Prefix.Replace(" ", "\\ ");
_globals.Repository.CommandNoisy("subtree", "split", "--prefix=" + p, "-b", p);
_globals.Repository.CommandNoisy("checkout", p);
//update subtree refs if needed
var owners = _globals.Repository.GetLastParentTfsCommits("HEAD").Where(x => !x.Remote.IsSubtree && x.Remote.TfsRepositoryPath == null).ToList();
foreach (var subtree in _globals.Repository.ReadAllTfsRemotes().Where(x => x.IsSubtree && string.Equals(x.Prefix, Prefix)))
{
var updateTo = owners.FirstOrDefault(x => string.Equals(x.Remote.Id, subtree.OwningRemoteId));
if (updateTo != null && updateTo.ChangesetId > subtree.MaxChangesetId)
{
subtree.UpdateTfsHead(updateTo.GitCommit, updateTo.ChangesetId);
}
}
return GitTfsExitCodes.OK;
}
private void ValidatePrefix()
{
if (!Directory.Exists(Prefix))
{
throw new GitTfsException(string.Format("Directory {0} does not exist", Prefix))
.WithRecommendation("Add the subtree using 'git tfs subtree add -p=<prefix> [tfs-server] [tfs-repository]'");
}
}
}
}