Files
git-tfs/GitTfs/Core/TfsChangeset.cs
T
2014-12-26 19:28:04 +01:00

220 lines
8.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Sep.Git.Tfs.Core.TfsInterop;
using Sep.Git.Tfs.Util;
namespace Sep.Git.Tfs.Core
{
public class TfsChangeset : ITfsChangeset
{
private readonly ITfsHelper _tfs;
private readonly IChangeset _changeset;
private readonly TextWriter _stdout;
private readonly AuthorsFile _authors;
public TfsChangesetInfo Summary { get; set; }
public int BaseChangesetId { get; set; }
public TfsChangeset(ITfsHelper tfs, IChangeset changeset, TextWriter stdout, AuthorsFile authors)
{
_tfs = tfs;
_changeset = changeset;
_stdout = stdout;
_authors = authors;
BaseChangesetId = _changeset.Changes.Max(c => c.Item.ChangesetId) - 1;
}
public LogEntry Apply(string lastCommit, IGitTreeModifier treeBuilder, ITfsWorkspace workspace, IDictionary<string, GitObject> initialTree, Action<Exception> ignorableErrorHandler)
{
if (initialTree.Empty())
Summary.Remote.Repository.GetObjects(lastCommit, initialTree);
var resolver = new PathResolver(Summary.Remote, initialTree);
var sieve = new ChangeSieve(_changeset, resolver);
if (sieve.RenameBranchCommmit)
{
IsRenameChangeset = true;
}
_changeset.Get(workspace, sieve.GetChangesToFetch(), ignorableErrorHandler);
foreach (var change in sieve.GetChangesToApply())
{
ignorableErrorHandler.Catch(() => {
Apply(change, treeBuilder, workspace, initialTree);
});
}
return MakeNewLogEntry();
}
private void Apply(ApplicableChange change, IGitTreeModifier treeBuilder, ITfsWorkspace workspace, IDictionary<string, GitObject> initialTree)
{
switch (change.Type)
{
case ChangeType.Update:
Update(change, treeBuilder, workspace, initialTree);
break;
case ChangeType.Delete:
Delete(change.GitPath, treeBuilder, initialTree);
break;
default:
throw new NotImplementedException("Unsupported change type: " + change.Type);
}
}
private void Update(ApplicableChange change, IGitTreeModifier treeBuilder, ITfsWorkspace workspace, IDictionary<string, GitObject> initialTree)
{
var localPath = workspace.GetLocalPath(change.GitPath);
if (File.Exists(localPath))
{
treeBuilder.Add(change.GitPath, localPath, change.Mode);
}
else
{
_stdout.WriteLine("Cannot checkout file '{0}' from TFS. Skip it", change.GitPath);
}
}
public IEnumerable<TfsTreeEntry> GetTree()
{
return GetFullTree().Where(item => item.Item.ItemType == TfsItemType.File && !Summary.Remote.ShouldSkip(item.FullName));
}
public bool IsMergeChangeset
{
get
{
if (_changeset == null || _changeset.Changes == null || !_changeset.Changes.Any())
return false;
return _changeset.Changes.Any(c => c.ChangeType.IncludesOneOf(TfsChangeType.Merge));
}
}
public IEnumerable<TfsTreeEntry> GetFullTree()
{
var treeInfo = Summary.Remote.Repository.CreateObjectsDictionary();
var resolver = new PathResolver(Summary.Remote, treeInfo);
IItem[] tfsItems;
if(Summary.Remote.TfsRepositoryPath != null)
{
tfsItems = _changeset.VersionControlServer.GetItems(Summary.Remote.TfsRepositoryPath, _changeset.ChangesetId, TfsRecursionType.Full);
}
else
{
tfsItems = Summary.Remote.TfsSubtreePaths.SelectMany(x => _changeset.VersionControlServer.GetItems(x, _changeset.ChangesetId, TfsRecursionType.Full)).ToArray();
}
var tfsItemsWithGitPaths = tfsItems.Select(item => new { item, gitPath = resolver.GetPathInGitRepo(item.ServerItem) });
return tfsItemsWithGitPaths.Where(x => x.gitPath != null).Select(x => new TfsTreeEntry(x.gitPath, x.item));
}
public LogEntry CopyTree(IGitTreeModifier treeBuilder, ITfsWorkspace workspace)
{
var startTime = DateTime.Now;
var itemsCopied = 0;
var maxChangesetId = 0;
var tfsTreeEntries = GetTree().ToArray();
if (tfsTreeEntries.Length == 0)
{
maxChangesetId = _changeset.ChangesetId;
}
else
{
workspace.Get(_changeset.ChangesetId);
foreach (var entry in tfsTreeEntries)
{
Add(entry.Item, entry.FullName, treeBuilder, workspace);
maxChangesetId = Math.Max(maxChangesetId, entry.Item.ChangesetId);
itemsCopied++;
if (DateTime.Now - startTime > TimeSpan.FromSeconds(30))
{
_stdout.WriteLine("{0} objects created...", itemsCopied);
startTime = DateTime.Now;
}
}
}
return MakeNewLogEntry(maxChangesetId == _changeset.ChangesetId ? _changeset : _tfs.GetChangeset(maxChangesetId));
}
private void Add(IItem item, string pathInGitRepo, IGitTreeModifier treeBuilder, ITfsWorkspace workspace)
{
if (item.DeletionId == 0)
{
treeBuilder.Add(pathInGitRepo, workspace.GetLocalPath(pathInGitRepo), LibGit2Sharp.Mode.NonExecutableFile);
}
}
private void Delete(string pathInGitRepo, IGitTreeModifier treeBuilder, IDictionary<string, GitObject> initialTree)
{
if (initialTree.ContainsKey(pathInGitRepo))
{
treeBuilder.Remove(initialTree[pathInGitRepo].Path);
Trace.WriteLine("\tD\t" + pathInGitRepo);
}
}
private LogEntry MakeNewLogEntry()
{
return MakeNewLogEntry(_changeset, Summary.Remote);
}
private LogEntry MakeNewLogEntry(IChangeset changesetToLog, IGitTfsRemote remote = null)
{
var identity = _tfs.GetIdentity(changesetToLog.Committer);
var name = changesetToLog.Committer;
var email = changesetToLog.Committer;
if (_authors != null && _authors.Authors.ContainsKey(changesetToLog.Committer))
{
name = _authors.Authors[changesetToLog.Committer].Name;
email = _authors.Authors[changesetToLog.Committer].Email;
}
else if (identity != null)
{
//This can be null if the user was deleted from AD.
//We want to keep their original history around with as little
//hassle to the end user as possible
if (!String.IsNullOrWhiteSpace(identity.DisplayName))
name = identity.DisplayName;
if (!String.IsNullOrWhiteSpace(identity.MailAddress))
email = identity.MailAddress;
}
else if (!String.IsNullOrWhiteSpace(changesetToLog.Committer))
{
string[] split = changesetToLog.Committer.Split('\\');
if (split.Length == 2)
{
name = split[1].ToLower();
email = string.Format("{0}@{1}.tfs.local", name, split[0].ToLower());
}
}
// committer's & author's name and email MUST NOT be empty as otherwise they would be picked
// by git from user.name and user.email config settings which is bad thing because commit could
// be different depending on whose machine it fetched
if (String.IsNullOrWhiteSpace(name))
{
name = "Unknown TFS user";
}
if (String.IsNullOrWhiteSpace(email))
{
email = "unknown@tfs.local";
}
return new LogEntry
{
Date = changesetToLog.CreationDate,
Log = changesetToLog.Comment + Environment.NewLine,
ChangesetId = changesetToLog.ChangesetId,
CommitterName = name,
AuthorName = name,
CommitterEmail = email,
AuthorEmail = email,
Remote = remote
};
}
public string OmittedParentBranch { get; set; }
public bool IsRenameChangeset { get; set; }
}
}