diff --git a/Source/GitSourceControl/Private/GitSourceControlOperations.cpp b/Source/GitSourceControl/Private/GitSourceControlOperations.cpp index 64c5c7b3..dea854a5 100644 --- a/Source/GitSourceControl/Private/GitSourceControlOperations.cpp +++ b/Source/GitSourceControl/Private/GitSourceControlOperations.cpp @@ -12,6 +12,7 @@ #include "GitSourceControlModule.h" #include "GitSourceControlCommand.h" #include "GitSourceControlUtils.h" +#include "Logging/MessageLog.h" #define LOCTEXT_NAMESPACE "GitSourceControl" @@ -188,11 +189,72 @@ bool FGitCheckInWorker::Execute(FGitSourceControlCommand& InCommand) // git-lfs: push and unlock files if(InCommand.bUsingGitLfsLocking && InCommand.bCommandSuccessful) { - TArray Parameters2; - // TODO Configure origin - Parameters2.Add(TEXT("origin")); - Parameters2.Add(TEXT("HEAD")); + TArray Parameters2; + // TODO Configure origin + Parameters2.Add(TEXT("origin")); + Parameters2.Add(TEXT("HEAD")); InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("push"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters2, TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); + if(!InCommand.bCommandSuccessful) + { + // if out of date, pull first, then try again + bool bWasOutOfDate = false; + for (const auto& PushError : InCommand.ErrorMessages) + { + if (PushError.Contains(TEXT("[rejected]")) && PushError.Contains(TEXT("non-fast-forward"))) + { + // Don't do it during iteration, want to append pull results to InCommand.ErrorMessages + bWasOutOfDate = true; + break; + } + } + if (bWasOutOfDate) + { + UE_LOG(LogSourceControl, Log, TEXT("Push failed because we're out of date, pulling automatically to try to resolve")); + // Use pull --rebase since that's what the pull command does by default + // This requires that we stash if dirty working copy though + bool bStashed = false; + bool bStashNeeded = false; + const TArray ParametersStatus{"--porcelain --untracked-files=no"}; + TArray StatusInfoMessages; + TArray StatusErrorMessages; + // Check if there is any modification to the working tree + const bool bStatusOk = GitSourceControlUtils::RunCommand(TEXT("status"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, ParametersStatus, TArray(), StatusInfoMessages, StatusErrorMessages); + if ((bStatusOk) && (StatusInfoMessages.Num() > 0)) + { + bStashNeeded = true; + const TArray ParametersStash{ "save \"Stashed by Unreal Engine Git Plugin\"" }; + bStashed = GitSourceControlUtils::RunCommand(TEXT("stash"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, ParametersStash, TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); + if (!bStashed) + { + FMessageLog SourceControlLog("SourceControl"); + SourceControlLog.Warning(LOCTEXT("SourceControlMenu_StashFailed", "Stashing away modifications failed!")); + SourceControlLog.Notify(); + } + } + if (!bStashNeeded || bStashed) + { + InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("pull --rebase"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); + if (InCommand.bCommandSuccessful) + { + // Repeat the push + InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("push origin HEAD"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); + } + + // Succeed or fail, restore the stash + if (bStashed) + { + const TArray ParametersStashPop{ "pop" }; + InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("stash"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, ParametersStashPop, TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); + if (!InCommand.bCommandSuccessful) + { + FMessageLog SourceControlLog("SourceControl"); + SourceControlLog.Warning(LOCTEXT("SourceControlMenu_UnstashFailed", "Unstashing previously saved modifications failed!")); + SourceControlLog.Notify(); + } + } + } + } + } if(InCommand.bCommandSuccessful) { // unlock files: execute the LFS command on relative filenames