diff --git a/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.cpp b/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.cpp index 383feb8e..15d50ddc 100644 --- a/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.cpp +++ b/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.cpp @@ -35,10 +35,13 @@ void FPlasticSourceControlProvider::CheckPlasticAvailability() // Find the path to the root Plastic directory (if any) const FString PathToGameDir = FPaths::ConvertRelativePathToFull(FPaths::GameDir()); const bool bRepositoryFound = PlasticSourceControlUtils::FindRootDirectory(PathToGameDir, PathToRepositoryRoot); + // Get user name (from the global Plastic SCM client config) + PlasticSourceControlUtils::GetUserName(UserName); if (bRepositoryFound) { - // Get branch name - PlasticSourceControlUtils::GetBranchName(BranchName); + // Get workspace name and branch name + PlasticSourceControlUtils::GetWorkspaceName(PathToRepositoryRoot, WorkspaceName); + PlasticSourceControlUtils::GetBranchName(PathToRepositoryRoot, BranchName); bPlasticRepositoryFound = true; } else @@ -82,10 +85,11 @@ TSharedRef FPlasticSourceContro FText FPlasticSourceControlProvider::GetStatusText() const { FFormatNamedArguments Args; - Args.Add( TEXT("RepositoryName"), FText::FromString(PathToRepositoryRoot) ); + Args.Add( TEXT("WorkspacePath"), FText::FromString(PathToRepositoryRoot) ); Args.Add( TEXT("BranchName"), FText::FromString(BranchName) ); + Args.Add( TEXT("UserName"), FText::FromString(UserName) ); - return FText::Format( NSLOCTEXT("Status", "Provider: Plastic\nEnabledLabel", "Workspace: {RepositoryName}\n{BranchName}"), Args ); + return FText::Format( NSLOCTEXT("Status", "Provider: Plastic\nEnabledLabel", "Workspace: {WorkspacePath}\n{BranchName}\nUser: {UserName}"), Args ); } /** Quick check if source control is enabled */ diff --git a/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.h b/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.h index 8c6a6325..bafd3893 100644 --- a/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.h +++ b/Source/PlasticSourceControl/Private/PlasticSourceControlProvider.h @@ -57,6 +57,24 @@ class FPlasticSourceControlProvider : public ISourceControlProvider return PathToRepositoryRoot; } + /** Get the Plastic current user */ + inline const FString& GetUserName() const + { + return UserName; + } + + /** Get the Plastic current workspace */ + inline const FString& GetWorkspaceName() const + { + return WorkspaceName; + } + + /** Get the Name of the current branch */ + inline const FString& GetBranchName() const + { + return BranchName; + } + /** Helper function used to update state cache */ TSharedRef GetStateInternal(const FString& Filename); @@ -89,7 +107,13 @@ class FPlasticSourceControlProvider : public ISourceControlProvider void OutputCommandMessages(const class FPlasticSourceControlCommand& InCommand) const; /** Path to the root of the Plastic repository: can be the GameDir itself, or any parent directory (found by the "Connect" operation) */ - FString PathToRepositoryRoot; + FString PathToRepositoryRoot; // TODO rename to PathToWorkspaceRoot + + /** Plastic current user */ + FString UserName; + + /** Plastic current workspace */ + FString WorkspaceName; /** Name of the current branch */ FString BranchName; diff --git a/Source/PlasticSourceControl/Private/PlasticSourceControlState.cpp b/Source/PlasticSourceControl/Private/PlasticSourceControlState.cpp index 8331543a..b4afcac6 100644 --- a/Source/PlasticSourceControl/Private/PlasticSourceControlState.cpp +++ b/Source/PlasticSourceControl/Private/PlasticSourceControlState.cpp @@ -58,6 +58,11 @@ TSharedPtr FPlasticSourceCont FName FPlasticSourceControlState::GetIconName() const { + if (!IsCurrent()) + { + return FName("Perforce.NotAtHeadRevision"); + } + switch(WorkspaceState) { case EWorkspaceState::CheckedOut: @@ -72,6 +77,8 @@ FName FPlasticSourceControlState::GetIconName() const case EWorkspaceState::Changed: case EWorkspaceState::Conflicted: return FName("Perforce.NotAtHeadRevision"); + case EWorkspaceState::LockedByOther: + return FName("Perforce.CheckedOutByOtherUser"); case EWorkspaceState::Private: return FName("Perforce.NotInDepot"); case EWorkspaceState::Unknown: @@ -84,6 +91,11 @@ FName FPlasticSourceControlState::GetIconName() const FName FPlasticSourceControlState::GetSmallIconName() const { + if (!IsCurrent()) + { + return FName("Perforce.NotAtHeadRevision_Small"); + } + switch(WorkspaceState) { case EWorkspaceState::CheckedOut: @@ -98,6 +110,8 @@ FName FPlasticSourceControlState::GetSmallIconName() const case EWorkspaceState::Changed: case EWorkspaceState::Conflicted: return FName("Perforce.NotAtHeadRevision_Small"); + case EWorkspaceState::LockedByOther: + return FName("Perforce.CheckedOutByOtherUser_Small"); case EWorkspaceState::Private: return FName("Perforce.NotInDepot_Small"); case EWorkspaceState::Unknown: @@ -134,6 +148,8 @@ FText FPlasticSourceControlState::GetDisplayName() const return LOCTEXT("Changed", "Changed"); case EWorkspaceState::Conflicted: return LOCTEXT("ContentsConflict", "Contents Conflict"); + case EWorkspaceState::LockedByOther: + return FText::Format(LOCTEXT("CheckedOutOther", "Checked out by: {0}"), FText::FromString(LockedBy)); case EWorkspaceState::Private: return LOCTEXT("NotControlled", "Not Under Source Control"); } @@ -224,9 +240,11 @@ bool FPlasticSourceControlState::IsCheckedOutOther(FString* Who) const { *Who = LockedBy; } - // TODO return State == EPerforceState::CheckedOutOther; Does Plastic uses a specific state? - if(0 < LockedBy.Len()) UE_LOG(LogSourceControl, Log, TEXT("IsCheckedOutOther(%s)=%s"), *LocalFilename, *LockedBy); - return (0 < LockedBy.Len()); + const bool bIsLockedByOther = WorkspaceState == EWorkspaceState::LockedByOther; + + if (bIsLockedByOther) UE_LOG(LogSourceControl, Log, TEXT("IsCheckedOutOther(%s)=%d '%s' '%s'"), *LocalFilename, bIsLockedByOther, *LockedBy, *LockedWhere); + + return bIsLockedByOther; } bool FPlasticSourceControlState::IsCurrent() const diff --git a/Source/PlasticSourceControl/Private/PlasticSourceControlState.h b/Source/PlasticSourceControl/Private/PlasticSourceControlState.h index 29bd8621..a146945b 100644 --- a/Source/PlasticSourceControl/Private/PlasticSourceControlState.h +++ b/Source/PlasticSourceControl/Private/PlasticSourceControlState.h @@ -20,6 +20,7 @@ namespace EWorkspaceState Deleted, Changed, // Modified but not CheckedOut Conflicted, + LockedByOther, // LockedBy with name of someone else than cm whoami Private, // "Not Controlled"/"Not In Depot"/"Untraked" }; } @@ -71,9 +72,12 @@ class FPlasticSourceControlState : public ISourceControlState, public TSharedFro /** File Id with which our local revision diverged from the remote revision */ FString PendingMergeBaseFileHash; - /** If another user has this file checked out, this contains his name. */ + /** If a user (another or ourself) has this file locked, this contains his name. */ FString LockedBy; + /** Location of the locked file. */ + FString LockedWhere; + /** State of the workspace */ EWorkspaceState::Type WorkspaceState; diff --git a/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.cpp b/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.cpp index 2c8caa2a..9ba6c705 100644 --- a/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.cpp +++ b/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.cpp @@ -258,16 +258,41 @@ bool FindRootDirectory(const FString& InPathToGameDir, FString& OutRepositoryRoo return bFound; } -void GetBranchName(FString& OutBranchName) +void GetUserName(FString& OutUserName) +{ + TArray InfoMessages; + TArray ErrorMessages; + const bool bResults = RunCommandInternal(TEXT("whoami"), TArray(), TArray(), InfoMessages, ErrorMessages); + if (bResults && InfoMessages.Num() > 0) + { + OutUserName = InfoMessages[0]; + } +} + +void GetWorkspaceName(const FString& InRepositoryRoot, FString& OutWorkspaceName) { - bool bResults; TArray InfoMessages; TArray ErrorMessages; TArray Parameters; + Parameters.Add(InRepositoryRoot); + Parameters.Add(TEXT("--format={0}")); + const bool bResults = RunCommandInternal(TEXT("getworkspacefrompath"), Parameters, TArray(), InfoMessages, ErrorMessages); + if (bResults && InfoMessages.Num() > 0) + { + OutWorkspaceName = InfoMessages[0]; + } +} + +void GetBranchName(const FString& InRepositoryRoot, FString& OutBranchName) +{ + TArray InfoMessages; + TArray ErrorMessages; + TArray Parameters; + Parameters.Add(InRepositoryRoot); Parameters.Add(TEXT("--wkconfig")); Parameters.Add(TEXT("--nochanges")); Parameters.Add(TEXT("--nostatus")); - bResults = RunCommandInternal(TEXT("status"), Parameters, TArray(), InfoMessages, ErrorMessages); + const bool bResults = RunCommandInternal(TEXT("status"), Parameters, TArray(), InfoMessages, ErrorMessages); if(bResults && InfoMessages.Num() > 0) { OutBranchName = InfoMessages[0]; @@ -486,7 +511,7 @@ static bool RunStatus(const TArray& InFiles, TArray& OutErrorM return bResult; } -// Parse the fileinfo output format "{RevisionChangeset};{RevisionHeadChangeset};{LockedBy}" +// Parse the fileinfo output format "{RevisionChangeset};{RevisionHeadChangeset};{LockedBy};{LockedWhere}" class FPlasticFileinfoParser { public: @@ -501,6 +526,10 @@ class FPlasticFileinfoParser if (NbElmts >= 3) { LockedBy = MoveTemp(Fileinfos[2]); + if (NbElmts >=4) + { + LockedWhere = MoveTemp(Fileinfos[3]); + } } } } @@ -508,17 +537,21 @@ class FPlasticFileinfoParser int32 RevisionChangeset; int32 RevisionHeadChangeset; FString LockedBy; + FString LockedWhere; }; -/** Parse the array of strings results of a 'cm fileinfo --format="{RevisionChangeset};{RevisionHeadChangeset};{LockedBy}"' command +/** Parse the array of strings results of a 'cm fileinfo --format="{RevisionChangeset};{RevisionHeadChangeset};{LockedBy};{LockedWhere}"' command * * Example cm fileinfo results: -16;16;False;; -14;14;False;; -16;16;False;; +16;16;; +14;15;; +17;17;srombauts;Workspace_2 */ static void ParseFileinfoResults(const TArray& InFiles, const TArray& InResults, TArray& InOutStates) { + FPlasticSourceControlModule& PlasticSourceControl = FModuleManager::LoadModuleChecked("PlasticSourceControl"); + FPlasticSourceControlProvider& Provider = PlasticSourceControl.GetProvider(); + // Iterate on all files and all status of the result (assuming no more line of results than number of files) for (int32 IdxResult = 0; IdxResult < InResults.Num(); IdxResult++) { @@ -530,9 +563,15 @@ static void ParseFileinfoResults(const TArray& InFiles, const TArray& InFiles, TArray& OutErro { TArray Results; TArray Parameters; - Parameters.Add(TEXT("--format=\"{RevisionChangeset};{RevisionHeadChangeset};{LockedBy}\"")); + Parameters.Add(TEXT("--format=\"{RevisionChangeset};{RevisionHeadChangeset};{LockedBy};{LockedWhere}\"")); TArray ErrorMessages; const bool bResult = RunCommand(TEXT("fileinfo"), Parameters, InFiles, Results, ErrorMessages); @@ -558,7 +597,7 @@ static bool RunFileinfo(const TArray& InFiles, TArray& OutErro bool RunUpdateStatus(const TArray& InFiles, TArray& OutErrorMessages, TArray& OutStates) { bool bResult; - + // Run a "status" command for each file to get workspace states bResult = RunStatus(InFiles, OutErrorMessages, OutStates); diff --git a/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.h b/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.h index 8ac754d6..59bfd3a4 100644 --- a/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.h +++ b/Source/PlasticSourceControl/Private/PlasticSourceControlUtils.h @@ -54,11 +54,25 @@ void Terminate(); */ bool FindRootDirectory(const FString& InPathToGameDir, FString& OutRepositoryRoot); +/** + * Get Plastic SCM current user + * @param OutUserName Name of the Plastic SCM user configured globaly + */ +void GetUserName(FString& OutUserName); + +/** + * Get Plastic workspace name + * @param InRepositoryRoot The workspace from where to run the command - usually the Game directory (can be empty) + * @param OutWorkspaceName Name of the current workspace + */ +void GetWorkspaceName(const FString& InRepositoryRoot, FString& OutWorkspaceName); + /** * Get Plastic current checked-out branch - * @param OutBranchName Name of the current checked-out branch (if any, ie. not in detached HEAD) + * @param InRepositoryRoot The workspace from where to run the command - usually the Game directory (can be empty) + * @param OutBranchName Name of the current checked-out branch (if any, ie. not in detached HEAD) */ -void GetBranchName(FString& OutBranchName); +void GetBranchName(const FString& InRepositoryRoot, FString& OutBranchName); /** * Run a Plastic command - output is a string TArray.