diff --git a/GitTfs.VsCommon/Wrappers.cs b/GitTfs.VsCommon/Wrappers.cs index 1de5a918..80aef735 100644 --- a/GitTfs.VsCommon/Wrappers.cs +++ b/GitTfs.VsCommon/Wrappers.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using Microsoft.TeamFoundation.Server; using Microsoft.TeamFoundation.VersionControl.Client; using Sep.Git.Tfs.Core.TfsInterop; @@ -455,6 +456,13 @@ namespace Sep.Git.Tfs.VsCommon _workspace.Get(new ChangesetVersionSpec(changeset), GetOptions.Overwrite | GetOptions.GetAll); } + public void GetSpecificVersion(IChangeset changeset) + { + var requests = from change in changeset.Changes + select new GetRequest(new ItemSpec(change.Item.ServerItem, RecursionType.None, change.Item.DeletionId), changeset.ChangesetId); + _workspace.Get(requests.ToArray(), GetOptions.Overwrite); + } + public string OwnerName { get { return _workspace.OwnerName; } diff --git a/GitTfs/Core/GitTfsRemote.cs b/GitTfs/Core/GitTfsRemote.cs index 40c044e8..0513b22d 100644 --- a/GitTfs/Core/GitTfsRemote.cs +++ b/GitTfs/Core/GitTfsRemote.cs @@ -296,11 +296,21 @@ namespace Sep.Git.Tfs.Core private LogEntry Apply(string lastCommit, ITfsChangeset changeset) { LogEntry result = null; - WithTemporaryIndex(() => Tfs.WithWorkspace(WorkingDirectory, this, changeset.Summary, workspace => + if (globals.Experimental) { - GitIndexInfo.Do(Repository, index => result = changeset.Apply(lastCommit, index/*, workspace*/)); - result.Tree = Repository.CommandOneline("write-tree"); - })); + WithTemporaryIndex(() => Tfs.WithWorkspace(WorkingDirectory, this, changeset.Summary, workspace => + { + GitIndexInfo.Do(Repository, index => result = changeset.Apply(lastCommit, index, workspace)); + result.Tree = Repository.CommandOneline("write-tree"); + })); + } + else + { + WithTemporaryIndex(() => + GitIndexInfo.Do(Repository, index => result = changeset.Apply(lastCommit, index))); + WithTemporaryIndex(() => + result.Tree = Repository.CommandOneline("write-tree")); + } if(!String.IsNullOrEmpty(lastCommit)) result.CommitParents.Add(lastCommit); return result; } diff --git a/GitTfs/Core/ITfsChangeset.cs b/GitTfs/Core/ITfsChangeset.cs index 703362bf..d7d9410d 100644 --- a/GitTfs/Core/ITfsChangeset.cs +++ b/GitTfs/Core/ITfsChangeset.cs @@ -5,6 +5,7 @@ namespace Sep.Git.Tfs.Core public interface ITfsChangeset { TfsChangesetInfo Summary { get; } + LogEntry Apply(string lastCommit, GitIndexInfo index, ITfsWorkspace workspace); LogEntry Apply(string lastCommit, GitIndexInfo index); LogEntry CopyTree(GitIndexInfo index, ITfsWorkspace workspace); IEnumerable GetTree(); diff --git a/GitTfs/Core/ITfsWorkspace.cs b/GitTfs/Core/ITfsWorkspace.cs index ed4cd155..c7fd66e8 100644 --- a/GitTfs/Core/ITfsWorkspace.cs +++ b/GitTfs/Core/ITfsWorkspace.cs @@ -1,4 +1,5 @@ using System; +using Sep.Git.Tfs.Core.TfsInterop; namespace Sep.Git.Tfs.Core { @@ -12,8 +13,15 @@ namespace Sep.Git.Tfs.Core /// Evaluates check-in policies and checks in all pending changes. /// long Checkin(); - + /// + /// Populates the workspace with a snapshot, as of the given changeset. + /// void Get(int changesetId); + /// + /// Gets the files changed in a given changeset. + /// + void Get(IChangeset changeset); + string GetLocalPath(string path); void Add(string path); void Edit(string path); diff --git a/GitTfs/Core/TfsChangeset.cs b/GitTfs/Core/TfsChangeset.cs index 0dc5db36..13e0fdfa 100644 --- a/GitTfs/Core/TfsChangeset.cs +++ b/GitTfs/Core/TfsChangeset.cs @@ -22,6 +22,17 @@ namespace Sep.Git.Tfs.Core _stdout = stdout; } + public LogEntry Apply(string lastCommit, GitIndexInfo index, ITfsWorkspace workspace) + { + var initialTree = Summary.Remote.Repository.GetObjects(lastCommit); + workspace.Get(_changeset); + foreach (var change in Sort(_changeset.Changes)) + { + Apply(change, index, workspace, initialTree); + } + return MakeNewLogEntry(); + } + public LogEntry Apply(string lastCommit, GitIndexInfo index) { var initialTree = Summary.Remote.Repository.GetObjects(lastCommit); @@ -56,6 +67,30 @@ namespace Sep.Git.Tfs.Core } } + private void Apply(IChange change, GitIndexInfo index, ITfsWorkspace workspace, IDictionary initialTree) + { + // If you make updates to a dir in TF, the changeset includes changes for all the children also, + // and git doesn't really care if you add or delete empty dirs. + if (change.Item.ItemType == TfsItemType.File) + { + var pathInGitRepo = GetPathInGitRepo(change.Item.ServerItem, initialTree); + if (pathInGitRepo == null || Summary.Remote.ShouldSkip(pathInGitRepo)) + return; + if (change.ChangeType.IncludesOneOf(TfsChangeType.Rename)) + { + Rename(change, pathInGitRepo, index, workspace, initialTree); + } + else if (change.ChangeType.IncludesOneOf(TfsChangeType.Delete)) + { + Delete(pathInGitRepo, index, initialTree); + } + else + { + Update(change, pathInGitRepo, index, workspace, initialTree); + } + } + } + private string GetPathInGitRepo(string tfsPath, IDictionary initialTree) { var pathInGitRepo = Summary.Remote.GetPathInGitRepo(tfsPath); @@ -77,6 +112,19 @@ namespace Sep.Git.Tfs.Core } } + private void Rename(IChange change, string pathInGitRepo, GitIndexInfo index, ITfsWorkspace workspace, IDictionary initialTree) + { + var oldPath = GetPathInGitRepo(GetPathBeforeRename(change.Item), initialTree); + if (oldPath != null) + { + Delete(oldPath, index, initialTree); + } + if (!change.ChangeType.IncludesOneOf(TfsChangeType.Delete)) + { + Update(change, pathInGitRepo, index, workspace, initialTree); + } + } + private IEnumerable Sort(IEnumerable changes) { return changes.OrderBy(change => Rank(change.ChangeType)); @@ -126,6 +174,18 @@ namespace Sep.Git.Tfs.Core } } + private void Update(IChange change, string pathInGitRepo, GitIndexInfo index, ITfsWorkspace workspace, IDictionary initialTree) + { + if (change.Item.DeletionId == 0) + { + index.Update( + GetMode(change, initialTree, pathInGitRepo), + pathInGitRepo, + workspace.GetLocalPath(pathInGitRepo) + ); + } + } + public IEnumerable GetTree() { var treeInfo = Summary.Remote.Repository.GetObjects(); diff --git a/GitTfs/Core/TfsInterop/IWorkspace.cs b/GitTfs/Core/TfsInterop/IWorkspace.cs index 39d05072..c9be1622 100644 --- a/GitTfs/Core/TfsInterop/IWorkspace.cs +++ b/GitTfs/Core/TfsInterop/IWorkspace.cs @@ -14,6 +14,7 @@ namespace Sep.Git.Tfs.Core.TfsInterop int PendRename(string pathFrom, string pathTo); void ForceGetFile(string path, int changeset); void GetSpecificVersion(int changeset); + void GetSpecificVersion(IChangeset changeset); string OwnerName { get; } } } \ No newline at end of file diff --git a/GitTfs/Core/TfsWorkspace.cs b/GitTfs/Core/TfsWorkspace.cs index 5ec20124..2da0488b 100644 --- a/GitTfs/Core/TfsWorkspace.cs +++ b/GitTfs/Core/TfsWorkspace.cs @@ -158,6 +158,11 @@ namespace Sep.Git.Tfs.Core _workspace.GetSpecificVersion(changesetId); } + public void Get(IChangeset changeset) + { + _workspace.GetSpecificVersion(changeset); + } + private IEnumerable GetWorkItemInfos() { return GetWorkItemInfosHelper(_tfsHelper.GetWorkItemInfos);