SPlatformSetupMessage::ESetupState SPlatformSetupMessage::GetSetupStateBasedOnFile(bool bForce) const { if (!FPaths::FileExists(TargetFilename)) { return MissingFiles; } else { ISourceControlModule& SCC = ISourceControlModule::Get(); if (SCC.IsEnabled() && SCC.GetProvider().IsAvailable()) { ISourceControlProvider& Provider = SCC.GetProvider(); FSourceControlStatePtr SourceControlState = Provider.GetState(TargetFilename, bForce ? EStateCacheUsage::ForceUpdate : EStateCacheUsage::Use); if (SourceControlState.IsValid()) { if (SourceControlState->IsSourceControlled()) { if (SourceControlState->CanCheckout()) { return NeedsCheckout; } } else { //@TODO: Should we instead try to add the file? } } } // SCC is disabled or unavailable const bool bIsReadOnly = FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*TargetFilename); return bIsReadOnly ? ReadOnlyFiles : ReadyToModify; } }
TSharedRef<SDockTab> FMerge::GenerateMergeWidget(const UBlueprint& Object, TSharedRef<FBlueprintEditor> Editor) { auto ActiveTabPtr = ActiveTab.Pin(); if( ActiveTabPtr.IsValid() ) { // just bring the tab to the foreground: auto CurrentTab = FGlobalTabmanager::Get()->InvokeTab(MergeToolTabId); check( CurrentTab == ActiveTabPtr ); return ActiveTabPtr.ToSharedRef(); } // merge the local asset with the depot, SCC provides us with the last common revision as // a basis for the merge: TSharedPtr<SWidget> Contents; if (!PendingMerge(Object)) { // this should load up the merge-tool, with an asset picker, where they // can pick the asset/revisions to merge against Contents = GenerateMergeTabContents(Editor, nullptr, FRevisionInfo::InvalidRevision(), nullptr, FRevisionInfo::InvalidRevision(), &Object, FOnMergeResolved()); } else { // @todo DO: this will probably need to be async.. pulling down some old versions of assets: const FString& PackageName = Object.GetOutermost()->GetName(); const FString& AssetName = Object.GetName(); FSourceControlStatePtr SourceControlState = FMergeToolUtils::GetSourceControlState(PackageName); if (!SourceControlState.IsValid()) { DisplayErrorMessage( FText::Format( LOCTEXT("MergeFailedNoSourceControl", "Aborted Load of {0} from {1} because the source control state was invalidated") , FText::FromString(AssetName) , FText::FromString(PackageName) ) ); Contents = SNew(SHorizontalBox); } else { ISourceControlState const& SourceControlStateRef = *SourceControlState; FRevisionInfo CurrentRevInfo = FRevisionInfo::InvalidRevision(); const UBlueprint* RemoteBlueprint = Cast< UBlueprint >(LoadHeadRev(PackageName, AssetName, SourceControlStateRef, CurrentRevInfo)); FRevisionInfo BaseRevInfo = FRevisionInfo::InvalidRevision(); const UBlueprint* BaseBlueprint = Cast< UBlueprint >(LoadBaseRev(PackageName, AssetName, SourceControlStateRef, BaseRevInfo)); Contents = GenerateMergeTabContents(Editor, BaseBlueprint, BaseRevInfo, RemoteBlueprint, CurrentRevInfo, &Object, FOnMergeResolved()); } } TSharedRef<SDockTab> Tab = FGlobalTabmanager::Get()->InvokeTab(MergeToolTabId); Tab->SetContent(Contents.ToSharedRef()); ActiveTab = Tab; return Tab; }
bool FGetStateLatentCommand::Update() { FSourceControlStatePtr SourceControlState = ISourceControlModule::Get().GetProvider().GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::Use); if(!SourceControlState.IsValid()) { UE_LOG(LogSourceControl, Error, TEXT("Failed to get a valid state for file: %s"), *Filename); } else { if(!SourceControlState->IsCheckedOut()) { UE_LOG(LogSourceControl, Error, TEXT("File '%s' should be checked out, but isnt."), *Filename); } else { if(SourceControlState->GetHistorySize() == 0) { UE_LOG(LogSourceControl, Error, TEXT("Failed to get a valid history for file: %s"), *Filename); } else { TSharedPtr<ISourceControlRevision, ESPMode::ThreadSafe> HistoryItem = SourceControlState->GetHistoryItem(0); if(!HistoryItem.IsValid()) { UE_LOG(LogSourceControl, Error, TEXT("Failed to get a valid history item 0 for file: %s"), *Filename); } } } } return true; }
const FSlateBrush* SWorldHierarchyItem::GetSCCStateImage() const { FSourceControlStatePtr SourceControlState = ISourceControlModule::Get().GetProvider().GetState(LevelModel->GetPackageFileName(), EStateCacheUsage::Use); if(SourceControlState.IsValid()) { return FEditorStyle::GetBrush(SourceControlState->GetSmallIconName()); } return NULL; }
FText SWorldHierarchyItem::GetSCCStateTooltip() const { FSourceControlStatePtr SourceControlState = ISourceControlModule::Get().GetProvider().GetState(LevelModel->GetPackageFileName(), EStateCacheUsage::Use); if(SourceControlState.IsValid()) { return SourceControlState->GetDisplayTooltip(); } return FText::GetEmpty(); }
void FPathContextMenu::ExecuteSCCOpenForAdd() { ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // Get a list of package names in the selected paths TArray<FString> PackageNames; GetPackageNamesInSelectedPaths(PackageNames); TArray<FString> PackagesToAdd; TArray<UPackage*> PackagesToSave; for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt ) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIt), EStateCacheUsage::Use); if ( SourceControlState.IsValid() && !SourceControlState->IsSourceControlled() ) { PackagesToAdd.Add(*PackageIt); // Make sure the file actually exists on disk before adding it FString Filename; if ( !FPackageName::DoesPackageExist(*PackageIt, NULL, &Filename) ) { UPackage* Package = FindPackage(NULL, **PackageIt); if ( Package ) { PackagesToSave.Add(Package); } } } } if ( PackagesToAdd.Num() > 0 ) { // If any of the packages are new, save them now if ( PackagesToSave.Num() > 0 ) { const bool bCheckDirty = false; const bool bPromptToSave = false; TArray<UPackage*> FailedPackages; const FEditorFileUtils::EPromptReturnCode Return = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave, &FailedPackages); if(FailedPackages.Num() > 0) { // don't try and add files that failed to save - remove them from the list for(auto FailedPackageIt = FailedPackages.CreateConstIterator(); FailedPackageIt; FailedPackageIt++) { PackagesToAdd.Remove((*FailedPackageIt)->GetName()); } } } if ( PackagesToAdd.Num() > 0 ) { SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), SourceControlHelpers::PackageFilenames(PackagesToAdd)); } } }
void SSettingsEditorCheckoutNotice::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); // cache selected settings object's configuration file state DefaultConfigCheckOutTimer += InDeltaTime; if (DefaultConfigCheckOutTimer >= 1.0f) { bool NewCheckOutNeeded = false; DefaultConfigQueryInProgress = true; FString CachedConfigFileName = ConfigFilePath.Get(); if (!CachedConfigFileName.IsEmpty()) { if (ISourceControlModule::Get().IsEnabled()) { // note: calling QueueStatusUpdate often does not spam status updates as an internal timer prevents this ISourceControlModule::Get().QueueStatusUpdate(CachedConfigFileName); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(CachedConfigFileName, EStateCacheUsage::Use); NewCheckOutNeeded = SourceControlState.IsValid() && SourceControlState->CanCheckout(); DefaultConfigQueryInProgress = SourceControlState.IsValid() && SourceControlState->IsUnknown(); } else { NewCheckOutNeeded = (FPaths::FileExists(CachedConfigFileName) && IFileManager::Get().IsReadOnly(*CachedConfigFileName)); DefaultConfigQueryInProgress = false; } // file has been checked in or reverted if ((NewCheckOutNeeded == true) && (DefaultConfigCheckOutNeeded == false)) { OnFileProbablyModifiedExternally.ExecuteIfBound(); } } DefaultConfigCheckOutNeeded = NewCheckOutNeeded; DefaultConfigCheckOutTimer = 0.0f; } }
void FAssetFixUpRedirectors::LoadReferencingPackages(TArray<FRedirectorRefs>& RedirectorsToFix, TArray<UPackage*>& OutReferencingPackagesToSave) const { FScopedSlowTask SlowTask( RedirectorsToFix.Num(), LOCTEXT( "LoadingReferencingPackages", "Loading Referencing Packages..." ) ); SlowTask.MakeDialog(); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // Load all packages that reference each redirector, if possible for ( auto RedirectorRefsIt = RedirectorsToFix.CreateIterator(); RedirectorRefsIt; ++RedirectorRefsIt ) { SlowTask.EnterProgressFrame(1); FRedirectorRefs& RedirectorRefs = *RedirectorRefsIt; if ( ISourceControlModule::Get().IsEnabled() ) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(RedirectorRefs.Redirector->GetOutermost(), EStateCacheUsage::Use); const bool bValidSCCState = !SourceControlState.IsValid() || SourceControlState->IsAdded() || SourceControlState->IsCheckedOut() || SourceControlState->CanCheckout() || !SourceControlState->IsSourceControlled() || SourceControlState->IsIgnored(); if ( !bValidSCCState ) { RedirectorRefs.bRedirectorValidForFixup = false; RedirectorRefs.FailureReason = LOCTEXT("RedirectorFixupFailed_BadSCC", "Redirector could not be checked out or marked for delete"); } } // Load all referencers for ( auto PackageNameIt = RedirectorRefs.ReferencingPackageNames.CreateConstIterator(); PackageNameIt; ++PackageNameIt ) { const FString PackageName = (*PackageNameIt).ToString(); // Find the package in memory. If it is not in memory, try to load it UPackage* Package = FindPackage(NULL, *PackageName); if ( !Package ) { Package = LoadPackage(NULL, *PackageName, LOAD_None); } if ( Package ) { if ( Package->PackageFlags & PKG_CompiledIn ) { // This is a script reference RedirectorRefs.bRedirectorValidForFixup = false; RedirectorRefs.FailureReason = FText::Format(LOCTEXT("RedirectorFixupFailed_CodeReference", "Redirector is referenced by code. Package: {0}"), FText::FromString(PackageName)); } else { // If we found a valid package, mark it for save OutReferencingPackagesToSave.AddUnique(Package); } } } } }
bool FMerge::PendingMerge(const UBlueprint& BlueprintObj) const { ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); bool bPendingMerge = false; if( SourceControlProvider.IsEnabled() ) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(BlueprintObj.GetOutermost(), EStateCacheUsage::Use); bPendingMerge = SourceControlState.IsValid() && SourceControlState->IsConflicted(); } return bPendingMerge; }
void UPropertyConfigFileDisplayRow::InitWithConfigAndProperty(const FString& InConfigFileName, UProperty* InEditProperty) { ConfigFileName = FPaths::ConvertRelativePathToFull(InConfigFileName); ExternalProperty = InEditProperty; ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // We will add source control soon... FSourceControlStatePtr SourceControlState = nullptr; // SourceControlProvider.GetState(ConfigFileName, EStateCacheUsage::Use); // Only include config files that are currently checked out or packages not under source control { if (FPaths::FileExists(ConfigFileName)) { if (SourceControlState.IsValid()) { bIsFileWritable = SourceControlState->IsCheckedOut() || SourceControlState->IsAdded(); } else { bIsFileWritable = !IFileManager::Get().IsReadOnly(*ConfigFileName); } } else { if (SourceControlState.IsValid()) { bIsFileWritable = (SourceControlState->IsSourceControlled() && SourceControlState->CanAdd()); } else { bIsFileWritable = false; } } } }
void FAssetDeleteModel::DeleteSourceContentFiles() { IFileManager& FileManager = IFileManager::Get(); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); for (const auto& Pair : SourceFileToAssetCount) { const auto& Path = Pair.Key; // We can only delete this path if there are no (non-deleted) objects referencing it if (Pair.Value != 0) { continue; } // One way or another this file is going to be deleted, but we don't want the import manager to react to the deletion GUnrealEd->AutoReimportManager->ReportExternalChange(Path, FFileChangeData::FCA_Removed); if (ISourceControlModule::Get().IsEnabled()) { const FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Path, EStateCacheUsage::ForceUpdate); const bool bIsSourceControlled = SourceControlState.IsValid() && SourceControlState->IsSourceControlled(); if (bIsSourceControlled) { // The file is managed by source control. Delete it through there. TArray<FString> DeleteFilenames; DeleteFilenames.Add(Path); // Revert the file if it is checked out const bool bIsAdded = SourceControlState->IsAdded(); if (SourceControlState->IsCheckedOut() || bIsAdded || SourceControlState->IsDeleted()) { SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), DeleteFilenames); } // If it wasn't already marked as an add, we can ask the source control provider to delete the file if (!bIsAdded) { // Open the file for delete SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), DeleteFilenames); continue; } } } // We'll just delete it ourself FileManager.Delete(*Path, false /* RequireExists */, true /* Even if read only */, true /* Quiet */); } }
void FLevelCollectionModel::CacheCanExecuteSourceControlVars() const { bCanExecuteSCCCheckOut = false; bCanExecuteSCCOpenForAdd = false; bCanExecuteSCCCheckIn = false; bCanExecuteSCC = false; ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It) { if (ISourceControlModule::Get().IsEnabled() && SourceControlProvider.IsAvailable()) { bCanExecuteSCC = true; ULevel* Level = (*It)->GetLevelObject(); if (Level) { // Check the SCC state for each package in the selected paths FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Level->GetOutermost(), EStateCacheUsage::Use); if (SourceControlState.IsValid()) { if (SourceControlState->CanCheckout()) { bCanExecuteSCCCheckOut = true; } else if (!SourceControlState->IsSourceControlled()) { bCanExecuteSCCOpenForAdd = true; } else if (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) { bCanExecuteSCCCheckIn = true; } } } } if (bCanExecuteSCCCheckOut && bCanExecuteSCCOpenForAdd && bCanExecuteSCCCheckIn) { // All options are available, no need to keep iterating break; } } }
bool FGatherTextSCC::RevertFile( const FString& InFile, FString& OutError ) { if( InFile.IsEmpty() ) { OutError = TEXT("Could not revert file."); return false; } FString SCCError; if( !IsReady( SCCError ) ) { OutError = SCCError; return false; } FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(InFile); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState( AbsoluteFilename, EStateCacheUsage::ForceUpdate ); bool bSuccessfullyReverted = false; TArray<FString> FilesToRevert; FilesToRevert.Add(AbsoluteFilename); if( SourceControlState.IsValid() && !SourceControlState->IsCheckedOut() && !SourceControlState->IsAdded() ) { bSuccessfullyReverted = true; } else { bSuccessfullyReverted = (SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), FilesToRevert) == ECommandResult::Succeeded); } if(!bSuccessfullyReverted) { OutError = TEXT("Could not revert file."); } else { CheckedOutFiles.Remove( AbsoluteFilename ); } return bSuccessfullyReverted; }
void FPathContextMenu::CacheCanExecuteVars() { // Cache whether we can execute any of the source control commands bCanExecuteSCCCheckOut = false; bCanExecuteSCCOpenForAdd = false; bCanExecuteSCCCheckIn = false; ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( SourceControlProvider.IsEnabled() && SourceControlProvider.IsAvailable() ) { TArray<FString> PackageNames; GetPackageNamesInSelectedPaths(PackageNames); // Check the SCC state for each package in the selected paths for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt ) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIt), EStateCacheUsage::Use); if(SourceControlState.IsValid()) { if ( SourceControlState->CanCheckout() ) { bCanExecuteSCCCheckOut = true; } else if ( !SourceControlState->IsSourceControlled() ) { bCanExecuteSCCOpenForAdd = true; } else if ( SourceControlState->CanCheckIn() ) { bCanExecuteSCCCheckIn = true; } } if ( bCanExecuteSCCCheckOut && bCanExecuteSCCOpenForAdd && bCanExecuteSCCCheckIn ) { // All SCC options are available, no need to keep iterating break; } } } }
bool FCollection::RevertCollection(FText& OutError) { if ( !ensure(SourceFilename.Len()) ) { OutError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( !ISourceControlModule::Get().IsEnabled() ) { OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu."); return false; } if ( !SourceControlProvider.IsAvailable() ) { OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again."); return false; } FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); if ( SourceControlState.IsValid() && !(SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) ) { OutError = FText::Format(LOCTEXT("Error_SCCNotCheckedOut", "Collection '{0}' not checked out or open for add."), FText::FromName(CollectionName)); return false; } if ( SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), AbsoluteFilename) == ECommandResult::Succeeded) { return true; } else { OutError = FText::Format(LOCTEXT("Error_SCCRevert", "Could not revert collection '{0}'"), FText::FromName(CollectionName)); return false; } }
void FLevelCollectionModel::SCCOpenForAdd(const FLevelModelList& InList) { ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); TArray<FString> FilenamesList = GetFilenamesList(InList); TArray<FString> FilenamesToAdd; TArray<UPackage*> PackagesToSave; for (auto It = FilenamesList.CreateConstIterator(); It; ++It) { const FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(*It, EStateCacheUsage::Use); if (SourceControlState.IsValid() && !SourceControlState->IsSourceControlled()) { FilenamesToAdd.Add(*It); // Make sure the file actually exists on disk before adding it FString LongPackageName = FPackageName::FilenameToLongPackageName(*It); if (!FPackageName::DoesPackageExist(LongPackageName)) { UPackage* Package = FindPackage(NULL, *LongPackageName); if (Package) { PackagesToSave.Add(Package); } } } } if (FilenamesToAdd.Num() > 0) { // If any of the packages are new, save them now if (PackagesToSave.Num() > 0) { const bool bCheckDirty = false; const bool bPromptToSave = false; const auto Return = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave); } SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), FilenamesToAdd); } }
void UUnrealEdEngine::OnSourceControlStateUpdated(const FSourceControlOperationRef& SourceControlOp, ECommandResult::Type ResultType, TWeakObjectPtr<UPackage> Package) { if (ResultType == ECommandResult::Succeeded && Package.IsValid()) { // Get the source control state of the package ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Package.Get(), EStateCacheUsage::Use); if (SourceControlState.IsValid()) { const UEditorLoadingSavingSettings* Settings = GetDefault<UEditorLoadingSavingSettings>(); check(Settings->bPromptForCheckoutOnAssetModification || Settings->bAutomaticallyCheckoutOnAssetModification); if (Settings->bAutomaticallyCheckoutOnAssetModification && SourceControlState->CanCheckout()) { // Automatically check out asset TArray<FString> Files; Files.Add(SourceControlHelpers::PackageFilename(Package.Get())); SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), SourceControlHelpers::AbsoluteFilenames(Files), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateUObject(this, &UUnrealEdEngine::OnPackageCheckedOut, TWeakObjectPtr<UPackage>(Package))); } else { if (SourceControlState->CanCheckout() || !SourceControlState->IsCurrent() || SourceControlState->IsCheckedOutOther()) { // To get here, either "prompt for checkout on asset modification" is set, or "automatically checkout on asset modification" // is set, but it failed. // Allow packages that are not checked out to pass through. // Allow packages that are not current or checked out by others pass through. // The user wont be able to checkout these packages but the checkout dialog will show up with a special icon // to let the user know they wont be able to checkout the package they are modifying. PackageToNotifyState.Add(Package, SourceControlState->CanCheckout() ? NS_PendingPrompt : NS_PendingWarning); // We need to prompt since a new package was added bNeedToPromptForCheckout = true; } } } } }
bool FGatherTextSCC::CheckinFiles( const FString& InChangeDescription, FString& OutError ) { if( CheckedOutFiles.Num() == 0 ) { return true; } FString SCCError; if( !IsReady( SCCError ) ) { OutError = SCCError; return false; } ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); SourceControlHelpers::RevertUnchangedFiles(SourceControlProvider, CheckedOutFiles); // remove unchanged files for (int32 VerifyIndex = CheckedOutFiles.Num()-1; VerifyIndex >= 0; --VerifyIndex) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState( CheckedOutFiles[VerifyIndex], EStateCacheUsage::ForceUpdate ); if (SourceControlState.IsValid() && !SourceControlState->IsCheckedOut() && !SourceControlState->IsAdded()) { CheckedOutFiles.RemoveAt(VerifyIndex); } } if (CheckedOutFiles.Num() > 0) { TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>(); CheckInOperation->SetDescription(InChangeDescription); if (!SourceControlProvider.Execute( CheckInOperation, CheckedOutFiles )) { OutError = TEXT("The checked out localization files could not be checked in."); return false; } CheckedOutFiles.Empty(); } return true; }
void UUnrealEdEngine::OnPackageCheckedOut(const FSourceControlOperationRef& SourceControlOp, ECommandResult::Type ResultType, TWeakObjectPtr<UPackage> Package) { if (Package.IsValid()) { // Get the source control state of the package ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Package.Get(), EStateCacheUsage::Use); FFormatNamedArguments Arguments; Arguments.Add(TEXT("Package"), FText::FromString(Package->GetName())); if (ResultType == ECommandResult::Succeeded) { if (SourceControlState.IsValid() && SourceControlState->IsCheckedOut()) { FNotificationInfo Notification(FText::Format(NSLOCTEXT("SourceControl", "AutoCheckOutNotification", "Package '{Package}' automatically checked out."), Arguments)); Notification.bFireAndForget = true; Notification.ExpireDuration = 4.0f; Notification.bUseThrobber = true; FSlateNotificationManager::Get().AddNotification(Notification); return; } } FNotificationInfo ErrorNotification(FText::Format(NSLOCTEXT("SourceControl", "AutoCheckOutFailedNotification", "Unable to automatically check out Package '{Package}'."), Arguments)); ErrorNotification.bFireAndForget = true; ErrorNotification.ExpireDuration = 4.0f; ErrorNotification.bUseThrobber = true; FSlateNotificationManager::Get().AddNotification(ErrorNotification); // Automatic checkout failed - pop up the notification for manual checkout PackageToNotifyState.Add(Package, SourceControlState->CanCheckout() ? NS_PendingPrompt : NS_PendingWarning); bNeedToPromptForCheckout = true; } }
bool FGetRevisionLatentCommand::Update() { // @todo: for the moment, getting revisions etc. is synchronous. FSourceControlStatePtr SourceControlState = ISourceControlModule::Get().GetProvider().GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::Use); if(!SourceControlState.IsValid()) { UE_LOG(LogSourceControl, Error, TEXT("Failed to get a valid state for file: %s"), *Filename); } else { if(SourceControlState->GetHistorySize() == 0) { UE_LOG(LogSourceControl, Error, TEXT("Failed to get a valid history for file: %s"), *Filename); } else { TSharedPtr<ISourceControlRevision, ESPMode::ThreadSafe> HistoryItem = SourceControlState->GetHistoryItem(0); if(!HistoryItem.IsValid()) { UE_LOG(LogSourceControl, Error, TEXT("Failed to get a valid history item 0 for file: %s"), *Filename); } else { FString TempGetFilename; HistoryItem->Get(TempGetFilename); if(TempGetFilename.Len() == 0 || !FPaths::FileExists(TempGetFilename)) { UE_LOG(LogSourceControl, Error, TEXT("Could not get revision of file '%s'"), *Filename); } } } } return true; }
bool FCollection::CheckoutCollection(FText& OutError) { if ( !ensure(SourceFilename.Len()) ) { OutError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( !ISourceControlModule::Get().IsEnabled() ) { OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu."); return false; } if ( !SourceControlProvider.IsAvailable() ) { OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again."); return false; } const FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); bool bSuccessfullyCheckedOut = false; if (SourceControlState.IsValid() && SourceControlState->IsDeleted()) { // Revert our delete if ( !RevertCollection(OutError) ) { return false; } // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } // If not at the head revision, sync up if (SourceControlState.IsValid() && !SourceControlState->IsCurrent()) { if ( SourceControlProvider.Execute(ISourceControlOperation::Create<FSync>(), AbsoluteFilename) == ECommandResult::Failed ) { // Could not sync up with the head revision OutError = FText::Format(LOCTEXT("Error_SCCSync", "Failed to sync collection '{0}' to the head revision."), FText::FromName(CollectionName)); return false; } // Check to see if the file exists at the head revision if ( IFileManager::Get().FileExists(*SourceFilename) ) { // File found! Load it and merge with our local changes FText LoadErrorText; FCollection NewCollection(SourceFilename, false); if ( !NewCollection.Load(LoadErrorText) ) { // Failed to load the head revision file so it isn't safe to delete it OutError = FText::Format(LOCTEXT("Error_SCCBadHead", "Failed to load the collection '{0}' at the head revision. {1}"), FText::FromName(CollectionName), LoadErrorText); return false; } // Loaded the head revision, now merge up so the files are in a consistent state MergeWithCollection(NewCollection); } // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } if(SourceControlState.IsValid()) { if(!SourceControlState->IsSourceControlled()) { // Not yet in the depot. We'll add it when we call CheckinCollection bSuccessfullyCheckedOut = true; } else if(SourceControlState->IsAdded() || SourceControlState->IsCheckedOut()) { // Already checked out or opened for add bSuccessfullyCheckedOut = true; } else if(SourceControlState->CanCheckout()) { // In depot and needs to be checked out bSuccessfullyCheckedOut = (SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), AbsoluteFilename) == ECommandResult::Succeeded); if (!bSuccessfullyCheckedOut) { OutError = FText::Format(LOCTEXT("Error_SCCCheckout", "Failed to check out collection '{0}'"), FText::FromName(CollectionName)); } } else if(!SourceControlState->IsCurrent()) { OutError = FText::Format(LOCTEXT("Error_SCCNotCurrent", "Collection '{0}' is not at head revision after sync."), FText::FromName(CollectionName)); } else if(SourceControlState->IsCheckedOutOther()) { OutError = FText::Format(LOCTEXT("Error_SCCCheckedOutOther", "Collection '{0}' is checked out by another user."), FText::FromName(CollectionName)); } else { OutError = FText::Format(LOCTEXT("Error_SCCUnknown", "Could not determine source control state for collection '{0}'"), FText::FromName(CollectionName)); } } else { OutError = LOCTEXT("Error_SCCInvalid", "Source control state is invalid."); } return bSuccessfullyCheckedOut; }
/** * Helper method designed to perform the necessary preparations required to complete an automated editor build * * @param BuildSettings Build settings that will be used for the editor build * @param OutPkgsToSubmit Set of packages that need to be saved and submitted after a successful build * @param OutErrorMessages Errors that resulted from the preparation (may or may not force the build to stop, depending on build settings) * * @return true if the preparation was successful and the build should continue; false if the preparation failed and the build should be aborted */ bool FEditorBuildUtils::PrepForAutomatedBuild( const FEditorAutomatedBuildSettings& BuildSettings, TSet<UPackage*>& OutPkgsToSubmit, FText& OutErrorMessages ) { // Assume the preparation is successful to start bool bBuildSuccessful = true; OutPkgsToSubmit.Empty(); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // Source control is required for the automated build, so ensure that SCC support is compiled in and // that the server is enabled and available for use if ( !ISourceControlModule::Get().IsEnabled() || !SourceControlProvider.IsAvailable() ) { bBuildSuccessful = false; LogErrorMessage( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_SCCError", "Cannot connect to source control; automated build aborted."), OutErrorMessages ); } // Empty changelists aren't allowed; abort the build if one wasn't provided if ( bBuildSuccessful && BuildSettings.ChangeDescription.Len() == 0 ) { bBuildSuccessful = false; LogErrorMessage( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_NoCLDesc", "A changelist description must be provided; automated build aborted."), OutErrorMessages ); } TArray<UPackage*> PreviouslySavedWorldPackages; TArray<UPackage*> PackagesToCheckout; TArray<ULevel*> LevelsToSave; if ( bBuildSuccessful ) { TArray<UWorld*> AllWorlds; FString UnsavedWorlds; EditorLevelUtils::GetWorlds( GWorld, AllWorlds, true ); // Check all of the worlds that will be built to ensure they have been saved before and have a filename // associated with them. If they don't, they won't be able to be submitted to source control. FString CurWorldPkgFileName; for ( TArray<UWorld*>::TConstIterator WorldIter( AllWorlds ); WorldIter; ++WorldIter ) { const UWorld* CurWorld = *WorldIter; check( CurWorld ); UPackage* CurWorldPackage = CurWorld->GetOutermost(); check( CurWorldPackage ); if ( FPackageName::DoesPackageExist( CurWorldPackage->GetName(), NULL, &CurWorldPkgFileName ) ) { PreviouslySavedWorldPackages.AddUnique( CurWorldPackage ); // Add all packages which have a corresponding file to the set of packages to submit for now. As preparation continues // any packages that can't be submitted due to some error will be removed. OutPkgsToSubmit.Add( CurWorldPackage ); } else { UnsavedWorlds += FString::Printf( TEXT("%s\n"), *CurWorldPackage->GetName() ); } } // If any of the worlds haven't been saved before, process the build setting's behavior to see if the build // should proceed or not if ( UnsavedWorlds.Len() > 0 ) { bBuildSuccessful = ProcessAutomatedBuildBehavior( BuildSettings.NewMapBehavior, FText::Format( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_UnsavedMap", "The following levels have never been saved before and cannot be submitted:\n\n{0}\n\nAttempt to continue the build?"), FText::FromString(UnsavedWorlds) ), OutErrorMessages ); } } // Load the asset tools module FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools"); if ( bBuildSuccessful ) { // Update the source control status of any relevant world packages in order to determine which need to be // checked out, added to the depot, etc. SourceControlProvider.Execute( ISourceControlOperation::Create<FUpdateStatus>(), SourceControlHelpers::PackageFilenames(PreviouslySavedWorldPackages) ); FString PkgsThatCantBeCheckedOut; for ( TArray<UPackage*>::TConstIterator PkgIter( PreviouslySavedWorldPackages ); PkgIter; ++PkgIter ) { UPackage* CurPackage = *PkgIter; const FString CurPkgName = CurPackage->GetName(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(CurPackage, EStateCacheUsage::ForceUpdate); if( !SourceControlState.IsValid() || (!SourceControlState->IsSourceControlled() && !SourceControlState->IsUnknown() && !SourceControlState->IsIgnored())) { FString CurFilename; if ( FPackageName::DoesPackageExist( CurPkgName, NULL, &CurFilename ) ) { if ( IFileManager::Get().IsReadOnly( *CurFilename ) ) { PkgsThatCantBeCheckedOut += FString::Printf( TEXT("%s\n"), *CurPkgName ); OutPkgsToSubmit.Remove( CurPackage ); } } } else if(SourceControlState->CanCheckout()) { PackagesToCheckout.Add( CurPackage ); } else { PkgsThatCantBeCheckedOut += FString::Printf( TEXT("%s\n"), *CurPkgName ); OutPkgsToSubmit.Remove( CurPackage ); } } // If any of the packages can't be checked out or are read-only, process the build setting's behavior to see if the build // should proceed or not if ( PkgsThatCantBeCheckedOut.Len() > 0 ) { bBuildSuccessful = ProcessAutomatedBuildBehavior( BuildSettings.UnableToCheckoutFilesBehavior, FText::Format( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_UnsaveableFiles", "The following assets cannot be checked out of source control (or are read-only) and cannot be submitted:\n\n{0}\n\nAttempt to continue the build?"), FText::FromString(PkgsThatCantBeCheckedOut) ), OutErrorMessages ); } } if ( bBuildSuccessful ) { // Check out all of the packages from source control that need to be checked out if ( PackagesToCheckout.Num() > 0 ) { TArray<FString> PackageFilenames = SourceControlHelpers::PackageFilenames(PackagesToCheckout); SourceControlProvider.Execute( ISourceControlOperation::Create<FCheckOut>(), PackageFilenames ); // Update the package status of the packages that were just checked out to confirm that they // were actually checked out correctly SourceControlProvider.Execute( ISourceControlOperation::Create<FUpdateStatus>(), PackageFilenames ); FString FilesThatFailedCheckout; for ( TArray<UPackage*>::TConstIterator CheckedOutIter( PackagesToCheckout ); CheckedOutIter; ++CheckedOutIter ) { UPackage* CurPkg = *CheckedOutIter; FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(CurPkg, EStateCacheUsage::ForceUpdate); // If any of the packages failed to check out, remove them from the set of packages to submit if ( !SourceControlState.IsValid() || (!SourceControlState->IsCheckedOut() && !SourceControlState->IsAdded() && SourceControlState->IsSourceControlled()) ) { FilesThatFailedCheckout += FString::Printf( TEXT("%s\n"), *CurPkg->GetName() ); OutPkgsToSubmit.Remove( CurPkg ); } } // If any of the packages failed to check out correctly, process the build setting's behavior to see if the build // should proceed or not if ( FilesThatFailedCheckout.Len() > 0 ) { bBuildSuccessful = ProcessAutomatedBuildBehavior( BuildSettings.UnableToCheckoutFilesBehavior, FText::Format( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_FilesFailedCheckout", "The following assets failed to checkout of source control and cannot be submitted:\n{0}\n\nAttempt to continue the build?"), FText::FromString(FilesThatFailedCheckout)), OutErrorMessages ); } } } // Verify there are still actually any packages left to submit. If there aren't, abort the build and warn the user of the situation. if ( bBuildSuccessful ) { bBuildSuccessful = OutPkgsToSubmit.Num() > 0; if ( !bBuildSuccessful ) { LogErrorMessage( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_NoValidLevels", "None of the current levels are valid for submission; automated build aborted."), OutErrorMessages ); } } // If the build is safe to commence, force all of the levels visible to make sure the build operates correctly if ( bBuildSuccessful ) { bool bVisibilityToggled = false; if ( !FLevelUtils::IsLevelVisible( GWorld->PersistentLevel ) ) { EditorLevelUtils::SetLevelVisibility( GWorld->PersistentLevel, true, false ); bVisibilityToggled = true; } for ( TArray<ULevelStreaming*>::TConstIterator LevelIter( GWorld->StreamingLevels ); LevelIter; ++LevelIter ) { ULevelStreaming* CurStreamingLevel = *LevelIter; if ( CurStreamingLevel && !FLevelUtils::IsLevelVisible( CurStreamingLevel ) ) { CurStreamingLevel->bShouldBeVisibleInEditor = true; bVisibilityToggled = true; } } if ( bVisibilityToggled ) { GWorld->FlushLevelStreaming(); } } return bBuildSuccessful; }
/** * Helper method to submit packages to source control as part of the automated build process * * @param InPkgsToSubmit Set of packages which should be submitted to source control * @param BuildSettings Build settings used during the automated build */ void FEditorBuildUtils::SubmitPackagesForAutomatedBuild( const TSet<UPackage*>& InPkgsToSubmit, const FEditorAutomatedBuildSettings& BuildSettings ) { TArray<FString> LevelsToAdd; TArray<FString> LevelsToSubmit; ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // first update the status of the packages SourceControlProvider.Execute(ISourceControlOperation::Create<FUpdateStatus>(), SourceControlHelpers::PackageFilenames(InPkgsToSubmit.Array())); // Iterate over the set of packages to submit, determining if they need to be checked in or // added to the depot for the first time for ( TSet<UPackage*>::TConstIterator PkgIter( InPkgsToSubmit ); PkgIter; ++PkgIter ) { const UPackage* CurPkg = *PkgIter; const FString PkgName = CurPkg->GetName(); const FString PkgFileName = SourceControlHelpers::PackageFilename(CurPkg); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(CurPkg, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid()) { if ( SourceControlState->IsCheckedOut() || SourceControlState->IsAdded() ) { LevelsToSubmit.Add( PkgFileName ); } else if ( BuildSettings.bAutoAddNewFiles && !SourceControlState->IsSourceControlled() && !SourceControlState->IsIgnored() ) { LevelsToSubmit.Add( PkgFileName ); LevelsToAdd.Add( PkgFileName ); } } } // Then, if we've also opted to check in any packages, iterate over that list as well if(BuildSettings.bCheckInPackages) { TArray<FString> PackageNames = BuildSettings.PackagesToCheckIn; for ( TArray<FString>::TConstIterator PkgIterName(PackageNames); PkgIterName; PkgIterName++ ) { const FString& PkgName = *PkgIterName; const FString PkgFileName = SourceControlHelpers::PackageFilename(PkgName); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(PkgFileName, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid()) { if ( SourceControlState->IsCheckedOut() || SourceControlState->IsAdded() ) { LevelsToSubmit.Add( PkgFileName ); } else if ( !SourceControlState->IsSourceControlled() && !SourceControlState->IsIgnored() ) { // note we add the files we need to add to the submit list as well LevelsToSubmit.Add( PkgFileName ); LevelsToAdd.Add( PkgFileName ); } } } } // first add files that need to be added SourceControlProvider.Execute( ISourceControlOperation::Create<FMarkForAdd>(), LevelsToAdd, EConcurrency::Synchronous ); // Now check in all the changes, including the files we added above TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = StaticCastSharedRef<FCheckIn>(ISourceControlOperation::Create<FCheckIn>()); CheckInOperation->SetDescription(NSLOCTEXT("UnrealEd", "AutomatedBuild_AutomaticSubmission", "[Automatic Submission]")); SourceControlProvider.Execute( CheckInOperation, LevelsToSubmit, EConcurrency::Synchronous ); }
bool FFrontendFilter_CheckedOut::PassesFilter(FAssetFilterType InItem) const { FSourceControlStatePtr SourceControlState = ISourceControlModule::Get().GetProvider().GetState(SourceControlHelpers::PackageFilename(InItem.PackageName.ToString()), EStateCacheUsage::Use); return SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()); }
void FLevelCollectionModel::SCCDiffAgainstDepot(const FLevelModelList& InList, UEditorEngine* InEditor) { // Load the asset registry module FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools"); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // Iterate over each selected asset for (auto It = InList.CreateConstIterator(); It; ++It) { ULevel* Level = (*It)->GetLevelObject(); if (Level == NULL) { return; } UPackage* OriginalPackage = Level->GetOutermost(); FString PackageName = OriginalPackage->GetName(); // Make sure our history is up to date auto UpdateStatusOperation = ISourceControlOperation::Create<FUpdateStatus>(); UpdateStatusOperation->SetUpdateHistory(true); SourceControlProvider.Execute(UpdateStatusOperation, OriginalPackage); // Get the SCC state FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState( OriginalPackage, EStateCacheUsage::Use ); // If the level is in SCC. if (SourceControlState.IsValid() && SourceControlState->IsSourceControlled()) { // Get the file name of package FString RelativeFileName; if(FPackageName::DoesPackageExist(PackageName, NULL, &RelativeFileName)) { if (SourceControlState->GetHistorySize() > 0) { auto Revision = SourceControlState->GetHistoryItem(0); check(Revision.IsValid()); // Get the head revision of this package from source control FString AbsoluteFileName = FPaths::ConvertRelativePathToFull(RelativeFileName); FString TempFileName; if (Revision->Get(TempFileName)) { // Forcibly disable compile on load in case we are loading old blueprints that might try to update/compile TGuardValue<bool> DisableCompileOnLoad(GForceDisableBlueprintCompileOnLoad, true); // Try and load that package FText NotMapReason; UPackage* OldPackage = LoadPackage(NULL, *TempFileName, LOAD_None); if(OldPackage != NULL && InEditor->PackageIsAMapFile(*TempFileName, NotMapReason)) { /* Set the revision information*/ UPackage* Package = OriginalPackage; FRevisionInfo OldRevision; OldRevision.Changelist = Revision->GetCheckInIdentifier(); OldRevision.Date = Revision->GetDate(); OldRevision.Revision = Revision->GetRevision(); FRevisionInfo NewRevision; NewRevision.Revision = TEXT(""); // Dump assets to temp text files FString OldTextFilename = AssetToolsModule.Get().DumpAssetToTempFile(OldPackage); FString NewTextFilename = AssetToolsModule.Get().DumpAssetToTempFile(OriginalPackage); FString DiffCommand = GetDefault<UEditorLoadingSavingSettings>()->TextDiffToolPath.FilePath; AssetToolsModule.Get().CreateDiffProcess(DiffCommand, OldTextFilename, NewTextFilename); AssetToolsModule.Get().DiffAssets(OldPackage, OriginalPackage, OldRevision, NewRevision); } } } } } } }
bool FGatherTextSCC::CheckOutFile( const FString& InFile, FString& OutError ) { if ( InFile.IsEmpty() ) { OutError = TEXT("Could not checkout file."); return false; } FString SCCError; if( !IsReady( SCCError ) ) { OutError = SCCError; return false; } FString AbsoluteFilename = FPaths::ConvertRelativePathToFull( InFile ); if( CheckedOutFiles.Contains( AbsoluteFilename ) ) { return true; } bool bSuccessfullyCheckedOut = false; TArray<FString> FilesToBeCheckedOut; FilesToBeCheckedOut.Add( AbsoluteFilename ); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState( AbsoluteFilename, EStateCacheUsage::ForceUpdate ); if(SourceControlState.IsValid()) { FString Other; 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) { OutError = FString::Printf(TEXT("Failed to check out file '%s'."), *InFile); } } else if(!SourceControlState->IsSourceControlled()) { bSuccessfullyCheckedOut = (SourceControlProvider.Execute( ISourceControlOperation::Create<FMarkForAdd>(), FilesToBeCheckedOut ) == ECommandResult::Succeeded); if (!bSuccessfullyCheckedOut) { OutError = FString::Printf(TEXT("Failed to add file '%s' to source control."), *InFile); } } else if(!SourceControlState->IsCurrent()) { OutError = FString::Printf(TEXT("File '%s' is not at head revision."), *InFile); } else if(SourceControlState->IsCheckedOutOther(&(Other))) { OutError = FString::Printf(TEXT("File '%s' is checked out by another ('%s')."), *InFile, *Other); } else { // Improper or invalid SCC state OutError = FString::Printf(TEXT("Could not determine source control state of file '%s'."), *InFile); } } else { // Improper or invalid SCC state OutError = FString::Printf(TEXT("Could not determine source control state of file '%s'."), *InFile); } if( bSuccessfullyCheckedOut ) { CheckedOutFiles.AddUnique(AbsoluteFilename); } return bSuccessfullyCheckedOut; }
bool FCollection::DeleteFromSourceControl(FText& OutError) { ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( !ISourceControlModule::Get().IsEnabled() ) { OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu."); return false; } if ( !SourceControlProvider.IsAvailable() ) { OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again."); return false; } bool bDeletedSuccessfully = false; const int32 DeleteProgressDenominator = 2; int32 DeleteProgressNumerator = 0; const FText CollectionNameText = FText::FromName( CollectionName ); FFormatNamedArguments Args; Args.Add( TEXT("CollectionName"), CollectionNameText ); const FText StatusUpdate = FText::Format( LOCTEXT("DeletingCollection", "Deleting Collection {CollectionName}"), Args ); GWarn->BeginSlowTask( StatusUpdate, true ); GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); // If checked out locally for some reason, revert if (SourceControlState.IsValid() && (SourceControlState->IsAdded() || SourceControlState->IsCheckedOut() || SourceControlState->IsDeleted())) { if ( !RevertCollection(OutError) ) { // Failed to revert, just bail out GWarn->EndSlowTask(); return false; } // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } // If not at the head revision, sync up if (SourceControlState.IsValid() && !SourceControlState->IsCurrent()) { if ( SourceControlProvider.Execute(ISourceControlOperation::Create<FSync>(), AbsoluteFilename) == ECommandResult::Failed) { // Could not sync up with the head revision GWarn->EndSlowTask(); OutError = FText::Format(LOCTEXT("Error_SCCSync", "Failed to sync collection '{0}' to the head revision."), FText::FromName(CollectionName)); return false; } // Check to see if the file exists at the head revision if ( !IFileManager::Get().FileExists(*SourceFilename) ) { // File was already deleted, consider this a success GWarn->EndSlowTask(); return true; } FCollection NewCollection(SourceFilename, false); FText LoadErrorText; if ( !NewCollection.Load(LoadErrorText) ) { // Failed to load the head revision file so it isn't safe to delete it GWarn->EndSlowTask(); OutError = FText::Format(LOCTEXT("Error_SCCBadHead", "Failed to load the collection '{0}' at the head revision. {1}"), FText::FromName(CollectionName), LoadErrorText); return false; } // Loaded the head revision, now merge up so the files are in a consistent state MergeWithCollection(NewCollection); // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); if(SourceControlState.IsValid()) { if(SourceControlState->IsAdded() || SourceControlState->IsCheckedOut()) { OutError = FText::Format(LOCTEXT("Error_SCCDeleteWhileCheckedOut", "Failed to delete collection '{0}' in source control because it is checked out or open for add."), FText::FromName(CollectionName)); } else if(SourceControlState->CanCheckout()) { if ( SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), AbsoluteFilename) == ECommandResult::Succeeded ) { // Now check in the delete const FText ChangelistDesc = FText::Format( LOCTEXT("CollectionDeletedDesc", "Deleted collection: {CollectionName}"), CollectionNameText ); TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>(); CheckInOperation->SetDescription(ChangelistDesc); if ( SourceControlProvider.Execute( CheckInOperation, AbsoluteFilename ) ) { // Deleted successfully! bDeletedSuccessfully = true; } else { FText Unused; if ( !RevertCollection(Unused) ) { UE_LOG(LogCollectionManager, Warning, TEXT("Failed to revert collection '%s' after failing to check in the file that was marked for delete."), *CollectionName.ToString()); } OutError = FText::Format(LOCTEXT("Error_SCCCheckIn", "Failed to check in collection '{0}'."), FText::FromName(CollectionName)); } } else { OutError = FText::Format(LOCTEXT("Error_SCCDeleteFailed", "Failed to delete collection '{0}' in source control."), FText::FromName(CollectionName)); } } else if(!SourceControlState->IsSourceControlled()) { // Not yet in the depot or deleted. We can just delete it from disk. bDeletedSuccessfully = IFileManager::Get().Delete(*AbsoluteFilename); if ( !bDeletedSuccessfully ) { OutError = FText::Format(LOCTEXT("Error_DiskDeleteFailed", "Failed to delete the collection file: {0}"), FText::FromString(AbsoluteFilename)); } } else if (!SourceControlState->IsCurrent()) { OutError = FText::Format(LOCTEXT("Error_SCCNotCurrent", "Collection '{0}' is not at head revision after sync."), FText::FromName(CollectionName)); } else if(SourceControlState->IsCheckedOutOther()) { OutError = FText::Format(LOCTEXT("Error_SCCCheckedOutOther", "Collection '{0}' is checked out by another user."), FText::FromName(CollectionName)); } else { OutError = FText::Format(LOCTEXT("Error_SCCUnknown", "Could not determine source control state for collection '{0}'"), FText::FromName(CollectionName)); } } else { OutError = LOCTEXT("Error_SCCInvalid", "Source control state is invalid."); } GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); GWarn->EndSlowTask(); return bDeletedSuccessfully; }
bool UThumbnailManager::CaptureProjectThumbnail(FViewport* Viewport, const FString& OutputFilename, bool bUseSCCIfPossible) { const uint32 AutoScreenshotSize = 192; //capture the thumbnail uint32 SrcWidth = Viewport->GetSizeXY().X; uint32 SrcHeight = Viewport->GetSizeXY().Y; // Read the contents of the viewport into an array. TArray<FColor> OrigBitmap; if (Viewport->ReadPixels(OrigBitmap)) { check(OrigBitmap.Num() == SrcWidth * SrcHeight); //pin to smallest value int32 CropSize = FMath::Min<uint32>(SrcWidth, SrcHeight); //pin to max size int32 ScaledSize = FMath::Min<uint32>(AutoScreenshotSize, CropSize); //calculations for cropping TArray<FColor> CroppedBitmap; CroppedBitmap.AddUninitialized(CropSize*CropSize); //Crop the image int32 CroppedSrcTop = (SrcHeight - CropSize) / 2; int32 CroppedSrcLeft = (SrcWidth - CropSize) / 2; for (int32 Row = 0; Row < CropSize; ++Row) { //Row*Side of a row*byte per color int32 SrcPixelIndex = (CroppedSrcTop+Row) * SrcWidth + CroppedSrcLeft; const void* SrcPtr = &(OrigBitmap[SrcPixelIndex]); void* DstPtr = &(CroppedBitmap[Row * CropSize]); FMemory::Memcpy(DstPtr, SrcPtr, CropSize * 4); } //Scale image down if needed TArray<FColor> ScaledBitmap; if (ScaledSize < CropSize) { FImageUtils::ImageResize( CropSize, CropSize, CroppedBitmap, ScaledSize, ScaledSize, ScaledBitmap, true ); } else { //just copy the data over. sizes are the same ScaledBitmap = CroppedBitmap; } // Compress the scaled image TArray<uint8> ScaledPng; FImageUtils::CompressImageArray(ScaledSize, ScaledSize, ScaledBitmap, ScaledPng); // Save to file const FString ScreenShotPath = FPaths::GetPath(OutputFilename); if ( IFileManager::Get().MakeDirectory(*ScreenShotPath, true) ) { // If source control is available, try to check out the file if necessary. // If not, silently continue. This is just a courtesy. bool bMarkFileForAdd = false; FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(OutputFilename); TArray<FString> FilesToBeCheckedOut; FilesToBeCheckedOut.Add(AbsoluteFilename); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( bUseSCCIfPossible && ISourceControlModule::Get().IsEnabled() && SourceControlProvider.IsAvailable() ) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid()) { if ( SourceControlState->CanCheckout() ) { SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), FilesToBeCheckedOut); } else if ( !SourceControlState->IsSourceControlled() ) { bMarkFileForAdd = true; } } } if ( FFileHelper::SaveArrayToFile( ScaledPng, *OutputFilename ) ) { if ( bMarkFileForAdd ) { SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), FilesToBeCheckedOut); } return true; } } } return false; }
int32 UFixupRedirectsCommandlet::Main( const FString& Params ) { // Retrieve list of all packages in .ini paths. TArray<FString> PackageList; FEditorFileUtils::FindAllPackageFiles(PackageList); if( !PackageList.Num() ) { return 0; } // process the commandline FString Token; const TCHAR* CommandLine = *Params; bool bIsQuietMode = false; bool bIsTestOnly = false; bool bShouldRestoreProgress= false; bool bIsSCCDisabled = false; bool bUpdateEnginePackages = false; bool bDeleteRedirects = true; bool bDeleteOnlyReferencedRedirects = false; bool bAutoSubmit = false; bool bSandboxed = IFileManager::Get().IsSandboxEnabled(); while (FParse::Token(CommandLine, Token, 1)) { if (Token == TEXT("-nowarn")) { bIsQuietMode = true; } else if (Token == TEXT("-testonly")) { bIsTestOnly = true; } else if (Token == TEXT("-restore")) { bShouldRestoreProgress = true; } else if (Token == TEXT("-NoAutoCheckout")) { bIsSCCDisabled = true; } else if (Token == TEXT("-AutoSubmit")) { bAutoSubmit = true; } else if (Token == TEXT("-UpdateEnginePackages")) { bUpdateEnginePackages = true; } else if (Token == TEXT("-NoDelete")) { bDeleteRedirects = false; } else if (Token == TEXT("-NoDeleteUnreferenced")) { bDeleteOnlyReferencedRedirects = true; } else if ( Token.Left(1) != TEXT("-") ) { FPackageName::SearchForPackageOnDisk(Token, &GRedirectCollector.FileToFixup); } } if (bSandboxed) { UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Running in a sandbox.")); bIsSCCDisabled = true; bAutoSubmit = false; bDeleteRedirects = false; } bool bShouldSkipErrors = false; // Setup file Output log in the Saved Directory const FString ProjectDir = FPaths::GetPath(FPaths::GetProjectFilePath()); const FString LogFilename = FPaths::Combine(*ProjectDir, TEXT("Saved"), TEXT("FixupRedirect"), TEXT("FixupRedirect.log")); FArchive* LogOutputFile = IFileManager::Get().CreateFileWriter(*LogFilename); LogOutputFile->Logf(TEXT("[Redirects]")); ///////////////////////////////////////////////////////////////////// // Display any script code referenced redirects ///////////////////////////////////////////////////////////////////// TArray<FString> UpdatePackages; TArray<FString> RedirectorsThatCantBeCleaned; if (!bShouldRestoreProgress) { // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++) { FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex]; UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Boot redirector can't be cleaned: %s(%s) -> %s(%s)"), *Redir.RedirectorName, *Redir.RedirectorPackageFilename, *Redir.DestinationObjectName, *Redir.PackageFilename); RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName); } ///////////////////////////////////////////////////////////////////// // Build a list of packages that need to be updated ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Generating list of tasks:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("-------------------------")); CLEAR_WARN_COLOR(); int32 GCIndex = 0; // process script code first pass, then everything else for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ ) { const FString& Filename = PackageList[PackageIndex]; // already loaded code, skip them, and skip autosave packages if (Filename.StartsWith(AutoSaveUtils::GetAutoSaveDir(), ESearchCase::IgnoreCase)) { continue; } UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Looking for redirects in %s..."), *Filename); // Assert if package couldn't be opened so we have no chance of messing up saving later packages. UPackage* Package = Cast<UPackage>(LoadPackage(NULL, *Filename, 0)); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); if (!Package) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... failed to open %s"), *Filename); CLEAR_WARN_COLOR(); // if we are not in quiet mode, and the user wants to continue, go on const FText Message = FText::Format( NSLOCTEXT("Unreal", "PackageFailedToOpen", "The package '{0}' failed to open. Should this commandlet continue operation, ignoring further load errors? Continuing may have adverse effects (object references may be lost)."), FText::FromString( Filename ) ); if (bShouldSkipErrors || (!bIsQuietMode && GWarn->YesNof( Message ))) { bShouldSkipErrors = true; continue; } else { return 1; } } // all notices here about redirects get this color SET_WARN_COLOR(COLOR_DARK_GREEN); // look for any redirects that were followed that are referenced by this package // (may have been loaded earlier if this package was loaded by script code) // any redirectors referenced by script code can't be cleaned up for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++) { FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex]; if (Redir.PackageFilename == Filename) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... references %s [-> %s]"), *Redir.RedirectorName, *Redir.DestinationObjectName); // put the source and dest packages in the list to be updated UpdatePackages.AddUnique(Redir.PackageFilename); UpdatePackages.AddUnique(Redir.RedirectorPackageFilename); } } // clear the flag for needing to collect garbage bool bNeedToCollectGarbage = false; // if this package has any redirectors, make sure we update it if (!GRedirectCollector.FileToFixup.Len() || GRedirectCollector.FileToFixup == FPackageName::FilenameToLongPackageName(Filename)) { TArray<UObject *> ObjectsInOuter; GetObjectsWithOuter(Package, ObjectsInOuter); for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ ) { UObject* Obj = ObjectsInOuter[Index]; UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj); if (Redir) { // make sure this package is in the list of packages to update UpdatePackages.AddUnique(Filename); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... has redirect %s [-> %s]"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName()); LogOutputFile->Logf(TEXT("%s = %s"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName()); // make sure we GC if we found a redirector bNeedToCollectGarbage = true; } } } CLEAR_WARN_COLOR(); // collect garbage every N packages, or if there was any redirectors in the package // (to make sure that redirectors get reloaded properly and followed by the callback) // also, GC after loading a map package, to make sure it's unloaded so that we can track // other packages loading a map package as a dependency if (((++GCIndex) % 50) == 0 || bNeedToCollectGarbage || Package->ContainsMap()) { // collect garbage to close the package CollectGarbage(RF_Native); UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC...")); // reset our counter GCIndex = 0; } } // save off restore point FArchive* Ar = IFileManager::Get().CreateFileWriter(*(FPaths::GameSavedDir() + TEXT("Fixup.bin"))); *Ar << GRedirectCollector.Redirections; *Ar << UpdatePackages; *Ar << RedirectorsThatCantBeCleaned; delete Ar; } else { // load restore point FArchive* Ar = IFileManager::Get().CreateFileReader(*(FPaths::GameSavedDir() + TEXT("Fixup.bin"))); if( Ar != NULL ) { *Ar << GRedirectCollector.Redirections; *Ar << UpdatePackages; *Ar << RedirectorsThatCantBeCleaned; delete Ar; } } // unregister the callback so we stop getting redirections added FCoreUObjectDelegates::RedirectorFollowed.RemoveAll(&GRedirectCollector); ///////////////////////////////////////////////////////////////////// // Explain to user what is happening ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_YELLOW); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Files that need to fixed up:")); SET_WARN_COLOR(COLOR_DARK_YELLOW); // print out the working set of packages for (int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" %s"), *UpdatePackages[PackageIndex]); } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); CLEAR_WARN_COLOR(); // if we are only testing, just just quiet before actually doing anything if (bIsTestOnly) { LogOutputFile->Close(); delete LogOutputFile; LogOutputFile = NULL; return 0; } ///////////////////////////////////////////////////////////////////// // Find redirectors that are referenced by packages we cant check out ///////////////////////////////////////////////////////////////////// bool bEngineRedirectorCantBeCleaned = false; ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // initialize source control if it hasn't been initialized yet if(!bIsSCCDisabled) { SourceControlProvider.Init(); //Make sure we can check out all packages - update their state first SourceControlProvider.Execute(ISourceControlOperation::Create<FUpdateStatus>(), UpdatePackages); } for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ ) { const FString& Filename = UpdatePackages[PackageIndex]; bool bCanEdit = true; if(!bIsSCCDisabled) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && !SourceControlState->CanEdit()) { bCanEdit = false; } } else if(IFileManager::Get().IsReadOnly(*Filename)) { bCanEdit = false; } if(!bCanEdit) { const bool bAllowCheckout = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ); if (!bIsSCCDisabled && bAllowCheckout) { FString PackageName(FPackageName::FilenameToLongPackageName(Filename)); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate); if (SourceControlState.IsValid() && SourceControlState->CanCheckout()) { SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), SourceControlHelpers::PackageFilename(PackageName)); FSourceControlStatePtr NewSourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate); bCanEdit = NewSourceControlState.IsValid() && NewSourceControlState->CanEdit(); } } // if the checkout failed for any reason, we can't clean it up if (bAllowCheckout && !bCanEdit) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't check out/edit %s..."), *Filename); CLEAR_WARN_COLOR(); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); // any redirectors that are pointed to by this read-only package can't be fixed up for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++) { FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex]; // any redirectors pointed to by this file we don't clean if (Redir.PackageFilename == Filename) { RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... can't fixup references to %s"), *Redir.RedirectorName); // if this redirector is in an Engine package, we need to alert the user, because // Engine packages are shared between games, so if one game still needs the redirector // to be valid, then we can't check in Engine packages after another game has // run that could if (!bEngineRedirectorCantBeCleaned) { FString PackagePath; FPackageName::DoesPackageExist(Redir.RedirectorPackageFilename, NULL, &PackagePath); if (PackagePath.StartsWith(FPaths::EngineDir())) { bEngineRedirectorCantBeCleaned = true; } } } // any redirectors in this file can't be deleted else if (Redir.RedirectorPackageFilename == Filename) { RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... can't delete %s"), *Redir.RedirectorName); } } } else { SET_WARN_COLOR(COLOR_DARK_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Checked out %s..."), *Filename); CLEAR_WARN_COLOR(); } } } // warn about an engine package that can't be cleaned up (may want to revert engine packages) if (bEngineRedirectorCantBeCleaned) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Engine package redirectors are referenced by packages that can't be cleaned. If you have multiple games, it's recommended you revert any checked out Engine packages.")); bUpdateEnginePackages=false; } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); ///////////////////////////////////////////////////////////////////// // Load and save packages to save out the proper fixed up references ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Fixing references to point to proper objects:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------------")); CLEAR_WARN_COLOR(); // keep a list of all packages that have ObjectRedirects in them. This is not needed if we aren't deleting redirects TArray<bool> PackagesWithRedirects; // Delete these packages entirely because they are empty TArray<bool> EmptyPackagesToDelete; if ( bDeleteRedirects ) { PackagesWithRedirects.AddZeroed(UpdatePackages.Num()); EmptyPackagesToDelete.AddZeroed(PackageList.Num()); } bool bSCCEnabled = ISourceControlModule::Get().IsEnabled(); // Iterate over all packages, loading and saving to remove all references to ObjectRedirectors (can't delete the Redirects yet) for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ ) { const FString& Filename = UpdatePackages[PackageIndex]; // we can't do anything with packages that are read-only or cant be edited due to source control (we don't want to fixup the redirects) if(bSCCEnabled) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && !SourceControlState->CanEdit()) { continue; } } else if(IFileManager::Get().IsReadOnly(*Filename)) { continue; } // Assert if package couldn't be opened so we have no chance of messing up saving later packages. UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Cleaning %s"), *Filename); UPackage* Package = LoadPackage( NULL, *Filename, LOAD_NoVerify ); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); // if it failed to open, we have already dealt with quitting if we are going to, so just skip it if (!Package) { continue; } if ( bDeleteOnlyReferencedRedirects && bDeleteRedirects ) { TArray<UObject *> ObjectsInOuter; GetObjectsWithOuter(Package, ObjectsInOuter); for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ ) { UObject* Obj = ObjectsInOuter[Index]; UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj); if (Redir) { PackagesWithRedirects[PackageIndex] = 1; break; } } } // Don't save packages in the engine directory if we did not opt to update engine packages if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) ) { // save out the package UWorld* World = UWorld::FindWorldInPackage(Package); GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn ); } // collect garbage to close the package UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Collecting Garbage...")); CollectGarbage(RF_Native); } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); if ( bDeleteRedirects ) { ///////////////////////////////////////////////////////////////////// // Delete all redirects that are no longer referenced ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting redirector objects:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------")); CLEAR_WARN_COLOR(); const TArray<FString>& PackagesToCleaseOfRedirects = bDeleteOnlyReferencedRedirects ? UpdatePackages : PackageList; int32 GCIndex = 0; // Again, iterate over all packages, loading and saving to and this time deleting all for( int32 PackageIndex = 0; PackageIndex < PackagesToCleaseOfRedirects.Num(); PackageIndex++ ) { const FString& Filename = PackagesToCleaseOfRedirects[PackageIndex]; if (bDeleteOnlyReferencedRedirects && PackagesWithRedirects[PackageIndex] == 0) { continue; } // The file should have already been checked out/be writable // If it is read only that means the checkout failed or we are not using source control and we can not write this file. if(bSCCEnabled) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && !SourceControlState->CanEdit()) { UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping file: source control says we cant edit the file %s..."), *Filename); continue; } } else if( IFileManager::Get().IsReadOnly( *Filename )) { UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping read-only file %s..."), *Filename); continue; } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Wiping %s..."), *Filename); UPackage* Package = LoadPackage( NULL, *Filename, 0 ); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); // if it failed to open, we have already dealt with quitting if we are going to, so just skip it if (!Package) { continue; } // assume that all redirs in this file are still-referenced bool bIsDirty = false; // see if this package is completely empty other than purged redirectors and metadata bool bIsEmpty = true; // delete all ObjectRedirects now TArray<UObjectRedirector*> Redirs; // find all redirectors, put into an array so delete doesn't mess up the iterator TArray<UObject *> ObjectsInOuter; GetObjectsWithOuter(Package, ObjectsInOuter); for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ ) { UObject* Obj = ObjectsInOuter[Index]; UObjectRedirector *Redir = Cast<UObjectRedirector>(Obj); if (Redir) { int32 Dummy; // if the redirector was marked as uncleanable, skip it if (RedirectorsThatCantBeCleaned.Find(Redir->GetFullName(), Dummy)) { SET_WARN_COLOR(COLOR_DARK_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... skipping still-referenced %s"), *Redir->GetFullName()); CLEAR_WARN_COLOR(); bIsEmpty = false; continue; } bIsDirty = true; SET_WARN_COLOR(COLOR_DARK_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... deleting %s"), *Redir->GetFullName()); CLEAR_WARN_COLOR(); Redirs.Add(Redir); } else if ( (!Obj->IsA(UMetaData::StaticClass()) && !Obj->IsA(UField::StaticClass())) || Obj->IsA(UUserDefinedEnum::StaticClass())) // UserDefinedEnum should not be deleted { // This package has a real object bIsEmpty = false; } } if (bIsEmpty && !bDeleteOnlyReferencedRedirects) { EmptyPackagesToDelete[PackageIndex] = 1; } // mark them for deletion. for (int32 RedirIndex = 0; RedirIndex < Redirs.Num(); RedirIndex++) { UObjectRedirector* Redirector = Redirs[RedirIndex]; // Remove standalone flag and mark as pending kill. Redirector->ClearFlags( RF_Standalone | RF_Public ); // We don't need redirectors in the root set, the references should already be fixed up if ( Redirector->IsRooted() ) { Redirector->RemoveFromRoot(); } Redirector->MarkPendingKill(); } Redirs.Empty(); if (bIsDirty) { // Don't save packages in the engine directory if we did not opt to update engine packages if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) ) { // save the package UWorld* World = UWorld::FindWorldInPackage(Package); GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn ); } } // collect garbage every N packages, or if there was any redirectors in the package if (((++GCIndex) % 50) == 0 || bIsDirty || Package->ContainsMap()) { // collect garbage to close the package CollectGarbage(RF_Native); UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC...")); // reset our counter GCIndex = 0; } } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); } else { SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Not deleting any redirector objects:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); CLEAR_WARN_COLOR(); } ///////////////////////////////////////////////////////////////////// // Clean up any unused trash ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting empty and unused packages:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------")); CLEAR_WARN_COLOR(); LogOutputFile->Logf(TEXT("")); LogOutputFile->Logf(TEXT("[Deleted]")); // Iterate over packages, attempting to delete unreferenced packages for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ ) { bool bDelete = false; const FString& Filename = PackageList[PackageIndex]; FString PackageName(FPackageName::FilenameToLongPackageName(Filename)); const bool bAllowDelete = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ); if (bDeleteRedirects && !bDeleteOnlyReferencedRedirects && EmptyPackagesToDelete[PackageIndex] && bAllowDelete) { // this package is completely empty, delete it bDelete = true; } if (bDelete == true) { struct Local { static bool DeleteFromDisk(const FString& InFilename, const FString& InMessage) { SET_WARN_COLOR(COLOR_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("%s"), *InMessage); if (!IFileManager::Get().Delete(*InFilename, false, true)) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... failed to delete from disk."), *InFilename); CLEAR_WARN_COLOR(); return false; } CLEAR_WARN_COLOR(); return true; } }; if (!bIsSCCDisabled) { // get file SCC status FString FileName = SourceControlHelpers::PackageFilename(PackageName); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(FileName, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) ) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Revert '%s' from source control..."), *Filename); SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), FileName); SET_WARN_COLOR(COLOR_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename); CLEAR_WARN_COLOR(); SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName); } else if(SourceControlState.IsValid() && SourceControlState->CanCheckout()) { SET_WARN_COLOR(COLOR_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename); CLEAR_WARN_COLOR(); SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName); } else if(SourceControlState.IsValid() && SourceControlState->IsCheckedOutOther()) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't delete '%s' from source control, someone has it checked out, skipping..."), *Filename); CLEAR_WARN_COLOR(); } else if(SourceControlState.IsValid() && !SourceControlState->IsSourceControlled()) { if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is not in source control, attempting to delete from disk..."), *Filename))) { LogOutputFile->Logf(*Filename); } } else { if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is in an unknown source control state, attempting to delete from disk..."), *Filename))) { LogOutputFile->Logf(*Filename); } } } else { if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("source control disabled while deleting '%s', attempting to delete from disk..."), *Filename))) { LogOutputFile->Logf(*Filename); } } } } LogOutputFile->Close(); delete LogOutputFile; LogOutputFile = NULL; if (!bIsSCCDisabled && bAutoSubmit) { ///////////////////////////////////////////////////////////////////// // Submit the results to source control ///////////////////////////////////////////////////////////////////// UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Submiting the results to source control")); // Work out the list of file to check in TArray <FString> FilesToSubmit; for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ ) { const FString& Filename = PackageList[PackageIndex]; FString PackageName(FPackageName::FilenameToLongPackageName(Filename)); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate); if( SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded() || SourceControlState->IsDeleted()) ) { // Only submit engine packages if we're requested to if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) ) { FilesToSubmit.Add(*PackageName); } } } // Check in all changed files const FText Description = NSLOCTEXT("FixupRedirectsCmdlet", "ChangelistDescription", "Fixed up Redirects"); TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>(); CheckInOperation->SetDescription( Description ); SourceControlProvider.Execute(CheckInOperation, SourceControlHelpers::PackageFilenames(FilesToSubmit)); // toss the SCC manager ISourceControlModule::Get().GetProvider().Close(); } return 0; }
bool FCollection::CheckinCollection(FText& OutError) { if ( !ensure(SourceFilename.Len()) ) { OutError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( !ISourceControlModule::Get().IsEnabled() ) { OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu."); return false; } if ( !SourceControlProvider.IsAvailable() ) { OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again."); return false; } const FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); if (SourceControlState.IsValid() && !SourceControlState->IsSourceControlled()) { // Not yet in the depot. Add it. const bool bWasAdded = (SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), AbsoluteFilename) == ECommandResult::Succeeded); if (!bWasAdded) { OutError = FText::Format(LOCTEXT("Error_SCCAdd", "Failed to add collection '{0}' to source control."), FText::FromName(CollectionName)); return false; } SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } if ( SourceControlState.IsValid() && !(SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) ) { OutError = FText::Format(LOCTEXT("Error_SCCNotCheckedOut", "Collection '{0}' not checked out or open for add."), FText::FromName(CollectionName)); return false; } // Form an appropriate summary for the changelist const FText CollectionNameText = FText::FromName( CollectionName ); FTextBuilder ChangelistDescBuilder; if (SourceControlState.IsValid() && SourceControlState->IsAdded()) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionAddedNewDesc", "Added collection '{0}'"), CollectionNameText); } else { if (IsDynamic()) { // @todo collection Change description for dynamic collections } else { // Gather differences from disk TArray<FName> ObjectsAdded; TArray<FName> ObjectsRemoved; GetObjectDifferencesFromDisk(ObjectsAdded, ObjectsRemoved); ObjectsAdded.Sort(); ObjectsRemoved.Sort(); // Report added files FFormatNamedArguments Args; Args.Add(TEXT("FirstObjectAdded"), ObjectsAdded.Num() > 0 ? FText::FromName(ObjectsAdded[0]) : NSLOCTEXT("Core", "None", "None")); Args.Add(TEXT("NumberAdded"), FText::AsNumber(ObjectsAdded.Num())); Args.Add(TEXT("FirstObjectRemoved"), ObjectsRemoved.Num() > 0 ? FText::FromName(ObjectsRemoved[0]) : NSLOCTEXT("Core", "None", "None")); Args.Add(TEXT("NumberRemoved"), FText::AsNumber(ObjectsRemoved.Num())); Args.Add(TEXT("CollectionName"), CollectionNameText); if (ObjectsAdded.Num() == 1) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionAddedSingleDesc", "Added '{FirstObjectAdded}' to collection '{CollectionName}'"), Args); } else if (ObjectsAdded.Num() > 1) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionAddedMultipleDesc", "Added {NumberAdded} objects to collection '{CollectionName}':"), Args); ChangelistDescBuilder.Indent(); for (const FName& AddedObjectName : ObjectsAdded) { ChangelistDescBuilder.AppendLine(FText::FromName(AddedObjectName)); } ChangelistDescBuilder.Unindent(); } if ( ObjectsRemoved.Num() == 1 ) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionRemovedSingleDesc", "Removed '{FirstObjectRemoved}' from collection '{CollectionName}'"), Args); } else if (ObjectsRemoved.Num() > 1) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionRemovedMultipleDesc", "Removed {NumberRemoved} objects from collection '{CollectionName}'"), Args); ChangelistDescBuilder.Indent(); for (const FName& RemovedObjectName : ObjectsRemoved) { ChangelistDescBuilder.AppendLine(FText::FromName(RemovedObjectName)); } ChangelistDescBuilder.Unindent(); } } // Parent change? if (DiskSnapshot.ParentCollectionGuid != ParentCollectionGuid) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionChangedParentDesc", "Changed the parent of collection '{0}'"), CollectionNameText); } // Version bump? if (FileVersion < ECollectionVersion::CurrentVersion) { ChangelistDescBuilder.AppendLineFormat(LOCTEXT("CollectionUpgradedDesc", "Upgraded collection '{0}' (was version {1}, now version {2})"), CollectionNameText, FText::AsNumber(FileVersion), FText::AsNumber(ECollectionVersion::CurrentVersion)); } } FText ChangelistDesc = ChangelistDescBuilder.ToText(); if (ChangelistDesc.IsEmpty()) { // No changes could be detected ChangelistDesc = FText::Format(LOCTEXT("CollectionNotModifiedDesc", "Collection '{0}' not modified"), CollectionNameText); } // Finally check in the file TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>(); CheckInOperation->SetDescription( ChangelistDesc ); if ( SourceControlProvider.Execute( CheckInOperation, AbsoluteFilename ) ) { return true; } else { OutError = FText::Format(LOCTEXT("Error_SCCCheckIn", "Failed to check in collection '{0}'."), FText::FromName(CollectionName)); return false; } }