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;
        }
Example #2
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);
        }
Example #3
0
 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);
		}