From 3f8fd9e44a4b31fa328f2abd3bb6bd7358f38659 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Wed, 22 Aug 2018 12:38:04 -0600 Subject: [PATCH 01/14] Add tests for removing entries from the modified files database --- .../Windows/Tests/DiskLayoutUpgradeTests.cs | 42 ++++--- .../ModifiedPathsTests.cs | 105 ++++++++++++++---- 2 files changed, 108 insertions(+), 39 deletions(-) diff --git a/GVFS/GVFS.FunctionalTests.Windows/Windows/Tests/DiskLayoutUpgradeTests.cs b/GVFS/GVFS.FunctionalTests.Windows/Windows/Tests/DiskLayoutUpgradeTests.cs index d3ea1f93b..eb1ce473d 100644 --- a/GVFS/GVFS.FunctionalTests.Windows/Windows/Tests/DiskLayoutUpgradeTests.cs +++ b/GVFS/GVFS.FunctionalTests.Windows/Windows/Tests/DiskLayoutUpgradeTests.cs @@ -324,24 +324,30 @@ public void MountCreatesModifiedPathsDatabase() this.Enlistment.MountGVFS(); this.Enlistment.UnmountGVFS(); - string expectedModifiedPaths = @"A .gitattributes -A developer/me/ -A developer/me/JLANGE9._prerazzle -A developer/me/StateSwitch.Save -A tools/x86/remote.exe -A tools/x86/runelevated.exe -A tools/amd64/remote.exe -A tools/amd64/runelevated.exe -A tools/perllib/MS/TraceLogging.dll -A tools/managed/v2.0/midldd.CheckedInExe -A tools/managed/v4.0/sdapi.dll -A tools/managed/v2.0/midlpars.dll -A tools/managed/v2.0/RPCDataSupport.dll -A tools/managed/v2.0/MidlStaticAnalysis.dll -A tools/perllib/MS/Somefile.txt -"; - - modifiedPathsDatabasePath.ShouldBeAFile(this.fileSystem).WithContents(expectedModifiedPaths); + string[] expectedModifiedPaths = + { + "A .gitattributes", + "A developer/me/", + "A developer/me/JLANGE9._prerazzle", + "A developer/me/StateSwitch.Save", + "A tools/x86/remote.exe", + "A tools/x86/runelevated.exe", + "A tools/amd64/remote.exe", + "A tools/amd64/runelevated.exe", + "A tools/perllib/MS/TraceLogging.dll", + "A tools/managed/v2.0/midldd.CheckedInExe", + "A tools/managed/v4.0/sdapi.dll", + "A tools/managed/v2.0/midlpars.dll", + "A tools/managed/v2.0/RPCDataSupport.dll", + "A tools/managed/v2.0/MidlStaticAnalysis.dll", + "A tools/perllib/MS/Somefile.txt", + }; + + modifiedPathsDatabasePath.ShouldBeAFile(this.fileSystem); + this.fileSystem.ReadAllText(modifiedPathsDatabasePath) + .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .OrderBy(x => x) + .ShouldMatchInOrder(expectedModifiedPaths.OrderBy(x => x)); this.ValidatePersistedVersionMatchesCurrentVersion(); } diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs index c57bffccd..62fa2a9f9 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs @@ -3,7 +3,9 @@ using GVFS.FunctionalTests.Tools; using GVFS.Tests.Should; using NUnit.Framework; +using System; using System.IO; +using System.Linq; namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase { @@ -23,26 +25,64 @@ public class ModifiedPathsTests : TestsWithEnlistmentPerTestCase private static readonly string FileToCreateOutsideRepo = $"{nameof(ModifiedPathsTests)}_outsideRepo.txt"; private static readonly string FolderToCreateOutsideRepo = $"{nameof(ModifiedPathsTests)}_outsideFolder"; private static readonly string FolderToDelete = "Scripts"; - private static readonly string ExpectedModifiedFilesContentsAfterRemount = -$@"A .gitattributes -A {GVFSHelpers.ConvertPathToGitFormat(FileToAdd)} -A {GVFSHelpers.ConvertPathToGitFormat(FileToUpdate)} -A {FileToDelete} -A {GVFSHelpers.ConvertPathToGitFormat(FileToRename)} -A {GVFSHelpers.ConvertPathToGitFormat(RenameFileTarget)} -A {FolderToCreate}/ -A {FolderToRename}/ -A {RenameFolderTarget}/ -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}/ -"; + private static readonly string[] ExpectedModifiedFilesContentsAfterRemount = + { + $"A .gitattributes", + $"A {GVFSHelpers.ConvertPathToGitFormat(FileToAdd)}", + $"A {GVFSHelpers.ConvertPathToGitFormat(FileToUpdate)}", + $"A {FileToDelete}", + $"A {GVFSHelpers.ConvertPathToGitFormat(FileToRename)}", + $"A {GVFSHelpers.ConvertPathToGitFormat(RenameFileTarget)}", + $"A {FolderToCreate}/", + $"A {FolderToRename}/", + $"A {RenameFolderTarget}/", + $"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}/", + }; + + [TestCaseSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)] + public void DeletedTempFileIsRemovedFromModifiedFiles(FileSystemRunner fileSystem) + { + string tempFile = this.CreateFile(fileSystem, "temp.txt"); + fileSystem.DeleteFile(tempFile); + tempFile.ShouldNotExistOnDisk(fileSystem); + + this.Enlistment.UnmountGVFS(); + this.ValidateModifiedPathsDoNotContain(fileSystem, "temp.txt"); + } + + [TestCaseSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)] + public void DeletedTempFolderIsRemovedFromModifiedFiles(FileSystemRunner fileSystem) + { + string tempFolder = this.CreateDirectory(fileSystem, "Temp"); + fileSystem.DeleteDirectory(tempFolder); + tempFolder.ShouldNotExistOnDisk(fileSystem); + + this.Enlistment.UnmountGVFS(); + this.ValidateModifiedPathsDoNotContain(fileSystem, "Temp/"); + } + + [TestCaseSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)] + public void DeletedTempFolderDeletesFilesFromModifiedFiles(FileSystemRunner fileSystem) + { + string tempFolder = this.CreateDirectory(fileSystem, "Temp"); + string tempFile1 = this.CreateFile(fileSystem, Path.Combine("Temp", "temp1.txt")); + string tempFile2 = this.CreateFile(fileSystem, Path.Combine("Temp", "temp2.txt")); + fileSystem.DeleteDirectory(tempFolder); + tempFolder.ShouldNotExistOnDisk(fileSystem); + tempFile1.ShouldNotExistOnDisk(fileSystem); + tempFile2.ShouldNotExistOnDisk(fileSystem); + + this.Enlistment.UnmountGVFS(); + this.ValidateModifiedPathsDoNotContain(fileSystem, "Temp/", "Temp/temp1.txt", "Temp/temp2.txt"); + } [Category(Categories.MacTODO.M2)] [TestCaseSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)] @@ -114,7 +154,8 @@ public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem) modifiedPathsDatabase.ShouldBeAFile(fileSystem); using (StreamReader reader = new StreamReader(File.Open(modifiedPathsDatabase, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { - reader.ReadToEnd().ShouldEqual(ExpectedModifiedFilesContentsAfterRemount); + reader.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).OrderBy(x => x) + .ShouldMatchInOrder(ExpectedModifiedFilesContentsAfterRemount.OrderBy(x => x)); } } @@ -160,5 +201,27 @@ A LinkToFileOutsideSrc.txt reader.ReadToEnd().ShouldEqual(ExpectedModifiedFilesContentsAfterHardlinks); } } + + private string CreateDirectory(FileSystemRunner fileSystem, string relativePath) + { + string tempFolder = this.Enlistment.GetVirtualPathTo(relativePath); + fileSystem.CreateDirectory(tempFolder); + tempFolder.ShouldBeADirectory(fileSystem); + return tempFolder; + } + + private string CreateFile(FileSystemRunner fileSystem, string relativePath) + { + string tempFile = this.Enlistment.GetVirtualPathTo(relativePath); + fileSystem.WriteAllText(tempFile, $"Contents for the {relativePath} file"); + tempFile.ShouldBeAFile(fileSystem); + return tempFile; + } + + private void ValidateModifiedPathsDoNotContain(FileSystemRunner fileSystem, params string[] paths) + { + GVFSHelpers.ModifiedPathsShouldNotContain(fileSystem, this.Enlistment.DotGVFSRoot, paths.Select(x => $"A {x}" + Environment.NewLine).ToArray()); + GVFSHelpers.ModifiedPathsShouldNotContain(fileSystem, this.Enlistment.DotGVFSRoot, paths.Select(x => $"D {x}" + Environment.NewLine).ToArray()); + } } } From a0f9b408c228b89fb7d6e5000d914bd14fa6408f Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Tue, 21 Aug 2018 11:00:38 -0600 Subject: [PATCH 02/14] Add TryRemove method to the ModifiedPathsDatabase --- GVFS/GVFS.Common/ModifiedPathsDatabase.cs | 99 ++++++++++++++++++----- 1 file changed, 80 insertions(+), 19 deletions(-) diff --git a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs index 043a1e3bc..409b36a3c 100644 --- a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs +++ b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs @@ -75,38 +75,99 @@ public bool TryAdd(string path, bool isFolder, out bool isRetryable) } catch (IOException e) { - if (this.Tracer != null) - { - EventMetadata metadata = new EventMetadata(); - metadata.Add("Area", "ModifiedPathsDatabase"); - metadata.Add(nameof(entry), entry); - metadata.Add(nameof(isFolder), isFolder); - metadata.Add("Exception", e.ToString()); - this.Tracer.RelatedWarning(metadata, $"IOException caught while processing {nameof(this.TryAdd)}"); - } - + this.TraceWarning(isFolder, entry, e, nameof(this.TryAdd)); return false; } catch (Exception e) { - if (this.Tracer != null) - { - EventMetadata metadata = new EventMetadata(); - metadata.Add("Area", "ModifiedPathsDatabase"); - metadata.Add(nameof(entry), entry); - metadata.Add(nameof(isFolder), isFolder); - metadata.Add("Exception", e.ToString()); - this.Tracer.RelatedError(metadata, $"Exception caught while processing {nameof(this.TryAdd)}"); - } + this.TraceError(isFolder, entry, e, nameof(this.TryAdd)); + isRetryable = false; + return false; + } + + return true; + } + return true; + } + + public bool TryRemove(string path, bool isFolder, out bool isRetryable) + { + isRetryable = true; + string entry = this.NormalizeEntryString(path, isFolder); + if (this.modifiedPaths.Contains(entry)) + { + isRetryable = true; + try + { + this.WriteRemoveEntry(entry, () => this.modifiedPaths.TryRemove(entry)); + } + catch (IOException e) + { + this.TraceWarning(isFolder, entry, e, nameof(this.TryRemove)); + return false; + } + catch (Exception e) + { + this.TraceError(isFolder, entry, e, nameof(this.TryRemove)); isRetryable = false; return false; } + + return true; } return true; } + public void WriteAllEntriesAndFlush() + { + try + { + this.WriteAndReplaceDataFile(this.GenerateDataLines); + } + catch (Exception e) + { + throw new FileBasedCollectionException(e); + } + } + + private static EventMetadata CreateEventMetadata(bool isFolder, string entry, Exception e) + { + EventMetadata metadata = new EventMetadata(); + metadata.Add("Area", "ModifiedPathsDatabase"); + metadata.Add(nameof(entry), entry); + metadata.Add(nameof(isFolder), isFolder); + metadata.Add("Exception", e.ToString()); + return metadata; + } + + private IEnumerable GenerateDataLines() + { + foreach (string entry in this.modifiedPaths) + { + yield return this.FormatAddLine(entry); + } + } + + private void TraceWarning(bool isFolder, string entry, Exception e, string method) + { + if (this.Tracer != null) + { + EventMetadata metadata = CreateEventMetadata(isFolder, entry, e); + this.Tracer.RelatedWarning(metadata, $"{e.GetType().Name} caught while processing {method}"); + } + } + + private void TraceError(bool isFolder, string entry, Exception e, string method) + { + if (this.Tracer != null) + { + EventMetadata metadata = CreateEventMetadata(isFolder, entry, e); + this.Tracer.RelatedError(metadata, $"{e.GetType().Name} caught while processing {method}"); + } + } + private bool TryParseAddLine(string line, out string key, out string value, out string error) { key = line; From ae5f9bace1a4da28fea769761d3cfc80c610753e Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Wed, 5 Sep 2018 14:28:36 -0600 Subject: [PATCH 03/14] Handle the file system callbacks to track created then deleted files --- .../FileSystemCallbacks.cs | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index a9ea5200e..d954ae6c4 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -33,6 +33,7 @@ public class FileSystemCallbacks : IDisposable, IHeartBeatMetadataProvider private GVFSContext context; private GVFSGitObjects gitObjects; private ModifiedPathsDatabase modifiedPaths; + private ConcurrentHashSet newlyCreatedFileAndFolderPaths; private ConcurrentDictionary placeHolderCreationCount; private BackgroundFileSystemTaskRunner backgroundFileSystemTaskRunner; private FileSystemVirtualizer fileSystemVirtualizer; @@ -75,6 +76,7 @@ public FileSystemCallbacks( this.fileSystemVirtualizer = fileSystemVirtualizer; this.placeHolderCreationCount = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + this.newlyCreatedFileAndFolderPaths = new ConcurrentHashSet(StringComparer.OrdinalIgnoreCase); string error; if (!ModifiedPathsDatabase.TryLoadOrCreate( @@ -364,6 +366,8 @@ public virtual void OnIndexFileChange() this.GitIndexProjection.InvalidateProjection(); this.InvalidateGitStatusCache(); } + + this.newlyCreatedFileAndFolderPaths.Clear(); } public void InvalidateGitStatusCache() @@ -400,6 +404,7 @@ public void OnExcludeFileChanged() public void OnFileCreated(string relativePath) { + this.newlyCreatedFileAndFolderPaths.Add(relativePath); this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFileCreated(relativePath)); } @@ -435,6 +440,7 @@ public void OnFileDeleted(string relativePath) public void OnFolderCreated(string relativePath) { + this.newlyCreatedFileAndFolderPaths.Add(relativePath); this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFolderCreated(relativePath)); } @@ -639,7 +645,15 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate case FileSystemTask.OperationType.OnFileDeleted: metadata.Add("virtualPath", gitUpdate.VirtualPath); - result = this.AddModifiedPathAndRemoveFromPlaceholderList(gitUpdate.VirtualPath); + if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) + { + result = this.TryRemoveModifiedPath(gitUpdate.VirtualPath, isFolder: false); + } + else + { + result = this.AddModifiedPathAndRemoveFromPlaceholderList(gitUpdate.VirtualPath); + } + break; case FileSystemTask.OperationType.OnFileOverwritten: @@ -736,7 +750,15 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate case FileSystemTask.OperationType.OnFolderDeleted: metadata.Add("virtualPath", gitUpdate.VirtualPath); - result = this.TryAddModifiedPath(gitUpdate.VirtualPath, isFolder: true); + if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) + { + result = this.TryRemoveModifiedPath(gitUpdate.VirtualPath, isFolder: true); + } + else + { + result = this.TryAddModifiedPath(gitUpdate.VirtualPath, isFolder: true); + } + break; case FileSystemTask.OperationType.OnFolderFirstWrite: @@ -768,6 +790,26 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate return result; } + private FileSystemTaskResult TryRemoveModifiedPath(string virtualPath, bool isFolder) + { + string fullPathToItem = Path.Combine(this.context.Enlistment.WorkingDirectoryRoot, virtualPath); + if ((isFolder && this.context.FileSystem.DirectoryExists(fullPathToItem)) || + (!isFolder && this.context.FileSystem.FileExists(fullPathToItem))) + { + return FileSystemTaskResult.Success; + } + + if (!this.modifiedPaths.TryRemove(virtualPath, isFolder, out bool isRetryable)) + { + return isRetryable ? FileSystemTaskResult.RetryableError : FileSystemTaskResult.FatalError; + } + + this.newlyCreatedFileAndFolderPaths.TryRemove(virtualPath); + + this.InvalidateGitStatusCache(); + return FileSystemTaskResult.Success; + } + private FileSystemTaskResult TryAddModifiedPath(string virtualPath, bool isFolder) { if (!this.modifiedPaths.TryAdd(virtualPath, isFolder, out bool isRetryable)) From b50dd1a0396042229940beb790493ad61799364f Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Thu, 6 Sep 2018 13:03:05 -0600 Subject: [PATCH 04/14] Add handling of rename of files for modified paths cleanup --- GVFS/GVFS.Virtualization/FileSystemCallbacks.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index d954ae6c4..de5541d4f 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -425,6 +425,7 @@ public void OnFileConvertedToFull(string relativePath) public virtual void OnFileRenamed(string oldRelativePath, string newRelativePath) { + this.newlyCreatedFileAndFolderPaths.Add(newRelativePath); this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFileRenamed(oldRelativePath, newRelativePath)); } @@ -631,7 +632,14 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate result = FileSystemTaskResult.Success; if (!string.IsNullOrEmpty(gitUpdate.OldVirtualPath) && !IsPathInsideDotGit(gitUpdate.OldVirtualPath)) { - result = this.AddModifiedPathAndRemoveFromPlaceholderList(gitUpdate.OldVirtualPath); + if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.OldVirtualPath)) + { + result = this.TryRemoveModifiedPath(gitUpdate.OldVirtualPath, isFolder: false); + } + else + { + result = this.AddModifiedPathAndRemoveFromPlaceholderList(gitUpdate.OldVirtualPath); + } } if (result == FileSystemTaskResult.Success && @@ -844,7 +852,7 @@ private FileSystemTaskResult AddModifiedPathAndRemoveFromPlaceholderList(string private FileSystemTaskResult PostBackgroundOperation() { - this.modifiedPaths.ForceFlush(); + this.modifiedPaths.WriteAllEntriesAndFlush(); this.gitStatusCache.RefreshAsynchronously(); return this.GitIndexProjection.CloseIndex(); } From 848e6d38fb4840aa1f715fa5b821763f9211a949 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Fri, 7 Sep 2018 12:37:43 -0600 Subject: [PATCH 05/14] Handle cleaning up modified paths when a folder is renamed --- .../FileSystemCallbacks.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index de5541d4f..c640d41a0 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -691,10 +691,20 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate result = this.TryAddModifiedPath(gitUpdate.VirtualPath, isFolder: true); if (result == FileSystemTaskResult.Success) { + if (!this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) + { + this.newlyCreatedFileAndFolderPaths.Add(gitUpdate.VirtualPath); + } + + if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.OldVirtualPath)) + { + this.TryRemoveModifiedPath(gitUpdate.OldVirtualPath, isFolder: true); + } + Queue relativeFolderPaths = new Queue(); relativeFolderPaths.Enqueue(gitUpdate.VirtualPath); - // Add all the files in the renamed folder to the always_exclude file + // Remove old paths from modified paths if in the newly created list while (relativeFolderPaths.Count > 0) { string folderPath = relativeFolderPaths.Dequeue(); @@ -705,14 +715,20 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate foreach (DirectoryItemInfo itemInfo in this.context.FileSystem.ItemsInDirectory(Path.Combine(this.context.Enlistment.WorkingDirectoryRoot, folderPath))) { string itemVirtualPath = Path.Combine(folderPath, itemInfo.Name); - if (itemInfo.IsDirectory) + string oldItemVirtualPath = gitUpdate.OldVirtualPath + itemVirtualPath.Substring(gitUpdate.VirtualPath.Length); + if (!this.newlyCreatedFileAndFolderPaths.Contains(itemVirtualPath)) { - relativeFolderPaths.Enqueue(itemVirtualPath); + this.newlyCreatedFileAndFolderPaths.Add(itemVirtualPath); } - else + + if (this.newlyCreatedFileAndFolderPaths.Contains(oldItemVirtualPath)) { - string oldItemVirtualPath = gitUpdate.OldVirtualPath + itemVirtualPath.Substring(gitUpdate.VirtualPath.Length); - result = this.TryAddModifiedPath(itemVirtualPath, isFolder: false); + this.TryRemoveModifiedPath(oldItemVirtualPath, isFolder: itemInfo.IsDirectory); + } + + if (itemInfo.IsDirectory) + { + relativeFolderPaths.Enqueue(itemVirtualPath); } } } From c189b2c356dcd3e279cbfd7024a43c66935fde18 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Fri, 7 Sep 2018 22:09:03 -0600 Subject: [PATCH 06/14] Add null check when creating trace metadata using and exception --- GVFS/GVFS.Common/ModifiedPathsDatabase.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs index 409b36a3c..945613115 100644 --- a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs +++ b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs @@ -138,7 +138,11 @@ private static EventMetadata CreateEventMetadata(bool isFolder, string entry, Ex metadata.Add("Area", "ModifiedPathsDatabase"); metadata.Add(nameof(entry), entry); metadata.Add(nameof(isFolder), isFolder); - metadata.Add("Exception", e.ToString()); + if (e != null) + { + metadata.Add("Exception", e.ToString()); + } + return metadata; } From 1772519160fefa052275c7238875f918f4fbbf7b Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Tue, 11 Sep 2018 13:45:24 -0600 Subject: [PATCH 07/14] Fix broken tests --- .../Tests/EnlistmentPerFixture/GitFilesTests.cs | 10 ++-------- .../Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs | 2 +- .../Tests/GitCommands/GitCommandsTests.cs | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs index eca57a1c2..88dbf1709 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs @@ -88,7 +88,6 @@ public void RenameEmptyFolderTest() string renamedFolderName = "folder3b"; string[] expectedModifiedEntries = { - folderName + "/", renamedFolderName + "/", }; @@ -110,19 +109,14 @@ public void RenameFolderTest() string[] fileNames = { "a", "b", "c" }; string[] expectedModifiedEntries = { - renamedFolderName + "/" + fileNames[0], - renamedFolderName + "/" + fileNames[1], - renamedFolderName + "/" + fileNames[2], - folderName + "/" + fileNames[0], - folderName + "/" + fileNames[1], - folderName + "/" + fileNames[2], + renamedFolderName + "/", }; this.Enlistment.GetVirtualPathTo(folderName).ShouldNotExistOnDisk(this.fileSystem); this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(folderName)); foreach (string fileName in fileNames) { - string filePath = folderName + "\\" + fileName; + string filePath = Path.Combine(folderName, fileName); this.fileSystem.CreateEmptyFile(this.Enlistment.GetVirtualPathTo(filePath)); this.Enlistment.GetVirtualPathTo(filePath).ShouldBeAFile(this.fileSystem); } diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs index 62fa2a9f9..14fe1a5be 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs @@ -109,7 +109,7 @@ public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem) string folderToRenameTarget = this.Enlistment.GetVirtualPathTo(RenameFolderTarget); fileSystem.MoveDirectory(folderToRename, folderToRenameTarget); - // Moving the new folder out of the repo should not change the modified paths + // Moving the new folder out of the repo should not change the modified paths file string folderTargetOutsideSrc = Path.Combine(this.Enlistment.EnlistmentRoot, RenameFolderTarget); folderTargetOutsideSrc.ShouldNotExistOnDisk(fileSystem); fileSystem.MoveDirectory(folderToRenameTarget, folderTargetOutsideSrc); diff --git a/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitCommandsTests.cs b/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitCommandsTests.cs index 0b3ccccce..c623f5ea7 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitCommandsTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitCommandsTests.cs @@ -1041,7 +1041,7 @@ private void BasicCommit(Action fileSystemAction, string addCommand, [CallerMemb fileSystemAction(); this.ValidateGitCommand("status"); this.ValidateGitCommand(addCommand); - this.RunGitCommand("commit -m \"BasicCommit for {test}\""); + this.RunGitCommand($"commit -m \"BasicCommit for {test}\""); } private void SwitchBranch(Action fileSystemAction, [CallerMemberName]string test = GitCommandsTests.UnknownTestName) From 9f8944e1686bb880c7e50c6d0e3eb15453f4a025 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Wed, 12 Sep 2018 09:06:46 -0600 Subject: [PATCH 08/14] Simplify checking the newly created files and removing from modified paths --- GVFS/GVFS.Virtualization/FileSystemCallbacks.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index c640d41a0..b41a2c634 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -691,14 +691,10 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate result = this.TryAddModifiedPath(gitUpdate.VirtualPath, isFolder: true); if (result == FileSystemTaskResult.Success) { - if (!this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) - { - this.newlyCreatedFileAndFolderPaths.Add(gitUpdate.VirtualPath); - } - + this.newlyCreatedFileAndFolderPaths.Add(gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.OldVirtualPath)) { - this.TryRemoveModifiedPath(gitUpdate.OldVirtualPath, isFolder: true); + result = this.TryRemoveModifiedPath(gitUpdate.OldVirtualPath, isFolder: true); } Queue relativeFolderPaths = new Queue(); @@ -716,14 +712,11 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate { string itemVirtualPath = Path.Combine(folderPath, itemInfo.Name); string oldItemVirtualPath = gitUpdate.OldVirtualPath + itemVirtualPath.Substring(gitUpdate.VirtualPath.Length); - if (!this.newlyCreatedFileAndFolderPaths.Contains(itemVirtualPath)) - { - this.newlyCreatedFileAndFolderPaths.Add(itemVirtualPath); - } + this.newlyCreatedFileAndFolderPaths.Add(itemVirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(oldItemVirtualPath)) { - this.TryRemoveModifiedPath(oldItemVirtualPath, isFolder: itemInfo.IsDirectory); + result = this.TryRemoveModifiedPath(oldItemVirtualPath, isFolder: itemInfo.IsDirectory); } if (itemInfo.IsDirectory) From f7e268a3fedbbdadf0de1f634459794a0d194def Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Thu, 20 Sep 2018 10:46:06 -0400 Subject: [PATCH 09/14] Fix broken tests for cleaning modified paths --- .../EnlistmentPerTestCase/ModifiedPathsTests.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs index 14fe1a5be..49981b86a 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs @@ -34,7 +34,6 @@ public class ModifiedPathsTests : TestsWithEnlistmentPerTestCase $"A {GVFSHelpers.ConvertPathToGitFormat(FileToRename)}", $"A {GVFSHelpers.ConvertPathToGitFormat(RenameFileTarget)}", $"A {FolderToCreate}/", - $"A {FolderToRename}/", $"A {RenameFolderTarget}/", $"A {RenameNewDotGitFileTarget}", $"A {FileToCreateOutsideRepo}", @@ -162,11 +161,12 @@ public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem) [TestCaseSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)] public void ModifiedPathsCorrectAfterHardLinking(FileSystemRunner fileSystem) { - const string ExpectedModifiedFilesContentsAfterHardlinks = -@"A .gitattributes -A LinkToReadme.md -A LinkToFileOutsideSrc.txt -"; + string[] expectedModifiedFilesContentsAfterHardlinks = + { + "A .gitattributes", + "A LinkToReadme.md", + "A LinkToFileOutsideSrc.txt", + }; // Create a link from src\LinkToReadme.md to src\Readme.md string existingFileInRepoPath = this.Enlistment.GetVirtualPathTo("Readme.md"); @@ -198,7 +198,8 @@ A LinkToFileOutsideSrc.txt modifiedPathsDatabase.ShouldBeAFile(fileSystem); using (StreamReader reader = new StreamReader(File.Open(modifiedPathsDatabase, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { - reader.ReadToEnd().ShouldEqual(ExpectedModifiedFilesContentsAfterHardlinks); + reader.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).OrderBy(x => x) + .ShouldMatchInOrder(expectedModifiedFilesContentsAfterHardlinks.OrderBy(x => x)); } } From a0023aa659997d6abc9cb697d7730ba6fa770101 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Fri, 21 Sep 2018 13:42:23 -0600 Subject: [PATCH 10/14] Remove extra return statments and adding to new paths during rename --- GVFS/GVFS.Common/ModifiedPathsDatabase.cs | 4 ---- GVFS/GVFS.Virtualization/FileSystemCallbacks.cs | 1 - 2 files changed, 5 deletions(-) diff --git a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs index 945613115..ffde4f692 100644 --- a/GVFS/GVFS.Common/ModifiedPathsDatabase.cs +++ b/GVFS/GVFS.Common/ModifiedPathsDatabase.cs @@ -84,8 +84,6 @@ public bool TryAdd(string path, bool isFolder, out bool isRetryable) isRetryable = false; return false; } - - return true; } return true; @@ -113,8 +111,6 @@ public bool TryRemove(string path, bool isFolder, out bool isRetryable) isRetryable = false; return false; } - - return true; } return true; diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index b41a2c634..4931aa1b3 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -425,7 +425,6 @@ public void OnFileConvertedToFull(string relativePath) public virtual void OnFileRenamed(string oldRelativePath, string newRelativePath) { - this.newlyCreatedFileAndFolderPaths.Add(newRelativePath); this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFileRenamed(oldRelativePath, newRelativePath)); } From dcf925fe61d618caea768fa7d8f7d1527f54e0eb Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Mon, 24 Sep 2018 15:36:55 -0600 Subject: [PATCH 11/14] Add PreDelete handler in BG operation to handle Mac --- .../MacFileSystemVirtualizer.cs | 2 +- .../WindowsFileSystemVirtualizer.cs | 2 +- .../Background/FileSystemTask.cs | 14 ++++- .../FileSystem/FileSystemVirtualizer.cs | 20 ++++++- .../FileSystemCallbacks.cs | 59 ++++++++++++++++--- 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs b/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs index 3d22f7f34..127173d0e 100644 --- a/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs +++ b/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs @@ -352,7 +352,7 @@ private Result OnPreDelete(string relativePath, bool isDirectory) } else { - this.OnWorkingDirectoryFileOrFolderDeleted(relativePath, isDirectory); + this.OnWorkingDirectoryFileOrFolderDeleteNotification(relativePath, isDirectory, isPreDelete: true); } } catch (Exception e) diff --git a/GVFS/GVFS.Platform.Windows/WindowsFileSystemVirtualizer.cs b/GVFS/GVFS.Platform.Windows/WindowsFileSystemVirtualizer.cs index 42c1f109a..de1963b99 100644 --- a/GVFS/GVFS.Platform.Windows/WindowsFileSystemVirtualizer.cs +++ b/GVFS/GVFS.Platform.Windows/WindowsFileSystemVirtualizer.cs @@ -1296,7 +1296,7 @@ private void NotifyFileHandleClosedFileModifiedOrDeletedHandler( } else { - this.OnWorkingDirectoryFileOrFolderDeleted(virtualPath, isDirectory); + this.OnWorkingDirectoryFileOrFolderDeleteNotification(virtualPath, isDirectory, isPreDelete: false); } } } diff --git a/GVFS/GVFS.Virtualization/Background/FileSystemTask.cs b/GVFS/GVFS.Virtualization/Background/FileSystemTask.cs index b5644bf86..eaab198a4 100644 --- a/GVFS/GVFS.Virtualization/Background/FileSystemTask.cs +++ b/GVFS/GVFS.Virtualization/Background/FileSystemTask.cs @@ -29,7 +29,9 @@ public enum OperationType OnFolderFirstWrite, OnIndexWriteWithoutProjectionChange, OnPlaceholderCreationsBlockedForGit, - OnFileHardLinkCreated + OnFileHardLinkCreated, + OnFilePreDelete, + OnFolderPreDelete, } public OperationType Operation { get; } @@ -57,6 +59,11 @@ public static FileSystemTask OnFileDeleted(string virtualPath) return new FileSystemTask(OperationType.OnFileDeleted, virtualPath, oldVirtualPath: null); } + public static FileSystemTask OnFilePreDelete(string virtualPath) + { + return new FileSystemTask(OperationType.OnFilePreDelete, virtualPath, oldVirtualPath: null); + } + public static FileSystemTask OnFileOverwritten(string virtualPath) { return new FileSystemTask(OperationType.OnFileOverwritten, virtualPath, oldVirtualPath: null); @@ -97,6 +104,11 @@ public static FileSystemTask OnFolderDeleted(string virtualPath) return new FileSystemTask(OperationType.OnFolderDeleted, virtualPath, oldVirtualPath: null); } + public static FileSystemTask OnFolderPreDelete(string virtualPath) + { + return new FileSystemTask(OperationType.OnFolderPreDelete, virtualPath, oldVirtualPath: null); + } + public static FileSystemTask OnIndexWriteWithoutProjectionChange() { return new FileSystemTask(OperationType.OnIndexWriteWithoutProjectionChange, virtualPath: null, oldVirtualPath: null); diff --git a/GVFS/GVFS.Virtualization/FileSystem/FileSystemVirtualizer.cs b/GVFS/GVFS.Virtualization/FileSystem/FileSystemVirtualizer.cs index c77e94669..70cac75d1 100644 --- a/GVFS/GVFS.Virtualization/FileSystem/FileSystemVirtualizer.cs +++ b/GVFS/GVFS.Virtualization/FileSystem/FileSystemVirtualizer.cs @@ -185,7 +185,7 @@ protected void OnDotGitFileOrFolderDeleted(string relativePath) } } - protected void OnWorkingDirectoryFileOrFolderDeleted(string relativePath, bool isDirectory) + protected void OnWorkingDirectoryFileOrFolderDeleteNotification(string relativePath, bool isDirectory, bool isPreDelete) { if (isDirectory) { @@ -193,12 +193,26 @@ protected void OnWorkingDirectoryFileOrFolderDeleted(string relativePath, bool i GitCommandLineParser gitCommand = new GitCommandLineParser(this.Context.Repository.GVFSLock.GetLockedGitCommand()); if (!gitCommand.IsValidGitCommand) { - this.FileSystemCallbacks.OnFolderDeleted(relativePath); + if (isPreDelete) + { + this.FileSystemCallbacks.OnFolderPreDelete(relativePath); + } + else + { + this.FileSystemCallbacks.OnFolderDeleted(relativePath); + } } } else { - this.FileSystemCallbacks.OnFileDeleted(relativePath); + if (isPreDelete) + { + this.FileSystemCallbacks.OnFilePreDelete(relativePath); + } + else + { + this.FileSystemCallbacks.OnFileDeleted(relativePath); + } } this.FileSystemCallbacks.InvalidateGitStatusCache(); diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index 4931aa1b3..44a0aaf62 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -438,6 +438,11 @@ public void OnFileDeleted(string relativePath) this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFileDeleted(relativePath)); } + public void OnFilePreDelete(string relativePath) + { + this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFilePreDelete(relativePath)); + } + public void OnFolderCreated(string relativePath) { this.newlyCreatedFileAndFolderPaths.Add(relativePath); @@ -454,6 +459,11 @@ public void OnFolderDeleted(string relativePath) this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFolderDeleted(relativePath)); } + public void OnFolderPreDelete(string relativePath) + { + this.backgroundFileSystemTaskRunner.Enqueue(FileSystemTask.OnFolderPreDelete(relativePath)); + } + public void OnPlaceholderFileCreated(string relativePath, string sha, string triggeringProcessImageFileName) { this.GitIndexProjection.OnPlaceholderFileCreated(relativePath, sha); @@ -650,6 +660,27 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate break; + case FileSystemTask.OperationType.OnFilePreDelete: + metadata.Add("virtualPath", gitUpdate.VirtualPath); + if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) + { + string fullPathToFolder = Path.Combine(this.context.Enlistment.WorkingDirectoryRoot, gitUpdate.VirtualPath); + if (!this.context.FileSystem.FileExists(fullPathToFolder)) + { + result = this.TryRemoveModifiedPath(gitUpdate.VirtualPath, isFolder: false); + } + else + { + result = FileSystemTaskResult.Success; + } + } + else + { + result = this.AddModifiedPathAndRemoveFromPlaceholderList(gitUpdate.VirtualPath); + } + + break; + case FileSystemTask.OperationType.OnFileDeleted: metadata.Add("virtualPath", gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) @@ -764,6 +795,27 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate break; + case FileSystemTask.OperationType.OnFolderPreDelete: + metadata.Add("virtualPath", gitUpdate.VirtualPath); + if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) + { + string fullPathToFolder = Path.Combine(this.context.Enlistment.WorkingDirectoryRoot, gitUpdate.VirtualPath); + if (!this.context.FileSystem.DirectoryExists(fullPathToFolder)) + { + result = this.TryRemoveModifiedPath(gitUpdate.VirtualPath, isFolder: true); + } + else + { + result = FileSystemTaskResult.Success; + } + } + else + { + result = this.TryAddModifiedPath(gitUpdate.VirtualPath, isFolder: true); + } + + break; + case FileSystemTask.OperationType.OnFolderDeleted: metadata.Add("virtualPath", gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) @@ -808,13 +860,6 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate private FileSystemTaskResult TryRemoveModifiedPath(string virtualPath, bool isFolder) { - string fullPathToItem = Path.Combine(this.context.Enlistment.WorkingDirectoryRoot, virtualPath); - if ((isFolder && this.context.FileSystem.DirectoryExists(fullPathToItem)) || - (!isFolder && this.context.FileSystem.FileExists(fullPathToItem))) - { - return FileSystemTaskResult.Success; - } - if (!this.modifiedPaths.TryRemove(virtualPath, isFolder, out bool isRetryable)) { return isRetryable ? FileSystemTaskResult.RetryableError : FileSystemTaskResult.FatalError; From 45159dbf5e41c2352e660605ea62ee03b333a2b2 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Tue, 25 Sep 2018 09:14:37 -0600 Subject: [PATCH 12/14] Add comment explaining only one of predelete or delete should be queued --- GVFS/GVFS.Virtualization/FileSystemCallbacks.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index 44a0aaf62..95618e10e 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -661,6 +661,9 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate break; case FileSystemTask.OperationType.OnFilePreDelete: + // This code assumes that the current implementations of FileSystemVirtualizer will call either + // the PreDelete or the Delete not both so if a new implementation starts calling both + // this will need to be cleaned up to not duplicate the work that is being done. metadata.Add("virtualPath", gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) { @@ -682,6 +685,9 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate break; case FileSystemTask.OperationType.OnFileDeleted: + // This code assumes that the current implementations of FileSystemVirtualizer will call either + // the PreDelete or the Delete not both so if a new implementation starts calling both + // this will need to be cleaned up to not duplicate the work that is being done. metadata.Add("virtualPath", gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) { @@ -796,6 +802,9 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate break; case FileSystemTask.OperationType.OnFolderPreDelete: + // This code assumes that the current implementations of FileSystemVirtualizer will call either + // the PreDelete or the Delete not both so if a new implementation starts calling both + // this will need to be cleaned up to not duplicate the work that is being done. metadata.Add("virtualPath", gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) { @@ -817,6 +826,9 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate break; case FileSystemTask.OperationType.OnFolderDeleted: + // This code assumes that the current implementations of FileSystemVirtualizer will call either + // the PreDelete or the Delete not both so if a new implementation starts calling both + // this will need to be cleaned up to not duplicate the work that is being done. metadata.Add("virtualPath", gitUpdate.VirtualPath); if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.VirtualPath)) { From 472904c45ef03f8757bdf151924652802e73d7f0 Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Tue, 25 Sep 2018 12:21:17 -0600 Subject: [PATCH 13/14] Fix case only folder rename test --- .../EnlistmentPerFixture/GitFilesTests.cs | 20 ++++++++++++++----- .../FileSystemCallbacks.cs | 12 ++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs index 88dbf1709..bfbdcc457 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs @@ -132,22 +132,32 @@ public void RenameFolderTest() [Category(Categories.MacTODO.M2)] public void CaseOnlyRenameOfNewFolderKeepsModifiedPathsEntries() { - string[] expectedModifiedPathsEntries = + if (this.fileSystem is PowerShellRunner) { - "Folder/", - "Folder/testfile", + Assert.Ignore("Powershell does not support case only renames."); + } + + string[] expectedModifiedPathsEntriesAfterCreate = + { + "A Folder/", + "A Folder/testfile", + }; + + string[] expectedModifiedPathsEntriesAfterRename = + { + "A folder/", }; this.fileSystem.CreateDirectory(Path.Combine(this.Enlistment.RepoRoot, "Folder")); this.fileSystem.CreateEmptyFile(Path.Combine(this.Enlistment.RepoRoot, "Folder", "testfile")); this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete."); - GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedPathsEntries); + GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedPathsEntriesAfterCreate); this.fileSystem.RenameDirectory(this.Enlistment.RepoRoot, "Folder", "folder"); this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete."); - GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedPathsEntries); + GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedPathsEntriesAfterRename); } [TestCase, Order(7)] diff --git a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs index 95618e10e..d7c0d57b4 100644 --- a/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs +++ b/GVFS/GVFS.Virtualization/FileSystemCallbacks.cs @@ -718,20 +718,22 @@ private FileSystemTaskResult ExecuteBackgroundOperation(FileSystemTask gitUpdate metadata.Add("oldVirtualPath", gitUpdate.OldVirtualPath); metadata.Add("virtualPath", gitUpdate.VirtualPath); + if (!string.IsNullOrEmpty(gitUpdate.OldVirtualPath) && + this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.OldVirtualPath)) + { + result = this.TryRemoveModifiedPath(gitUpdate.OldVirtualPath, isFolder: true); + } + // An empty destination path means the folder was renamed to somewhere outside of the repo // Note that only full folders can be moved\renamed, and so there will already be a recursive // sparse-checkout entry for the virtualPath of the folder being moved (meaning that no // additional work is needed for any files\folders inside the folder being moved) - if (!string.IsNullOrEmpty(gitUpdate.VirtualPath)) + if (result == FileSystemTaskResult.Success && !string.IsNullOrEmpty(gitUpdate.VirtualPath)) { result = this.TryAddModifiedPath(gitUpdate.VirtualPath, isFolder: true); if (result == FileSystemTaskResult.Success) { this.newlyCreatedFileAndFolderPaths.Add(gitUpdate.VirtualPath); - if (this.newlyCreatedFileAndFolderPaths.Contains(gitUpdate.OldVirtualPath)) - { - result = this.TryRemoveModifiedPath(gitUpdate.OldVirtualPath, isFolder: true); - } Queue relativeFolderPaths = new Queue(); relativeFolderPaths.Enqueue(gitUpdate.VirtualPath); From 243f6214d7334a69df31fee1ffa3563e5e45f3af Mon Sep 17 00:00:00 2001 From: Kevin Willford Date: Tue, 25 Sep 2018 16:03:35 -0600 Subject: [PATCH 14/14] Fix test that moved new folder outside the repo --- .../Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs index 49981b86a..e3e9de6a5 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/ModifiedPathsTests.cs @@ -34,7 +34,6 @@ public class ModifiedPathsTests : TestsWithEnlistmentPerTestCase $"A {GVFSHelpers.ConvertPathToGitFormat(FileToRename)}", $"A {GVFSHelpers.ConvertPathToGitFormat(RenameFileTarget)}", $"A {FolderToCreate}/", - $"A {RenameFolderTarget}/", $"A {RenameNewDotGitFileTarget}", $"A {FileToCreateOutsideRepo}", $"A {FolderToCreateOutsideRepo}/", @@ -108,7 +107,7 @@ public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem) string folderToRenameTarget = this.Enlistment.GetVirtualPathTo(RenameFolderTarget); fileSystem.MoveDirectory(folderToRename, folderToRenameTarget); - // Moving the new folder out of the repo should not change the modified paths file + // Moving the new folder out of the repo will remove it from the modified paths file string folderTargetOutsideSrc = Path.Combine(this.Enlistment.EnlistmentRoot, RenameFolderTarget); folderTargetOutsideSrc.ShouldNotExistOnDisk(fileSystem); fileSystem.MoveDirectory(folderToRenameTarget, folderTargetOutsideSrc);