Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d033f1d
Add a button in the right click context menu on assets which allows y…
Darcy3000s Dec 18, 2024
c72f689
Diff files before attempting to sync them from another branch so we o…
Darcy3000s Jan 3, 2025
0753e76
Change icon for Status Revert to use Revert icon
Darcy3000s Jan 3, 2025
bb3f434
First pass at fixing Revert To Status Branch
Damon3000s Apr 8, 2025
6968933
Improve naming and how we substring
Damon3000s Apr 8, 2025
bd038d2
Simplify path conversions
Damon3000s Apr 9, 2025
97e8ab1
Use ApplyOperationAndReloadPackages
Damon3000s Apr 9, 2025
43d06c5
Add missing include
Damon3000s Apr 10, 2025
607f5d8
Don't display Revert To Status Branch for maps
Damon3000s Apr 10, 2025
cf211f6
Don't display the Revert To Status Branch option for Maps
Damon3000s Apr 10, 2025
e8e8981
Explicitly capture variables in the Lambda
Damon3000s Apr 11, 2025
5ca30f0
Update comment that tries to disambiguate Unreal checkout and git che…
Damon3000s Apr 11, 2025
76b2948
Only commit if the revert was a success
Damon3000s Apr 11, 2025
e07ee7d
Only unlock locked files
Damon3000s Apr 11, 2025
ac43f7f
Merge remote-tracking branch 'origin/dev' into revert_to_status_branc…
Damon3000s Aug 12, 2025
0982186
Make a bool constexpr
Damon3000s Aug 12, 2025
13c0e5b
First pass of reverting files if 'sync to status branch' failed
Damon3000s Aug 12, 2025
8b374cb
Merge remote-tracking branch 'origin/revert_to_status_branch_clean_2'…
Damon3000s Aug 12, 2025
1594490
Optimise reverting files if they fail to sync to status branch
Damon3000s Aug 13, 2025
99254ff
Pass parameter by const ref
Damon3000s Aug 13, 2025
5ad5ce1
Replace checkin prompt with success message box
Damon3000s Aug 13, 2025
3a1b1b2
Update other messages
Damon3000s Aug 13, 2025
e6b5f7d
Fix incorrectly named variable
Damon3000s Aug 13, 2025
c99fb1e
Replace manual for loop converting paths to use existing function
Damon3000s Aug 13, 2025
6a8f634
Split the responsibility of FilesToSync for easier parsing
Damon3000s Aug 13, 2025
92464b8
Whitespace tweaks
Damon3000s Aug 13, 2025
6298777
Make failed dialog an error dialog
Damon3000s Aug 13, 2025
888b26b
Fix usage of auto
Damon3000s Aug 13, 2025
fd84a47
Remove the revert step if git checkout fails
Damon3000s Aug 13, 2025
7f1630f
Fix tabbing
Damon3000s Aug 13, 2025
5feae96
Always add the StatusRevert button, but disable if there is a map sel…
Damon3000s Aug 13, 2025
79e5d0d
Add basic cconfirmation before executing revert to status branch
Damon3000s Aug 14, 2025
57b379e
Update variable naming for consistency
Damon3000s Aug 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Source/GitSourceControl/GitSourceControl.Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public GitSourceControl(ReadOnlyTargetRules Target) : base(Target)
"UnrealEd",
"SourceControl",
"SourceControlWindows",
"Projects"
"Projects",
"Engine",
}
);

Expand Down
37 changes: 37 additions & 0 deletions Source/GitSourceControl/Private/GitSourceControlModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "GitSourceControlUtils.h"
#include "ISourceControlModule.h"
#include "SourceControlHelpers.h"
#include "Engine/World.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxExtender.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
Expand Down Expand Up @@ -199,6 +200,42 @@ void FGitSourceControlModule::CreateGitContentBrowserAssetMenu(FMenuBuilder& Men
#endif
FUIAction(FExecuteAction::CreateRaw( this, &FGitSourceControlModule::DiffAssetAgainstGitOriginBranch, SelectedAssets, BranchName ))
);

