diff --git a/GitTfs/Util/ChangeSieve.cs b/GitTfs/Util/ChangeSieve.cs index 53c53b02..a0f15de4 100644 --- a/GitTfs/Util/ChangeSieve.cs +++ b/GitTfs/Util/ChangeSieve.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Sep.Git.Tfs.Core; @@ -180,16 +181,19 @@ namespace Sep.Git.Tfs.Util var oldItem = item.VersionControlServer.GetItem(item.ItemId, previousChangeset); if (null == oldItem) { - var history = item.VersionControlServer.QueryHistory(item.ServerItem, item.ChangesetId, 0, + try + { + var history = item.VersionControlServer.QueryHistory(item.ServerItem, item.ChangesetId, 0, TfsRecursionType.None, null, 1, previousChangeset, 1, true, false, false); - var previousChange = history.FirstOrDefault(); - if (previousChange == null) + var previousChange = history.First(); + oldItem = previousChange.Changes[0].Item; + } + catch { Trace.WriteLine(string.Format("No history found for item {0} changesetId {1}", item.ServerItem, item.ChangesetId)); return null; } - oldItem = previousChange.Changes[0].Item; } return oldItem.ServerItem; } diff --git a/GitTfsTest/Core/ChangeSieveTests.cs b/GitTfsTest/Core/ChangeSieveTests.cs index 9b75d2e9..c2607c74 100644 --- a/GitTfsTest/Core/ChangeSieveTests.cs +++ b/GitTfsTest/Core/ChangeSieveTests.cs @@ -22,7 +22,7 @@ namespace Sep.Git.Tfs.Test.Core // Make this remote act like it's mapped to $/Project Remote.Stub(r => r.GetPathInGitRepo(null)) .Constraints(Is.Anything()) - .Do(new Function(path => path.StartsWith("$/Project/") ? path.Replace("$/Project/", "") : null)); + .Do(new Function(path => path != null && path.StartsWith("$/Project/") ? path.Replace("$/Project/", "") : null)); // Make this remote ignore any path that includes "ignored". Remote.Stub(r => r.ShouldSkip(null)) .Constraints(Is.Anything()) @@ -575,6 +575,7 @@ namespace Sep.Git.Tfs.Test.Core ApplicableChange.Update("file10.txt")); } } + public class WithDeleteMainFolderBranchAndSubItems : Base { public class Fixture : BaseFixture @@ -635,5 +636,118 @@ namespace Sep.Git.Tfs.Test.Core Assert.Equal(new string[] { "$/Project/file1.txt" }, Subject.GetChangesToFetch().Select(c => c.Item.ServerItem)); } } + + public class RenamedFromDeleted : Base + { + public class Fixture : BaseFixture + { + public Fixture() + { + Changeset.Changes = new IChange[] { + new RenamedFromDeletedChange("$/Project/file1.txt"), + }; + } + } + + [Fact] + public void FetchesItemRenamedAfterDelete() + { + AssertChanges(Subject.GetChangesToApply(), + ApplicableChange.Update("file1.txt")); + } + + // A Change/Item that only throws an exception when you try to query its history. + public class RenamedFromDeletedChange : IChange, IItem, IVersionControlServer + { + // This is the interesting part of this implementation. + // The TFS client throws an exception of type Microsoft.TeamFoundation.VersionControl.Client.ItemNotFoundException. + // ChangeSieve doesn't have a reference to the TFS client libs, so it can't catch that exact exception. + // This class, therefore, throws an exception that is of a type that ChangeSieve can't specifically catch. + IEnumerable IVersionControlServer.QueryHistory(string path, int version, int deletionId, TfsRecursionType recursion, string user, int versionFrom, int versionTo, int maxCount, bool includeChanges, bool slotMode, bool includeDownloadInfo) + { + throw new AnExceptionTypeThatYouCannotCatch(); + } + + class AnExceptionTypeThatYouCannotCatch : Exception + { + } + + // The rest of the implementation is pretty straight-forward. + + readonly string _serverItem; + + // Accept a name so that the name is more obviously matched between the Fixture + // and the assertion. + public RenamedFromDeletedChange(string serverItem) + { + _serverItem = serverItem; + } + + TfsChangeType IChange.ChangeType + { + get { return TfsChangeType.Rename; } + } + + IItem IChange.Item + { + get { return this; } + } + + IVersionControlServer IItem.VersionControlServer + { + get { return this; } + } + + int IItem.ChangesetId + { + get { return 100; } + } + + string IItem.ServerItem + { + get { return _serverItem; } + } + + int IItem.DeletionId + { + get { return 0; } + } + + TfsItemType IItem.ItemType + { + get { return TfsItemType.File; } + } + + int IItem.ItemId + { + get { return 200; } + } + + long IItem.ContentLength + { + get { return 0; } + } + + TemporaryFile IItem.DownloadFile() + { + return null; + } + + IItem IVersionControlServer.GetItem(int itemId, int changesetNumber) + { + return null; + } + + IItem IVersionControlServer.GetItem(string itemPath, int changesetNumber) + { + return null; + } + + IItem[] IVersionControlServer.GetItems(string itemPath, int changesetNumber, TfsRecursionType recursionType) + { + return null; + } + } + } } }