173 lines
7.6 KiB
C#
173 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using NDesk.Options;
|
|
using Sep.Git.Tfs.Core;
|
|
using StructureMap;
|
|
using Sep.Git.Tfs.Util;
|
|
using Sep.Git.Tfs.Core.TfsInterop;
|
|
|
|
namespace Sep.Git.Tfs.Commands
|
|
{
|
|
[Pluggable("clone")]
|
|
[Description("clone [options] tfs-url-or-instance-name repository-path <git-repository-path>\n ex : git tfs clone http://myTfsServer:8080/tfs/TfsRepository $/ProjectName/ProjectBranch\n")]
|
|
public class Clone : GitTfsCommand
|
|
{
|
|
private readonly Fetch fetch;
|
|
private readonly Init init;
|
|
private readonly Globals globals;
|
|
private readonly InitBranch initBranch;
|
|
private bool withBranches;
|
|
private TextWriter stdout;
|
|
|
|
public Clone(Globals globals, Fetch fetch, Init init, InitBranch initBranch, TextWriter stdout)
|
|
{
|
|
this.fetch = fetch;
|
|
this.init = init;
|
|
this.globals = globals;
|
|
this.initBranch = initBranch;
|
|
this.stdout = stdout;
|
|
}
|
|
|
|
public OptionSet OptionSet
|
|
{
|
|
get
|
|
{
|
|
return init.OptionSet.Merge(fetch.OptionSet)
|
|
.Add("with-branches", "init all the TFS branches during the clone", v => withBranches = v != null);
|
|
}
|
|
}
|
|
|
|
public int Run(string tfsUrl, string tfsRepositoryPath)
|
|
{
|
|
return Run(tfsUrl, tfsRepositoryPath, Path.GetFileName(tfsRepositoryPath));
|
|
}
|
|
|
|
public int Run(string tfsUrl, string tfsRepositoryPath, string gitRepositoryPath)
|
|
{
|
|
var currentDir = Environment.CurrentDirectory;
|
|
var repositoryDirCreated = InitGitDir(gitRepositoryPath);
|
|
|
|
// TFS string representations of repository paths do not end in trailing slashes
|
|
tfsRepositoryPath = (tfsRepositoryPath ?? string.Empty).TrimEnd('/');
|
|
|
|
try
|
|
{
|
|
var retVal = init.Run(tfsUrl, tfsRepositoryPath, gitRepositoryPath);
|
|
|
|
VerifyTfsPathToClone(tfsRepositoryPath);
|
|
|
|
if (retVal == 0) retVal = fetch.Run();
|
|
if (retVal == 0 && !init.IsBare) globals.Repository.CommandNoisy("merge", globals.Repository.ReadTfsRemote(globals.RemoteId).RemoteRef);
|
|
if (retVal == 0 && withBranches && initBranch != null)
|
|
{
|
|
initBranch.CloneAllBranches = true;
|
|
retVal = initBranch.Run();
|
|
}
|
|
return retVal;
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
// if we appeared to be inside repository dir when exception was thrown - we won't be able to delete it
|
|
Environment.CurrentDirectory = currentDir;
|
|
if (repositoryDirCreated)
|
|
Directory.Delete(gitRepositoryPath, recursive: true);
|
|
else
|
|
CleanDirectory(gitRepositoryPath);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
// swallow IOException. Smth went wrong before this and we're much more interested in that error
|
|
string msg = String.Format("Something went wrong, can't cleanup files because of IOException:\n{0}\n", e.IndentExceptionMessage());
|
|
Trace.WriteLine(msg);
|
|
}
|
|
catch (UnauthorizedAccessException e)
|
|
{
|
|
// swallow it also
|
|
string msg = String.Format("Something went wrong, can't cleanup files because of UnauthorizedAccessException:\n{0}\n", e.IndentExceptionMessage());
|
|
Trace.WriteLine(msg);
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private void VerifyTfsPathToClone(string tfsRepositoryPath)
|
|
{
|
|
if (initBranch != null)
|
|
{
|
|
var remote = globals.Repository.ReadTfsRemote(GitTfsConstants.DefaultRepositoryId);
|
|
|
|
if (!remote.Tfs.IsExistingInTfs(tfsRepositoryPath))
|
|
throw new GitTfsException("error: the path " + tfsRepositoryPath + " you want to clone doesn't exist!")
|
|
.WithRecommendation("To discover which branch to clone, you could use the command :\ngit tfs list-remote-branches " + remote.TfsUrl);
|
|
|
|
if (!remote.Tfs.CanGetBranchInformation)
|
|
return;
|
|
var tfsTrunkRepository = remote.Tfs.GetRootTfsBranchForRemotePath(tfsRepositoryPath, false);
|
|
if (tfsTrunkRepository == null)
|
|
{
|
|
var tfsRootBranches = remote.Tfs.GetAllTfsRootBranchesOrderedByCreation();
|
|
if (!tfsRootBranches.Any())
|
|
{
|
|
stdout.WriteLine("info: no TFS root found !\n");
|
|
return;
|
|
}
|
|
var cloneMsg = " => If you want to manage branches with git-tfs, clone one of this branch instead :\n"
|
|
+ " - " + tfsRootBranches.Aggregate((s1, s2) => s1 + "\n - " + s2)
|
|
+ "\n\nPS:if your branch is not listed here, perhaps you should convert the containing folder to a branch in TFS.";
|
|
|
|
if (withBranches)
|
|
throw new GitTfsException("error: cloning the whole repository or too high in the repository path doesn't permit to manage branches!\n" + cloneMsg);
|
|
stdout.WriteLine("warning: you are going to clone the whole repository or too high in the repository path !\n" + cloneMsg);
|
|
return;
|
|
}
|
|
|
|
var tfsBranchesPath = tfsTrunkRepository.GetAllChildren();
|
|
var tfsPathToClone = tfsRepositoryPath.TrimEnd('/').ToLower();
|
|
var tfsTrunkRepositoryPath = tfsTrunkRepository.Path;
|
|
if (tfsPathToClone != tfsTrunkRepositoryPath.ToLower())
|
|
{
|
|
if (tfsBranchesPath.Select(e=>e.Path.ToLower()).Contains(tfsPathToClone))
|
|
stdout.WriteLine("info: you are going to clone a branch instead of the trunk ( {0} )\n"
|
|
+ " => If you want to manage branches with git-tfs, clone {0} with '--with-branches' option instead...)", tfsTrunkRepositoryPath);
|
|
else
|
|
stdout.WriteLine("warning: you are going to clone a subdirectory of a branch and won't be able to manage branches :(\n"
|
|
+ " => If you want to manage branches with git-tfs, clone " + tfsTrunkRepositoryPath + " with '--with-branches' option instead...)");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool InitGitDir(string gitRepositoryPath)
|
|
{
|
|
bool repositoryDirCreated = false;
|
|
var di = new DirectoryInfo(gitRepositoryPath);
|
|
if (di.Exists)
|
|
{
|
|
if (di.EnumerateFileSystemInfos().Any())
|
|
throw new GitTfsException("error: Specified git repository directory is not empty");
|
|
}
|
|
else
|
|
{
|
|
repositoryDirCreated = true;
|
|
di.Create();
|
|
}
|
|
return repositoryDirCreated;
|
|
}
|
|
|
|
private static void CleanDirectory(string gitRepositoryPath)
|
|
{
|
|
var di = new DirectoryInfo(gitRepositoryPath);
|
|
foreach (var fileSystemInfo in di.EnumerateDirectories())
|
|
fileSystemInfo.Delete(true);
|
|
foreach (var fileSystemInfo in di.EnumerateFiles())
|
|
fileSystemInfo.Delete();
|
|
}
|
|
}
|
|
}
|