// Copyright Epic Games, Inc. All Rights Reserved. #include "ChangelistService.h" #include "Configuration/Configuration.h" #include "Logging/SubmitToolLog.h" #include "Logic/Services/Interfaces/ISTSourceControlService.h" #include "Models/ModelInterface.h" #include "Telemetry/TelemetryService.h" #include "CommandLine/CmdLineParameters.h" #include "ISourceControlProvider.h" #include "ISourceControlOperation.h" #include "SourceControlOperations.h" #include "SourceControlResultInfo.h" THIRD_PARTY_INCLUDES_START // used to retrieve the depot paths, UE hides all this p4 specific information under PerforceSourceControl private definitions #include THIRD_PARTY_INCLUDES_END namespace ChangelistServiceConstants { constexpr float TickDelay = 5.f; } FChangelistService::FChangelistService( const FGeneralParameters& InParameters, const TSharedPtr InPerforceService, const FOnChangeListReadyDelegate& InCLReadyCallback, const FOnChangelistRefreshDelegate& InCLRefreshCallback) : Parameters(InParameters), CLReadyCallback(InCLReadyCallback), CLRefreshCallback(InCLRefreshCallback), TickHandle(FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FChangelistService::P4Tick), ChangelistServiceConstants::TickDelay)), SourceControlService(InPerforceService) { FCmdLineParameters::Get().GetValue(FSubmitToolCmdLine::P4ChangeList, CLID); Init(); } FChangelistService::~FChangelistService() { OnCLDescriptionUpdated.Clear(); } void FChangelistService::Init() { if(SourceControlService->GetProvider().IsValid()) { UE::Tasks::Launch(UE_SOURCE_LOCATION, [this] { GetFilesDepotPaths(true); }); UE::Tasks::Launch(UE_SOURCE_LOCATION, [this] { GetShelvedFilesDepotPaths(true); }); CachedSCCProvider = SourceControlService->GetProvider().Get(); if(!CLID.IsNumeric() && CLID.Equals(TEXT("default"), ESearchCase::IgnoreCase)) { CreateCLFromDefaultCL(); } else { FindInitialChangelistsAsync(); } } else { UE_LOG(LogSubmitToolP4, Error, TEXT("Perforce Connection was invalid")); } } void FChangelistService::FindInitialChangelistsAsync() { TSharedRef UpdatePendingChangelistsOperation = ISourceControlOperation::Create(); UpdatePendingChangelistsOperation->SetUpdateAllChangelists(true); UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Finding available changelists")); ActiveP4Operations.Add(UpdatePendingChangelistsOperation); CachedSCCProvider->Execute(UpdatePendingChangelistsOperation, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda([this](const FSourceControlOperationRef& UpdateOperation, ECommandResult::Type Result) { if(Result == ECommandResult::Succeeded) { TArray Changelists = CachedSCCProvider->GetChangelists(EStateCacheUsage::Use); for(const FSourceControlChangelistRef& CL : Changelists) { if(CL->GetIdentifier() == CLID) { ChangelistPtr = CL; } } } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Cancelled finding available changelists")); } else if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("Failed to find available changelists.")); PrintErrorMessages(UpdateOperation->GetResultInfo()); } ActiveP4Operations.Remove(UpdateOperation); if(ChangelistPtr) { // Get state for our CL FetchChangelistDataAsync(); // Fire & Forget updating all CLs TSharedRef UpdatePendingChangelistsOperation = ISourceControlOperation::Create(); ActiveP4Operations.Add(UpdatePendingChangelistsOperation); UpdatePendingChangelistsOperation->SetUpdateFilesStates(true); UpdatePendingChangelistsOperation->SetUpdateShelvedFilesStates(true); UpdatePendingChangelistsOperation->SetUpdateAllChangelists(true); CachedSCCProvider->Execute(UpdatePendingChangelistsOperation, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, UpdatePendingChangelistsOperation](const FSourceControlOperationRef& Operation, ECommandResult::Type Result) { ActiveP4Operations.Remove(UpdatePendingChangelistsOperation); })); } else { UE_LOG(LogSubmitToolP4, Error, TEXT("Couldn't retrieve information from CL %s"), *CLID); FModelInterface::SetErrorState(); } })); } void FChangelistService::FetchChangelistDataAsync() { TSharedRef UpdatePendingChangelistsOperation = ISourceControlOperation::Create(); UpdatePendingChangelistsOperation->SetUpdateFilesStates(true); UpdatePendingChangelistsOperation->SetUpdateShelvedFilesStates(true); if(ChangelistPtr.IsValid()) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Updating CL %s changes from P4"), *GetCLID()); UpdatePendingChangelistsOperation.Get().SetChangelistsToUpdate(TArray{ ChangelistPtr.ToSharedRef() }); } else { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Retrieving CL %s information from P4"), *CLID); UpdatePendingChangelistsOperation->SetUpdateAllChangelists(true); } ActiveP4Operations.Add(UpdatePendingChangelistsOperation); CachedSCCProvider->Execute(UpdatePendingChangelistsOperation, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateRaw(this, &FChangelistService::FetchChangelistCallback)); } void FChangelistService::FetchChangelistCallback(const FSourceControlOperationRef& UpdateOperation, ECommandResult::Type Result) { if(Result == ECommandResult::Succeeded) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Update operation succeeded.")); PrintMessages(UpdateOperation->GetResultInfo()); // If this is the first time we fetch results from perforce, we cache our CL State object and let the app know if(!ChangelistState.IsValid()) { TArray Changelists = CachedSCCProvider->GetChangelists(EStateCacheUsage::Use); for(const FSourceControlChangelistRef& CL : Changelists) { if(CL->GetIdentifier() == CLID) { ChangelistState = CachedSCCProvider->GetState(ChangelistPtr.ToSharedRef(), EStateCacheUsage::Use); } } if(!ChangelistPtr.IsValid()) { UE_LOG(LogSubmitToolP4, Error, TEXT("Failed to fetch CL %s from P4"), *CLID); } else { OriginalDescription = ChangelistState->GetDescriptionText(); CLDescription = ChangelistState->GetDescriptionText().ToString(); FilesInCL = ChangelistState->GetFilesStates(); ShelvedFilesInCL = ChangelistState->GetShelvedFilesStates(); PrintFilesAndShelvedFiles(); } CLReadyCallback.ExecuteIfBound(ChangelistState.IsValid()); } else { ChangelistState = CachedSCCProvider->GetState(ChangelistPtr.ToSharedRef(), EStateCacheUsage::Use); RehydrateDataFromP4State(); } } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Update operation cancelled.")); } else if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Update operation failed.")); PrintErrorMessages(UpdateOperation->GetResultInfo()); } ActiveP4Operations.Remove(UpdateOperation); } void FChangelistService::RevertUnchangedFilesAsync(const FSourceControlOperationComplete& OnRevertComplete) { UE_LOG(LogSubmitToolP4, Log, TEXT("Reverting unchanged files from CL %s..."), *GetCLID()); TSharedRef RevertUnchangedOperation = ISourceControlOperation::Create(); ActiveP4Operations.Add(RevertUnchangedOperation); CachedSCCProvider->Execute(RevertUnchangedOperation, ChangelistPtr, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, OnRevertComplete](const FSourceControlOperationRef& Operation, ECommandResult::Type Result) { // Revert Unchanged returns as failed if there were no files to revert, check ErrorMessages to see actual failures if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Revert unchanged operation cancelled.")); } else if(Result == ECommandResult::Succeeded) { UE_LOG(LogSubmitToolP4, Log, TEXT("Revert unchanged operation succeeded.")); FilesInCL = ChangelistState->GetFilesStates(); FilesDepotPaths.Reset(FilesInCL.Num()); PrintFilesAndShelvedFiles(); } else if(Operation->GetResultInfo().ErrorMessages.Num() == 0) { UE_LOG(LogSubmitToolP4, Log, TEXT("There were no unchanged files to revert.")); } else if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("CL %s revert unchanged operation failed:"), *GetCLID()); PrintErrorMessages(Operation->GetResultInfo()); } ActiveP4Operations.Remove(Operation); OnRevertComplete.ExecuteIfBound(Operation, Result); } )); } void FChangelistService::RehydrateDataFromP4State() { ETaskArea ChangeType = ETaskArea::None; if(!AreCLDescriptionsIdentical()) { ChangeType |= ETaskArea::Changelist; UE_LOG(LogSubmitToolP4, Log, TEXT("CL %s Description was updated outside of Submit Tool while it was still open, Description has been updated to match P4V."), *GetCLID()); UE_LOG(LogSubmitToolP4Debug, Log, TEXT("\n - Original Description '%s'\n - Submit Tool Description '%s'\n - New Description '%s'"), *OriginalDescription.ToString(), *CLDescription, *ChangelistState->GetDescriptionText().ToString()); OriginalDescription = ChangelistState->GetDescriptionText(); CLDescription = ChangelistState->GetDescriptionText().ToString(); } if(ChangelistState->GetFilesStates() != FilesInCL) { ChangeType |= ETaskArea::LocalFiles; FilesInCL = ChangelistState->GetFilesStates(); FilesDepotPaths.Reset(FilesInCL.Num()); UE_LOG(LogSubmitToolP4, Log, TEXT("CL %s files were updated outside of Submit Tool while it was open, Validation state has been reset"), *GetCLID()); PrintFilesAndShelvedFiles(); } const TArray P4Shelf = ChangelistState->GetShelvedFilesStates(); bool bNewShelf = false; if (P4Shelf.Num() == ShelvedFilesInCL.Num()) { for (size_t i = 0; i < P4Shelf.Num(); ++i) { if (!P4Shelf[i]->GetFilename().Equals(ShelvedFilesInCL[i]->GetFilename(), ESearchCase::IgnoreCase)) { bNewShelf = true; break; } } } else { bNewShelf = true; } if(bNewShelf) { ChangeType |= ETaskArea::ShelvedFiles; ShelvedFilesInCL = ChangelistState->GetShelvedFilesStates(); ShelvedFilesDepotPaths.Reset(ShelvedFilesInCL.Num()); UE_LOG(LogSubmitToolP4, Log, TEXT("CL %s shelved files were updated outside of Submit Tool while it was open, Validation state has been reset"), *GetCLID()); PrintFilesAndShelvedFiles(); } if(ChangeType != ETaskArea::None) { CLRefreshCallback.ExecuteIfBound(ChangeType); } } bool FChangelistService::AreCLDescriptionsIdentical() { const FString NewDescription = ChangelistState->GetDescriptionText() .ToString() .TrimStartAndEnd() .TrimChar('\n'); const FString CurrentDescription = OriginalDescription .ToString() .TrimStartAndEnd() .TrimChar('\n'); return NewDescription.Equals(CurrentDescription, ESearchCase::IgnoreCase); } void FChangelistService::PrintMessages(const FSourceControlResultInfo& ResultInfo) const { for(const FText& Msg : ResultInfo.InfoMessages) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("%s"), *Msg.ToString()); } } void FChangelistService::PrintErrorMessages(const FSourceControlResultInfo& ResultInfo) const { for(const FText& errorMsg : ResultInfo.ErrorMessages) { UE_LOG(LogSubmitToolP4, Error, TEXT("%s"), *errorMsg.ToString()); } } void FChangelistService::Submit(const FString& InDescriptionAddendum, const FSourceControlOperationComplete& OnSubmitComplete) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Submit in progress for CL: %s. Please wait..."), *GetCLID()); TSharedRef CheckinOp = ISourceControlOperation::Create(); FString FinalDescription = FString::Format(TEXT("{0}{1}"), { CLDescription, InDescriptionAddendum }); CheckinOp->SetDescription(FText::FromString(FinalDescription)); ActiveP4Operations.Add(CheckinOp); CachedSCCProvider->Execute(CheckinOp, ChangelistPtr, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, OnSubmitComplete](const FSourceControlOperationRef& Operation, ECommandResult::Type Result) { if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("CL %s submit operation failed:"), *GetCLID()); PrintErrorMessages(Operation->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { TSharedRef CheckIn = StaticCastSharedRef(Operation); UE_LOG(LogSubmitToolP4, Log, TEXT("Submit operation succeeded: %s"), *CheckIn->GetSuccessMessage().ToString()); FTelemetryService::Get()->SubmitSucceeded(); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Submit operation cancelled.")); } ActiveP4Operations.Remove(Operation); OnSubmitComplete.ExecuteIfBound(Operation, Result); } )); } void FChangelistService::SendCLDescriptionToP4(EConcurrency::Type Concurrency, FOnP4OperationCompleteDelegate InCallback) { if(!CLDescription.Equals(OriginalDescription.ToString())) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Saving CL %s"), *GetCLID()); TSharedRef EditCLOp = ISourceControlOperation::Create(); EditCLOp->SetDescription(FText::FromString(CLDescription)); ActiveP4Operations.Add(EditCLOp); CachedSCCProvider->Execute(EditCLOp, ChangelistPtr, Concurrency, FSourceControlOperationComplete::CreateLambda( [this, InCallback](const FSourceControlOperationRef& UpdateOperation, ECommandResult::Type Result) { if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("CL %s edit changelist operation %s failed:"), *GetCLID(), *UpdateOperation->GetName().ToString()); PrintErrorMessages(UpdateOperation->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { UE_LOG(LogSubmitToolP4, Log, TEXT("CL %s edit changelist operation succeeded."), *GetCLID()); OriginalDescription = FText::FromString(CLDescription); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("CL %s edit changelist operation cancelled."), *GetCLID()); } ActiveP4Operations.Remove(UpdateOperation); InCallback.ExecuteIfBound(); } )); } else { InCallback.ExecuteIfBound(); } } void FChangelistService::DeleteShelvedFiles(const FSourceControlOperationComplete& OnDeleteComplete) { UE_LOG(LogSubmitToolP4, Log, TEXT("Removing shelved files in CL %s..."), *GetCLID()); const TSharedRef DeleteShelvedOp = ISourceControlOperation::Create(); ActiveP4Operations.Add(DeleteShelvedOp); CachedSCCProvider->Execute(DeleteShelvedOp, ChangelistPtr, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, OnDeleteComplete](const FSourceControlOperationRef& DeleteShelvedOp, ECommandResult::Type Result) { if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("CL %s delete shelved operation failed:"), *GetCLID()); PrintErrorMessages(DeleteShelvedOp->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { ShelvedFilesInCL.Reset(); ShelvedFilesDepotPaths.Reset(); UE_LOG(LogSubmitToolP4, Log, TEXT("CL %s delete shelved operation succeeded"), *GetCLID()); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("CL %s delete shelved operation cancelled."), *GetCLID()); } ActiveP4Operations.Remove(DeleteShelvedOp); OnDeleteComplete.ExecuteIfBound(DeleteShelvedOp, Result); } )); } void FChangelistService::CreateShelvedFiles(const FSourceControlOperationComplete& OnCreateComplete) { UE_LOG(LogSubmitToolP4, Log, TEXT("Creating shelved files for CL %s..."), *GetCLID()); const TSharedRef CreateShelvedOp = ISourceControlOperation::Create(); ActiveP4Operations.Add(CreateShelvedOp); CachedSCCProvider->Execute(CreateShelvedOp, ChangelistPtr, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, OnCreateComplete](const FSourceControlOperationRef& CreateShelvedOp, ECommandResult::Type Result) { if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("CL %s create shelved operation failed:"), *GetCLID()); PrintErrorMessages(CreateShelvedOp->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { ShelvedFilesInCL = ChangelistState->GetShelvedFilesStates(); ShelvedFilesDepotPaths.Reset(ShelvedFilesInCL.Num()); UE_LOG(LogSubmitToolP4, Log, TEXT("CL %s create shelved operation succeeded"), *GetCLID()); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("CL %s create shelved operation cancelled."), *GetCLID()); } ActiveP4Operations.Remove(CreateShelvedOp); OnCreateComplete.ExecuteIfBound(CreateShelvedOp, Result); } )); } bool FChangelistService::P4Tick(float DeltaTime) { for(const TSharedRef& operation : ActiveP4Operations) { UE_LOG(LogSubmitToolP4, Log, TEXT("%s operation still in progress: %s"), *operation->GetName().ToString(), *operation->GetInProgressString().ToString()); } return true; } bool FChangelistService::IsP4OperationRunning(FName OperationName) { if(OperationName == FName()) { return ActiveP4Operations.Num() > 0; } for(const TSharedRef& Operation : ActiveP4Operations) { if(Operation->GetName().IsEqual(OperationName, ENameCase::IgnoreCase)) { return true; } } return false; } void FChangelistService::CancelP4Operations(FName OperationName) { for(const TSharedRef& Operation : ActiveP4Operations) { if(OperationName.IsNone() || Operation->GetName().IsEqual(OperationName, ENameCase::IgnoreCase)) { if(CachedSCCProvider->CanCancelOperation(Operation)) { CachedSCCProvider->CancelOperation(Operation); UE_LOG(LogSubmitToolP4, Warning, TEXT("P4 Operation %s cancelling requested"), *Operation->GetName().ToString()) } } } } void FChangelistService::CreateCLFromDefaultCL() { // If we try to open submit tool with the default cl: Update all CL Status -> create a new CL -> move files from default to the new CL -> regular flow UE_LOG(LogSubmitToolP4, Log, TEXT("Default changelist is not supported by Submit Tool, creating a new CL and moving files...")); TSharedRef UpdatePendingChangelistsOperation = ISourceControlOperation::Create(); UpdatePendingChangelistsOperation->SetUpdateFilesStates(true); UpdatePendingChangelistsOperation->SetUpdateAllChangelists(true); ActiveP4Operations.Add(UpdatePendingChangelistsOperation); CachedSCCProvider->Execute(UpdatePendingChangelistsOperation, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this](const FSourceControlOperationRef& Operation, ECommandResult::Type Result) { ActiveP4Operations.Remove(Operation); if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("Failed to fetch changelists from p4.")); PrintErrorMessages(Operation->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { TArray FilesInDefault; for(const FSourceControlChangelistPtr cl : CachedSCCProvider->GetChangelists(EStateCacheUsage::Use)) { if(cl->IsDefault()) { for(FSourceControlStateRef file : CachedSCCProvider->GetState(cl.ToSharedRef(), EStateCacheUsage::Use)->GetFilesStates()) { FilesInDefault.Emplace(file->GetFilename()); } } } TSharedRef NewCLOp = ISourceControlOperation::Create(); FStringFormatNamedArguments FormatArgs = { { TEXT("FileCount") ,FilesInDefault.Num() } }; NewCLOp->SetDescription(FText::FromString(FString::Format(*Parameters.NewChangelistMessage, FormatArgs))); ActiveP4Operations.Add(NewCLOp); CachedSCCProvider->Execute(NewCLOp, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, FilesInDefault](const FSourceControlOperationRef& Operation, ECommandResult::Type Result) { ActiveP4Operations.Remove(Operation); if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("Failed to create new changelist from default")); PrintErrorMessages(Operation->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { TSharedRef NewCLOp = StaticCastSharedRef(Operation); ChangelistPtr = NewCLOp->GetNewChangelist(); ChangelistState = CachedSCCProvider->GetState(ChangelistPtr.ToSharedRef(), EStateCacheUsage::Use); OriginalDescription = ChangelistState->GetDescriptionText(); CLDescription = ChangelistState->GetDescriptionText().ToString(); TSharedRef MoveOp = ISourceControlOperation::Create(); ActiveP4Operations.Add(MoveOp); CachedSCCProvider->Execute(MoveOp, ChangelistPtr, FilesInDefault, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateLambda( [this, FileCount = FilesInDefault.Num()](const FSourceControlOperationRef& Operation, ECommandResult::Type Result) { ActiveP4Operations.Remove(Operation); if(Result == ECommandResult::Failed) { UE_LOG(LogSubmitToolP4, Error, TEXT("Move files to CL failed")); PrintErrorMessages(Operation->GetResultInfo()); } else if(Result == ECommandResult::Succeeded) { FilesInCL = ChangelistState->GetFilesStates(); FilesDepotPaths.Reset(FilesInCL.Num()); ShelvedFilesInCL = ChangelistState->GetShelvedFilesStates(); ShelvedFilesDepotPaths.Reset(ShelvedFilesInCL.Num()); CLID = ChangelistPtr->GetIdentifier(); FConfiguration::AddOrUpdateEntry(TEXT("$(CL)"), GetCLID()); UE_LOG(LogSubmitToolP4, Log, TEXT("Created CL %s and moved with %d files from the default CL."), *ChangelistPtr->GetIdentifier(), FileCount); UE_LOG(LogSubmitToolDebug, Log, TEXT("ChangeListService CLID Updated to %s"), *GetCLID()); UE_LOG(LogSubmitToolDebug, Log, TEXT("Configuration updated value $(CL) to %s"), *GetCLID()); CLReadyCallback.ExecuteIfBound(ChangelistState.IsValid()); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Move files to CL cancelled."), *GetCLID()); } })); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("Create new CL was cancelled."), *GetCLID()); } } )); } else if(Result == ECommandResult::Cancelled) { UE_LOG(LogSubmitToolP4, Warning, TEXT("CL %s edit changelist operation cancelled."), *GetCLID()); } })); } TArray FChangelistService::GetOtherChangelistsStates() { TArray Output; for(const FSourceControlChangelistRef& CL : CachedSCCProvider->GetChangelists(EStateCacheUsage::Use)) { if(CL->GetIdentifier() != CLID) { FSourceControlChangelistPtr ClPtr = CL; Output.Add(CachedSCCProvider->GetState(ClPtr.ToSharedRef(), EStateCacheUsage::Use)); } } return Output; } void FChangelistService::PrintFilesAndShelvedFiles() { if(FilesInCL.Num() > 0) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Files in CL:")); for(const FSourceControlStateRef& File : FilesInCL) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("\t%s"), *File->GetFilename()); } } if(ShelvedFilesInCL.Num() > 0) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("Shelved Files in CL:")); for(const FSourceControlStateRef& File : ShelvedFilesInCL) { UE_LOG(LogSubmitToolP4Debug, Log, TEXT("\t%s"), *File->GetFilename()); } } } const TArray& FChangelistService::GetFilesDepotPaths(bool bForce) { FScopeLock Lock(&Mutex); if(bForce || (FilesDepotPaths.IsEmpty() && !FilesInCL.IsEmpty())) { FilesDepotPaths.Reset(); UE::Tasks::TTask OpenedTask = SourceControlService->RunCommand(TEXT("opened"), { "-c", CLID }, FOnSCCCommandComplete::CreateLambda( [this](bool bSuccess, const FSCCRecordSet& InResultValues, const FSourceControlResultInfo& InResultsInfo) { if(bSuccess) { for(const TMap& Record : InResultValues) { if(Record.Contains(TEXT("depotFile"))) { FilesDepotPaths.Add(Record[TEXT("depotFile")]); } else { const FString Base = TEXT("depotFile"); size_t i = 0; FString PathsKey = FString::Printf(TEXT("%s%d"), *Base, i); while(Record.Contains(PathsKey)) { FilesDepotPaths.Add(Record[PathsKey]); ++i; PathsKey = FString::Printf(TEXT("%s%d"), *Base, i); } } } } })); OpenedTask.Wait(); } return FilesDepotPaths; } const TArray& FChangelistService::GetShelvedFilesDepotPaths(bool bForce) { // Don't bother with the default CL if (CLID.Equals("default", ESearchCase::IgnoreCase)) { return ShelvedFilesDepotPaths; } FScopeLock Lock(&Mutex); if(bForce || (ShelvedFilesDepotPaths.IsEmpty() && !ShelvedFilesInCL.IsEmpty())) { ShelvedFilesDepotPaths.Reset(); UE::Tasks::TTask DescribeTask = SourceControlService->RunCommand(TEXT("describe"), { "-S", CLID }, FOnSCCCommandComplete::CreateLambda( [this](bool bSuccess, const FSCCRecordSet& InResultValues, const FSourceControlResultInfo& InResultsInfo) { if(bSuccess) { for(const TMap& Record : InResultValues) { if(Record.Contains(TEXT("depotFile"))) { FilesDepotPaths.Add(Record[TEXT("depotFile")]); } else { const FString Base = TEXT("depotFile"); size_t i = 0; FString PathsKey = FString::Printf(TEXT("%s%d"), *Base, i); while(Record.Contains(PathsKey)) { ShelvedFilesDepotPaths.Add(Record[PathsKey]); ++i; PathsKey = FString::Printf(TEXT("%s%d"), *Base, i); } } } } })); DescribeTask.Wait(); } return ShelvedFilesDepotPaths; }