const FString WorldAssetName = UWorld::StaticClass()->GetName();
const bool bSelectedAssetsContainsWorld = SelectedAssets.ContainsByPredicate([&WorldAssetName](const FAssetData& AssetData)
{
return AssetData.AssetClassPath.GetAssetName() == WorldAssetName;
});
const bool bCanRevertToStatusBranch = !bSelectedAssetsContainsWorld;

MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("StatusRevert", "Revert to status branch: {0}"), FText::FromString(BranchName)),
FText::Format(LOCTEXT("StatusRevertDesc", "Revert this asset back to its state in status branch: {0}"), FText::FromString(BranchName)),
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
FSlateIcon(FAppStyle::GetAppStyleSetName(), "SourceControl.Actions.Revert"),
#else
FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Revert"),
#endif
FUIAction(
FExecuteAction::CreateLambda([SelectedAssets, BranchName]
{
const FText Message = FText::Format(LOCTEXT("StatusRevertAskForConfirmation", "Are you sure you want to revert the selected files to status branch {0}"), FText::FromString(BranchName));
const EAppReturnType::Type ConfirmationAnswer = FMessageDialog::Open(EAppMsgCategory::Warning, EAppMsgType::YesNoCancel, Message);

if (ConfirmationAnswer == EAppReturnType::Yes)
{
const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked<FGitSourceControlModule>("GitSourceControl");
const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath();
const FString& PathToRepositoryRoot = GitSourceControl.GetProvider().GetPathToRepositoryRoot();
GitSourceControlUtils::SyncAssetsFromBranch(PathToGitBinary, PathToRepositoryRoot, SelectedAssets, BranchName);
}
}),
FCanExecuteAction::CreateLambda([bCanRevertToStatusBranch]
{
return bCanRevertToStatusBranch;
})
)
);
}

void FGitSourceControlModule::DiffAssetAgainstGitOriginBranch(const TArray<FAssetData> SelectedAssets, FString BranchName) const
Expand Down
104 changes: 104 additions & 0 deletions Source/GitSourceControl/Private/GitSourceControlUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
#include "Async/Async.h"
#include "UObject/Linker.h"

#include "SourceControlHelpers.h"
#include "SourceControlOperations.h"
#include "SourceControlWindows.h"

#ifndef GIT_DEBUG_STATUS
#define GIT_DEBUG_STATUS 0
#endif
Expand Down Expand Up @@ -2529,6 +2533,106 @@ TSharedPtr<ISourceControlRevision, ESPMode::ThreadSafe> GetOriginRevisionOnBranc
return nullptr;
}

