void MergeTrees(FolderToClean LocalFolder, PerforceHaveFolder PerforceFolder, HashSet <string> OpenClientPaths) { if (PerforceFolder == null) { // Loop through all the local sub-folders foreach (FolderToClean LocalSubFolder in LocalFolder.NameToSubFolder.Values) { MergeTrees(LocalSubFolder, null, OpenClientPaths); } // Delete everything LocalFolder.FilesToDelete.AddRange(LocalFolder.NameToFile.Values); } else { // Loop through all the local sub-folders foreach (FolderToClean LocalSubFolder in LocalFolder.NameToSubFolder.Values) { PerforceHaveFolder PerforceSubFolder; PerforceFolder.NameToSubFolder.TryGetValue(LocalSubFolder.Name, out PerforceSubFolder); MergeTrees(LocalSubFolder, PerforceSubFolder, OpenClientPaths); } // Also merge all the Perforce folders that no longer exist foreach (KeyValuePair <string, PerforceHaveFolder> PerforceSubFolderPair in PerforceFolder.NameToSubFolder) { FolderToClean LocalSubFolder; if (!LocalFolder.NameToSubFolder.TryGetValue(PerforceSubFolderPair.Key, out LocalSubFolder)) { LocalSubFolder = new FolderToClean(new DirectoryInfo(Path.Combine(LocalFolder.Directory.FullName, PerforceSubFolderPair.Key))); MergeTrees(LocalSubFolder, PerforceSubFolderPair.Value, OpenClientPaths); LocalFolder.NameToSubFolder.Add(LocalSubFolder.Name, LocalSubFolder); } } // Find all the files that need to be re-synced foreach (KeyValuePair <string, PerforceFileRecord> FilePair in PerforceFolder.NameToFile) { FileInfo LocalFile; if (!LocalFolder.NameToFile.TryGetValue(FilePair.Key, out LocalFile)) { LocalFolder.FilesToSync.Add(new FileInfo(Path.Combine(LocalFolder.Directory.FullName, FilePair.Key))); } else if ((FilePair.Value.Flags & PerforceFileFlags.AlwaysWritable) == 0 && (LocalFile.Attributes & FileAttributes.ReadOnly) == 0 && !OpenClientPaths.Contains(FilePair.Value.ClientPath)) { LocalFolder.FilesToSync.Add(LocalFile); } } // Find all the files that should be deleted LocalFolder.FilesToDelete.AddRange(LocalFolder.NameToFile.Values.Where(x => !PerforceFolder.NameToFile.ContainsKey(x.Name))); } // Figure out if this folder is just an empty directory that needs to be removed LocalFolder.bEmptyLeaf = LocalFolder.NameToFile.Count == 0 && LocalFolder.NameToSubFolder.Count == 0 && LocalFolder.FilesToSync.Count == 0; // Figure out if it the folder will be empty after the clean operation LocalFolder.bEmptyAfterClean = LocalFolder.NameToSubFolder.Values.All(x => x.bEmptyAfterClean) && LocalFolder.FilesToDelete.Count == LocalFolder.NameToFile.Count && LocalFolder.FilesToSync.Count == 0; }
public bool Run(out string ErrorMessage) { Log.WriteLine("Finding files in workspace..."); Log.WriteLine(); // Query the have table PerforceHaveFolder HaveFolder = new PerforceHaveFolder(); foreach (string SyncPath in SyncPaths) { List <PerforceFileRecord> FileRecords; if (!PerforceClient.Have(SyncPath, out FileRecords, Log)) { ErrorMessage = "Couldn't query have table from Perforce."; return(false); } foreach (PerforceFileRecord FileRecord in FileRecords) { if (!FileRecord.ClientPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Failed to get have table; file '{0}' doesn't start with client root path ('{1}')", FileRecord.ClientPath, ClientRootPath); return(false); } string[] Tokens = FileRecord.ClientPath.Substring(ClientRootPath.Length).Split('/', '\\'); PerforceHaveFolder FileFolder = HaveFolder; for (int Idx = 0; Idx < Tokens.Length - 1; Idx++) { PerforceHaveFolder NextFileFolder; if (!FileFolder.SubFolders.TryGetValue(Tokens[Idx], out NextFileFolder)) { NextFileFolder = new PerforceHaveFolder(); FileFolder.SubFolders.Add(Tokens[Idx], NextFileFolder); } FileFolder = NextFileFolder; } FileFolder.Files.Add(Tokens[Tokens.Length - 1]); } } // Wait to finish scanning the directory FinishedScan.WaitOne(); // Remove all the files in the have table from the list of files to delete RemoveFilesToKeep(RootFolderToClean, HaveFolder); // Remove all the empty folders RemoveEmptyFolders(RootFolderToClean); ErrorMessage = null; return(true); }
void RemoveFilesToKeep(FolderToClean Folder, PerforceHaveFolder KeepFolder) { foreach (FolderToClean SubFolder in Folder.SubFolders) { PerforceHaveFolder KeepSubFolder; if (KeepFolder.SubFolders.TryGetValue(SubFolder.Directory.Name, out KeepSubFolder)) { RemoveFilesToKeep(SubFolder, KeepSubFolder); Folder.bEmptyAfterDelete &= SubFolder.bEmptyAfterDelete; } } Folder.bEmptyAfterDelete &= (Folder.FilesToClean.RemoveAll(x => KeepFolder.Files.Contains(x.Name)) == 0); }
public bool Run(out string ErrorMessage) { Log.WriteLine("Finding files in workspace..."); Log.WriteLine(); // Start enumerating all the files that exist locally foreach (string SyncPath in SyncPaths) { Debug.Assert(SyncPath.StartsWith(ClientRootPath)); if (SyncPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { string[] Fragments = SyncPath.Substring(ClientRootPath.Length).Split('/'); FolderToClean SyncFolder = RootFolderToClean; for (int Idx = 0; Idx < Fragments.Length - 1; Idx++) { FolderToClean NextSyncFolder; if (!SyncFolder.NameToSubFolder.TryGetValue(Fragments[Idx], out NextSyncFolder)) { NextSyncFolder = new FolderToClean(new DirectoryInfo(Path.Combine(SyncFolder.Directory.FullName, Fragments[Idx]))); SyncFolder.NameToSubFolder[NextSyncFolder.Name] = NextSyncFolder; } SyncFolder = NextSyncFolder; } string Wildcard = Fragments[Fragments.Length - 1]; if (Wildcard == "...") { QueueFolderToPopulate(SyncFolder); } else { if (SyncFolder.Directory.Exists) { foreach (FileInfo File in SyncFolder.Directory.EnumerateFiles(Wildcard)) { SyncFolder.NameToFile[File.Name] = File; } } } } } // Get the prefix for any local file string LocalRootPrefix = RootFolderToClean.Directory.FullName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; // Query the have table and build a separate tree from it PerforceHaveFolder RootHaveFolder = new PerforceHaveFolder(); foreach (string SyncPath in SyncPaths) { List <PerforceFileRecord> FileRecords; if (!PerforceClient.Stat(String.Format("{0}#have", SyncPath), out FileRecords, Log)) { ErrorMessage = "Couldn't query have table from Perforce."; return(false); } foreach (PerforceFileRecord FileRecord in FileRecords) { if (!FileRecord.ClientPath.StartsWith(LocalRootPrefix, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Failed to get have table; file '{0}' doesn't start with root path ('{1}')", FileRecord.ClientPath, RootFolderToClean.Directory.FullName); return(false); } string[] Tokens = FileRecord.ClientPath.Substring(LocalRootPrefix.Length).Split('/', '\\'); PerforceHaveFolder FileFolder = RootHaveFolder; for (int Idx = 0; Idx < Tokens.Length - 1; Idx++) { PerforceHaveFolder NextFileFolder; if (!FileFolder.NameToSubFolder.TryGetValue(Tokens[Idx], out NextFileFolder)) { NextFileFolder = new PerforceHaveFolder(); FileFolder.NameToSubFolder.Add(Tokens[Idx], NextFileFolder); } FileFolder = NextFileFolder; } FileFolder.NameToFile[Tokens[Tokens.Length - 1]] = FileRecord; } } // Find all the files which are currently open for edit. We don't want to force sync these. List <PerforceFileRecord> OpenFileRecords; if (!PerforceClient.GetOpenFiles("//...", out OpenFileRecords, Log)) { ErrorMessage = "Couldn't query open files from Perforce."; return(false); } // Build a set of all the open local files HashSet <string> OpenLocalFiles = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); foreach (PerforceFileRecord OpenFileRecord in OpenFileRecords) { if (!OpenFileRecord.ClientPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Failed to get open file list; file '{0}' doesn't start with client root path ('{1}')", OpenFileRecord.ClientPath, ClientRootPath); return(false); } OpenLocalFiles.Add(LocalRootPrefix + PerforceUtils.UnescapePath(OpenFileRecord.ClientPath).Substring(ClientRootPath.Length).Replace('/', Path.DirectorySeparatorChar)); } // Wait to finish scanning the directory FinishedScan.WaitOne(); // Find the value of the P4CONFIG variable string PerforceConfigFile; PerforceClient.GetSetting("P4CONFIG", out PerforceConfigFile, Log); // Merge the trees MergeTrees(RootFolderToClean, RootHaveFolder, OpenLocalFiles, PerforceConfigFile); // Remove all the empty folders RemoveEmptyFolders(RootFolderToClean); ErrorMessage = null; return(true); }
public bool Run(out string ErrorMessage) { Log.WriteLine("Finding files in workspace..."); Log.WriteLine(); // Query the have table PerforceHaveFolder HaveFolder = new PerforceHaveFolder(); foreach(string SyncPath in SyncPaths) { List<PerforceFileRecord> FileRecords; if(!PerforceClient.Have(SyncPath, out FileRecords, Log)) { ErrorMessage = "Couldn't query have table from Perforce."; return false; } foreach(PerforceFileRecord FileRecord in FileRecords) { if(!FileRecord.ClientPath.StartsWith(ClientRootPath, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Failed to get have table; file '{0}' doesn't start with client root path ('{1}')", FileRecord.ClientPath, ClientRootPath); return false; } string[] Tokens = FileRecord.ClientPath.Substring(ClientRootPath.Length).Split('/', '\\'); PerforceHaveFolder FileFolder = HaveFolder; for(int Idx = 0; Idx < Tokens.Length - 1; Idx++) { PerforceHaveFolder NextFileFolder; if(!FileFolder.SubFolders.TryGetValue(Tokens[Idx], out NextFileFolder)) { NextFileFolder = new PerforceHaveFolder(); FileFolder.SubFolders.Add(Tokens[Idx], NextFileFolder); } FileFolder = NextFileFolder; } FileFolder.Files.Add(Tokens[Tokens.Length - 1]); } } // Wait to finish scanning the directory FinishedScan.WaitOne(); // Remove all the files in the have table from the list of files to delete RemoveFilesToKeep(RootFolderToClean, HaveFolder); // Remove all the empty folders RemoveEmptyFolders(RootFolderToClean); ErrorMessage = null; return true; }
void RemoveFilesToKeep(FolderToClean Folder, PerforceHaveFolder KeepFolder) { foreach(FolderToClean SubFolder in Folder.SubFolders) { PerforceHaveFolder KeepSubFolder; if(KeepFolder.SubFolders.TryGetValue(SubFolder.Directory.Name, out KeepSubFolder)) { RemoveFilesToKeep(SubFolder, KeepSubFolder); Folder.bEmptyAfterDelete &= SubFolder.bEmptyAfterDelete; } } Folder.bEmptyAfterDelete &= (Folder.FilesToClean.RemoveAll(x => KeepFolder.Files.Contains(x.Name)) == 0); }