Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.Perforce.Managed/Transactions/RemoveTransaction.cs
2025-05-18 13:04:45 +08:00

100 lines
4.0 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using EpicGames.Core;
namespace EpicGames.Perforce.Managed
{
class RemoveTransaction
{
readonly Dictionary<FileContentId, CachedFileInfo> _contentIdToTrackedFile;
public WorkspaceDirectoryInfo _newWorkspaceRootDir;
public StreamSnapshot _streamSnapshot;
public ConcurrentDictionary<FileContentId, WorkspaceFileInfo> _filesToMove = new ConcurrentDictionary<FileContentId, WorkspaceFileInfo>();
public ConcurrentQueue<WorkspaceFileInfo> _filesToDelete = new ConcurrentQueue<WorkspaceFileInfo>();
public ConcurrentQueue<WorkspaceDirectoryInfo> _directoriesToDelete = new ConcurrentQueue<WorkspaceDirectoryInfo>();
public RemoveTransaction(WorkspaceDirectoryInfo workspaceRootDir, StreamSnapshot streamSnapshot, Dictionary<FileContentId, CachedFileInfo> contentIdToTrackedFile)
{
_newWorkspaceRootDir = new WorkspaceDirectoryInfo(workspaceRootDir.GetLocation());
_streamSnapshot = streamSnapshot;
_contentIdToTrackedFile = contentIdToTrackedFile;
}
public static async Task<RemoveTransaction> CreateAsync(WorkspaceDirectoryInfo workspaceRootDir, StreamSnapshot streamSnapshot, Dictionary<FileContentId, CachedFileInfo> contentIdToTrackedFile, int numWorkers)
{
RemoveTransaction transaction = new(workspaceRootDir, streamSnapshot, contentIdToTrackedFile);
using AsyncThreadPoolWorkQueue queue = new(numWorkers);
await queue.EnqueueAsync(_ => transaction.MergeAsync(workspaceRootDir, transaction._newWorkspaceRootDir, streamSnapshot.Root, queue));
await queue.ExecuteAsync();
return transaction;
}
async Task MergeAsync(WorkspaceDirectoryInfo workspaceDir, WorkspaceDirectoryInfo newWorkspaceDir, StreamTreeRef streamTreeRef, AsyncThreadPoolWorkQueue queue)
{
StreamTree streamDir = _streamSnapshot.Lookup(streamTreeRef);
// Update all the subdirectories
foreach (WorkspaceDirectoryInfo workspaceSubDir in workspaceDir.NameToSubDirectory.Values)
{
StreamTreeRef? streamSubTreeRef;
if (streamDir.NameToTree.TryGetValue(workspaceSubDir.Name, out streamSubTreeRef))
{
await MergeSubDirectoryAsync(workspaceSubDir.Name, newWorkspaceDir, workspaceSubDir, streamSubTreeRef, queue);
}
else
{
await RemoveDirectoryAsync(workspaceSubDir, queue);
}
}
// Update the staged files
foreach (WorkspaceFileInfo workspaceFile in workspaceDir.NameToFile.Values)
{
StreamFile? streamFile;
if (streamDir != null && streamDir.NameToFile.TryGetValue(workspaceFile.Name, out streamFile) && streamFile.ContentId.Equals(workspaceFile.ContentId))
{
newWorkspaceDir.NameToFile.Add(workspaceFile.Name, workspaceFile);
}
else
{
RemoveFile(workspaceFile);
}
}
}
async Task MergeSubDirectoryAsync(Utf8String name, WorkspaceDirectoryInfo newWorkspaceDir, WorkspaceDirectoryInfo workspaceSubDir, StreamTreeRef streamSubTreeRef, AsyncThreadPoolWorkQueue queue)
{
WorkspaceDirectoryInfo newWorkspaceSubDir = new WorkspaceDirectoryInfo(newWorkspaceDir, name, streamSubTreeRef);
newWorkspaceDir.NameToSubDirectory.Add(newWorkspaceSubDir.Name, newWorkspaceSubDir);
await queue.EnqueueAsync(_ => MergeAsync(workspaceSubDir, newWorkspaceSubDir, streamSubTreeRef, queue));
}
async Task RemoveDirectoryAsync(WorkspaceDirectoryInfo workspaceDir, AsyncThreadPoolWorkQueue queue)
{
_directoriesToDelete.Enqueue(workspaceDir);
foreach (WorkspaceDirectoryInfo workspaceSubDir in workspaceDir.NameToSubDirectory.Values)
{
await queue.EnqueueAsync(_ => RemoveDirectoryAsync(workspaceSubDir, queue));
}
foreach (WorkspaceFileInfo workspaceFile in workspaceDir.NameToFile.Values)
{
RemoveFile(workspaceFile);
}
}
void RemoveFile(WorkspaceFileInfo workspaceFile)
{
if (_contentIdToTrackedFile.ContainsKey(workspaceFile.ContentId) || !_filesToMove.TryAdd(workspaceFile.ContentId, workspaceFile))
{
_filesToDelete.Enqueue(workspaceFile);
}
}
}
}