void SyncAssetsFromBranch(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray<FAssetData>& SelectedAssets, const FString& BranchName)
{
TArray<FString> FilesToDiff;

for (const FAssetData& AssetData : SelectedAssets)
{
FString AbsoluteFilePath = SourceControlHelpers::PackageFilename(AssetData.PackageName.ToString());
FilesToDiff.Add(AbsoluteFilePath);
}

TArray<FString> DiffResults;
TArray<FString> ErrorMessages;
TArray<FString> DiffParametersLog{ TEXT("--pretty="), TEXT("--name-only"), FString::Printf(TEXT("HEAD..%s"), *BranchName), TEXT(""), TEXT("--") };
RunCommand(TEXT("diff"), InPathToGitBinary, InRepositoryRoot, DiffParametersLog, FilesToDiff, DiffResults, ErrorMessages);

// DiffResults is repo relative and we need to be
// in absolute so we can compare against FilesToSync
AbsoluteFilenames(InRepositoryRoot, DiffResults);

const TArray<FString> FilesToSync = DiffResults;

if (!FilesToSync.IsEmpty())
{
// Source control checkout is lock the file and mark for read.
FGitSourceControlModule* GitSourceControl = FGitSourceControlModule::GetThreadSafe();
if (!GitSourceControl)
{
return;
}
FGitSourceControlProvider& Provider = GitSourceControl->GetProvider();

TArray<FString> FilesToLock;
TArray<FString> UneditableAssets;

TArray<TSharedRef<ISourceControlState, ESPMode::ThreadSafe>> OutStates;
Provider.GetState(FilesToSync, OutStates, EStateCacheUsage::ForceUpdate);

FString FilePath;
for (const auto& SourceControlState : OutStates)
{
FilePath = SourceControlState->GetFilename();
if (SourceControlState->CanCheckout())
{
FilesToLock.Add(FilePath);
}
else if (!SourceControlState->IsCheckedOut())
{
UE_LOG(LogSourceControl, Warning, TEXT("File %s is not checked out, and cannot be checked out - failed to sync."), *FilePath);
UneditableAssets.Add(FilePath);
}
}

if (UneditableAssets.Num() != 0)
{
FString JoinedFiles = FString::Join(UneditableAssets, TEXT("\n"));
FMessageDialog::Open(EAppMsgCategory::Error, EAppMsgType::Ok, FText::Format(LOCTEXT("FailedBranchSync", "Failed to sync file from branch {0} because the following files cannot be checked out \n{1}"), { FText::FromString(BranchName), FText::FromString(JoinedFiles) }));
return;
}

Provider.Execute(ISourceControlOperation::Create<FCheckOut>(), FilesToLock);

const bool bOperationSuccess = USourceControlHelpers::ApplyOperationAndReloadPackages(FilesToSync,
[&InPathToGitBinary, &InRepositoryRoot, &BranchName, &FilesToSync](const TArray<FString>&)
{
// "checkout" in the context of git will download the file whereas "checkout"
// in the context of Unreal Engine/Perforce will add a lock and make it writable
TArray<FString> Results;
TArray<FString> Errors;
const FString GitCommand = TEXT("checkout");
bool bCommandSuccess = RunCommand(GitCommand, InPathToGitBinary, InRepositoryRoot, { BranchName, TEXT("--") }, FilesToSync, Results, Errors);

if (!bCommandSuccess)
{
UE_LOG(LogSourceControl, Error, TEXT("Git command %s failed"), *GitCommand);
for (const auto& Error : Errors)
{
UE_LOG(LogSourceControl, Error, TEXT("%s"), *Error);
}
}
return bCommandSuccess;
});

if (bOperationSuccess)
{
FText Message = FText::Format(LOCTEXT("SyncAssetsFromBranchSuccess", "Successfully reverted file(s) to match {0}"), FText::FromString(BranchName));
FMessageDialog::Open(EAppMsgCategory::Success, EAppMsgType::Ok, Message);
}
else
{
const FText Message = LOCTEXT("SyncAssetsFromBranchFailedGitCheckout", "Revert To Status Branch failed - git checkout failed. See the log for details and carefully review all files");
FMessageDialog::Open(EAppMsgCategory::Error, EAppMsgType::Ok, Message);
}
}
else
{
FMessageDialog::Open(EAppMsgCategory::Info, EAppMsgType::Ok, LOCTEXT("SyncAssetsFromBranchAssetsUnchanged", "Failed to sync files because the selected file(s) were unchanged"));
}
}


} // namespace GitSourceControlUtils

#undef LOCTEXT_NAMESPACE
2 changes: 2 additions & 0 deletions Source/GitSourceControl/Public/GitSourceControlUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,6 @@ bool PullOrigin(const FString& InPathToGitBinary, const FString& InPathToReposit

GITSOURCECONTROL_API TSharedPtr< class ISourceControlRevision, ESPMode::ThreadSafe > GetOriginRevisionOnBranch( const FString & InPathToGitBinary, const FString & InRepositoryRoot, const FString & InRelativeFileName, TArray< FString > & OutErrorMessages, const FString & BranchName );

void SyncAssetsFromBranch(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray<FAssetData>& SelectedAssets, const FString& BranchName);

}