You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
TTP# 334923 - EDITOR: Perforce Integration (Move -> Delete + Add instead of Integrate)
New API: Added ISourceControlRevision::GetBranchSource.
Made sure the P4 and SVN providers perform integrates/copies so that history is maintained across file copies & moves.
Also fixed SCC icons not refreshing sometimes until source control operation has completed.
Added new branch icon.
Fixed date display in history.
This required some fairly nasty fixup to get it all working within the Editor copy code path, but the majority of the changes are in the source control providers.
reviewed by Andrew.Brown
[CL 2095604 by Thomas Sarkanen in Main branch]
378 lines
12 KiB
C++
378 lines
12 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SourceControlPrivatePCH.h"
|
|
#include "SourceControlHelpers.h"
|
|
#include "ISourceControlProvider.h"
|
|
#include "ISourceControlState.h"
|
|
#include "ISourceControlModule.h"
|
|
#include "ISourceControlLabel.h"
|
|
#include "ISourceControlRevision.h"
|
|
#include "CoreUObject.h"
|
|
#include "MessageLog.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SourceControlHelpers"
|
|
|
|
namespace SourceControlHelpers
|
|
{
|
|
|
|
const FString& GetSettingsIni()
|
|
{
|
|
if(ISourceControlModule::Get().GetUseGlobalSettings())
|
|
{
|
|
return GetGlobalSettingsIni();
|
|
}
|
|
else
|
|
{
|
|
static FString SourceControlSettingsIni;
|
|
if(SourceControlSettingsIni.Len() == 0)
|
|
{
|
|
const FString SourceControlSettingsDir = FPaths::GeneratedConfigDir();
|
|
FConfigCacheIni::LoadGlobalIniFile(SourceControlSettingsIni, TEXT("SourceControlSettings"), NULL, NULL, false, false, true, *SourceControlSettingsDir);
|
|
}
|
|
return SourceControlSettingsIni;
|
|
}
|
|
}
|
|
|
|
const FString& GetGlobalSettingsIni()
|
|
{
|
|
static FString SourceControlGlobalSettingsIni;
|
|
if(SourceControlGlobalSettingsIni.Len() == 0)
|
|
{
|
|
const FString SourceControlSettingsDir = FPaths::EngineSavedDir() + TEXT("Config/");
|
|
FConfigCacheIni::LoadGlobalIniFile(SourceControlGlobalSettingsIni, TEXT("SourceControlSettings"), NULL, NULL, false, false, true, *SourceControlSettingsDir);
|
|
}
|
|
return SourceControlGlobalSettingsIni;
|
|
}
|
|
|
|
static FString PackageFilename_Internal( const FString& InPackageName )
|
|
{
|
|
FString Filename = InPackageName;
|
|
|
|
// Get the filename by finding it on disk first
|
|
if ( !FPackageName::DoesPackageExist(InPackageName, NULL, &Filename) )
|
|
{
|
|
// The package does not exist on disk, see if we can find it in memory and predict the file extension
|
|
// Only do this if the supplied package name is valid
|
|
const bool bIncludeReadOnlyRoots = false;
|
|
if ( FPackageName::IsValidLongPackageName(InPackageName, bIncludeReadOnlyRoots) )
|
|
{
|
|
UPackage* Package = FindPackage(NULL, *InPackageName);
|
|
if ( Package )
|
|
{
|
|
// This is a package in memory that has not yet been saved. Determine the extension and convert to a filename
|
|
const FString PackageExtension = Package->ContainsMap() ? FPackageName::GetMapPackageExtension() : FPackageName::GetAssetPackageExtension();
|
|
Filename = FPackageName::LongPackageNameToFilename(InPackageName, PackageExtension);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Filename;
|
|
}
|
|
|
|
FString PackageFilename( const FString& InPackageName )
|
|
{
|
|
return FPaths::ConvertRelativePathToFull(PackageFilename_Internal(InPackageName));
|
|
}
|
|
|
|
FString PackageFilename( const UPackage* InPackage )
|
|
{
|
|
FString Filename;
|
|
if(InPackage != NULL)
|
|
{
|
|
Filename = FPaths::ConvertRelativePathToFull(PackageFilename_Internal(InPackage->GetName()));
|
|
}
|
|
return Filename;
|
|
}
|
|
|
|
TArray<FString> PackageFilenames( const TArray<UPackage*>& InPackages )
|
|
{
|
|
TArray<FString> OutNames;
|
|
for (int32 PackageIndex = 0; PackageIndex < InPackages.Num(); PackageIndex++)
|
|
{
|
|
OutNames.Add(FPaths::ConvertRelativePathToFull(PackageFilename(InPackages[PackageIndex])));
|
|
}
|
|
|
|
return OutNames;
|
|
}
|
|
|
|
TArray<FString> PackageFilenames( const TArray<FString>& InPackageNames )
|
|
{
|
|
TArray<FString> OutNames;
|
|
for (int32 PackageIndex = 0; PackageIndex < InPackageNames.Num(); PackageIndex++)
|
|
{
|
|
OutNames.Add(FPaths::ConvertRelativePathToFull(PackageFilename_Internal(InPackageNames[PackageIndex])));
|
|
}
|
|
|
|
return OutNames;
|
|
}
|
|
|
|
TArray<FString> AbsoluteFilenames( const TArray<FString>& InFileNames )
|
|
{
|
|
TArray<FString> AbsoluteFiles;
|
|
for(const auto& FileName : InFileNames)
|
|
{
|
|
if(!FPaths::IsRelative(FileName))
|
|
{
|
|
AbsoluteFiles.Add(FileName);
|
|
}
|
|
else
|
|
{
|
|
AbsoluteFiles.Add(FPaths::ConvertRelativePathToFull(FileName));
|
|
}
|
|
}
|
|
|
|
return AbsoluteFiles;
|
|
}
|
|
|
|
void RevertUnchangedFiles( ISourceControlProvider& InProvider, const TArray<FString>& InFiles )
|
|
{
|
|
// Make sure we update the modified state of the files
|
|
TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> UpdateStatusOperation = ISourceControlOperation::Create<FUpdateStatus>();
|
|
UpdateStatusOperation->SetUpdateModifiedState(true);
|
|
InProvider.Execute(UpdateStatusOperation, InFiles);
|
|
|
|
TArray<FString> UnchangedFiles;
|
|
TArray< TSharedRef<ISourceControlState, ESPMode::ThreadSafe> > OutStates;
|
|
InProvider.GetState(InFiles, OutStates, EStateCacheUsage::Use);
|
|
|
|
for(TArray< TSharedRef<ISourceControlState, ESPMode::ThreadSafe> >::TConstIterator It(OutStates); It; It++)
|
|
{
|
|
TSharedRef<ISourceControlState, ESPMode::ThreadSafe> SourceControlState = *It;
|
|
if(SourceControlState->IsCheckedOut() && !SourceControlState->IsModified())
|
|
{
|
|
UnchangedFiles.Add(SourceControlState->GetFilename());
|
|
}
|
|
}
|
|
|
|
if(UnchangedFiles.Num())
|
|
{
|
|
InProvider.Execute( ISourceControlOperation::Create<FRevert>(), UnchangedFiles );
|
|
}
|
|
}
|
|
|
|
bool AnnotateFile( ISourceControlProvider& InProvider, const FString& InLabel, const FString& InFile, TArray<FAnnotationLine>& OutLines )
|
|
{
|
|
TArray< TSharedRef<ISourceControlLabel> > Labels = InProvider.GetLabels( InLabel );
|
|
if(Labels.Num() > 0)
|
|
{
|
|
TSharedRef<ISourceControlLabel> Label = Labels[0];
|
|
TArray< TSharedRef<ISourceControlRevision, ESPMode::ThreadSafe> > Revisions;
|
|
Label->GetFileRevisions(InFile, Revisions);
|
|
if(Revisions.Num() > 0)
|
|
{
|
|
TSharedRef<ISourceControlRevision, ESPMode::ThreadSafe> Revision = Revisions[0];
|
|
if(Revision->GetAnnotated(OutLines))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CheckOutFile( const FString& InFilePath )
|
|
{
|
|
if ( InFilePath.IsEmpty() )
|
|
{
|
|
FMessageLog("SourceControl").Error(LOCTEXT("UnspecifiedCheckoutFile", "Check out file not specified"));
|
|
return false;
|
|
}
|
|
|
|
if( !ISourceControlModule::Get().IsEnabled() )
|
|
{
|
|
FMessageLog("SourceControl").Error(LOCTEXT("SourceControlDisabled", "Source control is not enabled."));
|
|
return false;
|
|
}
|
|
|
|
if( !ISourceControlModule::Get().GetProvider().IsAvailable() )
|
|
{
|
|
FMessageLog("SourceControl").Error(LOCTEXT("SourceControlServerUnavailable", "Source control server is currently not available."));
|
|
return false;
|
|
}
|
|
|
|
bool bSuccessfullyCheckedOut = false;
|
|
TArray<FString> FilesToBeCheckedOut;
|
|
FilesToBeCheckedOut.Add( InFilePath );
|
|
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState( InFilePath, EStateCacheUsage::ForceUpdate );
|
|
if(SourceControlState.IsValid())
|
|
{
|
|
FString SimultaneousCheckoutUser;
|
|
if( SourceControlState->IsAdded() ||
|
|
SourceControlState->IsCheckedOut())
|
|
{
|
|
// Already checked out or opened for add
|
|
bSuccessfullyCheckedOut = true;
|
|
}
|
|
else
|
|
{
|
|
if(SourceControlState->CanCheckout())
|
|
{
|
|
bSuccessfullyCheckedOut = (SourceControlProvider.Execute( ISourceControlOperation::Create<FCheckOut>(), FilesToBeCheckedOut ) == ECommandResult::Succeeded);
|
|
if (!bSuccessfullyCheckedOut)
|
|
{
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add( TEXT("InFilePath"), FText::FromString(InFilePath) );
|
|
FMessageLog("SourceControl").Error(FText::Format(LOCTEXT("CheckoutFailed", "Failed to check out file '{InFilePath}'."), Arguments));
|
|
}
|
|
}
|
|
if(!SourceControlState->IsSourceControlled())
|
|
{
|
|
bSuccessfullyCheckedOut = (SourceControlProvider.Execute( ISourceControlOperation::Create<FMarkForAdd>(), FilesToBeCheckedOut ) == ECommandResult::Succeeded);
|
|
if (!bSuccessfullyCheckedOut)
|
|
{
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add( TEXT("InFilePath"), FText::FromString(InFilePath) );
|
|
FMessageLog("SourceControl").Error(FText::Format(LOCTEXT("AddFailed", "Failed to add file '{InFilePath}' to source control."), Arguments));
|
|
}
|
|
}
|
|
|
|
if(!SourceControlState->IsCurrent())
|
|
{
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add( TEXT("InFilePath"), FText::FromString(InFilePath) );
|
|
FMessageLog("SourceControl").Error(FText::Format(LOCTEXT("NotAtHeadRevision", "File '{InFilePath}' is not at head revision."), Arguments));
|
|
}
|
|
else if(SourceControlState->IsCheckedOutOther(&(SimultaneousCheckoutUser)))
|
|
{
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add( TEXT("InFilePath"), FText::FromString(InFilePath) );
|
|
Arguments.Add( TEXT("SimultaneousCheckoutUser"), FText::FromString(SimultaneousCheckoutUser) );
|
|
FMessageLog("SourceControl").Error(FText::Format(LOCTEXT("SimultaneousCheckout", "File '{InFilePath}' is checked out by another ('{SimultaneousCheckoutUser}')."), Arguments));
|
|
}
|
|
else
|
|
{
|
|
// Improper or invalid SCC state
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add( TEXT("InFilePath"), FText::FromString(InFilePath) );
|
|
FMessageLog("SourceControl").Error(FText::Format(LOCTEXT("CouldNotDetermineState", "Could not determine source control state of file '{InFilePath}'."), Arguments));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Improper or invalid SCC state
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add( TEXT("InFilePath"), FText::FromString(InFilePath) );
|
|
FMessageLog("SourceControl").Error(FText::Format(LOCTEXT("CouldNotDetermineState", "Could not determine source control state of file '{InFilePath}'."), Arguments));
|
|
}
|
|
|
|
return bSuccessfullyCheckedOut;
|
|
}
|
|
|
|
bool CheckoutOrMarkForAdd( const FString& InDestFile, const FText& InFileDescription, const FOnPostCheckOut& OnPostCheckOut, FText& OutFailReason )
|
|
{
|
|
bool bSucceeded = true;
|
|
|
|
ISourceControlProvider& Provider = ISourceControlModule::Get().GetProvider();
|
|
|
|
// first check for source control check out
|
|
if (ISourceControlModule::Get().IsEnabled())
|
|
{
|
|
FSourceControlStatePtr SourceControlState = Provider.GetState(InDestFile, EStateCacheUsage::ForceUpdate);
|
|
if (SourceControlState.IsValid())
|
|
{
|
|
if (SourceControlState->IsSourceControlled() && SourceControlState->CanCheckout())
|
|
{
|
|
ECommandResult::Type Result = Provider.Execute(ISourceControlOperation::Create<FCheckOut>(), InDestFile);
|
|
bSucceeded = (Result == ECommandResult::Succeeded);
|
|
if (!bSucceeded)
|
|
{
|
|
OutFailReason = FText::Format(LOCTEXT("SourceControlCheckoutError", "Could not check out {0} file."), InFileDescription);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bSucceeded)
|
|
{
|
|
if(OnPostCheckOut.IsBound())
|
|
{
|
|
bSucceeded = OnPostCheckOut.Execute(InDestFile, InFileDescription, OutFailReason);
|
|
}
|
|
}
|
|
|
|
// mark for add now if needed
|
|
if (bSucceeded && ISourceControlModule::Get().IsEnabled())
|
|
{
|
|
FSourceControlStatePtr SourceControlState = Provider.GetState(InDestFile, EStateCacheUsage::Use);
|
|
if (SourceControlState.IsValid())
|
|
{
|
|
if (!SourceControlState->IsSourceControlled())
|
|
{
|
|
ECommandResult::Type Result = Provider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), InDestFile);
|
|
bSucceeded = (Result == ECommandResult::Succeeded);
|
|
if (!bSucceeded)
|
|
{
|
|
OutFailReason = FText::Format(LOCTEXT("SourceControlMarkForAddError", "Could not mark {0} file for add."), InFileDescription);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bSucceeded;
|
|
}
|
|
|
|
bool CopyFileUnderSourceControl( const FString& InDestFile, const FString& InSourceFile, const FText& InFileDescription, FText& OutFailReason)
|
|
{
|
|
struct Local
|
|
{
|
|
static bool CopyFile(const FString& InDestFile, const FText& InFileDescription, FText& OutFailReason, FString InSourceFile)
|
|
{
|
|
const bool bReplace = true;
|
|
const bool bEvenIfReadOnly = true;
|
|
bool bSucceeded = (IFileManager::Get().Copy(*InDestFile, *InSourceFile, bReplace, bEvenIfReadOnly) == COPY_OK);
|
|
if (!bSucceeded)
|
|
{
|
|
OutFailReason = FText::Format(LOCTEXT("ExternalImageCopyError", "Could not overwrite {0} file."), InFileDescription);
|
|
}
|
|
|
|
return bSucceeded;
|
|
}
|
|
};
|
|
|
|
return CheckoutOrMarkForAdd(InDestFile, InFileDescription, FOnPostCheckOut::CreateStatic(&Local::CopyFile, InSourceFile), OutFailReason);
|
|
}
|
|
|
|
bool BranchFile( const FString& DestFilename, const FString& SourceFilename )
|
|
{
|
|
if(ISourceControlModule::Get().IsEnabled())
|
|
{
|
|
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
|
|
|
|
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceFilename, EStateCacheUsage::ForceUpdate);
|
|
if(SourceControlState.IsValid() && SourceControlState->IsSourceControlled())
|
|
{
|
|
TSharedRef<FCopy, ESPMode::ThreadSafe> CopyOperation = ISourceControlOperation::Create<FCopy>();
|
|
CopyOperation->SetDestination(DestFilename);
|
|
|
|
if(SourceControlProvider.Execute(CopyOperation, SourceFilename) != ECommandResult::Succeeded)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
FScopedSourceControl::FScopedSourceControl()
|
|
{
|
|
ISourceControlModule::Get().GetProvider().Init();
|
|
}
|
|
|
|
FScopedSourceControl::~FScopedSourceControl()
|
|
{
|
|
ISourceControlModule::Get().GetProvider().Close();
|
|
}
|
|
|
|
ISourceControlProvider& FScopedSourceControl::GetProvider()
|
|
{
|
|
return ISourceControlModule::Get().GetProvider();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |