diff --git a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs index 5a7e34c43..95fb18e73 100644 --- a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs +++ b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs @@ -52,6 +52,31 @@ public static bool TryLoadOrCreate(ITracer tracer, string dataDirectory, Physica return true; } + /// + /// This method will examine the modified paths to check if there is already a parent folder entry in + /// the modified paths. If there is a parent folder the entry does not need to be in the modified paths + /// and will be removed because the parent folder is recursive and covers any children. + /// + public void RemoveEntriesWithParentFolderEntry(ITracer tracer) + { + int startingCount = this.modifiedPaths.Count; + using (ITracer activity = tracer.StartActivity(nameof(this.RemoveEntriesWithParentFolderEntry), EventLevel.Informational)) + { + foreach (string modifiedPath in this.modifiedPaths) + { + if (this.ContainsParentDirectory(modifiedPath)) + { + this.modifiedPaths.TryRemove(modifiedPath); + } + } + + EventMetadata metadata = new EventMetadata(); + metadata.Add(nameof(startingCount), startingCount); + metadata.Add("EndCount", this.modifiedPaths.Count); + activity.Stop(metadata); + } + } + public bool Contains(string path, bool isFolder) { string entry = this.NormalizeEntryString(path, isFolder); diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs index f8e42331e..f2f255180 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs @@ -37,11 +37,6 @@ public class ModifiedPathsTests : TestsWithEnlistmentPerTestCase $"A {RenameNewDotGitFileTarget}", $"A {FileToCreateOutsideRepo}", $"A {FolderToCreateOutsideRepo}/", - $"A {FolderToDelete}/CreateCommonAssemblyVersion.bat", - $"A {FolderToDelete}/CreateCommonCliAssemblyVersion.bat", - $"A {FolderToDelete}/CreateCommonVersionHeader.bat", - $"A {FolderToDelete}/RunFunctionalTests.bat", - $"A {FolderToDelete}/RunUnitTests.bat", $"A {FolderToDelete}/", }; @@ -138,9 +133,16 @@ public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem) folderToCreateOutsideRepoTargetPath.ShouldBeADirectory(fileSystem); folderToCreateOutsideRepoPath.ShouldNotExistOnDisk(fileSystem); - string folderToDelete = this.Enlistment.GetVirtualPathTo(FolderToDelete); - fileSystem.DeleteDirectory(folderToDelete); - folderToDelete.ShouldNotExistOnDisk(fileSystem); + string folderToDeleteFullPath = this.Enlistment.GetVirtualPathTo(FolderToDelete); + fileSystem.WriteAllText(Path.Combine(folderToDeleteFullPath, "NewFile.txt"), "Contents for new file"); + string newFileToDelete = Path.Combine(folderToDeleteFullPath, "NewFileToDelete.txt"); + fileSystem.WriteAllText(newFileToDelete, "Contents for new file"); + fileSystem.DeleteFile(newFileToDelete); + fileSystem.WriteAllText(Path.Combine(folderToDeleteFullPath, "CreateCommonVersionHeader.bat"), "Changing the file contents"); + fileSystem.DeleteFile(Path.Combine(folderToDeleteFullPath, "RunUnitTests.bat")); + + fileSystem.DeleteDirectory(folderToDeleteFullPath); + folderToDeleteFullPath.ShouldNotExistOnDisk(fileSystem); // Remount this.Enlistment.UnmountGVFS(); diff --git a/GVFS/GVFS.UnitTests/Common/ModifiedPathsDatabaseTests.cs b/GVFS/GVFS.UnitTests/Common/ModifiedPathsDatabaseTests.cs index 663a76bb0..40bacf08f 100644 --- a/GVFS/GVFS.UnitTests/Common/ModifiedPathsDatabaseTests.cs +++ b/GVFS/GVFS.UnitTests/Common/ModifiedPathsDatabaseTests.cs @@ -1,6 +1,7 @@ using GVFS.Common; using GVFS.Tests.Should; using GVFS.UnitTests.Mock; +using GVFS.UnitTests.Mock.Common; using GVFS.UnitTests.Mock.FileSystem; using NUnit.Framework; using System; @@ -17,6 +18,21 @@ public class ModifiedPathsDatabaseTests private const string ExistingEntries = @"A file.txt A dir/file2.txt A dir1/dir2/file3.txt +"; + private const string EntriesToCompress = @"A file.txt +D deleted.txt +A dir/file2.txt +A dir/dir3/dir4/ +A dir1/dir2/file3.txt +A dir/ +D deleted/ +A dir1/dir2/ +A dir1/file.txt +A dir1/dir2/dir3/dir4/dir5/ +A dir/dir2/file3.txt +A dir/dir4/dir5/ +D dir/dir2/deleted.txt +A dir1/dir2 "; [TestCase] @@ -119,6 +135,19 @@ public void EntryNotAddedIfParentDirectoryExists() modifiedPathsDatabase.Contains("dir2/dir", isFolder: true).ShouldBeTrue(); } + [TestCase] + public void RemoveEntriesWithParentFolderEntry() + { + ModifiedPathsDatabase modifiedPathsDatabase = CreateModifiedPathsDatabase(EntriesToCompress); + modifiedPathsDatabase.RemoveEntriesWithParentFolderEntry(new MockTracer()); + modifiedPathsDatabase.Count.ShouldEqual(5); + modifiedPathsDatabase.Contains("file.txt", isFolder: false).ShouldBeTrue(); + modifiedPathsDatabase.Contains("dir/", isFolder: true).ShouldBeTrue(); + modifiedPathsDatabase.Contains("dir1/dir2", isFolder: false).ShouldBeTrue(); + modifiedPathsDatabase.Contains("dir1/dir2/", isFolder: true).ShouldBeTrue(); + modifiedPathsDatabase.Contains("dir1/file.txt", isFolder: false).ShouldBeTrue(); + } + private static void TestAddingPath(string path, bool isFolder = false) { TestAddingPath(path, path, isFolder); diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index d7c0d57b4..627c895a5 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -171,6 +171,9 @@ public static bool IsPathInsideDotGit(string relativePath) public bool TryStart(out string error) { + this.modifiedPaths.RemoveEntriesWithParentFolderEntry(this.context.Tracer); + this.modifiedPaths.WriteAllEntriesAndFlush(); + if (!this.fileSystemVirtualizer.TryStart(this, out error)) { return false;