// 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::GameSavedDir() + TEXT("Config/"); 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 PackageFilenames( const TArray& InPackages ) { TArray OutNames; for (int32 PackageIndex = 0; PackageIndex < InPackages.Num(); PackageIndex++) { OutNames.Add(FPaths::ConvertRelativePathToFull(PackageFilename(InPackages[PackageIndex]))); } return OutNames; } TArray PackageFilenames( const TArray& InPackageNames ) { TArray OutNames; for (int32 PackageIndex = 0; PackageIndex < InPackageNames.Num(); PackageIndex++) { OutNames.Add(FPaths::ConvertRelativePathToFull(PackageFilename_Internal(InPackageNames[PackageIndex]))); } return OutNames; } void RevertUnchangedFiles( ISourceControlProvider& InProvider, const TArray& InFiles ) { // Make sure we update the modified state of the files TSharedRef UpdateStatusOperation = ISourceControlOperation::Create(); UpdateStatusOperation->SetUpdateModifiedState(true); InProvider.Execute(UpdateStatusOperation, InFiles); TArray UnchangedFiles; TArray< TSharedRef > OutStates; InProvider.GetState(InFiles, OutStates, EStateCacheUsage::Use); for(TArray< TSharedRef >::TConstIterator It(OutStates); It; It++) { TSharedRef SourceControlState = *It; if(SourceControlState->IsCheckedOut() && !SourceControlState->IsModified()) { UnchangedFiles.Add(SourceControlState->GetFilename()); } } if(UnchangedFiles.Num()) { InProvider.Execute( ISourceControlOperation::Create(), UnchangedFiles ); } } bool AnnotateFile( ISourceControlProvider& InProvider, const FString& InLabel, const FString& InFile, TArray& OutLines ) { TArray< TSharedRef > Labels = InProvider.GetLabels( InLabel ); if(Labels.Num() > 0) { TSharedRef Label = Labels[0]; TArray< TSharedRef > Revisions; Label->GetFileRevisions(InFile, Revisions); if(Revisions.Num() > 0) { TSharedRef 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 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(), 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(), 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(), 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(), 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); } } FScopedSourceControl::FScopedSourceControl() { ISourceControlModule::Get().GetProvider().Init(); } FScopedSourceControl::~FScopedSourceControl() { ISourceControlModule::Get().GetProvider().Close(); } ISourceControlProvider& FScopedSourceControl::GetProvider() { return ISourceControlModule::Get().GetProvider(); } #undef LOCTEXT_NAMESPACE