I’ve reproduced this behavior locally in UE 5.5.4 (launcher release). Based on my research, the Git Source Control plugin does not currently support Git submodules. This is due to how the RepositoryRoot is consistenly used for all Git commands, without adjusting for assets located in plugins or submodules.
As a workaround, you can modify the engine source code to override the repository root dynamically inside RunGetHistory (located in GitSourceControlUtils.cpp). This allows the History option to run in the correct context for your assets inside plugin submodules.
Here’s an example of a hardcoded override for a plugin (and git submodule) named “TestPlugin”:
`// Run a Git “log” command and parse it.
bool RunGetHistory(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InFile, bool bMergeConflict, TArray& OutErrorMessages, TGitSourceControlHistory& OutHistory)
{
// HACK: override the repository root if the file is in a known plugin/submodule path
FString LocalRepoRoot = InRepositoryRoot;
if (InFile.Contains(TEXT(“Plugins/TestPlugin/”)))
{
LocalRepoRoot = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() / TEXT(“Plugins/TestPlugin”));
}
bool bResults;
{
TArray Results;
TArray Parameters;
Parameters.Add(TEXT(“–follow”)); // follow file renames
Parameters.Add(TEXT(“–date=raw”));
Parameters.Add(TEXT(“–name-status”)); // relative filename at this revision, preceded by a status character
Parameters.Add(TEXT(“–pretty=medium”)); // make sure format matches expected in ParseLogResults
if(bMergeConflict)
{
// In case of a merge conflict, we also need to get the tip of the “remote branch” (MERGE_HEAD) before the log of the “current branch” (HEAD)
// @todo does not work for a cherry-pick! Test for a rebase.
Parameters.Add(TEXT(“MERGE_HEAD”));
Parameters.Add(TEXT(“–max-count 1”));
}
TArray Files;
Files.Add(*InFile);
bResults = RunCommand(TEXT(“log”), InPathToGitBinary, LocalRepoRoot, Parameters, Files, Results, OutErrorMessages);
if(bResults)
{
ParseLogResults(Results, OutHistory);
}
}
for(auto& Revision : OutHistory)
{
// Get file (blob) sha1 id and size
TArray Results;
TArray Parameters;
Parameters.Add(TEXT(“–long”)); // Show object size of blob (file) entries.
Parameters.Add(Revision->GetRevision());
TArray Files;
Files.Add(*Revision->GetFilename());
bResults &= RunCommand(TEXT(“ls-tree”), InPathToGitBinary, LocalRepoRoot, Parameters, Files, Results, OutErrorMessages);
if(bResults && Results.Num())
{
FGitLsTreeParser LsTree(Results);
Revision->FileHash = LsTree.FileHash;
Revision->FileSize = LsTree.FileSize;
}
}
return bResults;
}`Once the change is in place and the engine is rebuilt, you should be able to view the full revision history for any assets within the specified plugin. Note that this change only affects the RunGetHistory method. All other Git commands will continue to use the original root path, and assets outside the specified submodule will continue behaving as before.
An image running the history command on a plugin submodule:
It seems like your described method enables the history itself, so that’s an improvement for sure.
However, there is still crucial functionality missing on my end.
Diffing blueprints still wont work. When I try to diff 2 commits, I am seeing this error:[Image Removed]Also, when trying to “Diff against depot”, nothing happens at all.
I don’t see the source control icons on the assets (that’s not a high priority though)
So it seems like there are more edits needed, apart from changes to GetHistory?
Great to hear the previous workaround helped. Git integration is not fully supported since we only modified the RunGetHistory method.
I’ve explored a cleaner and more flexible solution by dynamically overriding the repository root via an exposed method in the GitSourceControlProvider. This way all Git operations work based on the root you set.
This does require more structural changes to the Git Source Control Module:
1. Revert the previous changes made to GitSourceControlUtils.cpp (in RunGetHistory).
2. In the GitSourceControl/Source/GitSourceControl folder (next to the GitSourceControl.Build.cs file) create a Public folder and move all the header files from Private to Public.
3. Inside the GitSourceControl.Build.cs file add a new public include path like this:
`public class GitSourceControl : ModuleRules
{
public GitSourceControl(ReadOnlyTargetRules Target) : base(Target)
{
PrivateDependencyModuleNames.AddRange(
new string {
“Core”,
“Slate”,
“SlateCore”,
“InputCore”,
“DesktopWidgets”,
“SourceControl”,
}
);
// Make the header files public
PublicIncludePaths.AddRange(
new string {
“GitSourceControl/Public”
});
…
}
}`4. Open GitSourceControlProvider.h and update:
Add GITSOURCECONTROL_API next to FGitSourceControlProvider
`// Before
class FGitSourceControlProvider
//After
class GITSOURCECONTROL_API FGitSourceControlProvider`* Declare the new public method called OverrideRepositoryRoot
`// In GitSourceControlProvider.h
void OverrideRepositoryRoot(const FString& NewRoot);`5. Open GitSourceControlProvider.cpp and update:
Add the definition for the OverrideRepository method
`// GitSourceControlProvider.cpp
// Definition for OverrideRepository
void FGitSourceControlProvider::OverrideRepositoryRoot(const FString& NewRoot)
{
PathToRepositoryRoot = NewRoot;
bOverriddenPath = true;
UE_LOG(LogSourceControl, Warning, TEXT(“PathToRepositoryRoot overridden to: %s”), NewRoot);
CheckGitAvailability();
}` Modify the CheckRepositoryStatus method:
`void FGitSourceControlProvider::CheckRepositoryStatus(const FString& InPathToGitBinary)
{
// If the path has not been overridden, find the path to the root Git directory (if any)
if (!bOverriddenPath) {
const FString PathToProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir());
bGitRepositoryFound = GitSourceControlUtils::FindRootDirectory(PathToProjectDir, PathToRepositoryRoot);
}
else {
bGitRepositoryFound = true;
}
if(bGitRepositoryFound)
{
// Get branch name
bGitRepositoryFound = GitSourceControlUtils::GetBranchName(InPathToGitBinary, PathToRepositoryRoot, BranchName);
if(bGitRepositoryFound)
{
GitSourceControlUtils::GetRemoteUrl(InPathToGitBinary, PathToRepositoryRoot, RemoteUrl);
UE_LOG(LogSourceControl, Warning, TEXT(“Repo root(%s) RemoteUrl(%s)”), *PathToRepositoryRoot, *RemoteUrl);
}
else
{
UE_LOG(LogSourceControl, Error, TEXT(“‘%s’ is not a valid Git repository”), *PathToRepositoryRoot);
}
}
else
{
UE_LOG(LogSourceControl, Warning, TEXT(“‘%s’ is not part of a Git repository”), *FPaths::ProjectDir());
}
// Get user name & email (of the repository, else from the global Git config)
GitSourceControlUtils::GetUserConfig(InPathToGitBinary, PathToRepositoryRoot, UserName, UserEmail);
}`
Now, we have everything ready in the Git module, you just need a way to call the OverrideRepositoryRoot.
I added a console command in my custom TestPlayerController like this:
`// TestPlayerController.h
UCLASS()
class MYGITPROJECT_API ATestPlayerController : public APlayerController
{
GENERATED_BODY()
// Exposed in the UE Editor console as: SetGitRepoRoot “Your/Path”
UFUNCTION(Exec)
void SetGitRepoRoot(FString NewRoot);
};
// TestPlayerController.cpp
include “TestPlayerController.h” include “GitSourceControlModule.h” include “GitSourceControlProvider.h”
…
}
}`Once compiled (engine and project), you can launch a PIE session and run the command via the console like this:
SetGitRepoRoot E:/UDN/Launcher_5.5.4/GitPluginIssue/Plugins/TestPluginWhere TestPlugin is the directory of my plugin/submodule. Be aware that the path must be absolute and without any quotes.
[Image Removed]You should see Git-related logs and now history, diff and other operations should work in the plugin/submodule context.
Here’s an image of calling “Diff Against Depot” on a plugin BP:
[Image Removed]To go back to the original repo root you may call the command again using the project root or restart the editor.
While the way the function is triggered could certainly be improved, I believe this approach could sufficient for your revision needs.