added UEGitPlugin
This commit is contained in:
+83
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
#include "ISourceControlChangelist.h"
|
||||
|
||||
class FGitSourceControlChangelist : public ISourceControlChangelist
|
||||
{
|
||||
public:
|
||||
FGitSourceControlChangelist() = default;
|
||||
|
||||
explicit FGitSourceControlChangelist(FString&& InChangelistName, const bool bInInitialized = false)
|
||||
: ChangelistName(MoveTemp(InChangelistName))
|
||||
, bInitialized(bInInitialized)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool CanDelete() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const FGitSourceControlChangelist& InOther) const
|
||||
{
|
||||
return ChangelistName == InOther.ChangelistName;
|
||||
}
|
||||
|
||||
bool operator!=(const FGitSourceControlChangelist& InOther) const
|
||||
{
|
||||
return ChangelistName != InOther.ChangelistName;
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
|
||||
virtual bool IsDefault() const override
|
||||
{
|
||||
return ChangelistName == WorkingChangelist.ChangelistName;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetInitialized()
|
||||
{
|
||||
bInitialized = true;
|
||||
}
|
||||
|
||||
bool IsInitialized() const
|
||||
{
|
||||
return bInitialized;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
ChangelistName.Reset();
|
||||
bInitialized = false;
|
||||
}
|
||||
|
||||
friend FORCEINLINE uint32 GetTypeHash(const FGitSourceControlChangelist& InGitChangelist)
|
||||
{
|
||||
return GetTypeHash(InGitChangelist.ChangelistName);
|
||||
}
|
||||
|
||||
FString GetName() const
|
||||
{
|
||||
return ChangelistName;
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
|
||||
virtual FString GetIdentifier() const override
|
||||
{
|
||||
return ChangelistName;
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
static FGitSourceControlChangelist WorkingChangelist;
|
||||
static FGitSourceControlChangelist StagedChangelist;
|
||||
|
||||
private:
|
||||
FString ChangelistName;
|
||||
bool bInitialized = false;
|
||||
};
|
||||
|
||||
typedef TSharedRef<class FGitSourceControlChangelist, ESPMode::ThreadSafe> FGitSourceControlChangelistRef;
|
||||
#endif
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2014-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GitSourceControlChangelist.h"
|
||||
#include "ISourceControlProvider.h"
|
||||
#include "Misc/IQueuedWork.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
/** Accumulated error and info messages for a revision control operation. */
|
||||
struct FGitSourceControlResultInfo
|
||||
{
|
||||
/** Append any messages from another FSourceControlResultInfo, ensuring to keep any already accumulated info. */
|
||||
void Append(const FGitSourceControlResultInfo& InResultInfo)
|
||||
{
|
||||
InfoMessages.Append(InResultInfo.InfoMessages);
|
||||
ErrorMessages.Append(InResultInfo.ErrorMessages);
|
||||
}
|
||||
|
||||
/** Info and/or warning message storage */
|
||||
TArray<FString> InfoMessages;
|
||||
|
||||
/** Potential error message storage */
|
||||
TArray<FString> ErrorMessages;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to execute Git commands multi-threaded.
|
||||
*/
|
||||
class FGitSourceControlCommand : public IQueuedWork
|
||||
{
|
||||
public:
|
||||
|
||||
FGitSourceControlCommand(const TSharedRef<class ISourceControlOperation, ESPMode::ThreadSafe>& InOperation, const TSharedRef<class IGitSourceControlWorker, ESPMode::ThreadSafe>& InWorker, const FSourceControlOperationComplete& InOperationCompleteDelegate = FSourceControlOperationComplete());
|
||||
|
||||
/**
|
||||
* Modify the repo root if all selected files are in a plugin subfolder, and the plugin subfolder is a git repo
|
||||
* This supports the case where each plugin is a sub module
|
||||
*/
|
||||
void UpdateRepositoryRootIfSubmodule(TArray<FString>& AbsoluteFilePaths);
|
||||
|
||||
/**
|
||||
* This is where the real thread work is done. All work that is done for
|
||||
* this queued object should be done from within the call to this function.
|
||||
*/
|
||||
bool DoWork();
|
||||
|
||||
/**
|
||||
* Tells the queued work that it is being abandoned so that it can do
|
||||
* per object clean up as needed. This will only be called if it is being
|
||||
* abandoned before completion. NOTE: This requires the object to delete
|
||||
* itself using whatever heap it was allocated in.
|
||||
*/
|
||||
virtual void Abandon() override;
|
||||
|
||||
/**
|
||||
* This method is also used to tell the object to cleanup but not before
|
||||
* the object has finished it's work.
|
||||
*/
|
||||
virtual void DoThreadedWork() override;
|
||||
|
||||
/** Attempt to cancel the operation */
|
||||
void Cancel();
|
||||
|
||||
/** Is the operation canceled? */
|
||||
bool IsCanceled() const;
|
||||
|
||||
/** Save any results and call any registered callbacks. */
|
||||
ECommandResult::Type ReturnResults();
|
||||
|
||||
public:
|
||||
/** Path to the Git binary */
|
||||
FString PathToGitBinary;
|
||||
|
||||
/** Path to the root of the Unreal revision control repository: usually the ProjectDir */
|
||||
FString PathToRepositoryRoot;
|
||||
|
||||
/** Path to the root of the Git repository: can be the ProjectDir itself, or any parent directory (found by the "Connect" operation) */
|
||||
FString PathToGitRoot;
|
||||
|
||||
/** Tell if using the Git LFS file Locking workflow */
|
||||
bool bUsingGitLfsLocking;
|
||||
|
||||
/** Operation we want to perform - contains outward-facing parameters & results */
|
||||
TSharedRef<class ISourceControlOperation, ESPMode::ThreadSafe> Operation;
|
||||
|
||||
/** The object that will actually do the work */
|
||||
TSharedRef<class IGitSourceControlWorker, ESPMode::ThreadSafe> Worker;
|
||||
|
||||
/** Delegate to notify when this operation completes */
|
||||
FSourceControlOperationComplete OperationCompleteDelegate;
|
||||
|
||||
/**If true, this command has been processed by the revision control thread*/
|
||||
volatile int32 bExecuteProcessed;
|
||||
|
||||
/**If true, this command has been cancelled*/
|
||||
volatile int32 bCancelled;
|
||||
|
||||
/**If true, the revision control command succeeded*/
|
||||
bool bCommandSuccessful;
|
||||
|
||||
/** Current Commit full SHA1 */
|
||||
FString CommitId;
|
||||
|
||||
/** Current Commit description's Summary */
|
||||
FString CommitSummary;
|
||||
|
||||
/** If true, this command will be automatically cleaned up in Tick() */
|
||||
bool bAutoDelete;
|
||||
|
||||
/** Whether we are running multi-treaded or not*/
|
||||
EConcurrency::Type Concurrency;
|
||||
|
||||
/** Files to perform this operation on */
|
||||
TArray<FString> Files;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
/** Changelist to perform this operation on */
|
||||
FGitSourceControlChangelist Changelist;
|
||||
#endif
|
||||
|
||||
/** Potential error, warning and info message storage */
|
||||
FGitSourceControlResultInfo ResultInfo;
|
||||
|
||||
/** Branch names for status queries */
|
||||
TArray< FString > StatusBranchNames;
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ISourceControlProvider.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
struct FToolMenuSection;
|
||||
class FMenuBuilder;
|
||||
|
||||
/** Git extension of the Revision Control toolbar menu */
|
||||
class FGitSourceControlMenu
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
/** This functions will be bound to appropriate Command. */
|
||||
void CommitClicked();
|
||||
void PushClicked();
|
||||
void SyncClicked();
|
||||
void RevertClicked();
|
||||
void RefreshClicked();
|
||||
|
||||
protected:
|
||||
static void RevertAllCallback(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult);
|
||||
static void RevertAllCancelled(FSourceControlOperationRef InOperation);
|
||||
|
||||
private:
|
||||
bool HaveRemoteUrl() const;
|
||||
bool CanCommit() const;
|
||||
|
||||
bool SaveDirtyPackages();
|
||||
|
||||
bool StashAwayAnyModifications();
|
||||
void ReApplyStashedModifications();
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
void AddMenuExtension(FToolMenuSection& Builder);
|
||||
#else
|
||||
void AddMenuExtension(FMenuBuilder& Builder);
|
||||
TSharedRef<class FExtender> OnExtendLevelEditorViewMenu(const TSharedRef<class FUICommandList> CommandList);
|
||||
#endif
|
||||
|
||||
static void DisplayInProgressNotification(const FText& InOperationInProgressString);
|
||||
static void RemoveInProgressNotification();
|
||||
static void DisplaySucessNotification(const FName& InOperationName);
|
||||
static void DisplayFailureNotification(const FName& InOperationName);
|
||||
|
||||
private:
|
||||
#if ENGINE_MAJOR_VERSION < 5
|
||||
FDelegateHandle ViewMenuExtenderHandle;
|
||||
#endif
|
||||
|
||||
/** Was there a need to stash away modifications before Sync? */
|
||||
bool bStashMadeBeforeSync;
|
||||
|
||||
/** Loaded packages to reload after a Sync or Revert operation */
|
||||
TArray<UPackage*> PackagesToReload;
|
||||
|
||||
/** Current revision control operation from extended menu if any */
|
||||
static TWeakPtr<class SNotificationItem> OperationInProgressNotification;
|
||||
|
||||
/** Delegate called when a revision control operation has completed */
|
||||
void OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult);
|
||||
};
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleInterface.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#include "GitSourceControlSettings.h"
|
||||
#include "GitSourceControlProvider.h"
|
||||
|
||||
struct FAssetData;
|
||||
class FExtender;
|
||||
|
||||
/**
|
||||
|
||||
UEGitPlugin is a simple Git Revision Control Plugin for Unreal Engine
|
||||
|
||||
Written and contributed by Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
|
||||
### Supported features
|
||||
- initialize a new Git local repository ('git init') to manage your UE Game Project
|
||||
- can also create an appropriate .gitignore file as part of initialization
|
||||
- can also create a .gitattributes file to enable Git LFS (Large File System) as part of initialization
|
||||
- can also make the initial commit, with custom multi-line message
|
||||
- can also configure the default remote origin URL
|
||||
- display status icons to show modified/added/deleted/untracked files
|
||||
- show history of a file
|
||||
- visual diff of a blueprint against depot or between previous versions of a file
|
||||
- revert modifications of a file
|
||||
- add, delete, rename a file
|
||||
- checkin/commit a file (cannot handle atomically more than 50 files)
|
||||
- migrate an asset between two projects if both are using Git
|
||||
- solve a merge conflict on a blueprint
|
||||
- show current branch name in status text
|
||||
- Sync to Pull (rebase) the current branch
|
||||
- Git LFS (Github, Gitlab, Bitbucket) is working with Git 2.10+ under Windows
|
||||
- Git LFS 2 File Locking is working with Git 2.10+ and Git LFS 2.0.0
|
||||
- Windows, Mac and Linux
|
||||
|
||||
### TODO
|
||||
1. configure the name of the remote instead of default "origin"
|
||||
|
||||
### TODO LFS 2.x File Locking
|
||||
|
||||
Known issues:
|
||||
0. False error logs after a successful push:
|
||||
|
||||
Use "TODO LFS" in the code to track things left to do/improve/refactor:
|
||||
2. Implement FGitSourceControlProvider::bWorkingOffline like the SubversionSourceControl plugin
|
||||
3. Trying to deactivate Git LFS 2 file locking afterward on the "Login to Revision Control" (Connect/Configure) screen
|
||||
is not working after Git LFS 2 has switched "read-only" flag on files (which needs the Checkout operation to be editable)!
|
||||
- temporarily deactivating locks may be required if we want to be able to work while not connected (do we really need this ???)
|
||||
- does Git LFS have a command to do this deactivation ?
|
||||
- perhaps should we rely on detection of such flags to detect LFS 2 usage (ie. the need to do a checkout)
|
||||
- see SubversionSourceControl plugin that deals with such flags
|
||||
- this would need a rework of the way the "bIsUsingFileLocking" is propagated, since this would no more be a configuration (or not only) but a file state
|
||||
- else we should at least revert those read-only flags when going out of "Lock mode"
|
||||
|
||||
### What *cannot* be done presently
|
||||
- Branch/Merge are not in the current Editor workflow
|
||||
- Amend a commit is not in the current Editor workflow
|
||||
- Configure user name & email ('git config user.name' & git config user.email')
|
||||
|
||||
### Known issues
|
||||
- the Editor does not show deleted files (only when deleted externally?)
|
||||
- the Editor does not show missing files
|
||||
- missing localization for git specific messages
|
||||
- renaming a Blueprint in Editor leaves a redirector file, AND modify too much the asset to enable git to track its history through renaming
|
||||
- standard Editor commit dialog asks if user wants to "Keep Files Checked Out" => no use for Git or Mercurial CanCheckOut()==false
|
||||
*/
|
||||
class FGitSourceControlModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/** Access the Git revision control settings */
|
||||
FGitSourceControlSettings& AccessSettings()
|
||||
{
|
||||
return GitSourceControlSettings;
|
||||
}
|
||||
|
||||
const FGitSourceControlSettings& AccessSettings() const
|
||||
{
|
||||
return GitSourceControlSettings;
|
||||
}
|
||||
|
||||
/** Save the Git revision control settings */
|
||||
void SaveSettings();
|
||||
|
||||
/** Access the Git revision control provider */
|
||||
FGitSourceControlProvider& GetProvider()
|
||||
{
|
||||
return GitSourceControlProvider;
|
||||
}
|
||||
|
||||
const FGitSourceControlProvider& GetProvider() const
|
||||
{
|
||||
return GitSourceControlProvider;
|
||||
}
|
||||
|
||||
GITSOURCECONTROL_API static const TArray< FString > & GetEmptyStringArray()
|
||||
{
|
||||
return EmptyStringArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline FGitSourceControlModule& Get()
|
||||
{
|
||||
return FModuleManager::Get().LoadModuleChecked< FGitSourceControlModule >("GitSourceControl");
|
||||
}
|
||||
|
||||
static inline FGitSourceControlModule* GetThreadSafe()
|
||||
{
|
||||
IModuleInterface* ModulePtr = FModuleManager::Get().GetModule("GitSourceControl");
|
||||
if (!ModulePtr)
|
||||
{
|
||||
// Main thread should never have this unloaded.
|
||||
check(!IsInGameThread());
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<FGitSourceControlModule*>(ModulePtr);
|
||||
}
|
||||
|
||||
/** Set list of error messages that occurred after last git command */
|
||||
static void SetLastErrors(const TArray<FText>& InErrors);
|
||||
|
||||
private:
|
||||
TSharedRef<FExtender> OnExtendContentBrowserAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets);
|
||||
void CreateGitContentBrowserAssetMenu(FMenuBuilder& MenuBuilder, const TArray<FAssetData> SelectedAssets);
|
||||
void DiffAssetAgainstGitOriginBranch(const TArray<FAssetData> SelectedAssets, FString BranchName) const;
|
||||
void DiffAgainstOriginBranch(UObject* InObject, const FString& InPackagePath, const FString& InPackageName, const FString& BranchName) const;
|
||||
|
||||
/** The one and only Git revision control provider */
|
||||
FGitSourceControlProvider GitSourceControlProvider;
|
||||
|
||||
/** The settings for Git revision control */
|
||||
FGitSourceControlSettings GitSourceControlSettings;
|
||||
|
||||
static TArray<FString> EmptyStringArray;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
// ContentBrowserDelegate Handles
|
||||
FDelegateHandle CbdHandle_OnFilterChanged;
|
||||
FDelegateHandle CbdHandle_OnSearchBoxChanged;
|
||||
FDelegateHandle CbdHandle_OnAssetSelectionChanged;
|
||||
FDelegateHandle CbdHandle_OnSourcesViewChanged;
|
||||
FDelegateHandle CbdHandle_OnAssetPathChanged;
|
||||
#endif
|
||||
FDelegateHandle CbdHandle_OnExtendAssetSelectionMenu;
|
||||
};
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GitSourceControlChangelist.h"
|
||||
#include "ISourceControlProvider.h"
|
||||
#include "IGitSourceControlWorker.h"
|
||||
#include "GitSourceControlMenu.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
class FGitSourceControlChangelistState;
|
||||
class FGitSourceControlState;
|
||||
|
||||
class FGitSourceControlCommand;
|
||||
|
||||
DECLARE_DELEGATE_RetVal(FGitSourceControlWorkerRef, FGetGitSourceControlWorker)
|
||||
|
||||
/// Git version and capabilites extracted from the string "git version 2.11.0.windows.3"
|
||||
struct FGitVersion
|
||||
{
|
||||
// Git version extracted from the string "git version 2.11.0.windows.3" (Windows), "git version 2.11.0" (Linux/Mac/Cygwin/WSL) or "git version 2.31.1.vfs.0.3" (Microsoft)
|
||||
int Major; // 2 Major version number
|
||||
int Minor; // 31 Minor version number
|
||||
int Patch; // 1 Patch/bugfix number
|
||||
bool bIsFork;
|
||||
FString Fork; // "vfs"
|
||||
int ForkMajor; // 0 Fork specific revision number
|
||||
int ForkMinor; // 3
|
||||
int ForkPatch; // ?
|
||||
|
||||
FGitVersion()
|
||||
: Major(0)
|
||||
, Minor(0)
|
||||
, Patch(0)
|
||||
, bIsFork(false)
|
||||
, ForkMajor(0)
|
||||
, ForkMinor(0)
|
||||
, ForkPatch(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class GITSOURCECONTROL_API FGitSourceControlProvider final : public ISourceControlProvider
|
||||
{
|
||||
public:
|
||||
/* ISourceControlProvider implementation */
|
||||
virtual void Init(bool bForceConnection = true) override;
|
||||
virtual void Close() override;
|
||||
virtual FText GetStatusText() const override;
|
||||
virtual bool IsEnabled() const override;
|
||||
virtual bool IsAvailable() const override;
|
||||
virtual const FName& GetName(void) const override;
|
||||
virtual bool QueryStateBranchConfig(const FString& ConfigSrc, const FString& ConfigDest) override;
|
||||
virtual void RegisterStateBranches(const TArray<FString>& BranchNames, const FString& ContentRootIn) override;
|
||||
virtual int32 GetStateBranchIndex(const FString& BranchName) const override;
|
||||
virtual ECommandResult::Type GetState( const TArray<FString>& InFiles, TArray<FSourceControlStateRef>& OutState, EStateCacheUsage::Type InStateCacheUsage ) override;
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
virtual ECommandResult::Type GetState(const TArray<FSourceControlChangelistRef>& InChangelists, TArray<FSourceControlChangelistStateRef>& OutState, EStateCacheUsage::Type InStateCacheUsage) override;
|
||||
#endif
|
||||
virtual TArray<FSourceControlStateRef> GetCachedStateByPredicate(TFunctionRef<bool(const FSourceControlStateRef&)> Predicate) const override;
|
||||
virtual FDelegateHandle RegisterSourceControlStateChanged_Handle(const FSourceControlStateChanged::FDelegate& SourceControlStateChanged) override;
|
||||
virtual void UnregisterSourceControlStateChanged_Handle(FDelegateHandle Handle) override;
|
||||
#if ENGINE_MAJOR_VERSION < 5
|
||||
virtual ECommandResult::Type Execute( const FSourceControlOperationRef& InOperation, const TArray<FString>& InFiles, EConcurrency::Type InConcurrency = EConcurrency::Synchronous, const FSourceControlOperationComplete& InOperationCompleteDelegate = FSourceControlOperationComplete()) override;
|
||||
virtual bool CanCancelOperation( const FSourceControlOperationRef& InOperation ) const override;
|
||||
virtual void CancelOperation( const FSourceControlOperationRef& InOperation ) override;
|
||||
#else
|
||||
virtual ECommandResult::Type Execute(const FSourceControlOperationRef& InOperation, FSourceControlChangelistPtr InChangelist, const TArray<FString>& InFiles, EConcurrency::Type InConcurrency = EConcurrency::Synchronous, const FSourceControlOperationComplete& InOperationCompleteDelegate = FSourceControlOperationComplete()) override;
|
||||
virtual bool CanCancelOperation( const FSourceControlOperationRef& InOperation ) const override;
|
||||
virtual void CancelOperation( const FSourceControlOperationRef& InOperation ) override;
|
||||
#endif
|
||||
virtual bool UsesLocalReadOnlyState() const override;
|
||||
virtual bool UsesChangelists() const override;
|
||||
virtual bool UsesCheckout() const override;
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
|
||||
virtual bool UsesFileRevisions() const override;
|
||||
#if ENGINE_MINOR_VERSION >= 8
|
||||
virtual TOptional<bool> HasChangesToSync() const override;
|
||||
virtual TOptional<bool> HasChangesToCheckIn() const override;
|
||||
#else
|
||||
virtual TOptional<bool> IsAtLatestRevision() const override;
|
||||
virtual TOptional<int> GetNumLocalChanges() const override;
|
||||
#endif
|
||||
#endif
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2
|
||||
virtual bool AllowsDiffAgainstDepot() const override;
|
||||
virtual bool UsesUncontrolledChangelists() const override;
|
||||
virtual bool UsesSnapshots() const override;
|
||||
#endif
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8
|
||||
virtual bool UsesSoftRevertOnDelete() const override;
|
||||
#endif
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
|
||||
virtual bool CanExecuteOperation( const FSourceControlOperationRef& InOperation ) const override;
|
||||
virtual TMap<EStatus, FString> GetStatus() const override;
|
||||
#endif
|
||||
virtual void Tick() override;
|
||||
virtual TArray< TSharedRef<class ISourceControlLabel> > GetLabels( const FString& InMatchingSpec ) const override;
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 7
|
||||
virtual bool GetStateBranchAtIndex(int32 BranchIndex, FString& OutBranchName) const override;
|
||||
#endif
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
virtual TArray<FSourceControlChangelistRef> GetChangelists( EStateCacheUsage::Type InStateCacheUsage ) override;
|
||||
#endif
|
||||
|
||||
#if SOURCE_CONTROL_WITH_SLATE
|
||||
virtual TSharedRef<class SWidget> MakeSettingsWidget() const override;
|
||||
#endif
|
||||
|
||||
using ISourceControlProvider::Execute;
|
||||
|
||||
/**
|
||||
* Check configuration, else standard paths, and run a Git "version" command to check the availability of the binary.
|
||||
*/
|
||||
void CheckGitAvailability();
|
||||
|
||||
/** Refresh Git settings from revision control settings */
|
||||
void UpdateSettings();
|
||||
|
||||
/**
|
||||
* Find the .git/ repository and check its status.
|
||||
*/
|
||||
void CheckRepositoryStatus();
|
||||
|
||||
/** Is git binary found and working. */
|
||||
inline bool IsGitAvailable() const
|
||||
{
|
||||
return bGitAvailable;
|
||||
}
|
||||
|
||||
/** Git version for feature checking */
|
||||
inline const FGitVersion& GetGitVersion() const
|
||||
{
|
||||
return GitVersion;
|
||||
}
|
||||
|
||||
/** Path to the root of the Unreal revision control repository: usually the ProjectDir */
|
||||
inline const FString& GetPathToRepositoryRoot() const
|
||||
{
|
||||
return PathToRepositoryRoot;
|
||||
}
|
||||
|
||||
/** Path to the root of the Git repository: can be the ProjectDir itself, or any parent directory (found by the "Connect" operation) */
|
||||
inline const FString& GetPathToGitRoot() const
|
||||
{
|
||||
return PathToGitRoot;
|
||||
}
|
||||
|
||||
/** Gets the path to the Git binary */
|
||||
inline const FString& GetGitBinaryPath() const
|
||||
{
|
||||
return PathToGitBinary;
|
||||
}
|
||||
|
||||
/** Git config user.name */
|
||||
inline const FString& GetUserName() const
|
||||
{
|
||||
return UserName;
|
||||
}
|
||||
|
||||
/** Git config user.email */
|
||||
inline const FString& GetUserEmail() const
|
||||
{
|
||||
return UserEmail;
|
||||
}
|
||||
|
||||
/** Git remote origin url */
|
||||
inline const FString& GetRemoteUrl() const
|
||||
{
|
||||
return RemoteUrl;
|
||||
}
|
||||
|
||||
inline const FString& GetLockUser() const
|
||||
{
|
||||
return LockUser;
|
||||
}
|
||||
|
||||
/** Helper function used to update state cache */
|
||||
TSharedRef<FGitSourceControlState, ESPMode::ThreadSafe> GetStateInternal(const FString& Filename);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
/** Helper function used to update changelists state cache */
|
||||
TSharedRef<FGitSourceControlChangelistState, ESPMode::ThreadSafe> GetStateInternal(const FGitSourceControlChangelist& InChangelist);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Register a worker with the provider.
|
||||
* This is used internally so the provider can maintain a map of all available operations.
|
||||
*/
|
||||
void RegisterWorker( const FName& InName, const FGetGitSourceControlWorker& InDelegate );
|
||||
|
||||
/** Set list of error messages that occurred after last perforce command */
|
||||
void SetLastErrors(const TArray<FText>& InErrors);
|
||||
|
||||
/** Get list of error messages that occurred after last perforce command */
|
||||
TArray<FText> GetLastErrors() const;
|
||||
|
||||
/** Get number of error messages seen after running last perforce command */
|
||||
int32 GetNumLastErrors() const;
|
||||
|
||||
/** Remove a named file from the state cache */
|
||||
bool RemoveFileFromCache(const FString& Filename);
|
||||
|
||||
/** Get files in cache */
|
||||
TArray<FString> GetFilesInCache();
|
||||
|
||||
bool AddFileToIgnoreForceCache(const FString& Filename);
|
||||
|
||||
bool RemoveFileFromIgnoreForceCache(const FString& Filename);
|
||||
|
||||
const FString& GetBranchName() const
|
||||
{
|
||||
return BranchName;
|
||||
}
|
||||
|
||||
const FString& GetRemoteBranchName() const { return RemoteBranchName; }
|
||||
|
||||
TArray<FString> GetStatusBranchNames() const;
|
||||
|
||||
/** Indicates editor binaries are to be updated upon next sync */
|
||||
bool bPendingRestart;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
uint32 TicksUntilNextForcedUpdate = 0;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/** Is git binary found and working. */
|
||||
bool bGitAvailable = false;
|
||||
|
||||
/** Is git repository found. */
|
||||
bool bGitRepositoryFound = false;
|
||||
|
||||
/** Is LFS locking enabled? */
|
||||
bool bUsingGitLfsLocking = false;
|
||||
|
||||
FString PathToGitBinary;
|
||||
|
||||
FString LockUser;
|
||||
|
||||
/** Critical section for thread safety of error messages that occurred after last perforce command */
|
||||
mutable FCriticalSection LastErrorsCriticalSection;
|
||||
|
||||
/** List of error messages that occurred after last perforce command */
|
||||
TArray<FText> LastErrors;
|
||||
|
||||
/** Helper function for Execute() */
|
||||
TSharedPtr<class IGitSourceControlWorker, ESPMode::ThreadSafe> CreateWorker(const FName& InOperationName) const;
|
||||
|
||||
/** Helper function for running command synchronously. */
|
||||
ECommandResult::Type ExecuteSynchronousCommand(class FGitSourceControlCommand& InCommand, const FText& Task, bool bSuppressResponseMsg);
|
||||
/** Issue a command asynchronously if possible. */
|
||||
ECommandResult::Type IssueCommand(class FGitSourceControlCommand& InCommand, const bool bSynchronous = false );
|
||||
|
||||
/** Output any messages this command holds */
|
||||
void OutputCommandMessages(const class FGitSourceControlCommand& InCommand) const;
|
||||
|
||||
/** Update repository status on Connect and UpdateStatus operations */
|
||||
void UpdateRepositoryStatus(const class FGitSourceControlCommand& InCommand);
|
||||
|
||||
/** Path to the root of the Unreal revision control repository: usually the ProjectDir */
|
||||
FString PathToRepositoryRoot;
|
||||
|
||||
/** Path to the root of the Git repository: can be the ProjectDir itself, or any parent directory (found by the "Connect" operation) */
|
||||
FString PathToGitRoot;
|
||||
|
||||
/** Git config user.name (from local repository, else globally) */
|
||||
FString UserName;
|
||||
|
||||
/** Git config user.email (from local repository, else globally) */
|
||||
FString UserEmail;
|
||||
|
||||
/** Name of the current branch */
|
||||
FString BranchName;
|
||||
|
||||
/** Name of the current remote branch */
|
||||
FString RemoteBranchName;
|
||||
|
||||
/** URL of the "origin" default remote server */
|
||||
FString RemoteUrl;
|
||||
|
||||
/** Current Commit full SHA1 */
|
||||
FString CommitId;
|
||||
|
||||
/** Current Commit description's Summary */
|
||||
FString CommitSummary;
|
||||
|
||||
/** State cache */
|
||||
TMap<FString, TSharedRef<class FGitSourceControlState, ESPMode::ThreadSafe> > StateCache;
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
TMap<FGitSourceControlChangelist, TSharedRef<class FGitSourceControlChangelistState, ESPMode::ThreadSafe> > ChangelistsStateCache;
|
||||
#endif
|
||||
|
||||
/** The currently registered revision control operations */
|
||||
TMap<FName, FGetGitSourceControlWorker> WorkersMap;
|
||||
|
||||
/** Queue for commands given by the main thread */
|
||||
TArray < FGitSourceControlCommand* > CommandQueue;
|
||||
|
||||
/** For notifying when the revision control states in the cache have changed */
|
||||
FSourceControlStateChanged OnSourceControlStateChanged;
|
||||
|
||||
/** Git version for feature checking */
|
||||
FGitVersion GitVersion;
|
||||
|
||||
/** Revision Control Menu Extension */
|
||||
FGitSourceControlMenu GitSourceControlMenu;
|
||||
|
||||
/**
|
||||
Ignore these files when forcing status updates. We add to this list when we've just updated the status already.
|
||||
UE's SourceControl has a habit of performing a double status update, immediately after an operation.
|
||||
*/
|
||||
TArray<FString> IgnoreForceCache;
|
||||
|
||||
/** Array of branch name patterns for status queries */
|
||||
TArray<FString> StatusBranchNamePatternsInternal;
|
||||
|
||||
class FGitSourceControlRunner* Runner = nullptr;
|
||||
};
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2014-2023 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISourceControlRevision.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "Misc/DateTime.h"
|
||||
|
||||
/** Revision of a file, linked to a specific commit */
|
||||
class FGitSourceControlRevision : public ISourceControlRevision
|
||||
{
|
||||
public:
|
||||
/** ISourceControlRevision interface */
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
virtual bool Get( FString& InOutFilename, EConcurrency::Type InConcurrency = EConcurrency::Synchronous ) const override;
|
||||
#else
|
||||
virtual bool Get( FString& InOutFilename ) const override;
|
||||
#endif
|
||||
|
||||
virtual bool GetAnnotated( TArray<FAnnotationLine>& OutLines ) const override;
|
||||
virtual bool GetAnnotated( FString& InOutFilename ) const override;
|
||||
virtual const FString& GetFilename() const override;
|
||||
virtual int32 GetRevisionNumber() const override;
|
||||
virtual const FString& GetRevision() const override;
|
||||
virtual const FString& GetDescription() const override;
|
||||
virtual const FString& GetUserName() const override;
|
||||
virtual const FString& GetClientSpec() const override;
|
||||
virtual const FString& GetAction() const override;
|
||||
virtual TSharedPtr<class ISourceControlRevision, ESPMode::ThreadSafe> GetBranchSource() const override;
|
||||
virtual const FDateTime& GetDate() const override;
|
||||
virtual int32 GetCheckInIdentifier() const override;
|
||||
virtual int32 GetFileSize() const override;
|
||||
|
||||
public:
|
||||
|
||||
/** The filename this revision refers to */
|
||||
FString Filename;
|
||||
|
||||
/** The full hexadecimal SHA1 id of the commit this revision refers to */
|
||||
FString CommitId;
|
||||
|
||||
/** The short hexadecimal SHA1 id (8 first hex char out of 40) of the commit: the string to display */
|
||||
FString ShortCommitId;
|
||||
|
||||
/** The numeric value of the short SHA1 (8 first hex char out of 40) */
|
||||
int32 CommitIdNumber = 0;
|
||||
|
||||
/** The index of the revision in the history (SBlueprintRevisionMenu assumes order for the "Depot" label) */
|
||||
int32 RevisionNumber = 0;
|
||||
|
||||
/** The SHA1 identifier of the file at this revision */
|
||||
FString FileHash;
|
||||
|
||||
/** The description of this revision */
|
||||
FString Description;
|
||||
|
||||
/** The user that made the change */
|
||||
FString UserName;
|
||||
|
||||
/** The action (add, edit, branch etc.) performed at this revision */
|
||||
FString Action;
|
||||
|
||||
/** Source of move ("branch" in Perforce term) if any */
|
||||
TSharedPtr<FGitSourceControlRevision, ESPMode::ThreadSafe> BranchSource;
|
||||
|
||||
/** The date this revision was made */
|
||||
FDateTime Date;
|
||||
|
||||
/** The size of the file at this revision */
|
||||
int32 FileSize;
|
||||
|
||||
/** Dynamic repository root **/
|
||||
FString PathToRepoRoot;
|
||||
};
|
||||
|
||||
/** History composed of the last 100 revisions of the file */
|
||||
typedef TArray< TSharedRef<FGitSourceControlRevision, ESPMode::ThreadSafe> > TGitSourceControlHistory;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "HAL/CriticalSection.h"
|
||||
|
||||
class GITSOURCECONTROL_API FGitSourceControlSettings
|
||||
{
|
||||
public:
|
||||
/** Get the Git Binary Path */
|
||||
const FString & GetBinaryPath() const;
|
||||
|
||||
/** Set the Git Binary Path */
|
||||
bool SetBinaryPath(const FString& InString);
|
||||
|
||||
/** Tell if using the Git LFS file Locking workflow */
|
||||
bool IsUsingGitLfsLocking() const;
|
||||
|
||||
/** Configure the usage of Git LFS file Locking workflow */
|
||||
bool SetUsingGitLfsLocking(const bool InUsingGitLfsLocking);
|
||||
|
||||
/** Get the username used by the Git LFS 2 File Locks server */
|
||||
const FString GetLfsUserName() const;
|
||||
|
||||
/** Set the username used by the Git LFS 2 File Locks server */
|
||||
bool SetLfsUserName(const FString& InString);
|
||||
|
||||
/** Load settings from ini file */
|
||||
void LoadSettings();
|
||||
|
||||
/** Save settings to ini file */
|
||||
void SaveSettings() const;
|
||||
|
||||
private:
|
||||
/** A critical section for settings access */
|
||||
mutable FCriticalSection CriticalSection;
|
||||
|
||||
/** Git binary path */
|
||||
FString BinaryPath;
|
||||
|
||||
/** Tells if using the Git LFS file Locking workflow */
|
||||
bool bUsingGitLfsLocking = true;
|
||||
|
||||
/** Username used by the Git LFS 2 File Locks server */
|
||||
FString LfsUserName;
|
||||
};
|
||||
@@ -0,0 +1,223 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GitSourceControlChangelist.h"
|
||||
#include "GitSourceControlRevision.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
/** A consolidation of state priorities. */
|
||||
namespace EGitState
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Unset,
|
||||
NotAtHead,
|
||||
#if 0
|
||||
AddedAtHead,
|
||||
DeletedAtHead,
|
||||
#endif
|
||||
LockedOther,
|
||||
NotLatest,
|
||||
/** Unmerged state (modified, but conflicts) */
|
||||
Unmerged,
|
||||
Added,
|
||||
Deleted,
|
||||
Modified,
|
||||
/** Not modified, but locked explicitly. */
|
||||
CheckedOut,
|
||||
Untracked,
|
||||
Lockable,
|
||||
Unmodified,
|
||||
Ignored,
|
||||
/** Whatever else. */
|
||||
None,
|
||||
};
|
||||
}
|
||||
|
||||
/** Corresponds to diff file states. */
|
||||
namespace EFileState
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Unset,
|
||||
Unknown,
|
||||
Added,
|
||||
Copied,
|
||||
Deleted,
|
||||
Modified,
|
||||
Renamed,
|
||||
Missing,
|
||||
Unmerged,
|
||||
};
|
||||
}
|
||||
|
||||
/** Where in the world is this file? */
|
||||
namespace ETreeState
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Unset,
|
||||
/** This file is synced to commit */
|
||||
Unmodified,
|
||||
/** This file is modified, but not in staging tree */
|
||||
Working,
|
||||
/** This file is in staging tree (git add) */
|
||||
Staged,
|
||||
/** This file is not tracked in the repo yet */
|
||||
Untracked,
|
||||
/** This file is ignored by the repo */
|
||||
Ignored,
|
||||
/** This file is outside the repo folder */
|
||||
NotInRepo,
|
||||
};
|
||||
}
|
||||
|
||||
/** LFS locks status of this file */
|
||||
namespace ELockState
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Unset,
|
||||
Unknown,
|
||||
Unlockable,
|
||||
NotLocked,
|
||||
Locked,
|
||||
LockedOther,
|
||||
};
|
||||
}
|
||||
|
||||
/** What is this file doing at HEAD? */
|
||||
namespace ERemoteState
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Unset,
|
||||
/** Up to date */
|
||||
UpToDate,
|
||||
/** Local version is behind remote */
|
||||
NotAtHead,
|
||||
#if 0
|
||||
// TODO: handle these
|
||||
/** Remote file does not exist on local */
|
||||
AddedAtHead,
|
||||
/** Local was deleted on remote */
|
||||
DeletedAtHead,
|
||||
#endif
|
||||
/** Not at the latest revision amongst the tracked branches */
|
||||
NotLatest,
|
||||
};
|
||||
}
|
||||
|
||||
/** Combined state, for updating cache in a map. */
|
||||
struct FGitState
|
||||
{
|
||||
EFileState::Type FileState = EFileState::Unknown;
|
||||
ETreeState::Type TreeState = ETreeState::NotInRepo;
|
||||
ELockState::Type LockState = ELockState::Unknown;
|
||||
/** Name of user who has locked the file */
|
||||
FString LockUser;
|
||||
ERemoteState::Type RemoteState = ERemoteState::UpToDate;
|
||||
/** The branch with the latest commit for this file */
|
||||
FString HeadBranch;
|
||||
};
|
||||
|
||||
class GITSOURCECONTROL_API FGitSourceControlState : public ISourceControlState
|
||||
{
|
||||
public:
|
||||
explicit FGitSourceControlState(const FString &InLocalFilename) :
|
||||
LocalFilename( InLocalFilename ),
|
||||
TimeStamp( 0 ),
|
||||
HeadAction( TEXT( "Changed" ) ),
|
||||
HeadModTime( 0 ),
|
||||
HeadCommit( TEXT( "Unknown" ) )
|
||||
{
|
||||
}
|
||||
|
||||
/** ISourceControlState interface */
|
||||
virtual int32 GetHistorySize() const override;
|
||||
virtual TSharedPtr<class ISourceControlRevision, ESPMode::ThreadSafe> GetHistoryItem(int32 HistoryIndex) const override;
|
||||
virtual TSharedPtr<class ISourceControlRevision, ESPMode::ThreadSafe> FindHistoryRevision(int32 RevisionNumber) const override;
|
||||
virtual TSharedPtr<class ISourceControlRevision, ESPMode::ThreadSafe> FindHistoryRevision(const FString& InRevision) const override;
|
||||
#if ENGINE_MAJOR_VERSION < 5 || ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3
|
||||
virtual TSharedPtr<class ISourceControlRevision, ESPMode::ThreadSafe> GetBaseRevForMerge() const override;
|
||||
#else
|
||||
virtual FResolveInfo GetResolveInfo() const override;
|
||||
#endif
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2
|
||||
virtual TSharedPtr<class ISourceControlRevision, ESPMode::ThreadSafe> GetCurrentRevision() const override;
|
||||
#endif
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
virtual FSlateIcon GetIcon() const override;
|
||||
#else
|
||||
virtual FName GetIconName() const override;
|
||||
virtual FName GetSmallIconName() const override;
|
||||
#endif
|
||||
virtual FText GetDisplayName() const override;
|
||||
virtual FText GetDisplayTooltip() const override;
|
||||
virtual const FString& GetFilename() const override;
|
||||
virtual const FDateTime& GetTimeStamp() const override;
|
||||
virtual bool CanCheckIn() const override;
|
||||
virtual bool CanCheckout() const override;
|
||||
virtual bool IsCheckedOut() const override;
|
||||
virtual bool IsCheckedOutOther(FString* Who = NULL) const override;
|
||||
virtual bool IsCheckedOutInOtherBranch(const FString& CurrentBranch = FString()) const override;
|
||||
virtual bool IsModifiedInOtherBranch(const FString& CurrentBranch = FString()) const override;
|
||||
virtual bool IsCheckedOutOrModifiedInOtherBranch(const FString& CurrentBranch = FString()) const override { return IsModifiedInOtherBranch(CurrentBranch); }
|
||||
virtual TArray<FString> GetCheckedOutBranches() const override { return TArray<FString>(); }
|
||||
virtual FString GetOtherUserBranchCheckedOuts() const override { return FString(); }
|
||||
virtual bool GetOtherBranchHeadModification(FString& HeadBranchOut, FString& ActionOut, int32& HeadChangeListOut) const override;
|
||||
virtual bool IsCurrent() const override;
|
||||
virtual bool IsSourceControlled() const override;
|
||||
virtual bool IsAdded() const override;
|
||||
virtual bool IsDeleted() const override;
|
||||
virtual bool IsIgnored() const override;
|
||||
virtual bool CanEdit() const override;
|
||||
virtual bool IsUnknown() const override;
|
||||
virtual bool IsModified() const override;
|
||||
virtual bool CanAdd() const override;
|
||||
virtual bool CanDelete() const override;
|
||||
virtual bool IsConflicted() const override;
|
||||
virtual bool CanRevert() const override;
|
||||
|
||||
private:
|
||||
EGitState::Type GetGitState() const;
|
||||
|
||||
public:
|
||||
/** History of the item, if any */
|
||||
TGitSourceControlHistory History;
|
||||
|
||||
/** Filename on disk */
|
||||
FString LocalFilename;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
|
||||
/** Pending rev info with which a file must be resolved, invalid if no resolve pending */
|
||||
FResolveInfo PendingResolveInfo;
|
||||
|
||||
UE_DEPRECATED(5.3, "Use PendingResolveInfo.BaseRevision instead")
|
||||
#endif
|
||||
/** File Id with which our local revision diverged from the remote revision */
|
||||
FString PendingMergeBaseFileHash;
|
||||
|
||||
/** Status of the file */
|
||||
FGitState State;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
FGitSourceControlChangelist Changelist;
|
||||
#endif
|
||||
|
||||
/** The timestamp of the last update */
|
||||
FDateTime TimeStamp;
|
||||
|
||||
/** The action within the head branch TODO */
|
||||
FString HeadAction;
|
||||
|
||||
/** The last file modification time in the head branch TODO */
|
||||
int64 HeadModTime;
|
||||
|
||||
/** The change list the last modification TODO */
|
||||
FString HeadCommit;
|
||||
};
|
||||
@@ -0,0 +1,393 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GitSourceControlRevision.h"
|
||||
#include "GitSourceControlState.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
#endif
|
||||
|
||||
class FGitSourceControlState;
|
||||
|
||||
class FGitSourceControlCommand;
|
||||
|
||||
/**
|
||||
* Helper struct for maintaining temporary files for passing to commands
|
||||
*/
|
||||
class FGitScopedTempFile
|
||||
{
|
||||
public:
|
||||
|
||||
/** Constructor - open & write string to temp file */
|
||||
FGitScopedTempFile(const FText& InText);
|
||||
|
||||
/** Destructor - delete temp file */
|
||||
~FGitScopedTempFile();
|
||||
|
||||
/** Get the filename of this temp file - empty if it failed to be created */
|
||||
const FString& GetFilename() const;
|
||||
|
||||
private:
|
||||
/** The filename we are writing to */
|
||||
FString Filename;
|
||||
};
|
||||
|
||||
struct FGitVersion;
|
||||
|
||||
class FGitLockedFilesCache
|
||||
{
|
||||
public:
|
||||
static FDateTime LastUpdated;
|
||||
|
||||
static const TMap<FString, FString>& GetLockedFiles() { return LockedFiles; }
|
||||
static void SetLockedFiles(const TMap<FString, FString>& newLocks);
|
||||
static void AddLockedFile(const FString& filePath, const FString& lockUser);
|
||||
static void RemoveLockedFile(const FString& filePath);
|
||||
|
||||
private:
|
||||
static void OnFileLockChanged(const FString& filePath, const FString& lockUser, bool locked);
|
||||
// update local read/write state when our own lock statuses change
|
||||
static TMap<FString, FString> LockedFiles;
|
||||
};
|
||||
|
||||
namespace GitSourceControlUtils
|
||||
{
|
||||
/**
|
||||
* Returns an updated repo root if all selected files are in a plugin subfolder, and the plugin subfolder is a git repo
|
||||
* This supports the case where each plugin is a sub module
|
||||
*
|
||||
* @param AbsoluteFilePaths The list of files in the SC operation
|
||||
* @param PathToRepositoryRoot The original path to the repository root (used by default)
|
||||
*/
|
||||
FString ChangeRepositoryRootIfSubmodule(TArray<FString>& AbsoluteFilePaths, const FString& PathToRepositoryRoot);
|
||||
|
||||
/**
|
||||
* Returns an updated repo root if all selected file is in a plugin subfolder, and the plugin subfolder is a git repo
|
||||
* This supports the case where each plugin is a sub module
|
||||
*
|
||||
* @param AbsoluteFilePath The file in the SC operation
|
||||
* @param PathToRepositoryRoot The original path to the repository root (used by default)
|
||||
*/
|
||||
FString ChangeRepositoryRootIfSubmodule(FString & AbsoluteFilePath, const FString& PathToRepositoryRoot);
|
||||
|
||||
/**
|
||||
* Find the path to the Git binary, looking into a few places (standalone Git install, and other common tools embedding Git)
|
||||
* @returns the path to the Git binary if found, or an empty string.
|
||||
*/
|
||||
FString FindGitBinaryPath();
|
||||
|
||||
/**
|
||||
* Run a Git "version" command to check the availability of the binary.
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param OutGitVersion If provided, populate with the git version parsed from "version" command
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool CheckGitAvailability(const FString& InPathToGitBinary, FGitVersion* OutVersion = nullptr);
|
||||
|
||||
/**
|
||||
* Parse the output from the "version" command into GitMajorVersion and GitMinorVersion.
|
||||
* @param InVersionString The version string returned by `git --version`
|
||||
* @param OutVersion The FGitVersion to populate
|
||||
*/
|
||||
void ParseGitVersion(const FString& InVersionString, FGitVersion* OutVersion);
|
||||
|
||||
/**
|
||||
* Check git for various optional capabilities by various means.
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param OutGitVersion If provided, populate with the git version parsed from "version" command
|
||||
*/
|
||||
void FindGitCapabilities(const FString& InPathToGitBinary, FGitVersion* OutVersion);
|
||||
|
||||
/**
|
||||
* Run a Git "lfs" command to check the availability of the "Large File System" extension.
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param OutGitVersion If provided, populate with the git version parsed from "version" command
|
||||
*/
|
||||
void FindGitLfsCapabilities(const FString& InPathToGitBinary, FGitVersion* OutVersion);
|
||||
|
||||
/**
|
||||
* Find the root of the Git repository, looking from the provided path and upward in its parent directories
|
||||
* @param InPath The path to the Game Directory (or any path or file in any git repository)
|
||||
* @param OutRepositoryRoot The path to the root directory of the Git repository if found, else the path to the ProjectDir
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool FindRootDirectory(const FString& InPath, FString& OutRepositoryRoot);
|
||||
|
||||
/**
|
||||
* Get Git config user.name & user.email
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory (can be empty)
|
||||
* @param OutUserName Name of the Git user configured for this repository (or globaly)
|
||||
* @param OutEmailName E-mail of the Git user configured for this repository (or globaly)
|
||||
*/
|
||||
void GetUserConfig(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutUserName, FString& OutUserEmail);
|
||||
|
||||
/**
|
||||
* Get Git current checked-out branch
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param OutBranchName Name of the current checked-out branch (if any, ie. not in detached HEAD)
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool GetBranchName(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutBranchName);
|
||||
|
||||
/**
|
||||
* Get Git remote tracking branch
|
||||
* @returns false if the branch is not tracking a remote
|
||||
*/
|
||||
bool GetRemoteBranchName(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutBranchName);
|
||||
|
||||
/**
|
||||
* Get Git remote tracking branches that match wildcard
|
||||
* @returns false if no matching branches
|
||||
*/
|
||||
bool GetRemoteBranchesWildcard(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& PatternMatch, TArray<FString>& OutBranchNames);
|
||||
|
||||
/**
|
||||
* Get Git current commit details
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param OutCommitId Current Commit full SHA1
|
||||
* @param OutCommitSummary Current Commit description's Summary
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool GetCommitInfo(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutCommitId, FString& OutCommitSummary);
|
||||
|
||||
/**
|
||||
* Get the URL of the "origin" defaut remote server
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param OutRemoteUrl URL of "origin" defaut remote server
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool GetRemoteUrl(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutRemoteUrl);
|
||||
|
||||
// A list of asset paths that we will always check locks, status, etc on startup and periodically
|
||||
TArray<FString> GetSourceControlledAssetPaths();
|
||||
|
||||
/**
|
||||
* Run a Git command - output is a string TArray.
|
||||
*
|
||||
* @param InCommand The Git command - e.g. commit
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory (can be empty)
|
||||
* @param InParameters The parameters to the Git command
|
||||
* @param InFiles The files to be operated on
|
||||
* @param OutResults The results (from StdOut) as an array per-line
|
||||
* @param OutErrorMessages Any errors (from StdErr) as an array per-line
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
GITSOURCECONTROL_API bool RunCommand( const FString & InCommand, const FString & InPathToGitBinary, const FString & InRepositoryRoot, const TArray< FString > & InParameters, const TArray< FString > & InFiles, TArray< FString > & OutResults, TArray< FString > & OutErrorMessages );
|
||||
bool RunCommandInternalRaw(const FString& InCommand, const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray<FString>& InParameters, const TArray<FString>& InFiles, FString& OutResults, FString& OutErrors, const int32 ExpectedReturnCode = 0);
|
||||
|
||||
/**
|
||||
* Unloads packages of specified named files
|
||||
*/
|
||||
TArray<class UPackage*> UnlinkPackages(const TArray<FString>& InPackageNames);
|
||||
|
||||
/**
|
||||
* Reloads packages for these packages
|
||||
*/
|
||||
void ReloadPackages(TArray<UPackage*>& InPackagesToReload);
|
||||
|
||||
/**
|
||||
* Gets all Git tracked files, including within directories, recursively
|
||||
*/
|
||||
bool ListFilesInDirectoryRecurse(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InDirectory, TArray<FString>& OutFiles);
|
||||
|
||||
/**
|
||||
* Run a Git "commit" command by batches.
|
||||
*
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param InParameter The parameters to the Git commit command
|
||||
* @param InFiles The files to be operated on
|
||||
* @param OutErrorMessages Any errors (from StdErr) as an array per-line
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool RunCommit(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray<FString>& InParameters, const TArray<FString>& InFiles, TArray<FString>& OutResults, TArray<FString>& OutErrorMessages);
|
||||
|
||||
/**
|
||||
* @brief Detects how to parse the result of a "status" command to get workspace file states
|
||||
*
|
||||
* It is either a command for a whole directory (ie. "Content/", in case of "Submit to Revision Control" menu),
|
||||
* or for one or more files all on a same directory (by design, since we group files by directory in RunUpdateStatus())
|
||||
*
|
||||
* @param[in] InPathToGitBinary The path to the Git binary
|
||||
* @param[in] InRepositoryRoot The Git repository from where to run the command - usually the Game directory (can be empty)
|
||||
* @param[in] InUsingLfsLocking Tells if using the Git LFS file Locking workflow
|
||||
* @param[in] InFiles List of files in a directory, or the path to the directory itself (never empty).
|
||||
* @param[out] InResults Results from the "status" command
|
||||
* @param[out] OutStates States of files for witch the status has been gathered (distinct than InFiles in case of a "directory status")
|
||||
*/
|
||||
GITSOURCECONTROL_API void ParseStatusResults( const FString & InPathToGitBinary, const FString & InRepositoryRoot, const bool InUsingLfsLocking, const TArray< FString > & InFiles, const TMap< FString, FString > & InResults, TMap< FString, FGitSourceControlState > & OutStates );
|
||||
|
||||
/**
|
||||
* Checks remote branches to see file differences.
|
||||
*
|
||||
* @param CurrentBranchName The current branch we are on.
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param OnePath The file to be checked
|
||||
* @param OutErrorMessages Any errors (from StdErr) as an array per-line
|
||||
*/
|
||||
void CheckRemote(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray<FString>& Files,
|
||||
TArray<FString>& OutErrorMessages, TMap<FString, FGitSourceControlState>& OutStates);
|
||||
|
||||
/**
|
||||
* Run a Git "status" command and parse it.
|
||||
*
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory (can be empty)
|
||||
* @param InUsingLfsLocking Tells if using the Git LFS file Locking workflow
|
||||
* @param InFiles The files to be operated on
|
||||
* @param OutErrorMessages Any errors (from StdErr) as an array per-line
|
||||
* @param OutStates The resultant states
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool RunUpdateStatus(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool InUsingLfsLocking, const TArray<FString>& InFiles,
|
||||
TArray<FString>& OutErrorMessages, TMap<FString, FGitSourceControlState>& OutStates);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
/**
|
||||
* Keep Consistency of being file staged
|
||||
*
|
||||
* @param Filename Saved filename
|
||||
* @param Pkg Package (for adapting delegate)
|
||||
* @param ObjectSaveContext Context for save (for adapting delegate)
|
||||
*/
|
||||
void UpdateFileStagingOnSaved(const FString& Filename, UPackage* Pkg, FObjectPostSaveContext ObjectSaveContext);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Keep Consistency of being file staged with simple argument
|
||||
*
|
||||
* @param Filename Saved filename
|
||||
*/
|
||||
bool UpdateFileStagingOnSavedInternal(const FString& Filename);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param Filename Saved filename
|
||||
* @param Pkg Package (for adapting delegate)
|
||||
* @param ObjectSaveContext Context for save (for adapting delegate)
|
||||
*/
|
||||
void UpdateStateOnAssetRename(const FAssetData& InAssetData, const FString& InOldName);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param Filename Saved filename
|
||||
* @param Pkg Package (for adapting delegate)
|
||||
* @param ObjectSaveContext Context for save (for adapting delegate)
|
||||
*/
|
||||
bool UpdateChangelistStateByCommand();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Run a Git "cat-file" command to dump the binary content of a revision into a file.
|
||||
*
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param InParameter The parameters to the Git show command (rev:path)
|
||||
* @param InDumpFileName The temporary file to dump the revision
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool RunDumpToFile(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InParameter, const FString& InDumpFileName);
|
||||
|
||||
/**
|
||||
* Run a Git "log" command and parse it.
|
||||
*
|
||||
* @param InPathToGitBinary The path to the Git binary
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param InFile The file to be operated on
|
||||
* @param 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)
|
||||
* @param OutErrorMessages Any errors (from StdErr) as an array per-line
|
||||
* @param OutHistory The history of the file
|
||||
*/
|
||||
bool RunGetHistory(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InFile, bool bMergeConflict, TArray<FString>& OutErrorMessages, TGitSourceControlHistory& OutHistory);
|
||||
|
||||
/**
|
||||
* Helper function to convert a filename array to relative paths.
|
||||
* @param InFileNames The filename array
|
||||
* @param InRelativeTo Path to the WorkspaceRoot
|
||||
* @return an array of filenames, transformed into relative paths
|
||||
*/
|
||||
TArray<FString> RelativeFilenames(const TArray<FString>& InFileNames, const FString& InRelativeTo);
|
||||
|
||||
/**
|
||||
* Helper function to convert a filename array to absolute paths.
|
||||
* @param InFileNames The filename array (relative paths)
|
||||
* @param InRelativeTo Path to the WorkspaceRoot
|
||||
* @return an array of filenames, transformed into absolute paths
|
||||
*/
|
||||
TArray<FString> AbsoluteFilenames(const TArray<FString>& InFileNames, const FString& InRelativeTo);
|
||||
|
||||
/**
|
||||
* Remove redundant errors (that contain a particular string) and also
|
||||
* update the commands success status if all errors were removed.
|
||||
*/
|
||||
void RemoveRedundantErrors(FGitSourceControlCommand& InCommand, const FString& InFilter);
|
||||
|
||||
bool RunLFSCommand(const FString& InCommand, const FString& InRepositoryRoot, const FString& GitBinaryFallback, const TArray<FString>& InParameters, const TArray<FString>& InFiles, TArray<FString>& OutResults, TArray<FString>& OutErrorMessages);
|
||||
|
||||
/**
|
||||
* Helper function for various commands to update cached states.
|
||||
* @returns true if any states were updated
|
||||
*/
|
||||
GITSOURCECONTROL_API bool UpdateCachedStates( const TMap< const FString, FGitState > & InResults );
|
||||
|
||||
/**
|
||||
* Helper function for various commands to collect new states.
|
||||
* @returns true if any states were updated
|
||||
*/
|
||||
GITSOURCECONTROL_API bool CollectNewStates( const TMap< FString, FGitSourceControlState > & InStates, TMap< const FString, FGitState > & OutResults );
|
||||
|
||||
/**
|
||||
* Helper function for various commands to collect new states.
|
||||
* @returns true if any states were updated
|
||||
*/
|
||||
bool CollectNewStates(const TArray<FString>& InFiles, TMap<const FString, FGitState>& OutResults, EFileState::Type FileState, ETreeState::Type TreeState = ETreeState::Unset, ELockState::Type LockState = ELockState::Unset, ERemoteState::Type RemoteState = ERemoteState::Unset);
|
||||
|
||||
/**
|
||||
* Run 'git lfs locks" to extract all lock information for all files in the repository
|
||||
*
|
||||
* @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory
|
||||
* @param GitBinaryFallBack The Git binary fallback path
|
||||
* @param OutErrorMessages Any errors (from StdErr) as an array per-line
|
||||
* @param OutLocks The lock results (file, username)
|
||||
* @returns true if the command succeeded and returned no errors
|
||||
*/
|
||||
bool GetAllLocks(const FString& InRepositoryRoot, const FString& GitBinaryFallBack, TArray<FString>& OutErrorMessages, TMap<FString, FString>& OutLocks, bool bInvalidateCache = false);
|
||||
|
||||
/**
|
||||
* Gets locks from state cache
|
||||
*/
|
||||
void GetLockedFiles(const TArray<FString>& InFiles, TArray<FString>& OutFiles);
|
||||
|
||||
/**
|
||||
* Checks cache for if this file type is lockable
|
||||
*/
|
||||
bool IsFileLFSLockable(const FString& InFile);
|
||||
|
||||
/**
|
||||
* Gets Git attribute to see if these extensions are lockable
|
||||
*/
|
||||
bool CheckLFSLockable(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray<FString>& InFiles, TArray<FString>& OutErrorMessages);
|
||||
|
||||
GITSOURCECONTROL_API bool FetchRemote( const FString & InPathToGitBinary, const FString & InPathToRepositoryRoot, bool InUsingGitLfsLocking, TArray< FString > & OutResults, TArray< FString > & OutErrorMessages );
|
||||
|
||||
bool PullOrigin(const FString& InPathToGitBinary, const FString& InPathToRepositoryRoot, const TArray<FString>& InFiles, TArray<FString>& OutFiles,
|
||||
TArray<FString>& OutResults, TArray<FString>& OutErrorMessages);
|
||||
|
||||
|
||||
GITSOURCECONTROL_API TSharedPtr< class ISourceControlRevision, ESPMode::ThreadSafe > GetOriginRevisionOnBranch( const FString & InPathToGitBinary, const FString & InRepositoryRoot, const FString & InRelativeFileName, TArray< FString > & OutErrorMessages, const FString & BranchName );
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com)
|
||||
//
|
||||
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
|
||||
// or copy at http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Templates/SharedPointer.h"
|
||||
|
||||
class IGitSourceControlWorker
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Name describing the work that this worker does. Used for factory method hookup.
|
||||
*/
|
||||
virtual FName GetName() const = 0;
|
||||
|
||||
/**
|
||||
* Function that actually does the work. Can be executed on another thread.
|
||||
*/
|
||||
virtual bool Execute( class FGitSourceControlCommand& InCommand ) = 0;
|
||||
|
||||
/**
|
||||
* Updates the state of any items after completion (if necessary). This is always executed on the main thread.
|
||||
* @returns true if states were updated
|
||||
*/
|
||||
virtual bool UpdateStates() const = 0;
|
||||
};
|
||||
|
||||
typedef TSharedRef<IGitSourceControlWorker, ESPMode::ThreadSafe> FGitSourceControlWorkerRef;
|
||||
Reference in New Issue
Block a user