Files
git-tfs/GitTfs.VsCommon/TfsHelper.Common.cs
T
2014-07-05 13:47:36 +02:00

813 lines
34 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using SEP.Extensions;
using Sep.Git.Tfs.Commands;
using Sep.Git.Tfs.Core;
using Sep.Git.Tfs.Core.TfsInterop;
using Sep.Git.Tfs.Util;
using StructureMap;
using StructureMap.Attributes;
namespace Sep.Git.Tfs.VsCommon
{
public abstract class TfsHelperBase : ITfsHelper
{
private readonly TextWriter _stdout;
private readonly TfsApiBridge _bridge;
private readonly IContainer _container;
public TfsHelperBase(TextWriter stdout, TfsApiBridge bridge, IContainer container)
{
_stdout = stdout;
_bridge = bridge;
_container = container;
}
[SetterProperty]
public Janitor Janitor { get; set; }
public abstract string TfsClientLibraryVersion { get; }
public string Url { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool HasCredentials
{
get { return !String.IsNullOrEmpty(Username); }
}
public abstract void EnsureAuthenticated();
private string[] _legacyUrls;
protected NetworkCredential GetCredential()
{
var idx = Username.IndexOf('\\');
if (idx >= 0)
{
string domain = Username.Substring(0, idx);
string login = Username.Substring(idx + 1);
return new NetworkCredential(login, Password, domain);
}
return new NetworkCredential(Username, Password);
}
protected abstract T GetService<T>();
private VersionControlServer _versionControl;
protected VersionControlServer VersionControl
{
get
{
if (_versionControl != null)
return _versionControl;
_versionControl = GetService<VersionControlServer>();
_versionControl.NonFatalError += NonFatalError;
_versionControl.Getting += Getting;
return _versionControl;
}
}
private WorkItemStore WorkItems
{
get { return GetService<WorkItemStore>(); }
}
private void NonFatalError(object sender, ExceptionEventArgs e)
{
if (e.Failure != null)
{
_stdout.WriteLine(e.Failure.Message);
Trace.WriteLine("Failure: " + e.Failure.Inspect(), "tfs non-fatal error");
}
if (e.Exception != null)
{
_stdout.WriteLine(e.Exception.Message);
Trace.WriteLine("Exception: " + e.Exception.Inspect(), "tfs non-fatal error");
}
}
private void Getting(object sender, GettingEventArgs e)
{
Trace.WriteLine("get [C" + e.Version + "]" + e.ServerItem);
}
private IGroupSecurityService GroupSecurityService
{
get { return GetService<IGroupSecurityService>(); }
}
private ILinking _linking;
private ILinking Linking
{
get { return _linking ?? (_linking = GetService<ILinking>()); }
}
public virtual IEnumerable<ITfsChangeset> GetChangesets(string path, long startVersion, IGitTfsRemote remote)
{
var changesets = Retry.Do(() => VersionControl.QueryHistory(path, VersionSpec.Latest, 0, RecursionType.Full,
null, new ChangesetVersionSpec((int)startVersion), VersionSpec.Latest, int.MaxValue, true, true, true)
.Cast<Changeset>().OrderBy(changeset => changeset.ChangesetId).ToArray());
// don't take the enumerator produced by a foreach statement or a yield statement, as there are references
// to the old (iterated) elements and thus the referenced changesets won't be disposed until all elements were iterated.
for (int i = 0; i < changesets.Length; i++)
{
yield return BuildTfsChangeset(changesets[i], remote);
changesets[i] = null;
}
}
public virtual int FindMergeChangesetParent(string path, long targetChangeset, GitTfsRemote remote)
{
var targetVersion = new ChangesetVersionSpec((int)targetChangeset);
var mergeInfo = VersionControl.QueryMerges(null, null, path, targetVersion, targetVersion, targetVersion, RecursionType.Full);
return mergeInfo.Max(x => x.SourceVersion);
}
public virtual bool CanGetBranchInformation { get { return false; } }
public virtual IEnumerable<string> GetAllTfsRootBranchesOrderedByCreation()
{
throw new NotImplementedException();
}
public virtual IEnumerable<IBranchObject> GetBranches(bool getDeletedBranches = false)
{
throw new NotImplementedException();
}
public virtual IList<RootBranch> GetRootChangesetForBranch(string tfsPathBranchToCreate, string tfsPathParentBranch = null)
{
Trace.WriteLine("TFS 2008 Compatible mode!");
int firstChangesetIdOfParentBranch = 1;
if (string.IsNullOrWhiteSpace(tfsPathParentBranch))
throw new GitTfsException("This version of TFS Server doesn't permit to use this command :(\nTry using option '--parent-branch'...");
var changesetIdsFirstChangesetInMainBranch = VersionControl.GetMergeCandidates(tfsPathParentBranch, tfsPathBranchToCreate, RecursionType.Full).Select(c => c.Changeset.ChangesetId).FirstOrDefault();
if (changesetIdsFirstChangesetInMainBranch == 0)
{
Trace.WriteLine("No changeset in main branch since branch done... (need only to find the last changeset in the main branch)");
return new List<RootBranch> { new RootBranch(VersionControl.QueryHistory(tfsPathParentBranch, VersionSpec.Latest, 0,
RecursionType.Full, null, new ChangesetVersionSpec(firstChangesetIdOfParentBranch), VersionSpec.Latest,
1, false, false).Cast<Changeset>().First().ChangesetId, tfsPathBranchToCreate)};
}
Trace.WriteLine("First changeset in the main branch after branching : " + changesetIdsFirstChangesetInMainBranch);
Trace.WriteLine("Try to find the previous changeset...");
int step = 100;
int upperBound = changesetIdsFirstChangesetInMainBranch - 1;
int lowerBound = Math.Max(upperBound - step, 1);
//for optimization, retrieve the lesser possible changesets... so 100 by 100
while (true)
{
Trace.WriteLine("Looking for the changeset between changeset id " + lowerBound + " and " + upperBound);
var firstBranchChangesetIds = VersionControl.QueryHistory(tfsPathParentBranch, VersionSpec.Latest, 0, RecursionType.Full,
null, new ChangesetVersionSpec(lowerBound), new ChangesetVersionSpec(upperBound), int.MaxValue, false,
false, false).Cast<Changeset>().Select(c => c.ChangesetId).ToList();
if (firstBranchChangesetIds.Count != 0)
return new List<RootBranch> { new RootBranch(firstBranchChangesetIds.First(cId => cId < changesetIdsFirstChangesetInMainBranch), tfsPathBranchToCreate)};
else
{
if (upperBound == 1)
{
throw new GitTfsException("An unexpected error occured when trying to find the root changeset.\nFailed to find a previous changeset to changeset n°" + changesetIdsFirstChangesetInMainBranch + " in the branch!!!");
}
upperBound = Math.Max(upperBound - step, 1);
lowerBound = Math.Max(upperBound - step, 1);
}
}
}
protected ITfsChangeset BuildTfsChangeset(Changeset changeset, IGitTfsRemote remote)
{
var tfsChangeset = _container.With<ITfsHelper>(this).With<IChangeset>(_bridge.Wrap<WrapperForChangeset, Changeset>(changeset)).GetInstance<TfsChangeset>();
tfsChangeset.Summary = new TfsChangesetInfo { ChangesetId = changeset.ChangesetId, Remote = remote };
if (HasWorkItems(changeset))
{
tfsChangeset.Summary.Workitems = changeset.WorkItems.Select(wi => new TfsWorkitem
{
Id = wi.Id,
Title = wi.Title,
Description = wi.Description,
Url = Linking.GetArtifactUrl(wi.Uri.AbsoluteUri)
});
}
foreach (var checkinNote in changeset.CheckinNote.Values)
{
switch (checkinNote.Name)
{
case GitTfsConstants.CodeReviewer:
tfsChangeset.Summary.CodeReviewer = checkinNote.Value;
break;
case GitTfsConstants.SecurityReviewer:
tfsChangeset.Summary.SecurityReviewer = checkinNote.Value;
break;
case GitTfsConstants.PerformanceReviewer:
tfsChangeset.Summary.PerformanceReviewer = checkinNote.Value;
break;
}
}
tfsChangeset.Summary.PolicyOverrideComment = changeset.PolicyOverride.Comment;
return tfsChangeset;
}
protected virtual bool HasWorkItems(Changeset changeset)
{
// This method wraps changeset.WorkItems, because
// changeset.WorkItems might result to ConnectionException: TF26175: Team Foundation Core Services attribute 'AttachmentServerUrl' not found.
// in this case assume that it is initialized to null
// NB: in VS2011 a new property appeared (AssociatedWorkItems), which works correctly
WorkItem[] result = null;
try
{
result = Retry.Do(() => changeset.WorkItems);
}
catch (ConnectionException exception)
{
if (!exception.Message.StartsWith("TF26175:"))
throw;
}
return result != null && result.Length > 0;
}
Dictionary<string, Workspace> _workspaces = new Dictionary<string, Workspace>();
public void WithWorkspace(string localDirectory, IGitTfsRemote remote, IEnumerable<Tuple<string, string>> mappings, TfsChangesetInfo versionToFetch, Action<ITfsWorkspace> action)
{
Workspace workspace;
if (!_workspaces.TryGetValue(remote.Id, out workspace))
{
Trace.WriteLine("Setting up a TFS workspace with subtrees at " + localDirectory);
var folders = mappings.Select(x => new WorkingFolder(x.Item1, Path.Combine(localDirectory, x.Item2))).ToArray();
_workspaces.Add(remote.Id, workspace = Retry.Do(() =>GetWorkspace(folders)));
Janitor.CleanThisUpWhenWeClose(() =>
{
Trace.WriteLine("Deleting workspace " + workspace.Name);
Retry.Do(() => workspace.Delete());
});
}
var tfsWorkspace = _container.With("localDirectory").EqualTo(localDirectory)
.With("remote").EqualTo(remote)
.With("contextVersion").EqualTo(versionToFetch)
.With("workspace").EqualTo(_bridge.Wrap<WrapperForWorkspace, Workspace>(workspace))
.With("tfsHelper").EqualTo(this)
.GetInstance<TfsWorkspace>();
action(tfsWorkspace);
}
public void WithWorkspace(string localDirectory, IGitTfsRemote remote, TfsChangesetInfo versionToFetch, Action<ITfsWorkspace> action)
{
Trace.WriteLine("Setting up a TFS workspace at " + localDirectory);
var workspace = Retry.Do(() => GetWorkspace(new WorkingFolder(remote.TfsRepositoryPath, localDirectory)));
try
{
var tfsWorkspace = _container.With("localDirectory").EqualTo(localDirectory)
.With("remote").EqualTo(remote)
.With("contextVersion").EqualTo(versionToFetch)
.With("workspace").EqualTo(_bridge.Wrap<WrapperForWorkspace, Workspace>(workspace))
.With("tfsHelper").EqualTo(this)
.GetInstance<TfsWorkspace>();
action(tfsWorkspace);
}
finally
{
Retry.Do(() => workspace.Delete());
}
}
private Workspace GetWorkspace(params WorkingFolder[] folders)
{
var workspace = VersionControl.CreateWorkspace(GenerateWorkspaceName());
try
{
foreach(WorkingFolder folder in folders)
workspace.CreateMapping(folder);
}
catch (MappingConflictException e)
{
workspace.Delete();
throw new GitTfsException(e.Message).WithRecommendation("Run 'git tfs cleanup-workspaces' to remove the workspace.");
}
catch
{
workspace.Delete();
throw;
}
return workspace;
}
private string GenerateWorkspaceName()
{
return "git-tfs-" + Guid.NewGuid();
}
public abstract long ShowCheckinDialog(IWorkspace workspace, IPendingChange[] pendingChanges, IEnumerable<IWorkItemCheckedInfo> checkedInfos, string checkinComment);
public void CleanupWorkspaces(string workingDirectory)
{
// workingDirectory is the path to a TFS workspace managed by git-tfs.
// By default, this will be something like `.git/tfs/default/workspace`.
// If `git-tfs.workspace-dir` is set, workingDirectory will be that path.
Trace.WriteLine("Looking for workspaces mapped to @\"" + workingDirectory + "\"...", "cleanup-workspaces");
var workspace = VersionControl.TryGetWorkspace(workingDirectory);
if (workspace != null)
{
Trace.WriteLine("Found mapping in workspace \"" + workspace.DisplayName + "\".", "cleanup-workspaces");
if (workspace.Folders.Length == 1)
{
// Normally, the workspace will have one mapping, mapped to the git-tfs
// workspace folder. In that case, we just delete the workspace.
_stdout.WriteLine("Removing workspace \"" + workspace.DisplayName + "\".");
workspace.Delete();
}
else
{
// If something outside of git-tfs set up a workspace, the workspace
// might be set at a higher directory. If there is more than one mapping
// in the workspace, we only need to remove the one that includes the working
// directory that we want to set.
var fullWorkingDirectoryPath = Path.GetFullPath(workingDirectory);
foreach (var mapping in workspace.Folders.Where(f => fullWorkingDirectoryPath.StartsWith(Path.GetFullPath(f.LocalItem), StringComparison.CurrentCultureIgnoreCase)))
{
_stdout.WriteLine("Removing @\"" + mapping.LocalItem + "\" from workspace \"" + workspace.DisplayName + "\".");
workspace.DeleteMapping(mapping);
}
}
}
}
public bool HasShelveset(string shelvesetName)
{
var matchingShelvesets = VersionControl.QueryShelvesets(shelvesetName, GetAuthenticatedUser());
return matchingShelvesets != null && matchingShelvesets.Length > 0;
}
protected abstract string GetAuthenticatedUser();
public abstract bool CanShowCheckinDialog { get; }
public ITfsChangeset GetShelvesetData(IGitTfsRemote remote, string shelvesetOwner, string shelvesetName)
{
shelvesetOwner = shelvesetOwner == "all" ? null : (shelvesetOwner ?? GetAuthenticatedUser());
var shelvesets = VersionControl.QueryShelvesets(shelvesetName, shelvesetOwner);
if (shelvesets.Length != 1)
{
throw new GitTfsException("Unable to find " + shelvesetOwner + "'s shelveset \"" + shelvesetName + "\" (" + shelvesets.Length + " matches).")
.WithRecommendation("Try providing the shelveset owner.");
}
var shelveset = shelvesets.First();
var itemSpec = new ItemSpec(remote.TfsRepositoryPath, RecursionType.Full);
var change = VersionControl.QueryShelvedChanges(shelveset, new ItemSpec[] { itemSpec }).Single();
var wrapperForVersionControlServer =
_bridge.Wrap<WrapperForVersionControlServer, VersionControlServer>(VersionControl);
// TODO - containerify this (no `new`)!
var fakeChangeset = new Unshelveable(shelveset, change, wrapperForVersionControlServer, _bridge);
var tfsChangeset = new TfsChangeset(remote.Tfs, fakeChangeset, _stdout, null) { Summary = new TfsChangesetInfo { Remote = remote } };
return tfsChangeset;
}
public int ListShelvesets(ShelveList shelveList, IGitTfsRemote remote)
{
var shelvesetOwner = shelveList.Owner == "all" ? null : (shelveList.Owner ?? GetAuthenticatedUser());
IEnumerable<Shelveset> shelvesets;
try
{
shelvesets = VersionControl.QueryShelvesets(null, shelvesetOwner);
}
catch (IdentityNotFoundException)
{
_stdout.WriteLine("User '{0}' not found", shelveList.Owner);
return GitTfsExitCodes.InvalidArguments;
}
if (shelvesets.Empty())
{
_stdout.WriteLine("No changesets found.");
return GitTfsExitCodes.OK;
}
string sortBy = shelveList.SortBy;
if (sortBy != null)
{
switch (sortBy.ToLowerInvariant())
{
case "date":
shelvesets = shelvesets.OrderBy(s => s.CreationDate);
break;
case "owner":
shelvesets = shelvesets.OrderBy(s => s.OwnerName).ThenBy(s => s.CreationDate);
break;
case "name":
shelvesets = shelvesets.OrderBy(s => s.Name);
break;
case "comment":
shelvesets = shelvesets.OrderBy(s => s.Comment);
break;
default:
_stdout.WriteLine("ERROR: sorting criteria '{0}' is invalid. Possible values are: date, owner, name, comment", sortBy);
return GitTfsExitCodes.InvalidArguments;
}
}
else
shelvesets = shelvesets.OrderBy(s => s.CreationDate);
if (shelveList.FullFormat)
WriteShelvesetsToStdoutDetailed(shelvesets);
else
WriteShelvesetsToStdout(shelvesets);
return GitTfsExitCodes.OK;
}
private void WriteShelvesetsToStdout(IEnumerable<Shelveset> shelvesets)
{
foreach (var shelveset in shelvesets)
{
_stdout.WriteLine("{0,-22} {1,-20}", shelveset.OwnerName, shelveset.Name);
}
}
private void WriteShelvesetsToStdoutDetailed(IEnumerable<Shelveset> shelvesets)
{
foreach (var shelveset in shelvesets)
{
_stdout.WriteLine("Name : {0}", shelveset.Name);
_stdout.WriteLine("Owner : {0}", shelveset.OwnerName);
_stdout.WriteLine("Date : {0:g}", shelveset.CreationDate);
_stdout.WriteLine("Comment: {0}", shelveset.Comment);
_stdout.WriteLine();
}
}
#region Fake classes for unshelve
private class Unshelveable : IChangeset
{
private readonly Shelveset _shelveset;
private readonly PendingSet _pendingSet;
private readonly IVersionControlServer _versionControlServer;
private readonly TfsApiBridge _bridge;
private readonly IChange[] _changes;
public Unshelveable(Shelveset shelveset, PendingSet pendingSet, IVersionControlServer versionControlServer, TfsApiBridge bridge)
{
_shelveset = shelveset;
_versionControlServer = versionControlServer;
_bridge = bridge;
_pendingSet = pendingSet;
_changes = _pendingSet.PendingChanges.Select(x => new UnshelveChange(x, _bridge, versionControlServer)).Cast<IChange>().ToArray();
}
public IChange[] Changes
{
get { return _changes; }
}
public string Committer
{
get { return _pendingSet.OwnerName; }
}
public DateTime CreationDate
{
get { return _shelveset.CreationDate; }
}
public string Comment
{
get { return _shelveset.Comment; }
}
public int ChangesetId
{
get { return -1; }
}
public IVersionControlServer VersionControlServer
{
get { return _versionControlServer; }
}
public void Get(ITfsWorkspace workspace, IEnumerable<IChange> changes)
{
foreach (var change in changes)
{
var item = (UnshelveItem)change.Item;
item.Get(workspace);
}
}
}
private class UnshelveChange : IChange
{
private readonly PendingChange _pendingChange;
private readonly TfsApiBridge _bridge;
private readonly UnshelveItem _fakeItem;
public UnshelveChange(PendingChange pendingChange, TfsApiBridge bridge, IVersionControlServer versionControlServer)
{
_pendingChange = pendingChange;
_bridge = bridge;
_fakeItem = new UnshelveItem(_pendingChange, _bridge, versionControlServer);
}
public TfsChangeType ChangeType
{
get { return _bridge.Convert<TfsChangeType>(_pendingChange.ChangeType); }
}
public IItem Item
{
get { return _fakeItem; }
}
}
private class UnshelveItem : IItem
{
private readonly PendingChange _pendingChange;
private readonly TfsApiBridge _bridge;
private readonly IVersionControlServer _versionControlServer;
private long _contentLength = -1;
public UnshelveItem(PendingChange pendingChange, TfsApiBridge bridge, IVersionControlServer versionControlServer)
{
_pendingChange = pendingChange;
_bridge = bridge;
_versionControlServer = versionControlServer;
}
public IVersionControlServer VersionControlServer
{
get { return _versionControlServer; }
}
public int ChangesetId
{
get
{
// some operations like applying rename gets previous item state
// via looking at version of item minus 1. So will try to emulate
// that this shelve is real revision.
return _pendingChange.Version + 1;
}
}
public string ServerItem
{
get { return _pendingChange.ServerItem; }
}
public int DeletionId
{
get { return _pendingChange.DeletionId; }
}
public TfsItemType ItemType
{
get { return _bridge.Convert<TfsItemType>(_pendingChange.ItemType); }
}
public int ItemId
{
get { return _pendingChange.ItemId; }
}
public long ContentLength
{
get
{
if (_contentLength < 0)
throw new InvalidOperationException("You can't query ContentLength before downloading the file");
// It is not great solution, but at least makes the contract explicit.
// We can't actually save downloaded file in this class, because if nobody asks
// for it - we won't know when it is safe to delete it and it will stay in the
// system forever, which is bad. Implementing finalizer to delete file is also bad solution:
// suppose process was killed in the middle of many-megabyte operation on thousands of files
// if we delete them as soon as they are not used - only current file will remain. Otherwise
// all of them.
// With this exception at least it would be evident asap that something went wrong, so we could fix it.
return _contentLength;
}
}
public TemporaryFile DownloadFile()
{
var temp = new TemporaryFile();
_pendingChange.DownloadShelvedFile(temp);
_contentLength = new FileInfo(temp).Length;
return temp;
}
public void Get(ITfsWorkspace workspace)
{
_pendingChange.DownloadShelvedFile(workspace.GetLocalItemForServerItem(_pendingChange.ServerItem));
}
}
#endregion
public IShelveset CreateShelveset(IWorkspace workspace, string shelvesetName)
{
var shelveset = new Shelveset(_bridge.Unwrap<Workspace>(workspace).VersionControlServer, shelvesetName, workspace.OwnerName);
return _bridge.Wrap<WrapperForShelveset, Shelveset>(shelveset);
}
public IIdentity GetIdentity(string username)
{
return _bridge.Wrap<WrapperForIdentity, Identity>(Retry.Do(() => GroupSecurityService.ReadIdentity(SearchFactor.AccountName, username, QueryMembership.None)));
}
public ITfsChangeset GetLatestChangeset(IGitTfsRemote remote)
{
var history = VersionControl.QueryHistory(remote.TfsRepositoryPath, VersionSpec.Latest, 0,
RecursionType.Full, null, null, VersionSpec.Latest, 1, true, false,
false).Cast<Changeset>().ToList();
if (history.Empty())
throw new GitTfsException("error: remote TFS repository path was not found");
return BuildTfsChangeset(history.Single(), remote);
}
public IChangeset GetChangeset(int changesetId)
{
return _bridge.Wrap<WrapperForChangeset, Changeset>(VersionControl.GetChangeset(changesetId));
}
public ITfsChangeset GetChangeset(int changesetId, IGitTfsRemote remote)
{
return BuildTfsChangeset(VersionControl.GetChangeset(changesetId), remote);
}
public IEnumerable<IWorkItemCheckinInfo> GetWorkItemInfos(IEnumerable<string> workItems, TfsWorkItemCheckinAction checkinAction)
{
return
GetWorkItemInfosHelper<IWorkItemCheckinInfo, WrapperForWorkItemCheckinInfo, WorkItemCheckinInfo>(
workItems, checkinAction, GetWorkItemInfo);
}
public IEnumerable<IWorkItemCheckedInfo> GetWorkItemCheckedInfos(IEnumerable<string> workItems, TfsWorkItemCheckinAction checkinAction)
{
return
GetWorkItemInfosHelper<IWorkItemCheckedInfo, WrapperForWorkItemCheckedInfo, WorkItemCheckedInfo>(
workItems, checkinAction, GetWorkItemCheckedInfo);
}
public ICheckinNote CreateCheckinNote(Dictionary<string, string> checkinNotes)
{
if (checkinNotes.IsEmpty())
{
return null;
}
var index = 0;
var values = new CheckinNoteFieldValue[checkinNotes.Count];
foreach (var pair in checkinNotes)
{
values[index++] = new CheckinNoteFieldValue(pair.Key, pair.Value);
}
return _bridge.Wrap<WrapperForCheckinNote, CheckinNote>(new CheckinNote(values));
}
private IEnumerable<TInterface> GetWorkItemInfosHelper<TInterface, TWrapper, TInstance>(
IEnumerable<string> workItems,
TfsWorkItemCheckinAction checkinAction,
Func<string, WorkItemCheckinAction, TInstance> func
)
where TWrapper : class
{
return (from workItem in workItems
select _bridge.Wrap<TWrapper, TInstance>(
func(workItem, _bridge.Convert<WorkItemCheckinAction>(checkinAction))))
.Cast<TInterface>();
}
private WorkItemCheckinInfo GetWorkItemInfo(string workItem, WorkItemCheckinAction checkinAction)
{
return new WorkItemCheckinInfo(WorkItems.GetWorkItem(Convert.ToInt32(workItem)), checkinAction);
}
private static WorkItemCheckedInfo GetWorkItemCheckedInfo(string workitem, WorkItemCheckinAction checkinAction)
{
return new WorkItemCheckedInfo(Convert.ToInt32(workitem), true, checkinAction);
}
public IEnumerable<TfsLabel> GetLabels(string tfsPathBranch, string nameFilter = null)
{
foreach (var labelDefinition in VersionControl.QueryLabels(nameFilter, tfsPathBranch, null, false, tfsPathBranch, VersionSpec.Latest))
{
var label = VersionControl.QueryLabels(labelDefinition.Name, tfsPathBranch, null, true, tfsPathBranch, VersionSpec.Latest).FirstOrDefault();
if (label == null)
{
throw new GitTfsException("error: data for the label '" + labelDefinition.Name + "' can't be loaded!");
}
var tfsLabel = new TfsLabel
{
Id = label.LabelId,
Name = label.Name,
Comment = label.Comment,
Owner = label.OwnerName,
Date = label.LastModifiedDate,
};
foreach (var item in label.Items)
{
if (item.ServerItem.StartsWith(tfsPathBranch))
{
if (item.ChangesetId > tfsLabel.ChangesetId)
{
tfsLabel.ChangesetId = item.ChangesetId;
}
}
else
{
tfsLabel.IsTransBranch = true;
}
}
yield return tfsLabel;
}
}
public virtual void CreateBranch(string sourcePath, string targetPath, int changesetId, string comment = null)
{
throw new NotImplementedException();
}
public void CreateTfsRootBranch(string projectName, string mainBranch, string gitRepositoryPath, bool createTeamProjectFolder)
{
var projectPath = "$/" + projectName;
var directoryForBranch = Path.Combine(gitRepositoryPath, mainBranch);
Workspace workspace = null;
try
{
if (!VersionControl.ServerItemExists(projectPath, ItemType.Any))
{
if (createTeamProjectFolder)
VersionControl.CreateTeamProjectFolder(new TeamProjectFolderOptions(projectName));
else
throw new GitTfsException("error: the team project folder '" + projectPath + "' doesn't exist!",
new List<string>()
{
"Verify that the name of the project '" + projectName +"' is well spelled",
"Create the team project folder in TFS before (recommanded)",
"Use the flag '--create-project-folder' to create the team project folder during the process"
});
}
workspace = GetWorkspace(new WorkingFolder(projectPath, gitRepositoryPath));
if (!Directory.Exists(directoryForBranch))
Directory.CreateDirectory(directoryForBranch);
workspace.PendAdd(directoryForBranch);
var changes = workspace.GetPendingChanges();
if (!changes.Any())
return;
workspace.CheckIn(changes, "Creation project folder '" + mainBranch + "'");
ConvertFolderIntoBranch(projectPath + "/" + mainBranch);
}
catch (GitTfsException)
{
throw;
}
catch (Exception ex)
{
throw new GitTfsException("error: Unable to create project folder!", ex);
}
finally
{
if (Directory.Exists(directoryForBranch))
Directory.Delete(directoryForBranch);
if (workspace != null)
workspace.DeleteMapping(workspace.GetWorkingFolderForLocalItem(gitRepositoryPath));
}
}
public bool IsExistingInTfs(string path)
{
return VersionControl.ServerItemExists(path, ItemType.Any);
}
protected virtual void ConvertFolderIntoBranch(string tfsRepositoryPath)
{
}
}
}