diff --git a/Config/steamvr_ue_editor_app.json b/Config/steamvr_ue_editor_app.json index 4a9d08f..cff9b96 100644 --- a/Config/steamvr_ue_editor_app.json +++ b/Config/steamvr_ue_editor_app.json @@ -5,7 +5,7 @@ "app_key": "application.generated.ue.bookworm-17735300.ue4editor.exe", "launch_type": "url", "url": "steam://launch/", - "action_manifest_path": "D:/UnrealProjects/BookWorm/Config/SteamVRBindings/steamvr_manifest.json", + "action_manifest_path": "P:/UnrealProjects/BookWorm/Config/SteamVRBindings/steamvr_manifest.json", "strings": { "en_us": diff --git a/Content/Materials/Book/CoverImages/DecalInstance.uasset b/Content/Materials/Book/CoverImages/DecalInstance.uasset new file mode 100644 index 0000000..523d868 --- /dev/null +++ b/Content/Materials/Book/CoverImages/DecalInstance.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d56625eeb95d2d153c8c9d51f05f8b84d58f0be2e9bc5da456609c6043e2fe53 +size 112796 diff --git a/Content/blueprints/BP_CollectableBook.uasset b/Content/blueprints/BP_CollectableBook.uasset index d10d2ad..22bd4f8 100644 --- a/Content/blueprints/BP_CollectableBook.uasset +++ b/Content/blueprints/BP_CollectableBook.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce28c8dc49ab720da5dc8ded7b7bfe91b79105dbf35ce8d1de9b5eec85966d53 -size 191860 +oid sha256:b848a71149840eae49083adb40cbb16c1c1430666b4a3c54fc725714abe89c16 +size 210706 diff --git a/Plugins/ProceduralDungeon/ProceduralDungeon.uplugin b/Plugins/ProceduralDungeon/ProceduralDungeon.uplugin index 57552ff..aab0515 100644 --- a/Plugins/ProceduralDungeon/ProceduralDungeon.uplugin +++ b/Plugins/ProceduralDungeon/ProceduralDungeon.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "2.0.0", + "VersionName": "2.0.1", "FriendlyName": "ProceduralDungeon", "Description": "Create procedural dungeons like \"The Binding of Isaac\" or \"Rogue Legacy\" but in 3D.\r\nYou can define your own generation rules.", "Category": "Procedural", diff --git a/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Private/DungeonGenerator.cpp b/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Private/DungeonGenerator.cpp index 4f4becc..9529a1f 100644 --- a/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Private/DungeonGenerator.cpp +++ b/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Private/DungeonGenerator.cpp @@ -158,7 +158,6 @@ void ADungeonGenerator::CreateDungeon() } while (TriesLeft > 0 && !IsValidDungeon()); } - void ADungeonGenerator::InstantiateRoom(URoom* Room) { // Instantiate room @@ -257,7 +256,6 @@ TArray ADungeonGenerator::AddNewRooms(URoom& ParentRoom) return newRooms; } - void ADungeonGenerator::LoadAllRooms() { // When a level is correct, load all rooms @@ -544,3 +542,50 @@ int ADungeonGenerator::CountTotalRoomData(TArray RoomDataList) } return count; } + +bool ADungeonGenerator::HasAlreadyRoomType(TSubclassOf RoomType) +{ + return CountRoomType(RoomType) > 0; +} + +bool ADungeonGenerator::HasAlreadyOneRoomTypeFrom(TArray> RoomTypeList) +{ + return CountTotalRoomType(RoomTypeList) > 0; +} + +int ADungeonGenerator::CountRoomType(TSubclassOf RoomType) +{ + int count = 0; + for (int i = 0; i < RoomList.Num(); i++) + { + if (RoomList[i]->GetRoomData()->GetClass()->IsChildOf(RoomType)) + { + count++; + } + } + return count; +} + +int ADungeonGenerator::CountTotalRoomType(TArray> RoomTypeList) +{ + int count = 0; + for (int i = 0; i < RoomList.Num(); i++) + { + URoomData* roomData = RoomList[i]->GetRoomData(); + if (RoomTypeList.ContainsByPredicate([&roomData](const TSubclassOf roomType) { return roomData->GetClass()->IsChildOf(roomType); } )) + { + count++; + } + } + return count; +} + +void ADungeonGenerator::SetSeed(int32 NewSeed) +{ + Seed = static_cast(NewSeed); +} + +int32 ADungeonGenerator::GetSeed() +{ + return static_cast(Seed); +} \ No newline at end of file diff --git a/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Public/DungeonGenerator.h b/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Public/DungeonGenerator.h index 818dd3a..73d616f 100644 --- a/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Public/DungeonGenerator.h +++ b/Plugins/ProceduralDungeon/Source/ProceduralDungeon/Public/DungeonGenerator.h @@ -116,6 +116,22 @@ public: UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Dungeon Generator") int CountTotalRoomData(TArray RoomDataList); + // Return true if a specific RoomData type is already in the dungeon + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Dungeon Generator") + bool HasAlreadyRoomType(TSubclassOf RoomType); + + // Return true if at least one of the RoomData type from the list provided is already in the dungeon + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Dungeon Generator") + bool HasAlreadyOneRoomTypeFrom(TArray> RoomTypeList); + + // Return the number of a specific RoomData type in the dungeon + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Dungeon Generator") + int CountRoomType(TSubclassOf RoomType); + + // Return the total number of RoomData type in the dungeon from the list provided + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Dungeon Generator") + int CountTotalRoomType(TArray> RoomTypeList); + // Return a random RoomData from the array provided UFUNCTION(BlueprintCallable, Category = "Dungeon Generator") URoomData* GetRandomRoomData(TArray RoomDataArray); @@ -220,11 +236,19 @@ private: UFUNCTION() void DispatchRoomAdded(URoomData* NewRoom); -private: - UPROPERTY(EditAnywhere, Category = "Procedural Generation") +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Procedural Generation") EGenerationType GenerationType; - UPROPERTY(EditAnywhere, Category = "Procedural Generation") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Procedural Generation") ESeedType SeedType; + + UFUNCTION(BlueprintCallable, Category = "Dungeon Generator") + void SetSeed(int32 NewSeed); + + UFUNCTION(BlueprintPure, Category = "Dungeon Generator", meta=(CompactNodeTitle = "Seed")) + int32 GetSeed(); + +private: UPROPERTY(EditAnywhere, Category = "Procedural Generation") uint32 Seed; diff --git a/Plugins/UE4GitPlugin-2.17-beta/.gitbugtraq b/Plugins/UE4GitPlugin-2.17-beta/.gitbugtraq deleted file mode 100644 index e47406d..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/.gitbugtraq +++ /dev/null @@ -1,7 +0,0 @@ -# .gitbugtraq for Git GUIs (SmartGit/TortoiseGit) to show links to the Github issue tracker. -# Instead of the repository root directory, it could be added as an additional section to $GIT_DIR/config. -# (note that '\' need to be escaped). -[bugtraq] - url = https://github.com/SRombauts/UE4GitPlugin/issues/%BUGID% - loglinkregex = "#\\d+" - logregex = \\d+ diff --git a/Plugins/UE4GitPlugin-2.17-beta/.gitignore b/Plugins/UE4GitPlugin-2.17-beta/.gitignore deleted file mode 100644 index 2ca2cfb..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/Binaries/*/*.pdb -/Binaries/*/*Debug* -/Binaries/*/*.dylib -/Binaries/*/*.modules -/Intermediate diff --git a/Plugins/UE4GitPlugin-2.17-beta/GitSourceControl.uplugin b/Plugins/UE4GitPlugin-2.17-beta/GitSourceControl.uplugin deleted file mode 100644 index 661c4b8..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/GitSourceControl.uplugin +++ /dev/null @@ -1,25 +0,0 @@ -{ - "FileVersion" : 3, - "Version" : 37, - "VersionName" : "2.17", - "FriendlyName" : "Git LFS 2", - "Description" : "Git source control management (dev)", - "Category" : "Source Control", - "CreatedBy" : "SRombauts", - "CreatedByURL" : "http://srombauts.github.com", - "DocsURL" : "", - "MarketplaceURL" : "", - "SupportURL" : "", - "EnabledByDefault" : true, - "CanContainContent" : false, - "IsBetaVersion" : true, - "Installed" : false, - "Modules" : - [ - { - "Name" : "GitSourceControl", - "Type" : "Editor", - "LoadingPhase" : "Default" - } - ] -} \ No newline at end of file diff --git a/Plugins/UE4GitPlugin-2.17-beta/LICENSE.txt b/Plugins/UE4GitPlugin-2.17-beta/LICENSE.txt deleted file mode 100644 index 4cdc13c..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Plugins/UE4GitPlugin-2.17-beta/README.md b/Plugins/UE4GitPlugin-2.17-beta/README.md deleted file mode 100644 index 4c0ce4d..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/README.md +++ /dev/null @@ -1,201 +0,0 @@ -Unreal Engine 4 Git Source Control Plugin ------------------------------------------ - -[![release](https://img.shields.io/github/release/SRombauts/UE4GitPlugin.svg)](https://github.com/SRombauts/UE4GitPlugin/releases) -[![Git Plugin issues](https://img.shields.io/github/issues/SRombauts/UE4GitPlugin.svg)](https://github.com/SRombauts/UE4GitPlugin/issues) -[![Join the chat at https://gitter.im/SRombauts/UE4GitPlugin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SRombauts/UE4GitPlugin) -UE4GitPlugin is a simple Git Source Control Plugin for **Unreal Engine 4.26**. - -Developed and contributed by Sébastien Rombauts 2014-2020 (sebastien.rombauts@gmail.com) - - -- First version of the plugin has been **integrated by default in UE4.7 in "beta version"**. -- This is a developement fork named "**Git LFS 2**" adding File Locks supported by Github. - -You need to install it into your Project **Plugins/** folder, and it will overwrite (replace) the default "Git (beta version)" Source Control Provider with the "Git LFS 2" plugin. - -Have a look at the [Git Plugin Tutorial on the Wiki](https://wiki.unrealengine.com/Git_source_control_%28Tutorial%29). ([alternate link](https://michaeljcole.github.io/wiki.unrealengine.com/Git_source_control_%28Tutorial%29/)) - -Written and contributed by Sebastien Rombauts (sebastien.rombauts@gmail.com) - -Source Control Login window to create a new workspace/a new repository: -![Source Control Login window - create a new repository](Screenshots/SourceControlLogin_Init.png) - -Source Control status tooltip, when hovering the Source Control icon in toolbar: -![Source Control Status Tooltip](Screenshots/SourceControlStatusTooltip.png) - -Source Control top Menu, extended with a few commands specific to Git: -![Source Control Status Tooltip](Screenshots/SourceControlMenu.png) - -Submit Files to Source Control window, to commit assets: -![Submit Files to Source Control](Screenshots/SubmitFiles.png) - -File History window, to see the changelog of an asset: -![History of a file](Screenshots/FileHistory.png) - -Visual Diffing of two revisions of a Blueprint: - - -Merge conflict of a Blueprint: - - -Status Icons: - -![New/Unsaved/Untracked](Screenshots/Icons/New.png) -![Added](Screenshots/Icons/Added.png) -![Unchanged](Screenshots/Icons/Unchanged.png) -![Modified](Screenshots/Icons/Modified.png) -![Moved/Renamed](Screenshots/Icons/Renamed.png) - -### Supported features -- initialize a new Git local repository ('git init') to manage your UE4 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 enable Git LFS 2.x File Locks as part of initialization - - can also make the initial commit, with custom multi-line message -- display status icons to show modified/added/deleted/untracked files, not at head and conflicted -- show history of a file -- visual diff of a blueprint against depot or between previous versions of a file -- revert modifications of a file (works best with "Content Hot-Reload" experimental option of UE4.15, by default since 4.16) -- 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 -- Configure remote origin URL ('git remote add origin url') -- Sync to Pull (rebase) the current branch if there is no local modified files -- Push the current branch -- Git LFS (Github, Gitlab, Bitbucket), git-annex, git-fat and git-media are working with Git 2.10+ -- Git LFS 2 File Locks -- Windows, Mac and Linux - -### What *cannot* be done presently -- Branch/Merge are not in the current Editor workflow -- Amend a commit is not in the current Editor workflow -- Revert All (using either "Stash" or "reset --hard") -- Configure user name & email ('git config user.name' & git config user.email') -- Authentication is not managed if needed for Sync (Pull) - -### Known issues -- #34 "outside repository" fatal error -- #37 Rebase workflow: conflicts not detected! -- #41 UE-44637: Deleting an asset is unsuccessful if the asset is marked for add (since UE4.13) -- #46 Merge Conflicts - Accept Target - causes engine to crash bug -- #47 Git LFS conflict resolution not working -- #49 Git LFS 2: False error in logs after a successful push -- #51 Git LFS 2: cannot revert a modified/unchecked-out asset -- #53 Git LFS 2: document the configuration and workflow -- #54 Poor performances of 'lfs locks' on Windows command line -- #55 Git LFS 2: Unlocking a renamed asset - -- missing localisation for git specific messages -- displaying states of 'Engine' assets (also needs management of 'out of tree' files) -- renaming a Blueprint in Editor leaves a redirector file, AND modify too much the asset to enable git to track its history through renaming - -### Getting started - -Quick demo of the Git Plugin on Unreal Engine 4.12 (preview) -[![Git Plugin on Unreal Engine 4.12 (preview)](https://img.youtube.com/vi/rRhPl9vL58Q/0.jpg)](https://youtu.be/rRhPl9vL58Q) - -#### Install Git - -Under Windows 64bits, you should install the standard standalone Git for Windows -(now comming with Git LFS 2 with File Locking) with default parameters, -usually in "C:\Program Files\Git\bin\git.exe". - -Then you have to configure your name and e-mail that will appear in each of your commits: - -``` -git config --global user.name "Sébastien Rombauts" -git config --global user.email sebastien.rombauts@gmail.com -``` - -#### Install this Git Plugin (dev) into your Game Project - -Unreal Engine comes with a stable version of this plugin, so no need to install it. - -This alternate "Git development plugin" needs to be installed into a subfolder or your Game Project "Plugins" directory -(that is, you cannot install it into the Engine Plugins directory): - -``` -/Plugins -``` - -You will obviously only be able to use the plugin within this project. - -See also the [Plugins official Documentation](https://docs.unrealengine.com/latest/INT/Programming/Plugins/index.html) - -#### Activate Git Source Control for your Game Project - -Load your Game Project in Unreal Engine, then open: - -``` -File->Connect To Source Control... -> Git -``` - -##### Project already managed by Git - -If your project is already under Git (it contains a ".git" subfolder), just click on "Accept Settings". This connect the Editor to your local Git repository ("Depot"). - -##### Project not already under Git - -Otherwise, the Git Plugin is able to create (initialize) a new local Git Repository with your project Assets and Sources files: - - - -Click "Initialize project with Git" that will add all relevant files to source control and make the initial commit with the customizable message. -When everything is done, click on "Accept Settings". - -#### Using the Git Source Control Provider in the Unreal Engine Editor - -The plugin mostly interacts with you local Git repository ("Depot"), not much with the remote server (usually "origin"). - -It displays Git status icons on top of assets in the Asset Browser: -- No icon means that the file is under source control and unchanged since last commit, or ignored. -- A red mark is for "modified" assets, that is the one that needs to be committed (so not the same as "Check-out" in Perforce/SVN/Plastic SCM). -- A red cross is for "added" assets, that also needs to be committed -- A blue lightning means "renamed". -- A yellow exclamation point is for files in conflict after a merge, or is not at head (latest revision on the current remote branch). -- A yellow question mark is for files not in source control. - -TODO: -- specifics of rename and redirectors, and "Fix Up Redirector in Folder" command -- history / visual diff -- CheckIn = Commit -- CheckOut = Commit+Push+unlock (when using LFS 2) - -See also the [Source Control official Documentation](https://docs.unrealengine.com/latest/INT/Engine/UI/SourceControl/index.html) - -### License - -Copyright (c) 2014-2020 Sébastien Rombauts (sebastien.rombauts@gmail.com) - -Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt -or copy at http://opensource.org/licenses/MIT) - -## How to contribute -### GitHub website -The most efficient way to help and contribute to this wrapper project is to -use the tools provided by GitHub: -- please fill bug reports and feature requests here: https://github.com/SRombauts/UE4GitPlugin/issues -- fork the repository, make some small changes and submit them with independent pull-requests - -### Contact -- You can use the Unreal Engine forums. -- You can also email me directly, I will answer any questions and requests. - -### Coding Style Guidelines -The source code follow the UnreaEngine official [Coding Standard](https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html): -- CamelCase naming convention, with a prefix letter to differentiate classes ('F'), interfaces ('I'), templates ('T') -- files (.cpp/.h) are named like the class they contains -- Doxygen comments, documentation is located with declaration, on headers -- Use portable common features of C++11 like nullptr, auto, range based for, override keyword -- Braces on their own line -- Tabs to indent code, with a width of 4 characters - -## See also - -- [Git Source Control Tutorial on the Wikis](https://wiki.unrealengine.com/Git_source_control_(Tutorial)) -- [UE4 Git Plugin website](http://srombauts.github.com/UE4GitPlugin) - -- [ue4-hg-plugin for Mercurial (and bigfiles)](https://github.com/enlight/ue4-hg-plugin) diff --git a/Plugins/UE4GitPlugin-2.17-beta/Resources/Icon128.png b/Plugins/UE4GitPlugin-2.17-beta/Resources/Icon128.png deleted file mode 100644 index 3d9e864..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Resources/Icon128.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/FileHistory.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/FileHistory.png deleted file mode 100644 index 2b9e0f5..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/FileHistory.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Added.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Added.png deleted file mode 100644 index fff4f19..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Added.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Modified.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Modified.png deleted file mode 100644 index a7f7789..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Modified.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/New.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/New.png deleted file mode 100644 index cfea784..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/New.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Renamed.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Renamed.png deleted file mode 100644 index d66b81b..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Renamed.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Unchanged.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Unchanged.png deleted file mode 100644 index 17307ad..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Unchanged.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlLogin_Init.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlLogin_Init.png deleted file mode 100644 index 3b2f1c3..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlLogin_Init.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlMenu.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlMenu.png deleted file mode 100644 index d4f85b7..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlMenu.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlStatusTooltip.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlStatusTooltip.png deleted file mode 100644 index ef36f1f..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlStatusTooltip.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SubmitFiles.png b/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SubmitFiles.png deleted file mode 100644 index 2545aae..0000000 Binary files a/Plugins/UE4GitPlugin-2.17-beta/Screenshots/SubmitFiles.png and /dev/null differ diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/GitSourceControl.Build.cs b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/GitSourceControl.Build.cs deleted file mode 100644 index d16ebb3..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/GitSourceControl.Build.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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) - -using UnrealBuildTool; - -public class GitSourceControl : ModuleRules -{ - public GitSourceControl(ReadOnlyTargetRules Target) : base(Target) - { - // Enable the Include-What-You-Use (IWYU) UE4.15 policy (see https://docs.unrealengine.com/en-us/Programming/UnrealBuildSystem/IWYUReferenceGuide) - // "Shared PCHs may be used if an explicit private PCH is not set through PrivatePCHHeaderFile. In either case, none of the source files manually include a module PCH, and should include a matching header instead." - bEnforceIWYU = true; - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PrivatePCHHeaderFile = "Private/GitSourceControlPrivatePCH.h"; - - PrivateDependencyModuleNames.AddRange( - new string[] { - "Core", - "CoreUObject", - "Slate", - "SlateCore", - "InputCore", - "DesktopWidgets", - "EditorStyle", - "UnrealEd", - "SourceControl", - "Projects", - } - ); - } -} diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.cpp deleted file mode 100644 index 1bf25ec..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// 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) - -#include "GitSourceControlCommand.h" - -#include "Modules/ModuleManager.h" -#include "GitSourceControlModule.h" - -FGitSourceControlCommand::FGitSourceControlCommand(const TSharedRef& InOperation, const TSharedRef& InWorker, const FSourceControlOperationComplete& InOperationCompleteDelegate) - : Operation(InOperation) - , Worker(InWorker) - , OperationCompleteDelegate(InOperationCompleteDelegate) - , bExecuteProcessed(0) - , bCommandSuccessful(false) - , bConnectionDropped(false) - , bAutoDelete(true) - , Concurrency(EConcurrency::Synchronous) -{ - // grab the providers settings here, so we don't access them once the worker thread is launched - check(IsInGameThread()); - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked( "GitSourceControl" ); - PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - bUsingGitLfsLocking = GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); - PathToRepositoryRoot = GitSourceControl.GetProvider().GetPathToRepositoryRoot(); -} - -bool FGitSourceControlCommand::DoWork() -{ - bCommandSuccessful = Worker->Execute(*this); - FPlatformAtomics::InterlockedExchange(&bExecuteProcessed, 1); - - return bCommandSuccessful; -} - -void FGitSourceControlCommand::Abandon() -{ - FPlatformAtomics::InterlockedExchange(&bExecuteProcessed, 1); -} - -void FGitSourceControlCommand::DoThreadedWork() -{ - Concurrency = EConcurrency::Asynchronous; - DoWork(); -} - -ECommandResult::Type FGitSourceControlCommand::ReturnResults() -{ - // Save any messages that have accumulated - for (FString& String : InfoMessages) - { - Operation->AddInfoMessge(FText::FromString(String)); - } - for (FString& String : ErrorMessages) - { - Operation->AddErrorMessge(FText::FromString(String)); - } - - // run the completion delegate if we have one bound - ECommandResult::Type Result = bCommandSuccessful ? ECommandResult::Succeeded : ECommandResult::Failed; - OperationCompleteDelegate.ExecuteIfBound(Operation, Result); - - return Result; -} diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.h deleted file mode 100644 index 42c1a89..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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 "Misc/IQueuedWork.h" - -/** - * Used to execute Git commands multi-threaded. - */ -class FGitSourceControlCommand : public IQueuedWork -{ -public: - - FGitSourceControlCommand(const TSharedRef& InOperation, const TSharedRef& InWorker, const FSourceControlOperationComplete& InOperationCompleteDelegate = FSourceControlOperationComplete() ); - - /** - * 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; - - /** 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 Git repository: can be the ProjectDir itself, or any parent directory (found by the "Connect" operation) */ - FString PathToRepositoryRoot; - - /** Tell if using the Git LFS file Locking workflow */ - bool bUsingGitLfsLocking; - - /** Operation we want to perform - contains outward-facing parameters & results */ - TSharedRef Operation; - - /** The object that will actually do the work */ - TSharedRef Worker; - - /** Delegate to notify when this operation completes */ - FSourceControlOperationComplete OperationCompleteDelegate; - - /**If true, this command has been processed by the source control thread*/ - volatile int32 bExecuteProcessed; - - /**If true, the source control command succeeded*/ - bool bCommandSuccessful; - - /** TODO LFS If true, the source control connection was dropped while this command was being executed*/ - bool bConnectionDropped; - - /** 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 Files; - - /**Info and/or warning message storage*/ - TArray InfoMessages; - - /**Potential error message storage*/ - TArray ErrorMessages; -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.cpp deleted file mode 100644 index faeea9d..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.cpp +++ /dev/null @@ -1,515 +0,0 @@ -// 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) - -#include "GitSourceControlMenu.h" - -#include "GitSourceControlModule.h" -#include "GitSourceControlProvider.h" -#include "GitSourceControlOperations.h" -#include "GitSourceControlUtils.h" - -#include "ISourceControlModule.h" -#include "ISourceControlOperation.h" -#include "SourceControlOperations.h" - -#include "LevelEditor.h" -#include "Widgets/Notifications/SNotificationList.h" -#include "Framework/Notifications/NotificationManager.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "Misc/MessageDialog.h" -#include "EditorStyleSet.h" - -#include "PackageTools.h" -#include "FileHelpers.h" - -#include "Logging/MessageLog.h" - -static const FName GitSourceControlMenuTabName("GitSourceControlMenu"); - -#define LOCTEXT_NAMESPACE "GitSourceControl" - -void FGitSourceControlMenu::Register() -{ - // Register the extension with the level editor - FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr("LevelEditor"); - if (LevelEditorModule) - { - FLevelEditorModule::FLevelEditorMenuExtender ViewMenuExtender = FLevelEditorModule::FLevelEditorMenuExtender::CreateRaw(this, &FGitSourceControlMenu::OnExtendLevelEditorViewMenu); - auto& MenuExtenders = LevelEditorModule->GetAllLevelEditorToolbarSourceControlMenuExtenders(); - MenuExtenders.Add(ViewMenuExtender); - ViewMenuExtenderHandle = MenuExtenders.Last().GetHandle(); - } -} - -void FGitSourceControlMenu::Unregister() -{ - // Unregister the level editor extensions - FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr("LevelEditor"); - if (LevelEditorModule) - { - LevelEditorModule->GetAllLevelEditorToolbarSourceControlMenuExtenders().RemoveAll([=](const FLevelEditorModule::FLevelEditorMenuExtender& Extender) { return Extender.GetHandle() == ViewMenuExtenderHandle; }); - } -} - -bool FGitSourceControlMenu::HaveRemoteUrl() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - const FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - return !Provider.GetRemoteUrl().IsEmpty(); -} - -/// Prompt to save or discard all packages -bool FGitSourceControlMenu::SaveDirtyPackages() -{ - const bool bPromptUserToSave = true; - const bool bSaveMapPackages = true; - const bool bSaveContentPackages = true; - const bool bFastSave = false; - const bool bNotifyNoPackagesSaved = false; - const bool bCanBeDeclined = true; // If the user clicks "don't save" this will continue and lose their changes - bool bHadPackagesToSave = false; - - bool bSaved = FEditorFileUtils::SaveDirtyPackages(bPromptUserToSave, bSaveMapPackages, bSaveContentPackages, bFastSave, bNotifyNoPackagesSaved, bCanBeDeclined, &bHadPackagesToSave); - - // bSaved can be true if the user selects to not save an asset by unchecking it and clicking "save" - if (bSaved) - { - TArray DirtyPackages; - FEditorFileUtils::GetDirtyWorldPackages(DirtyPackages); - FEditorFileUtils::GetDirtyContentPackages(DirtyPackages); - bSaved = DirtyPackages.Num() == 0; - } - - return bSaved; -} - -/// Find all packages in Content directory -TArray FGitSourceControlMenu::ListAllPackages() -{ - TArray PackageRelativePaths; - FPackageName::FindPackagesInDirectory(PackageRelativePaths, *FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir())); - - TArray PackageNames; - PackageNames.Reserve(PackageRelativePaths.Num()); - for (const FString& Path : PackageRelativePaths) - { - FString PackageName; - FString FailureReason; - if (FPackageName::TryConvertFilenameToLongPackageName(Path, PackageName, &FailureReason)) - { - PackageNames.Add(PackageName); - } - else - { - FMessageLog("SourceControl").Error(FText::FromString(FailureReason)); - } - } - - return PackageNames; -} - -/// Unkink all loaded packages to allow to update them -TArray FGitSourceControlMenu::UnlinkPackages(const TArray& InPackageNames) -{ - TArray LoadedPackages; - - // Inspired from ContentBrowserUtils::SyncPathsFromSourceControl() - if (InPackageNames.Num() > 0) - { - // Form a list of loaded packages to reload... - LoadedPackages.Reserve(InPackageNames.Num()); - for (const FString& PackageName : InPackageNames) - { - UPackage* Package = FindPackage(nullptr, *PackageName); - if (Package) - { - LoadedPackages.Emplace(Package); - - // Detach the linkers of any loaded packages so that SCC can overwrite the files... - if (!Package->IsFullyLoaded()) - { - FlushAsyncLoading(); - Package->FullyLoad(); - } - ResetLoaders(Package); - } - } - UE_LOG(LogSourceControl, Log, TEXT("Reseted Loader for %d Packages"), LoadedPackages.Num()); - } - - return LoadedPackages; -} - -void FGitSourceControlMenu::ReloadPackages(TArray& InPackagesToReload) -{ - UE_LOG(LogSourceControl, Log, TEXT("Reloading %d Packages..."), InPackagesToReload.Num()); - - // Syncing may have deleted some packages, so we need to unload those rather than re-load them... - TArray PackagesToUnload; - InPackagesToReload.RemoveAll([&](UPackage* InPackage) -> bool - { - const FString PackageExtension = InPackage->ContainsMap() ? FPackageName::GetMapPackageExtension() : FPackageName::GetAssetPackageExtension(); - const FString PackageFilename = FPackageName::LongPackageNameToFilename(InPackage->GetName(), PackageExtension); - if (!FPaths::FileExists(PackageFilename)) - { - PackagesToUnload.Emplace(InPackage); - return true; // remove package - } - return false; // keep package - }); - - // Hot-reload the new packages... - UPackageTools::ReloadPackages(InPackagesToReload); - - // Unload any deleted packages... - UPackageTools::UnloadPackages(PackagesToUnload); -} - -// Ask the user if he wants to stash any modification and try to unstash them afterward, which could lead to conflicts -bool FGitSourceControlMenu::StashAwayAnyModifications() -{ - bool bStashOk = true; - - FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - const FString& PathToRespositoryRoot = Provider.GetPathToRepositoryRoot(); - const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - const TArray ParametersStatus{"--porcelain --untracked-files=no"}; - TArray InfoMessages; - TArray ErrorMessages; - // Check if there is any modification to the working tree - const bool bStatusOk = GitSourceControlUtils::RunCommand(TEXT("status"), PathToGitBinary, PathToRespositoryRoot, ParametersStatus, TArray(), InfoMessages, ErrorMessages); - if ((bStatusOk) && (InfoMessages.Num() > 0)) - { - // Ask the user before stashing - const FText DialogText(LOCTEXT("SourceControlMenu_Stash_Ask", "Stash (save) all modifications of the working tree? Required to Sync/Pull!")); - const EAppReturnType::Type Choice = FMessageDialog::Open(EAppMsgType::OkCancel, DialogText); - if (Choice == EAppReturnType::Ok) - { - const TArray ParametersStash{ "save \"Stashed by Unreal Engine Git Plugin\"" }; - bStashMadeBeforeSync = GitSourceControlUtils::RunCommand(TEXT("stash"), PathToGitBinary, PathToRespositoryRoot, ParametersStash, TArray(), InfoMessages, ErrorMessages); - if (!bStashMadeBeforeSync) - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_StashFailed", "Stashing away modifications failed!")); - SourceControlLog.Notify(); - } - } - else - { - bStashOk = false; - } - } - - return bStashOk; -} - -// Unstash any modifications if a stash was made at the beginning of the Sync operation -void FGitSourceControlMenu::ReApplyStashedModifications() -{ - if (bStashMadeBeforeSync) - { - FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - const FString& PathToRespositoryRoot = Provider.GetPathToRepositoryRoot(); - const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - const TArray ParametersStash{ "pop" }; - TArray InfoMessages; - TArray ErrorMessages; - const bool bUnstashOk = GitSourceControlUtils::RunCommand(TEXT("stash"), PathToGitBinary, PathToRespositoryRoot, ParametersStash, TArray(), InfoMessages, ErrorMessages); - if (!bUnstashOk) - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_UnstashFailed", "Unstashing previously saved modifications failed!")); - SourceControlLog.Notify(); - } - } -} - -void FGitSourceControlMenu::SyncClicked() -{ - if (!OperationInProgressNotification.IsValid()) - { - // Ask the user to save any dirty assets opened in Editor - const bool bSaved = SaveDirtyPackages(); - if (bSaved) - { - // Find and Unlink all packages in Content directory to allow to update them - PackagesToReload = UnlinkPackages(ListAllPackages()); - - // Ask the user if he wants to stash any modification and try to unstash them afterward, which could lead to conflicts - const bool bStashed = StashAwayAnyModifications(); - if (bStashed) - { - // Launch a "Sync" operation - FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - TSharedRef SyncOperation = ISourceControlOperation::Create(); - const ECommandResult::Type Result = Provider.Execute(SyncOperation, TArray(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateRaw(this, &FGitSourceControlMenu::OnSourceControlOperationComplete)); - if (Result == ECommandResult::Succeeded) - { - // Display an ongoing notification during the whole operation (packages will be reloaded at the completion of the operation) - DisplayInProgressNotification(SyncOperation->GetInProgressString()); - } - else - { - // Report failure with a notification and Reload all packages - DisplayFailureNotification(SyncOperation->GetName()); - ReloadPackages(PackagesToReload); - } - } - else - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_Sync_Unsaved", "Stash away all modifications before attempting to Sync!")); - SourceControlLog.Notify(); - } - } - else - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_Sync_Unsaved", "Save All Assets before attempting to Sync!")); - SourceControlLog.Notify(); - } - } - else - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_InProgress", "Source control operation already in progress")); - SourceControlLog.Notify(); - } -} - -void FGitSourceControlMenu::PushClicked() -{ - if (!OperationInProgressNotification.IsValid()) - { - // Launch a "Push" Operation - FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - TSharedRef PushOperation = ISourceControlOperation::Create(); - const ECommandResult::Type Result = Provider.Execute(PushOperation, TArray(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateRaw(this, &FGitSourceControlMenu::OnSourceControlOperationComplete)); - if (Result == ECommandResult::Succeeded) - { - // Display an ongoing notification during the whole operation - DisplayInProgressNotification(PushOperation->GetInProgressString()); - } - else - { - // Report failure with a notification - DisplayFailureNotification(PushOperation->GetName()); - } - } - else - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_InProgress", "Source control operation already in progress")); - SourceControlLog.Notify(); - } -} - -void FGitSourceControlMenu::RevertClicked() -{ - if (!OperationInProgressNotification.IsValid()) - { - // Ask the user before reverting all! - const FText DialogText(LOCTEXT("SourceControlMenu_Revert_Ask", "Revert all modifications of the working tree?")); - const EAppReturnType::Type Choice = FMessageDialog::Open(EAppMsgType::OkCancel, DialogText); - if (Choice == EAppReturnType::Ok) - { - // NOTE No need to force the user to SaveDirtyPackages(); since he will be presented with a choice by the Editor - - // Find and Unlink all packages in Content directory to allow to update them - PackagesToReload = UnlinkPackages(ListAllPackages()); - - // Launch a "Revert" Operation - FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - TSharedRef RevertOperation = ISourceControlOperation::Create(); - const ECommandResult::Type Result = Provider.Execute(RevertOperation, TArray(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateRaw(this, &FGitSourceControlMenu::OnSourceControlOperationComplete)); - if (Result == ECommandResult::Succeeded) - { - // Display an ongoing notification during the whole operation - DisplayInProgressNotification(RevertOperation->GetInProgressString()); - } - else - { - // Report failure with a notification and Reload all packages - DisplayFailureNotification(RevertOperation->GetName()); - ReloadPackages(PackagesToReload); - } - } - } - else - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_InProgress", "Source control operation already in progress")); - SourceControlLog.Notify(); - } -} - -void FGitSourceControlMenu::RefreshClicked() -{ - if (!OperationInProgressNotification.IsValid()) - { - // Launch an "UpdateStatus" Operation - FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - TSharedRef RefreshOperation = ISourceControlOperation::Create(); - RefreshOperation->SetCheckingAllFiles(true); - const ECommandResult::Type Result = Provider.Execute(RefreshOperation, TArray(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateRaw(this, &FGitSourceControlMenu::OnSourceControlOperationComplete)); - if (Result == ECommandResult::Succeeded) - { - // Display an ongoing notification during the whole operation - DisplayInProgressNotification(RefreshOperation->GetInProgressString()); - } - else - { - // Report failure with a notification - DisplayFailureNotification(RefreshOperation->GetName()); - } - } - else - { - FMessageLog SourceControlLog("SourceControl"); - SourceControlLog.Warning(LOCTEXT("SourceControlMenu_InProgress", "Source control operation already in progress")); - SourceControlLog.Notify(); - } -} - -// Display an ongoing notification during the whole operation -void FGitSourceControlMenu::DisplayInProgressNotification(const FText& InOperationInProgressString) -{ - if (!OperationInProgressNotification.IsValid()) - { - FNotificationInfo Info(InOperationInProgressString); - Info.bFireAndForget = false; - Info.ExpireDuration = 0.0f; - Info.FadeOutDuration = 1.0f; - OperationInProgressNotification = FSlateNotificationManager::Get().AddNotification(Info); - if (OperationInProgressNotification.IsValid()) - { - OperationInProgressNotification.Pin()->SetCompletionState(SNotificationItem::CS_Pending); - } - } -} - -// Remove the ongoing notification at the end of the operation -void FGitSourceControlMenu::RemoveInProgressNotification() -{ - if (OperationInProgressNotification.IsValid()) - { - OperationInProgressNotification.Pin()->ExpireAndFadeout(); - OperationInProgressNotification.Reset(); - } -} - -// Display a temporary success notification at the end of the operation -void FGitSourceControlMenu::DisplaySucessNotification(const FName& InOperationName) -{ - const FText NotificationText = FText::Format( - LOCTEXT("SourceControlMenu_Success", "{0} operation was successful!"), - FText::FromName(InOperationName) - ); - FNotificationInfo Info(NotificationText); - Info.bUseSuccessFailIcons = true; - Info.Image = FEditorStyle::GetBrush(TEXT("NotificationList.SuccessImage")); - FSlateNotificationManager::Get().AddNotification(Info); - UE_LOG(LogSourceControl, Log, TEXT("%s"), *NotificationText.ToString()); -} - -// Display a temporary failure notification at the end of the operation -void FGitSourceControlMenu::DisplayFailureNotification(const FName& InOperationName) -{ - const FText NotificationText = FText::Format( - LOCTEXT("SourceControlMenu_Failure", "Error: {0} operation failed!"), - FText::FromName(InOperationName) - ); - FNotificationInfo Info(NotificationText); - Info.ExpireDuration = 8.0f; - FSlateNotificationManager::Get().AddNotification(Info); - UE_LOG(LogSourceControl, Error, TEXT("%s"), *NotificationText.ToString()); -} - -void FGitSourceControlMenu::OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult) -{ - RemoveInProgressNotification(); - - if ((InOperation->GetName() == "Sync") || (InOperation->GetName() == "Revert")) - { - // Unstash any modifications if a stash was made at the beginning of the Sync operation - ReApplyStashedModifications(); - // Reload packages that where unlinked at the beginning of the Sync/Revert operation - ReloadPackages(PackagesToReload); - } - - // Report result with a notification - if (InResult == ECommandResult::Succeeded) - { - DisplaySucessNotification(InOperation->GetName()); - } - else - { - DisplayFailureNotification(InOperation->GetName()); - } -} - -void FGitSourceControlMenu::AddMenuExtension(FMenuBuilder& Builder) -{ - Builder.AddMenuEntry( - LOCTEXT("GitPush", "Push"), - LOCTEXT("GitPushTooltip", "Push all local commits to the remote server."), - FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Submit"), - FUIAction( - FExecuteAction::CreateRaw(this, &FGitSourceControlMenu::PushClicked), - FCanExecuteAction::CreateRaw(this, &FGitSourceControlMenu::HaveRemoteUrl) - ) - ); - - Builder.AddMenuEntry( - LOCTEXT("GitSync", "Sync/Pull"), - LOCTEXT("GitSyncTooltip", "Update all files in the local repository to the latest version of the remote server."), - FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Sync"), - FUIAction( - FExecuteAction::CreateRaw(this, &FGitSourceControlMenu::SyncClicked), - FCanExecuteAction::CreateRaw(this, &FGitSourceControlMenu::HaveRemoteUrl) - ) - ); - - Builder.AddMenuEntry( - LOCTEXT("GitRevert", "Revert"), - LOCTEXT("GitRevertTooltip", "Revert all files in the repository to their unchanged state."), - FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Revert"), - FUIAction( - FExecuteAction::CreateRaw(this, &FGitSourceControlMenu::RevertClicked), - FCanExecuteAction() - ) - ); - - Builder.AddMenuEntry( - LOCTEXT("GitRefresh", "Refresh"), - LOCTEXT("GitRefreshTooltip", "Update the source control status of all files in the local repository."), - FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Refresh"), - FUIAction( - FExecuteAction::CreateRaw(this, &FGitSourceControlMenu::RefreshClicked), - FCanExecuteAction() - ) - ); -} - -TSharedRef FGitSourceControlMenu::OnExtendLevelEditorViewMenu(const TSharedRef CommandList) -{ - TSharedRef Extender(new FExtender()); - - Extender->AddMenuExtension( - "SourceControlActions", - EExtensionHook::After, - nullptr, - FMenuExtensionDelegate::CreateRaw(this, &FGitSourceControlMenu::AddMenuExtension)); - - return Extender; -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.h deleted file mode 100644 index e7890d4..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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" - -class FToolBarBuilder; -class FMenuBuilder; - -/** Git extension of the Source Control toolbar menu */ -class FGitSourceControlMenu -{ -public: - void Register(); - void Unregister(); - - /** This functions will be bound to appropriate Command. */ - void PushClicked(); - void SyncClicked(); - void RevertClicked(); - void RefreshClicked(); - -private: - bool HaveRemoteUrl() const; - - bool SaveDirtyPackages(); - TArray ListAllPackages(); - TArray UnlinkPackages(const TArray& InPackageNames); - void ReloadPackages(TArray& InPackagesToReload); - - bool StashAwayAnyModifications(); - void ReApplyStashedModifications(); - - void AddMenuExtension(FMenuBuilder& Builder); - - TSharedRef OnExtendLevelEditorViewMenu(const TSharedRef CommandList); - - void DisplayInProgressNotification(const FText& InOperationInProgressString); - void RemoveInProgressNotification(); - void DisplaySucessNotification(const FName& InOperationName); - void DisplayFailureNotification(const FName& InOperationName); - -private: - FDelegateHandle ViewMenuExtenderHandle; - - /** Was there a need to stash away modifications before Sync? */ - bool bStashMadeBeforeSync; - - /** Loaded packages to reload after a Sync or Revert operation */ - TArray PackagesToReload; - - /** Current source control operation from extended menu if any */ - TWeakPtr OperationInProgressNotification; - - /** Delegate called when a source control operation has completed */ - void OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult); -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.cpp deleted file mode 100644 index ce92181..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// 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) - -#include "GitSourceControlModule.h" - -#include "Misc/App.h" -#include "Modules/ModuleManager.h" -#include "GitSourceControlOperations.h" -#include "Features/IModularFeatures.h" - -#define LOCTEXT_NAMESPACE "GitSourceControl" - -template -static TSharedRef CreateWorker() -{ - return MakeShareable( new Type() ); -} - -void FGitSourceControlModule::StartupModule() -{ - // Register our operations (implemented in GitSourceControlOperations.cpp by subclassing from Engine\Source\Developer\SourceControl\Public\SourceControlOperations.h) - GitSourceControlProvider.RegisterWorker( "Connect", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - // Note: this provider uses the "CheckOut" command only with Git LFS 2 "lock" command, since Git itself has no lock command (all tracked files in the working copy are always already checked-out). - GitSourceControlProvider.RegisterWorker( "CheckOut", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "UpdateStatus", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "MarkForAdd", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "Delete", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "Revert", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "Sync", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "Push", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "CheckIn", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "Copy", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - GitSourceControlProvider.RegisterWorker( "Resolve", FGetGitSourceControlWorker::CreateStatic( &CreateWorker ) ); - - // load our settings - GitSourceControlSettings.LoadSettings(); - - // Bind our source control provider to the editor - IModularFeatures::Get().RegisterModularFeature( "SourceControl", &GitSourceControlProvider ); -} - -void FGitSourceControlModule::ShutdownModule() -{ - // shut down the provider, as this module is going away - GitSourceControlProvider.Close(); - - // unbind provider from editor - IModularFeatures::Get().UnregisterModularFeature("SourceControl", &GitSourceControlProvider); -} - -void FGitSourceControlModule::SaveSettings() -{ - if (FApp::IsUnattended() || IsRunningCommandlet()) - { - return; - } - - GitSourceControlSettings.SaveSettings(); -} - -IMPLEMENT_MODULE(FGitSourceControlModule, GitSourceControl); - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.h deleted file mode 100644 index 161e3db..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.h +++ /dev/null @@ -1,114 +0,0 @@ -// 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 "Modules/ModuleInterface.h" -#include "GitSourceControlSettings.h" -#include "GitSourceControlProvider.h" - -/** - -UE4GitPlugin is a simple Git Source 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 UE4 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: -To https://github.com/SRombauts/UE4GitLfs2FileLocks.git - ee44ff5..59da15e HEAD -> master - -Use "TODO LFS" in the code to track things left to do/improve/refactor: -1. IsUsingGitLfsLocking() should be cached in the Provider to avoid calling AccessSettings() too frequently - it can not change without re-initializing (at least re-connect) the Provider! -2. Implement FGitSourceControlProvider::bWorkingOffline like the SubversionSourceControl plugin -3. Trying to deactivate Git LFS 2 file locking afterward on the "Login to Source 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" si 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" -4. Optimize usage of "git lfs locks", ie reduce the use of UdpateStatus() in Operations - -### What *cannot* be done presently -- Branch/Merge are not in the current Editor workflow -- Fetch is 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 -- displaying states of 'Engine' assets (also needs management of 'out of tree' files) -- 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 source control settings */ - FGitSourceControlSettings& AccessSettings() - { - return GitSourceControlSettings; - } - const FGitSourceControlSettings& AccessSettings() const - { - return GitSourceControlSettings; - } - - /** Save the Git source control settings */ - void SaveSettings(); - - /** Access the Git source control provider */ - FGitSourceControlProvider& GetProvider() - { - return GitSourceControlProvider; - } - const FGitSourceControlProvider& GetProvider() const - { - return GitSourceControlProvider; - } - -private: - /** The Git source control provider */ - FGitSourceControlProvider GitSourceControlProvider; - - /** The settings for Git source control */ - FGitSourceControlSettings GitSourceControlSettings; -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.cpp deleted file mode 100644 index 0c48986..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.cpp +++ /dev/null @@ -1,684 +0,0 @@ -// 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) - -#include "GitSourceControlOperations.h" - -#include "Misc/Paths.h" -#include "Modules/ModuleManager.h" -#include "SourceControlOperations.h" -#include "ISourceControlModule.h" -#include "GitSourceControlModule.h" -#include "GitSourceControlCommand.h" -#include "GitSourceControlUtils.h" -#include "Logging/MessageLog.h" - -#define LOCTEXT_NAMESPACE "GitSourceControl" - -FName FGitPush::GetName() const -{ - return "Push"; -} - -FText FGitPush::GetInProgressString() const -{ - // TODO Configure origin - return LOCTEXT("SourceControl_Push", "Pushing local commits to remote origin..."); -} - - -FName FGitConnectWorker::GetName() const -{ - return "Connect"; -} - -bool FGitConnectWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - TSharedRef Operation = StaticCastSharedRef(InCommand.Operation); - - // Check Git Availability - if((InCommand.PathToGitBinary.Len() > 0) && GitSourceControlUtils::CheckGitAvailability(InCommand.PathToGitBinary)) - { - // Now update the status of assets in Content/ directory and also Config files - TArray ProjectDirs; - ProjectDirs.Add(FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir())); - ProjectDirs.Add(FPaths::ConvertRelativePathToFull(FPaths::ProjectConfigDir())); - InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, ProjectDirs, InCommand.ErrorMessages, States); - if(!InCommand.bCommandSuccessful || InCommand.ErrorMessages.Num() > 0) - { - Operation->SetErrorText(LOCTEXT("NotAGitRepository", "Failed to enable Git source control. You need to initialize the project as a Git repository first.")); - InCommand.bCommandSuccessful = false; - } - else - { - GitSourceControlUtils::GetCommitInfo(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.CommitId, InCommand.CommitSummary); - - if(InCommand.bUsingGitLfsLocking) - { - // Check server connection by checking lock status (when using Git LFS file Locking worflow) - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("lfs locks"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); - } - } - } - else - { - Operation->SetErrorText(LOCTEXT("GitNotFound", "Failed to enable Git source control. You need to install Git and specify a valid path to git executable.")); - InCommand.bCommandSuccessful = false; - } - - return InCommand.bCommandSuccessful; -} - -bool FGitConnectWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -FName FGitCheckOutWorker::GetName() const -{ - return "CheckOut"; -} - -bool FGitCheckOutWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - - if(InCommand.bUsingGitLfsLocking) - { - // lock files: execute the LFS command on relative filenames - InCommand.bCommandSuccessful = true; - const TArray RelativeFiles = GitSourceControlUtils::RelativeFilenames(InCommand.Files, InCommand.PathToRepositoryRoot); - for(const auto& RelativeFile : RelativeFiles) - { - TArray OneFile; - OneFile.Add(RelativeFile); - InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(TEXT("lfs lock"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), OneFile, InCommand.InfoMessages, InCommand.ErrorMessages); - } - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - } - else - { - InCommand.bCommandSuccessful = false; - } - - return InCommand.bCommandSuccessful; -} - -bool FGitCheckOutWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -static FText ParseCommitResults(const TArray& InResults) -{ - if(InResults.Num() >= 1) - { - const FString& FirstLine = InResults[0]; - return FText::Format(LOCTEXT("CommitMessage", "Commited {0}."), FText::FromString(FirstLine)); - } - return LOCTEXT("CommitMessageUnknown", "Submitted revision."); -} - -// Get Locked Files (that is, CheckedOut files, not Added ones) -const TArray GetLockedFiles(const TArray& InFiles) -{ - TArray LockedFiles; - - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - - TArray> LocalStates; - Provider.GetState(InFiles, LocalStates, EStateCacheUsage::Use); - for(const auto& State : LocalStates) - { - if(State->IsCheckedOut()) - { - LockedFiles.Add(State->GetFilename()); - } - } - - return LockedFiles; -} - -FName FGitCheckInWorker::GetName() const -{ - return "CheckIn"; -} - -bool FGitCheckInWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - - TSharedRef Operation = StaticCastSharedRef(InCommand.Operation); - - // make a temp file to place our commit message in - FGitScopedTempFile CommitMsgFile(Operation->GetDescription()); - if(CommitMsgFile.GetFilename().Len() > 0) - { - TArray Parameters; - FString ParamCommitMsgFilename = TEXT("--file=\""); - ParamCommitMsgFilename += FPaths::ConvertRelativePathToFull(CommitMsgFile.GetFilename()); - ParamCommitMsgFilename += TEXT("\""); - Parameters.Add(ParamCommitMsgFilename); - - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommit(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages); - if(InCommand.bCommandSuccessful) - { - // Remove any deleted files from status cache - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - - TArray> LocalStates; - Provider.GetState(InCommand.Files, LocalStates, EStateCacheUsage::Use); - for(const auto& State : LocalStates) - { - if(State->IsDeleted()) - { - Provider.RemoveFileFromCache(State->GetFilename()); - } - } - - Operation->SetSuccessMessage(ParseCommitResults(InCommand.InfoMessages)); - const FString Message = (InCommand.InfoMessages.Num() > 0) ? InCommand.InfoMessages[0] : TEXT(""); - UE_LOG(LogSourceControl, Log, TEXT("commit successful: %s"), *Message); - - // git-lfs: push and unlock files - if(InCommand.bUsingGitLfsLocking && InCommand.bCommandSuccessful) - { - 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 - // (unlock only locked files, that is, not Added files) - const TArray LockedFiles = GetLockedFiles(InCommand.Files); - if(LockedFiles.Num() > 0) - { - const TArray RelativeFiles = GitSourceControlUtils::RelativeFilenames(LockedFiles, InCommand.PathToRepositoryRoot); - for(const auto& RelativeFile : RelativeFiles) - { - TArray OneFile; - OneFile.Add(RelativeFile); - GitSourceControlUtils::RunCommand(TEXT("lfs unlock"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), OneFile, InCommand.InfoMessages, InCommand.ErrorMessages); - } - } - } - } - } - } - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - GitSourceControlUtils::GetCommitInfo(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.CommitId, InCommand.CommitSummary); - - return InCommand.bCommandSuccessful; -} - -bool FGitCheckInWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -FName FGitMarkForAddWorker::GetName() const -{ - return "MarkForAdd"; -} - -bool FGitMarkForAddWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages); - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - - return InCommand.bCommandSuccessful; -} - -bool FGitMarkForAddWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -FName FGitDeleteWorker::GetName() const -{ - return "Delete"; -} - -bool FGitDeleteWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("rm"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages); - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - - return InCommand.bCommandSuccessful; -} - -bool FGitDeleteWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - - -// Get lists of Missing files (ie "deleted"), Modified files, and "other than Added" Existing files -void GetMissingVsExistingFiles(const TArray& InFiles, TArray& OutMissingFiles, TArray& OutAllExistingFiles, TArray& OutOtherThanAddedExistingFiles) -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - - const TArray Files = (InFiles.Num() > 0) ? (InFiles) : (Provider.GetFilesInCache()); - - TArray> LocalStates; - Provider.GetState(Files, LocalStates, EStateCacheUsage::Use); - for(const auto& State : LocalStates) - { - if(FPaths::FileExists(State->GetFilename())) - { - if(State->IsAdded()) - { - OutAllExistingFiles.Add(State->GetFilename()); - } - else if(State->IsModified()) - { - OutOtherThanAddedExistingFiles.Add(State->GetFilename()); - OutAllExistingFiles.Add(State->GetFilename()); - } - else if(State->CanRevert()) // for locked but unmodified files - { - OutOtherThanAddedExistingFiles.Add(State->GetFilename()); - } - } - else - { - if (State->IsSourceControlled()) - { - OutMissingFiles.Add(State->GetFilename()); - } - } - } -} - -FName FGitRevertWorker::GetName() const -{ - return "Revert"; -} - -bool FGitRevertWorker::Execute(FGitSourceControlCommand& InCommand) -{ - // Filter files by status to use the right "revert" commands on them - TArray MissingFiles; - TArray AllExistingFiles; - TArray OtherThanAddedExistingFiles; - GetMissingVsExistingFiles(InCommand.Files, MissingFiles, AllExistingFiles, OtherThanAddedExistingFiles); - - InCommand.bCommandSuccessful = true; - if(MissingFiles.Num() > 0) - { - // "Added" files that have been deleted needs to be removed from source control - InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(TEXT("rm"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), MissingFiles, InCommand.InfoMessages, InCommand.ErrorMessages); - } - if(AllExistingFiles.Num() > 0) - { - // reset any changes already added to the index - InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(TEXT("reset"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), AllExistingFiles, InCommand.InfoMessages, InCommand.ErrorMessages); - } - if(OtherThanAddedExistingFiles.Num() > 0) - { - // revert any changes in working copy (this would fails if the asset was in "Added" state, since after "reset" it is now "untracked") - InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(TEXT("checkout"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), OtherThanAddedExistingFiles, InCommand.InfoMessages, InCommand.ErrorMessages); - } - - if(InCommand.bUsingGitLfsLocking) - { - // unlock files: execute the LFS command on relative filenames - // (unlock only locked files, that is, not Added files) - const TArray LockedFiles = GetLockedFiles(OtherThanAddedExistingFiles); - if(LockedFiles.Num() > 0) - { - const TArray RelativeFiles = GitSourceControlUtils::RelativeFilenames(LockedFiles, InCommand.PathToRepositoryRoot); - for(const auto& RelativeFile : RelativeFiles) - { - TArray OneFile; - OneFile.Add(RelativeFile); - GitSourceControlUtils::RunCommand(TEXT("lfs unlock"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), OneFile, InCommand.InfoMessages, InCommand.ErrorMessages); - } - } - } - - // If no files were specified (full revert), refresh all relevant files instead of the specified files (which is an empty list in full revert) - // This is required so that files that were "Marked for add" have their status updated after a full revert. - TArray FilesToUpdate = InCommand.Files; - if (InCommand.Files.Num() <= 0) - { - for (const auto& File : MissingFiles) FilesToUpdate.Add(File); - for (const auto& File : AllExistingFiles) FilesToUpdate.Add(File); - for (const auto& File : OtherThanAddedExistingFiles) FilesToUpdate.Add(File); - } - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, FilesToUpdate, InCommand.ErrorMessages, States); - - return InCommand.bCommandSuccessful; -} - -bool FGitRevertWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -FName FGitSyncWorker::GetName() const -{ - return "Sync"; -} - -bool FGitSyncWorker::Execute(FGitSourceControlCommand& InCommand) -{ - // pull the branch to get remote changes by rebasing any local commits (not merging them to avoid complex graphs) - TArray Parameters; - Parameters.Add(TEXT("--rebase")); - Parameters.Add(TEXT("--autostash")); - // TODO Configure origin - Parameters.Add(TEXT("origin")); - Parameters.Add(TEXT("HEAD")); - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("pull"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - GitSourceControlUtils::GetCommitInfo(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.CommitId, InCommand.CommitSummary); - - return InCommand.bCommandSuccessful; -} - -bool FGitSyncWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - - -FName FGitPushWorker::GetName() const -{ - return "Push"; -} - -bool FGitPushWorker::Execute(FGitSourceControlCommand& InCommand) -{ - - // If we have any locked files, check if we should unlock them - TArray FilesToUnlock; - if (InCommand.bUsingGitLfsLocking) - { - TMap Locks; - // Get locks as relative paths - GitSourceControlUtils::GetAllLocks(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, false, InCommand.ErrorMessages, Locks); - if(Locks.Num() > 0) - { - // test to see what lfs files we would push, and compare to locked files, unlock after if push OK - FString BranchName; - GitSourceControlUtils::GetBranchName(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, BranchName); - - TArray LfsPushParameters; - LfsPushParameters.Add(TEXT("push")); - LfsPushParameters.Add(TEXT("--dry-run")); - LfsPushParameters.Add(TEXT("origin")); - LfsPushParameters.Add(BranchName); - TArray LfsPushInfoMessages; - TArray LfsPushErrMessages; - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("lfs"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, LfsPushParameters, TArray(), LfsPushInfoMessages, LfsPushErrMessages); - - if(InCommand.bCommandSuccessful) - { - // Result format is of the form - // push f4ee401c063058a78842bb3ed98088e983c32aa447f346db54fa76f844a7e85e => Path/To/Asset.uasset - // With some potential informationals we can ignore - for (auto& Line : LfsPushInfoMessages) - { - if (Line.StartsWith(TEXT("push"))) - { - FString Prefix, Filename; - if (Line.Split(TEXT("=>"), &Prefix, &Filename)) - { - Filename = Filename.TrimStartAndEnd(); - if (Locks.Contains(Filename)) - { - // We do not need to check user or if the file has local modifications before attempting unlocking, git-lfs will reject the unlock if so - // No point duplicating effort here - FilesToUnlock.Add(Filename); - UE_LOG(LogSourceControl, Log, TEXT("Post-push will try to unlock: %s"), *Filename); - } - } - } - } - } - } - - } - // push the branch to its default remote - // (works only if the default remote "origin" is set and does not require authentication) - TArray Parameters; - Parameters.Add(TEXT("--set-upstream")); - // TODO Configure origin - Parameters.Add(TEXT("origin")); - Parameters.Add(TEXT("HEAD")); - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("push"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, TArray(), InCommand.InfoMessages, InCommand.ErrorMessages); - - if(InCommand.bCommandSuccessful && InCommand.bUsingGitLfsLocking && FilesToUnlock.Num() > 0) - { - // unlock files: execute the LFS command on relative filenames - for(const auto& FileToUnlock : FilesToUnlock) - { - TArray OneFile; - OneFile.Add(FileToUnlock); - bool bUnlocked = GitSourceControlUtils::RunCommand(TEXT("lfs unlock"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), OneFile, InCommand.InfoMessages, InCommand.ErrorMessages); - if (!bUnlocked) - { - // Report but don't fail, it's not essential - UE_LOG(LogSourceControl, Log, TEXT("Unlock failed for %s"), *FileToUnlock); - } - } - - // We need to update status if we unlock - // This command needs absolute filenames - TArray AbsFilesToUnlock = GitSourceControlUtils::AbsoluteFilenames(FilesToUnlock, InCommand.PathToRepositoryRoot); - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, AbsFilesToUnlock, InCommand.ErrorMessages, States); - - } - - return InCommand.bCommandSuccessful; -} - -bool FGitPushWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -FName FGitUpdateStatusWorker::GetName() const -{ - return "UpdateStatus"; -} - -bool FGitUpdateStatusWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - - TSharedRef Operation = StaticCastSharedRef(InCommand.Operation); - - if(InCommand.Files.Num() > 0) - { - InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - GitSourceControlUtils::RemoveRedundantErrors(InCommand, TEXT("' is outside repository")); - - if(Operation->ShouldUpdateHistory()) - { - for(int32 Index = 0; Index < States.Num(); Index++) - { - FString& File = InCommand.Files[Index]; - TGitSourceControlHistory History; - - if(States[Index].IsConflicted()) - { - // In case of a merge conflict, we first need to get the tip of the "remote branch" (MERGE_HEAD) - GitSourceControlUtils::RunGetHistory(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, File, true, InCommand.ErrorMessages, History); - } - // Get the history of the file in the current branch - InCommand.bCommandSuccessful &= GitSourceControlUtils::RunGetHistory(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, File, false, InCommand.ErrorMessages, History); - Histories.Add(*File, History); - } - } - } - else - { - // no path provided: only update the status of assets in Content/ directory and also Config files - TArray ProjectDirs; - ProjectDirs.Add(FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir())); - ProjectDirs.Add(FPaths::ConvertRelativePathToFull(FPaths::ProjectConfigDir())); - InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, ProjectDirs, InCommand.ErrorMessages, States); - } - - GitSourceControlUtils::GetCommitInfo(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.CommitId, InCommand.CommitSummary); - - // don't use the ShouldUpdateModifiedState() hint here as it is specific to Perforce: the above normal Git status has already told us this information (like Git and Mercurial) - - return InCommand.bCommandSuccessful; -} - -bool FGitUpdateStatusWorker::UpdateStates() const -{ - bool bUpdated = GitSourceControlUtils::UpdateCachedStates(States); - - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked( "GitSourceControl" ); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - - const FDateTime Now = FDateTime::Now(); - - // add history, if any - for(const auto& History : Histories) - { - TSharedRef State = Provider.GetStateInternal(History.Key); - State->History = History.Value; - State->TimeStamp = Now; - bUpdated = true; - } - - return bUpdated; -} - -FName FGitCopyWorker::GetName() const -{ - return "Copy"; -} - -bool FGitCopyWorker::Execute(FGitSourceControlCommand& InCommand) -{ - check(InCommand.Operation->GetName() == GetName()); - - // Copy or Move operation on a single file : Git does not need an explicit copy nor move, - // but after a Move the Editor create a redirector file with the old asset name that points to the new asset. - // The redirector needs to be commited with the new asset to perform a real rename. - // => the following is to "MarkForAdd" the redirector, but it still need to be committed by selecting the whole directory and "check-in" - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages); - - return InCommand.bCommandSuccessful; -} - -bool FGitCopyWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -FName FGitResolveWorker::GetName() const -{ - return "Resolve"; -} - -bool FGitResolveWorker::Execute( class FGitSourceControlCommand& InCommand ) -{ - check(InCommand.Operation->GetName() == GetName()); - - // mark the conflicting files as resolved: - TArray Results; - InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray(), InCommand.Files, Results, InCommand.ErrorMessages); - - // now update the status of our files - GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.bUsingGitLfsLocking, InCommand.Files, InCommand.ErrorMessages, States); - - return InCommand.bCommandSuccessful; -} - -bool FGitResolveWorker::UpdateStates() const -{ - return GitSourceControlUtils::UpdateCachedStates(States); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.h deleted file mode 100644 index 5b9fb81..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.h +++ /dev/null @@ -1,192 +0,0 @@ -// 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 "IGitSourceControlWorker.h" -#include "GitSourceControlState.h" - -#include "ISourceControlOperation.h" - -/** - * Internal operation used to push local commits to configured remote origin -*/ -class FGitPush : public ISourceControlOperation -{ -public: - // ISourceControlOperation interface - virtual FName GetName() const override; - - virtual FText GetInProgressString() const override; -}; - -/** Called when first activated on a project, and then at project load time. - * Look for the root directory of the git repository (where the ".git/" subdirectory is located). */ -class FGitConnectWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitConnectWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Lock (check-out) a set of files using Git LFS 2. */ -class FGitCheckOutWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitCheckOutWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Commit (check-in) a set of files to the local depot. */ -class FGitCheckInWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitCheckInWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Add an untraked file to source control (so only a subset of the git add command). */ -class FGitMarkForAddWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitMarkForAddWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Delete a file and remove it from source control. */ -class FGitDeleteWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitDeleteWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Revert any change to a file to its state on the local depot. */ -class FGitRevertWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitRevertWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Git pull --rebase to update branch from its configured remote */ -class FGitSyncWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitSyncWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Git push to publish branch for its configured remote */ -class FGitPushWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitPushWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** Get source control status of files on local working copy. */ -class FGitUpdateStatusWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitUpdateStatusWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; - - /** Map of filenames to history */ - TMap Histories; -}; - -/** Copy or Move operation on a single file */ -class FGitCopyWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitCopyWorker() {} - // IGitSourceControlWorker interface - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -public: - /** Temporary states for results */ - TArray States; -}; - -/** git add to mark a conflict as resolved */ -class FGitResolveWorker : public IGitSourceControlWorker -{ -public: - virtual ~FGitResolveWorker() {} - virtual FName GetName() const override; - virtual bool Execute(class FGitSourceControlCommand& InCommand) override; - virtual bool UpdateStates() const override; - -private: - /** Temporary states for results */ - TArray States; -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlPrivatePCH.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlPrivatePCH.h deleted file mode 100644 index e5d9454..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlPrivatePCH.h +++ /dev/null @@ -1,15 +0,0 @@ -// 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 "ISourceControlModule.h" -#include "ISourceControlOperation.h" -#include "ISourceControlProvider.h" -#include "ISourceControlRevision.h" -#include "ISourceControlState.h" -#include "Misc/Paths.h" -#include "Modules/ModuleManager.h" diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.cpp deleted file mode 100644 index aa6c256..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.cpp +++ /dev/null @@ -1,467 +0,0 @@ -// 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) - -#include "GitSourceControlProvider.h" - -#include "HAL/PlatformProcess.h" -#include "Misc/Paths.h" -#include "Misc/QueuedThreadPool.h" -#include "Modules/ModuleManager.h" -#include "Widgets/DeclarativeSyntaxSupport.h" -#include "GitSourceControlCommand.h" -#include "ISourceControlModule.h" -#include "GitSourceControlModule.h" -#include "GitSourceControlUtils.h" -#include "SGitSourceControlSettings.h" -#include "Logging/MessageLog.h" -#include "ScopedSourceControlProgress.h" -#include "SourceControlHelpers.h" -#include "SourceControlOperations.h" -#include "Interfaces/IPluginManager.h" - -#define LOCTEXT_NAMESPACE "GitSourceControl" - -static FName ProviderName("Git LFS 2"); - -void FGitSourceControlProvider::Init(bool bForceConnection) -{ - // Init() is called multiple times at startup: do not check git each time - if(!bGitAvailable) - { - const TSharedPtr Plugin = IPluginManager::Get().FindPlugin(TEXT("GitSourceControl")); - if(Plugin.IsValid()) - { - UE_LOG(LogSourceControl, Log, TEXT("Git plugin '%s'"), *(Plugin->GetDescriptor().VersionName)); - } - - CheckGitAvailability(); - - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - bUsingGitLfsLocking = GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); - } - - // bForceConnection: not used anymore -} - -void FGitSourceControlProvider::CheckGitAvailability() -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - FString PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - if(PathToGitBinary.IsEmpty()) - { - // Try to find Git binary, and update settings accordingly - PathToGitBinary = GitSourceControlUtils::FindGitBinaryPath(); - if(!PathToGitBinary.IsEmpty()) - { - GitSourceControl.AccessSettings().SetBinaryPath(PathToGitBinary); - } - } - - if(!PathToGitBinary.IsEmpty()) - { - UE_LOG(LogSourceControl, Log, TEXT("Using '%s'"), *PathToGitBinary); - bGitAvailable = GitSourceControlUtils::CheckGitAvailability(PathToGitBinary, &GitVersion); - if(bGitAvailable) - { - CheckRepositoryStatus(PathToGitBinary); - } - } - else - { - bGitAvailable = false; - } -} - -void FGitSourceControlProvider::CheckRepositoryStatus(const FString& InPathToGitBinary) -{ - // Find the path to the root Git directory (if any, else uses the ProjectDir) - const FString PathToProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); - bGitRepositoryFound = GitSourceControlUtils::FindRootDirectory(PathToProjectDir, PathToRepositoryRoot); - if(bGitRepositoryFound) - { - GitSourceControlMenu.Register(); - - // Get branch name - bGitRepositoryFound = GitSourceControlUtils::GetBranchName(InPathToGitBinary, PathToRepositoryRoot, BranchName); - if(bGitRepositoryFound) - { - GitSourceControlUtils::GetRemoteUrl(InPathToGitBinary, 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); -} - -void FGitSourceControlProvider::Close() -{ - // clear the cache - StateCache.Empty(); - // Remove all extensions to the "Source Control" menu in the Editor Toolbar - GitSourceControlMenu.Unregister(); - - bGitAvailable = false; - bGitRepositoryFound = false; - UserName.Empty(); - UserEmail.Empty(); -} - -TSharedRef FGitSourceControlProvider::GetStateInternal(const FString& Filename) -{ - TSharedRef* State = StateCache.Find(Filename); - if(State != NULL) - { - // found cached item - return (*State); - } - else - { - // cache an unknown state for this item - TSharedRef NewState = MakeShareable( new FGitSourceControlState(Filename, bUsingGitLfsLocking) ); - StateCache.Add(Filename, NewState); - return NewState; - } -} - -FText FGitSourceControlProvider::GetStatusText() const -{ - FFormatNamedArguments Args; - Args.Add( TEXT("RepositoryName"), FText::FromString(PathToRepositoryRoot) ); - Args.Add( TEXT("RemoteUrl"), FText::FromString(RemoteUrl) ); - Args.Add( TEXT("UserName"), FText::FromString(UserName) ); - Args.Add( TEXT("UserEmail"), FText::FromString(UserEmail) ); - Args.Add( TEXT("BranchName"), FText::FromString(BranchName) ); - Args.Add( TEXT("CommitId"), FText::FromString(CommitId.Left(8)) ); - Args.Add( TEXT("CommitSummary"), FText::FromString(CommitSummary) ); - - return FText::Format( NSLOCTEXT("Status", "Provider: Git\nEnabledLabel", "Local repository: {RepositoryName}\nRemote origin: {RemoteUrl}\nUser: {UserName}\nE-mail: {UserEmail}\n[{BranchName} {CommitId}] {CommitSummary}"), Args ); -} - -/** Quick check if source control is enabled */ -bool FGitSourceControlProvider::IsEnabled() const -{ - return bGitRepositoryFound; -} - -/** Quick check if source control is available for use (useful for server-based providers) */ -bool FGitSourceControlProvider::IsAvailable() const -{ - return bGitRepositoryFound; -} - -const FName& FGitSourceControlProvider::GetName(void) const -{ - return ProviderName; -} - -ECommandResult::Type FGitSourceControlProvider::GetState( const TArray& InFiles, TArray< TSharedRef >& OutState, EStateCacheUsage::Type InStateCacheUsage ) -{ - if(!IsEnabled()) - { - return ECommandResult::Failed; - } - - TArray AbsoluteFiles = SourceControlHelpers::AbsoluteFilenames(InFiles); - - if(InStateCacheUsage == EStateCacheUsage::ForceUpdate) - { - Execute(ISourceControlOperation::Create(), AbsoluteFiles); - } - - for(const auto& AbsoluteFile : AbsoluteFiles) - { - OutState.Add(GetStateInternal(*AbsoluteFile)); - } - - return ECommandResult::Succeeded; -} - -TArray FGitSourceControlProvider::GetCachedStateByPredicate(TFunctionRef Predicate) const -{ - TArray Result; - for(const auto& CacheItem : StateCache) - { - FSourceControlStateRef State = CacheItem.Value; - if(Predicate(State)) - { - Result.Add(State); - } - } - return Result; -} - -bool FGitSourceControlProvider::RemoveFileFromCache(const FString& Filename) -{ - return StateCache.Remove(Filename) > 0; -} - -/** Get files in cache */ -TArray FGitSourceControlProvider::GetFilesInCache() -{ - TArray Files; - for (const auto& State : StateCache) - { - Files.Add(State.Key); - } - return Files; -} - -FDelegateHandle FGitSourceControlProvider::RegisterSourceControlStateChanged_Handle( const FSourceControlStateChanged::FDelegate& SourceControlStateChanged ) -{ - return OnSourceControlStateChanged.Add( SourceControlStateChanged ); -} - -void FGitSourceControlProvider::UnregisterSourceControlStateChanged_Handle( FDelegateHandle Handle ) -{ - OnSourceControlStateChanged.Remove( Handle ); -} - -ECommandResult::Type FGitSourceControlProvider::Execute( const TSharedRef& InOperation, const TArray& InFiles, EConcurrency::Type InConcurrency, const FSourceControlOperationComplete& InOperationCompleteDelegate ) -{ - if(!IsEnabled() && !(InOperation->GetName() == "Connect")) // Only Connect operation allowed while not Enabled (Repository found) - { - InOperationCompleteDelegate.ExecuteIfBound(InOperation, ECommandResult::Failed); - return ECommandResult::Failed; - } - - TArray AbsoluteFiles = SourceControlHelpers::AbsoluteFilenames(InFiles); - - // Query to see if we allow this operation - TSharedPtr Worker = CreateWorker(InOperation->GetName()); - if(!Worker.IsValid()) - { - // this operation is unsupported by this source control provider - FFormatNamedArguments Arguments; - Arguments.Add( TEXT("OperationName"), FText::FromName(InOperation->GetName()) ); - Arguments.Add( TEXT("ProviderName"), FText::FromName(GetName()) ); - FText Message(FText::Format(LOCTEXT("UnsupportedOperation", "Operation '{OperationName}' not supported by source control provider '{ProviderName}'"), Arguments)); - FMessageLog("SourceControl").Error(Message); - InOperation->AddErrorMessge(Message); - - InOperationCompleteDelegate.ExecuteIfBound(InOperation, ECommandResult::Failed); - return ECommandResult::Failed; - } - - FGitSourceControlCommand* Command = new FGitSourceControlCommand(InOperation, Worker.ToSharedRef()); - Command->Files = AbsoluteFiles; - Command->OperationCompleteDelegate = InOperationCompleteDelegate; - - // fire off operation - if(InConcurrency == EConcurrency::Synchronous) - { - Command->bAutoDelete = false; - - UE_LOG(LogSourceControl, Log, TEXT("ExecuteSynchronousCommand(%s)"), *InOperation->GetName().ToString()); - return ExecuteSynchronousCommand(*Command, InOperation->GetInProgressString()); - } - else - { - Command->bAutoDelete = true; - - UE_LOG(LogSourceControl, Log, TEXT("IssueAsynchronousCommand(%s)"), *InOperation->GetName().ToString()); - return IssueCommand(*Command); - } -} - -bool FGitSourceControlProvider::CanCancelOperation( const TSharedRef& InOperation ) const -{ - return false; -} - -void FGitSourceControlProvider::CancelOperation( const TSharedRef& InOperation ) -{ -} - -bool FGitSourceControlProvider::UsesLocalReadOnlyState() const -{ - return bUsingGitLfsLocking; // Git LFS Lock uses read-only state -} - -bool FGitSourceControlProvider::UsesChangelists() const -{ - return false; -} - -bool FGitSourceControlProvider::UsesCheckout() const -{ - return bUsingGitLfsLocking; // Git LFS Lock uses read-only state -} - -TSharedPtr FGitSourceControlProvider::CreateWorker(const FName& InOperationName) const -{ - const FGetGitSourceControlWorker* Operation = WorkersMap.Find(InOperationName); - if(Operation != nullptr) - { - return Operation->Execute(); - } - - return nullptr; -} - -void FGitSourceControlProvider::RegisterWorker( const FName& InName, const FGetGitSourceControlWorker& InDelegate ) -{ - WorkersMap.Add( InName, InDelegate ); -} - -void FGitSourceControlProvider::OutputCommandMessages(const FGitSourceControlCommand& InCommand) const -{ - FMessageLog SourceControlLog("SourceControl"); - - for(int32 ErrorIndex = 0; ErrorIndex < InCommand.ErrorMessages.Num(); ++ErrorIndex) - { - SourceControlLog.Error(FText::FromString(InCommand.ErrorMessages[ErrorIndex])); - } - - for(int32 InfoIndex = 0; InfoIndex < InCommand.InfoMessages.Num(); ++InfoIndex) - { - SourceControlLog.Info(FText::FromString(InCommand.InfoMessages[InfoIndex])); - } -} - -void FGitSourceControlProvider::UpdateRepositoryStatus(const class FGitSourceControlCommand& InCommand) -{ - // For all operations running UpdateStatus, get Commit informations: - if (!InCommand.CommitId.IsEmpty()) - { - CommitId = InCommand.CommitId; - CommitSummary = InCommand.CommitSummary; - } -} - -void FGitSourceControlProvider::Tick() -{ - bool bStatesUpdated = false; - - for(int32 CommandIndex = 0; CommandIndex < CommandQueue.Num(); ++CommandIndex) - { - FGitSourceControlCommand& Command = *CommandQueue[CommandIndex]; - if(Command.bExecuteProcessed) - { - // Remove command from the queue - CommandQueue.RemoveAt(CommandIndex); - - // Update respository status on UpdateStatus operations - UpdateRepositoryStatus(Command); - - // let command update the states of any files - bStatesUpdated |= Command.Worker->UpdateStates(); - - // dump any messages to output log - OutputCommandMessages(Command); - - // run the completion delegate callback if we have one bound - Command.ReturnResults(); - - // commands that are left in the array during a tick need to be deleted - if(Command.bAutoDelete) - { - // Only delete commands that are not running 'synchronously' - delete &Command; - } - - // only do one command per tick loop, as we dont want concurrent modification - // of the command queue (which can happen in the completion delegate) - break; - } - } - - if(bStatesUpdated) - { - OnSourceControlStateChanged.Broadcast(); - } -} - -TArray< TSharedRef > FGitSourceControlProvider::GetLabels( const FString& InMatchingSpec ) const -{ - TArray< TSharedRef > Tags; - - // NOTE list labels. Called by CrashDebugHelper() (to remote debug Engine crash) - // and by SourceControlHelpers::AnnotateFile() (to add source file to report) - // Reserved for internal use by Epic Games with Perforce only - return Tags; -} - -#if SOURCE_CONTROL_WITH_SLATE -TSharedRef FGitSourceControlProvider::MakeSettingsWidget() const -{ - return SNew(SGitSourceControlSettings); -} -#endif - -ECommandResult::Type FGitSourceControlProvider::ExecuteSynchronousCommand(FGitSourceControlCommand& InCommand, const FText& Task) -{ - ECommandResult::Type Result = ECommandResult::Failed; - - // Display the progress dialog if a string was provided - { - FScopedSourceControlProgress Progress(Task); - - // Issue the command asynchronously... - IssueCommand( InCommand ); - - // ... then wait for its completion (thus making it synchronous) - while(!InCommand.bExecuteProcessed) - { - // Tick the command queue and update progress. - Tick(); - - Progress.Tick(); - - // Sleep so we don't busy-wait so much. - FPlatformProcess::Sleep(0.01f); - } - - // always do one more Tick() to make sure the command queue is cleaned up. - Tick(); - - if(InCommand.bCommandSuccessful) - { - Result = ECommandResult::Succeeded; - } - } - - // Delete the command now (asynchronous commands are deleted in the Tick() method) - check(!InCommand.bAutoDelete); - - // ensure commands that are not auto deleted do not end up in the command queue - if ( CommandQueue.Contains( &InCommand ) ) - { - CommandQueue.Remove( &InCommand ); - } - delete &InCommand; - - return Result; -} - -ECommandResult::Type FGitSourceControlProvider::IssueCommand(FGitSourceControlCommand& InCommand) -{ - if(GThreadPool != nullptr) - { - // Queue this to our worker thread(s) for resolving - GThreadPool->AddQueuedWork(&InCommand); - CommandQueue.Add(&InCommand); - return ECommandResult::Succeeded; - } - else - { - FText Message(LOCTEXT("NoSCCThreads", "There are no threads available to process the source control command.")); - - FMessageLog("SourceControl").Error(Message); - InCommand.bCommandSuccessful = false; - InCommand.Operation->AddErrorMessge(Message); - - return InCommand.ReturnResults(); - } -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.h deleted file mode 100644 index 97dce5b..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.h +++ /dev/null @@ -1,210 +0,0 @@ -// 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 "ISourceControlOperation.h" -#include "ISourceControlState.h" -#include "ISourceControlProvider.h" -#include "IGitSourceControlWorker.h" -#include "GitSourceControlState.h" -#include "GitSourceControlMenu.h" - -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) or "git version 2.11.0" (Linux/Mac/Cygwin/WSL) - int Major; // 2 Major version number - int Minor; // 11 Minor version number - int Patch; // 0 Patch/bugfix number - int Windows; // 3 Windows specific revision number (under Windows only) - - uint32 bHasCatFileWithFilters : 1; - uint32 bHasGitLfs : 1; - uint32 bHasGitLfsLocking : 1; - - FGitVersion() - : Major(0) - , Minor(0) - , Patch(0) - , Windows(0) - , bHasCatFileWithFilters(false) - , bHasGitLfs(false) - , bHasGitLfsLocking(false) - { - } - - inline bool IsGreaterOrEqualThan(int InMajor, int InMinor) const - { - return (Major > InMajor) || (Major == InMajor && (Minor >= InMinor)); - } -}; - -class FGitSourceControlProvider : public ISourceControlProvider -{ -public: - /** Constructor */ - FGitSourceControlProvider() - : bGitAvailable(false) - , bGitRepositoryFound(false) - { - } - - /* 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 UE4.20 */ { return false; } - virtual void RegisterStateBranches(const TArray& BranchNames, const FString& ContentRoot) /* override UE4.20 */ {} - virtual int32 GetStateBranchIndex(const FString& InBranchName) const /* override UE4.20 */ { return INDEX_NONE; } - virtual ECommandResult::Type GetState( const TArray& InFiles, TArray< TSharedRef >& OutState, EStateCacheUsage::Type InStateCacheUsage ) override; - virtual TArray GetCachedStateByPredicate(TFunctionRef Predicate) const override; - virtual FDelegateHandle RegisterSourceControlStateChanged_Handle(const FSourceControlStateChanged::FDelegate& SourceControlStateChanged) override; - virtual void UnregisterSourceControlStateChanged_Handle(FDelegateHandle Handle) override; - virtual ECommandResult::Type Execute(const TSharedRef& InOperation, const TArray& InFiles, EConcurrency::Type InConcurrency = EConcurrency::Synchronous, const FSourceControlOperationComplete& InOperationCompleteDelegate = FSourceControlOperationComplete()) override; - virtual bool CanCancelOperation( const TSharedRef& InOperation ) const override; - virtual void CancelOperation( const TSharedRef& InOperation ) override; - virtual bool UsesLocalReadOnlyState() const override; - virtual bool UsesChangelists() const override; - virtual bool UsesCheckout() const override; - virtual void Tick() override; - virtual TArray< TSharedRef > GetLabels( const FString& InMatchingSpec ) const override; -#if SOURCE_CONTROL_WITH_SLATE - virtual TSharedRef MakeSettingsWidget() const override; -#endif - - /** - * Check configuration, else standard paths, and run a Git "version" command to check the availability of the binary. - */ - void CheckGitAvailability(); - - /** - * Find the .git/ repository and check it's status. - */ - void CheckRepositoryStatus(const FString& InPathToGitBinary); - - /** Is git binary found and working. */ - inline bool IsGitAvailable() const - { - return bGitAvailable; - } - - /** Git version for feature checking */ - inline const FGitVersion& GetGitVersion() const - { - return GitVersion; - } - - /** Get the path to the root of the Git repository: can be the ProjectDir itself, or any parent directory */ - inline const FString& GetPathToRepositoryRoot() const - { - return PathToRepositoryRoot; - } - - /** 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; - } - - /** Helper function used to update state cache */ - TSharedRef GetStateInternal(const FString& Filename); - - /** - * 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 ); - - /** Remove a named file from the state cache */ - bool RemoveFileFromCache(const FString& Filename); - - /** Get files in cache */ - TArray GetFilesInCache(); - -private: - - /** Is git binary found and working. */ - bool bGitAvailable; - - /** Is git repository found. */ - bool bGitRepositoryFound; - - /** Is LFS File Locking enabled? */ - bool bUsingGitLfsLocking = false; - - /** Helper function for Execute() */ - TSharedPtr CreateWorker(const FName& InOperationName) const; - - /** Helper function for running command synchronously. */ - ECommandResult::Type ExecuteSynchronousCommand(class FGitSourceControlCommand& InCommand, const FText& Task); - /** Issue a command asynchronously if possible. */ - ECommandResult::Type IssueCommand(class FGitSourceControlCommand& InCommand); - - /** 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 Git repository: can be the ProjectDir itself, or any parent directory (found by the "Connect" operation) */ - FString PathToRepositoryRoot; - - /** 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; - - /** URL of the "origin" defaut remote server */ - FString RemoteUrl; - - /** Current Commit full SHA1 */ - FString CommitId; - - /** Current Commit description's Summary */ - FString CommitSummary; - - /** State cache */ - TMap > StateCache; - - /** The currently registered source control operations */ - TMap WorkersMap; - - /** Queue for commands given by the main thread */ - TArray < FGitSourceControlCommand* > CommandQueue; - - /** For notifying when the source control states in the cache have changed */ - FSourceControlStateChanged OnSourceControlStateChanged; - - /** Git version for feature checking */ - FGitVersion GitVersion; - - /** Source Control Menu Extension */ - FGitSourceControlMenu GitSourceControlMenu; -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.cpp deleted file mode 100644 index 0858f7f..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// 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) - -#include "GitSourceControlRevision.h" - -#include "HAL/FileManager.h" -#include "Misc/Paths.h" -#include "Modules/ModuleManager.h" -#include "GitSourceControlModule.h" -#include "GitSourceControlUtils.h" - -#define LOCTEXT_NAMESPACE "GitSourceControl" - -bool FGitSourceControlRevision::Get( FString& InOutFilename ) const -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const FString PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - const FString PathToRepositoryRoot = GitSourceControl.GetProvider().GetPathToRepositoryRoot(); - - // if a filename for the temp file wasn't supplied generate a unique-ish one - if(InOutFilename.Len() == 0) - { - // create the diff dir if we don't already have it (Git wont) - IFileManager::Get().MakeDirectory(*FPaths::DiffDir(), true); - // create a unique temp file name based on the unique commit Id - const FString TempFileName = FString::Printf(TEXT("%stemp-%s-%s"), *FPaths::DiffDir(), *CommitId, *FPaths::GetCleanFilename(Filename)); - InOutFilename = FPaths::ConvertRelativePathToFull(TempFileName); - } - - // Diff against the revision - const FString Parameter = FString::Printf(TEXT("%s:%s"), *CommitId, *Filename); - - bool bCommandSuccessful; - if(FPaths::FileExists(InOutFilename)) - { - bCommandSuccessful = true; // if the temp file already exists, reuse it directly - } - else - { - bCommandSuccessful = GitSourceControlUtils::RunDumpToFile(PathToGitBinary, PathToRepositoryRoot, Parameter, InOutFilename); - } - return bCommandSuccessful; -} - -bool FGitSourceControlRevision::GetAnnotated( TArray& OutLines ) const -{ - return false; -} - -bool FGitSourceControlRevision::GetAnnotated( FString& InOutFilename ) const -{ - return false; -} - -const FString& FGitSourceControlRevision::GetFilename() const -{ - return Filename; -} - -int32 FGitSourceControlRevision::GetRevisionNumber() const -{ - return RevisionNumber; -} - -const FString& FGitSourceControlRevision::GetRevision() const -{ - return ShortCommitId; -} - -const FString& FGitSourceControlRevision::GetDescription() const -{ - return Description; -} - -const FString& FGitSourceControlRevision::GetUserName() const -{ - return UserName; -} - -const FString& FGitSourceControlRevision::GetClientSpec() const -{ - static FString EmptyString(TEXT("")); - return EmptyString; -} - -const FString& FGitSourceControlRevision::GetAction() const -{ - return Action; -} - -TSharedPtr FGitSourceControlRevision::GetBranchSource() const -{ - // if this revision was copied/moved from some other revision - return BranchSource; -} - -const FDateTime& FGitSourceControlRevision::GetDate() const -{ - return Date; -} - -int32 FGitSourceControlRevision::GetCheckInIdentifier() const -{ - return CommitIdNumber; -} - -int32 FGitSourceControlRevision::GetFileSize() const -{ - return FileSize; -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.h deleted file mode 100644 index d2cf7ab..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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 "ISourceControlRevision.h" - -/** Revision of a file, linked to a specific commit */ -class FGitSourceControlRevision : public ISourceControlRevision, public TSharedFromThis -{ -public: - FGitSourceControlRevision() - : RevisionNumber(0) - { - } - - /** ISourceControlRevision interface */ - virtual bool Get( FString& InOutFilename ) const override; - virtual bool GetAnnotated( TArray& 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 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; - - /** The index of the revision in the history (SBlueprintRevisionMenu assumes order for the "Depot" label) */ - int32 RevisionNumber; - - /** 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 BranchSource; - - /** The date this revision was made */ - FDateTime Date; - - /** The size of the file at this revision */ - int32 FileSize; -}; - -/** History composed of the last 100 revisions of the file */ -typedef TArray< TSharedRef > TGitSourceControlHistory; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.cpp deleted file mode 100644 index b03c6e5..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// 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) - -#include "GitSourceControlSettings.h" - -#include "Misc/ScopeLock.h" -#include "Misc/ConfigCacheIni.h" -#include "Modules/ModuleManager.h" -#include "SourceControlHelpers.h" -#include "GitSourceControlModule.h" -#include "GitSourceControlUtils.h" - -namespace GitSettingsConstants -{ - -/** The section of the ini file we load our settings from */ -static const FString SettingsSection = TEXT("GitSourceControl.GitSourceControlSettings"); - -} - -const FString FGitSourceControlSettings::GetBinaryPath() const -{ - FScopeLock ScopeLock(&CriticalSection); - return BinaryPath; // Return a copy to be thread-safe -} - -bool FGitSourceControlSettings::SetBinaryPath(const FString& InString) -{ - FScopeLock ScopeLock(&CriticalSection); - const bool bChanged = (BinaryPath != InString); - if(bChanged) - { - BinaryPath = InString; - } - return bChanged; -} - -/** Tell if using the Git LFS file Locking workflow */ -bool FGitSourceControlSettings::IsUsingGitLfsLocking() const -{ - FScopeLock ScopeLock(&CriticalSection); - return bUsingGitLfsLocking; -} - -/** Configure the usage of Git LFS file Locking workflow */ -bool FGitSourceControlSettings::SetUsingGitLfsLocking(const bool InUsingGitLfsLocking) -{ - FScopeLock ScopeLock(&CriticalSection); - const bool bChanged = (bUsingGitLfsLocking != InUsingGitLfsLocking); - bUsingGitLfsLocking = InUsingGitLfsLocking; - return bChanged; -} - -const FString FGitSourceControlSettings::GetLfsUserName() const -{ - FScopeLock ScopeLock(&CriticalSection); - return LfsUserName; // Return a copy to be thread-safe -} - -bool FGitSourceControlSettings::SetLfsUserName(const FString& InString) -{ - FScopeLock ScopeLock(&CriticalSection); - const bool bChanged = (LfsUserName != InString); - if (bChanged) - { - LfsUserName = InString; - } - return bChanged; -} - -// This is called at startup nearly before anything else in our module: BinaryPath will then be used by the provider -void FGitSourceControlSettings::LoadSettings() -{ - FScopeLock ScopeLock(&CriticalSection); - const FString& IniFile = SourceControlHelpers::GetSettingsIni(); - GConfig->GetString(*GitSettingsConstants::SettingsSection, TEXT("BinaryPath"), BinaryPath, IniFile); - GConfig->GetBool(*GitSettingsConstants::SettingsSection, TEXT("UsingGitLfsLocking"), bUsingGitLfsLocking, IniFile); - GConfig->GetString(*GitSettingsConstants::SettingsSection, TEXT("LfsUserName"), LfsUserName, IniFile); -} - -void FGitSourceControlSettings::SaveSettings() const -{ - FScopeLock ScopeLock(&CriticalSection); - const FString& IniFile = SourceControlHelpers::GetSettingsIni(); - GConfig->SetString(*GitSettingsConstants::SettingsSection, TEXT("BinaryPath"), *BinaryPath, IniFile); - GConfig->SetBool(*GitSettingsConstants::SettingsSection, TEXT("UsingGitLfsLocking"), bUsingGitLfsLocking, IniFile); - GConfig->SetString(*GitSettingsConstants::SettingsSection, TEXT("LfsUserName"), *LfsUserName, IniFile); -} diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.h deleted file mode 100644 index 3a9549f..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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" - -class 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; - - /** Username used by the Git LFS 2 File Locks server */ - FString LfsUserName; -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.cpp deleted file mode 100644 index 8801ffd..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.cpp +++ /dev/null @@ -1,386 +0,0 @@ -// 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) - -#include "GitSourceControlState.h" - -#define LOCTEXT_NAMESPACE "GitSourceControl.State" - -int32 FGitSourceControlState::GetHistorySize() const -{ - return History.Num(); -} - -TSharedPtr FGitSourceControlState::GetHistoryItem( int32 HistoryIndex ) const -{ - check(History.IsValidIndex(HistoryIndex)); - return History[HistoryIndex]; -} - -TSharedPtr FGitSourceControlState::FindHistoryRevision( int32 RevisionNumber ) const -{ - for(const auto& Revision : History) - { - if(Revision->GetRevisionNumber() == RevisionNumber) - { - return Revision; - } - } - - return nullptr; -} - -TSharedPtr FGitSourceControlState::FindHistoryRevision(const FString& InRevision) const -{ - for(const auto& Revision : History) - { - if(Revision->GetRevision() == InRevision) - { - return Revision; - } - } - - return nullptr; -} - -TSharedPtr FGitSourceControlState::GetBaseRevForMerge() const -{ - for(const auto& Revision : History) - { - // look for the the SHA1 id of the file, not the commit id (revision) - if(Revision->FileHash == PendingMergeBaseFileHash) - { - return Revision; - } - } - - return nullptr; -} - -// @todo add Slate icons for git specific states (NotAtHead vs Conflicted...) -FName FGitSourceControlState::GetIconName() const -{ - if(LockState == ELockState::Locked) - { - return FName("Subversion.CheckedOut"); - } - else if(LockState == ELockState::LockedOther) - { - return FName("Subversion.CheckedOutByOtherUser"); - } - else if (!IsCurrent()) - { - return FName("Subversion.NotAtHeadRevision"); - } - - switch(WorkingCopyState) - { - case EWorkingCopyState::Modified: - if(bUsingGitLfsLocking) - { - return FName("Subversion.NotInDepot"); - } - else - { - return FName("Subversion.CheckedOut"); - } - case EWorkingCopyState::Added: - return FName("Subversion.OpenForAdd"); - case EWorkingCopyState::Renamed: - case EWorkingCopyState::Copied: - return FName("Subversion.Branched"); - case EWorkingCopyState::Deleted: // Deleted & Missing files does not show in Content Browser - case EWorkingCopyState::Missing: - return FName("Subversion.MarkedForDelete"); - case EWorkingCopyState::Conflicted: - return FName("Subversion.ModifiedOtherBranch"); - case EWorkingCopyState::NotControlled: - return FName("Subversion.NotInDepot"); - case EWorkingCopyState::Unknown: - case EWorkingCopyState::Unchanged: // Unchanged is the same as "Pristine" (not checked out) for Perforce, ie no icon - case EWorkingCopyState::Ignored: - default: - return NAME_None; - } - - return NAME_None; -} - -FName FGitSourceControlState::GetSmallIconName() const -{ - if(LockState == ELockState::Locked) - { - return FName("Subversion.CheckedOut_Small"); - } - else if(LockState == ELockState::LockedOther) - { - return FName("Subversion.CheckedOutByOtherUser_Small"); - } - else if (!IsCurrent()) - { - return FName("Subversion.NotAtHeadRevision_Small"); - } - - switch(WorkingCopyState) - { - case EWorkingCopyState::Modified: - if(bUsingGitLfsLocking) - { - return FName("Subversion.NotInDepot_Small"); - } - else - { - return FName("Subversion.CheckedOut_Small"); - } - case EWorkingCopyState::Added: - return FName("Subversion.OpenForAdd_Small"); - case EWorkingCopyState::Renamed: - case EWorkingCopyState::Copied: - return FName("Subversion.Branched_Small"); - case EWorkingCopyState::Deleted: // Deleted & Missing files can appear in the Submit to Source Control window - case EWorkingCopyState::Missing: - return FName("Subversion.MarkedForDelete_Small"); - case EWorkingCopyState::Conflicted: - return FName("Subversion.ModifiedOtherBranch_Small"); - case EWorkingCopyState::NotControlled: - return FName("Subversion.NotInDepot_Small"); - case EWorkingCopyState::Unknown: - case EWorkingCopyState::Unchanged: // Unchanged is the same as "Pristine" (not checked out) for Perforce, ie no icon - case EWorkingCopyState::Ignored: - default: - return NAME_None; - } - - return NAME_None; -} - -FText FGitSourceControlState::GetDisplayName() const -{ - if(LockState == ELockState::Locked) - { - return LOCTEXT("Locked", "Locked For Editing"); - } - else if(LockState == ELockState::LockedOther) - { - return FText::Format( LOCTEXT("LockedOther", "Locked by "), FText::FromString(LockUser) ); - } - else if (!IsCurrent()) - { - return LOCTEXT("NotCurrent", "Not current"); - } - - switch(WorkingCopyState) - { - case EWorkingCopyState::Unknown: - return LOCTEXT("Unknown", "Unknown"); - case EWorkingCopyState::Unchanged: - return LOCTEXT("Unchanged", "Unchanged"); - case EWorkingCopyState::Added: - return LOCTEXT("Added", "Added"); - case EWorkingCopyState::Deleted: - return LOCTEXT("Deleted", "Deleted"); - case EWorkingCopyState::Modified: - return LOCTEXT("Modified", "Modified"); - case EWorkingCopyState::Renamed: - return LOCTEXT("Renamed", "Renamed"); - case EWorkingCopyState::Copied: - return LOCTEXT("Copied", "Copied"); - case EWorkingCopyState::Conflicted: - return LOCTEXT("ContentsConflict", "Contents Conflict"); - case EWorkingCopyState::Ignored: - return LOCTEXT("Ignored", "Ignored"); - case EWorkingCopyState::NotControlled: - return LOCTEXT("NotControlled", "Not Under Source Control"); - case EWorkingCopyState::Missing: - return LOCTEXT("Missing", "Missing"); - } - - return FText(); -} - -FText FGitSourceControlState::GetDisplayTooltip() const -{ - if(LockState == ELockState::Locked) - { - return LOCTEXT("Locked_Tooltip", "Locked for editing by current user"); - } - else if(LockState == ELockState::LockedOther) - { - return FText::Format( LOCTEXT("LockedOther_Tooltip", "Locked for editing by: {0}"), FText::FromString(LockUser) ); - } - else if (!IsCurrent()) - { - return LOCTEXT("NotCurrent_Tooltip", "The file(s) are not at the head revision"); - } - - switch(WorkingCopyState) - { - case EWorkingCopyState::Unknown: - return LOCTEXT("Unknown_Tooltip", "Unknown source control state"); - case EWorkingCopyState::Unchanged: - return LOCTEXT("Pristine_Tooltip", "There are no modifications"); - case EWorkingCopyState::Added: - return LOCTEXT("Added_Tooltip", "Item is scheduled for addition"); - case EWorkingCopyState::Deleted: - return LOCTEXT("Deleted_Tooltip", "Item is scheduled for deletion"); - case EWorkingCopyState::Modified: - return LOCTEXT("Modified_Tooltip", "Item has been modified"); - case EWorkingCopyState::Renamed: - return LOCTEXT("Renamed_Tooltip", "Item has been renamed"); - case EWorkingCopyState::Copied: - return LOCTEXT("Copied_Tooltip", "Item has been copied"); - case EWorkingCopyState::Conflicted: - return LOCTEXT("ContentsConflict_Tooltip", "The contents of the item conflict with updates received from the repository."); - case EWorkingCopyState::Ignored: - return LOCTEXT("Ignored_Tooltip", "Item is being ignored."); - case EWorkingCopyState::NotControlled: - return LOCTEXT("NotControlled_Tooltip", "Item is not under version control."); - case EWorkingCopyState::Missing: - return LOCTEXT("Missing_Tooltip", "Item is missing (e.g., you moved or deleted it without using Git). This also indicates that a directory is incomplete (a checkout or update was interrupted)."); - } - - return FText(); -} - -const FString& FGitSourceControlState::GetFilename() const -{ - return LocalFilename; -} - -const FDateTime& FGitSourceControlState::GetTimeStamp() const -{ - return TimeStamp; -} - -// Deleted and Missing assets cannot appear in the Content Browser, but the do in the Submit files to Source Control window! -bool FGitSourceControlState::CanCheckIn() const -{ - if(bUsingGitLfsLocking) - { - return ( ( (LockState == ELockState::Locked) && !IsConflicted() ) || (WorkingCopyState == EWorkingCopyState::Added) ) && IsCurrent(); - } - else - { - return (WorkingCopyState == EWorkingCopyState::Added - || WorkingCopyState == EWorkingCopyState::Deleted - || WorkingCopyState == EWorkingCopyState::Missing - || WorkingCopyState == EWorkingCopyState::Modified - || WorkingCopyState == EWorkingCopyState::Renamed) && IsCurrent(); - } -} - -bool FGitSourceControlState::CanCheckout() const -{ - if(bUsingGitLfsLocking) - { - // We don't want to allow checkout if the file is out-of-date, as modifying an out-of-date binary file will most likely result in a merge conflict - return (WorkingCopyState == EWorkingCopyState::Unchanged || WorkingCopyState == EWorkingCopyState::Modified) && LockState == ELockState::NotLocked && IsCurrent(); - } - else - { - return false; // With Git all tracked files in the working copy are always already checked-out (as opposed to Perforce) - } -} - -bool FGitSourceControlState::IsCheckedOut() const -{ - if (bUsingGitLfsLocking) - { - return LockState == ELockState::Locked; - } - else - { - return IsSourceControlled(); // With Git all tracked files in the working copy are always checked-out (as opposed to Perforce) - } -} - -bool FGitSourceControlState::IsCheckedOutOther(FString* Who) const -{ - if (Who != NULL) - { - *Who = LockUser; - } - return LockState == ELockState::LockedOther; -} - -bool FGitSourceControlState::IsCurrent() const -{ - return !bNewerVersionOnServer; -} - -bool FGitSourceControlState::IsSourceControlled() const -{ - return WorkingCopyState != EWorkingCopyState::NotControlled && WorkingCopyState != EWorkingCopyState::Ignored && WorkingCopyState != EWorkingCopyState::Unknown; -} - -bool FGitSourceControlState::IsAdded() const -{ - return WorkingCopyState == EWorkingCopyState::Added; -} - -bool FGitSourceControlState::IsDeleted() const -{ - return WorkingCopyState == EWorkingCopyState::Deleted || WorkingCopyState == EWorkingCopyState::Missing; -} - -bool FGitSourceControlState::IsIgnored() const -{ - return WorkingCopyState == EWorkingCopyState::Ignored; -} - -bool FGitSourceControlState::CanEdit() const -{ - return IsCurrent(); // With Git all files in the working copy are always editable (as opposed to Perforce) -} - -bool FGitSourceControlState::CanDelete() const -{ - return !IsCheckedOutOther() && IsSourceControlled() && IsCurrent(); -} - -bool FGitSourceControlState::IsUnknown() const -{ - return WorkingCopyState == EWorkingCopyState::Unknown; -} - -bool FGitSourceControlState::IsModified() const -{ - // Warning: for Perforce, a checked-out file is locked for modification (whereas with Git all tracked files are checked-out), - // so for a clean "check-in" (commit) checked-out files unmodified should be removed from the changeset (the index) - // http://stackoverflow.com/questions/12357971/what-does-revert-unchanged-files-mean-in-perforce - // - // Thus, before check-in UE4 Editor call RevertUnchangedFiles() in PromptForCheckin() and CheckinFiles(). - // - // So here we must take care to enumerate all states that need to be commited, - // all other will be discarded : - // - Unknown - // - Unchanged - // - NotControlled - // - Ignored - return WorkingCopyState == EWorkingCopyState::Added - || WorkingCopyState == EWorkingCopyState::Deleted - || WorkingCopyState == EWorkingCopyState::Modified - || WorkingCopyState == EWorkingCopyState::Renamed - || WorkingCopyState == EWorkingCopyState::Copied - || WorkingCopyState == EWorkingCopyState::Missing - || WorkingCopyState == EWorkingCopyState::Conflicted; -} - - -bool FGitSourceControlState::CanAdd() const -{ - return WorkingCopyState == EWorkingCopyState::NotControlled; -} - -bool FGitSourceControlState::IsConflicted() const -{ - return WorkingCopyState == EWorkingCopyState::Conflicted; -} - -bool FGitSourceControlState::CanRevert() const -{ - return CanCheckIn(); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.h deleted file mode 100644 index d1ea0fa..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.h +++ /dev/null @@ -1,117 +0,0 @@ -// 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 "ISourceControlState.h" -#include "ISourceControlRevision.h" -#include "GitSourceControlRevision.h" - -namespace EWorkingCopyState -{ - enum Type - { - Unknown, - Unchanged, // called "clean" in SVN, "Pristine" in Perforce - Added, - Deleted, - Modified, - Renamed, - Copied, - Missing, - Conflicted, - NotControlled, - Ignored, - }; -} - -namespace ELockState -{ - enum Type - { - Unknown, - NotLocked, - Locked, - LockedOther, - }; -} - -class FGitSourceControlState : public ISourceControlState, public TSharedFromThis -{ -public: - FGitSourceControlState( const FString& InLocalFilename, const bool InUsingLfsLocking) - : LocalFilename(InLocalFilename) - , WorkingCopyState(EWorkingCopyState::Unknown) - , LockState(ELockState::Unknown) - , bUsingGitLfsLocking(InUsingLfsLocking) - , bNewerVersionOnServer(false) - , TimeStamp(0) - { - } - - /** ISourceControlState interface */ - virtual int32 GetHistorySize() const override; - virtual TSharedPtr GetHistoryItem(int32 HistoryIndex) const override; - virtual TSharedPtr FindHistoryRevision(int32 RevisionNumber) const override; - virtual TSharedPtr FindHistoryRevision(const FString& InRevision) const override; - virtual TSharedPtr GetBaseRevForMerge() const override; - virtual FName GetIconName() const override; - virtual FName GetSmallIconName() const override; - 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 = nullptr) const override; - virtual bool IsCheckedOutInOtherBranch(const FString& CurrentBranch = FString()) const /* UE4.20 override */ { return false; } - virtual bool IsModifiedInOtherBranch(const FString& CurrentBranch = FString()) const /* UE4.20 override */ { return false; } - virtual bool IsCheckedOutOrModifiedInOtherBranch(const FString& CurrentBranch = FString()) const /* UE4.20 override */ { return IsCheckedOutInOtherBranch(CurrentBranch) || IsModifiedInOtherBranch(CurrentBranch); } - virtual TArray GetCheckedOutBranches() const /* UE4.20 override */ { return TArray(); } - virtual FString GetOtherUserBranchCheckedOuts() const /* UE4.20 override */ { return FString(); } - virtual bool GetOtherBranchHeadModification(FString& HeadBranchOut, FString& ActionOut, int32& HeadChangeListOut) const /* UE4.20 override */ { return false; } - 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 CanDelete() const override; - virtual bool IsUnknown() const override; - virtual bool IsModified() const override; - virtual bool CanAdd() const override; - virtual bool IsConflicted() const override; - virtual bool CanRevert() const override; - -public: - /** History of the item, if any */ - TGitSourceControlHistory History; - - /** Filename on disk */ - FString LocalFilename; - - /** File Id with which our local revision diverged from the remote revision */ - FString PendingMergeBaseFileHash; - - /** State of the working copy */ - EWorkingCopyState::Type WorkingCopyState; - - /** Lock state */ - ELockState::Type LockState; - - /** Name of user who has locked the file */ - FString LockUser; - - /** Tells if using the Git LFS file Locking workflow */ - bool bUsingGitLfsLocking; - - /** Whether a newer version exists on the server */ - bool bNewerVersionOnServer; - - /** The timestamp of the last update */ - FDateTime TimeStamp; -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.cpp deleted file mode 100644 index 4201458..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.cpp +++ /dev/null @@ -1,1556 +0,0 @@ -// 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) - -#include "GitSourceControlUtils.h" - -#include "GitSourceControlCommand.h" -#include "HAL/PlatformProcess.h" -#include "HAL/PlatformFilemanager.h" -#include "HAL/FileManager.h" -#include "Misc/FileHelper.h" -#include "Misc/Paths.h" -#include "Modules/ModuleManager.h" -#include "ISourceControlModule.h" -#include "GitSourceControlModule.h" -#include "GitSourceControlProvider.h" - -#if PLATFORM_LINUX -#include -#endif - - -namespace GitSourceControlConstants -{ - /** The maximum number of files we submit in a single Git command */ - const int32 MaxFilesPerBatch = 50; -} - -FGitScopedTempFile::FGitScopedTempFile(const FText& InText) -{ - Filename = FPaths::CreateTempFilename(*FPaths::ProjectLogDir(), TEXT("Git-Temp"), TEXT(".txt")); - if(!FFileHelper::SaveStringToFile(InText.ToString(), *Filename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) - { - UE_LOG(LogSourceControl, Error, TEXT("Failed to write to temp file: %s"), *Filename); - } -} - -FGitScopedTempFile::~FGitScopedTempFile() -{ - if(FPaths::FileExists(Filename)) - { - if(!FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*Filename)) - { - UE_LOG(LogSourceControl, Error, TEXT("Failed to delete temp file: %s"), *Filename); - } - } -} - -const FString& FGitScopedTempFile::GetFilename() const -{ - return Filename; -} - - -namespace GitSourceControlUtils -{ - -// Launch the Git command line process and extract its results & errors -static bool RunCommandInternalRaw(const FString& InCommand, const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray& InParameters, const TArray& InFiles, FString& OutResults, FString& OutErrors, const int32 ExpectedReturnCode = 0) -{ - int32 ReturnCode = 0; - FString FullCommand; - FString LogableCommand; // short version of the command for logging purpose - - if(!InRepositoryRoot.IsEmpty()) - { - FString RepositoryRoot = InRepositoryRoot; - - // Detect a "migrate asset" scenario (a "git add" command is applied to files outside the current project) - if ( (InFiles.Num() > 0) && !FPaths::IsRelative(InFiles[0]) && !InFiles[0].StartsWith(InRepositoryRoot) ) - { - // in this case, find the git repository (if any) of the destination Project - FString DestinationRepositoryRoot; - if(FindRootDirectory(FPaths::GetPath(InFiles[0]), DestinationRepositoryRoot)) - { - RepositoryRoot = DestinationRepositoryRoot; // if found use it for the "add" command (else not, to avoid producing one more error in logs) - } - } - - // Specify the working copy (the root) of the git repository (before the command itself) - FullCommand = TEXT("-C \""); - FullCommand += RepositoryRoot; - FullCommand += TEXT("\" "); - } - // then the git command itself ("status", "log", "commit"...) - LogableCommand += InCommand; - - // Append to the command all parameters, and then finally the files - for(const auto& Parameter : InParameters) - { - LogableCommand += TEXT(" "); - LogableCommand += Parameter; - } - for(const auto& File : InFiles) - { - LogableCommand += TEXT(" \""); - LogableCommand += File; - LogableCommand += TEXT("\""); - } - // Also, Git does not have a "--non-interactive" option, as it auto-detects when there are no connected standard input/output streams - - FullCommand += LogableCommand; - - UE_LOG(LogSourceControl, Log, TEXT("RunCommand: 'git %s'"), *LogableCommand); - - FString PathToGitOrEnvBinary = InPathToGitBinary; -#if PLATFORM_MAC - // The Cocoa application does not inherit shell environment variables, so add the path expected to have git-lfs to PATH - FString PathEnv = FPlatformMisc::GetEnvironmentVariable(TEXT("PATH")); - FString GitInstallPath = FPaths::GetPath(InPathToGitBinary); - - TArray PathArray; - PathEnv.ParseIntoArray(PathArray, FPlatformMisc::GetPathVarDelimiter()); - bool bHasGitInstallPath = false; - for (auto Path : PathArray) - { - if (GitInstallPath.Equals(Path, ESearchCase::CaseSensitive)) - { - bHasGitInstallPath = true; - break; - } - } - - if (!bHasGitInstallPath) - { - PathToGitOrEnvBinary = FString("/usr/bin/env"); - FullCommand = FString::Printf(TEXT("PATH=\"%s%s%s\" \"%s\" %s"), *GitInstallPath, FPlatformMisc::GetPathVarDelimiter(), *PathEnv, *InPathToGitBinary, *FullCommand); - } -#endif - FPlatformProcess::ExecProcess(*PathToGitOrEnvBinary, *FullCommand, &ReturnCode, &OutResults, &OutErrors); - - // TODO: add a setting to easily enable Verbose logging - UE_LOG(LogSourceControl, Verbose, TEXT("RunCommand(%s):\n%s"), *InCommand, *OutResults); - if(ReturnCode != ExpectedReturnCode || OutErrors.Len() > 0) - { - UE_LOG(LogSourceControl, Warning, TEXT("RunCommand(%s) ReturnCode=%d:\n%s"), *InCommand, ReturnCode, *OutErrors); - } - - // Move push/pull progress information from the error stream to the info stream - if(ReturnCode == ExpectedReturnCode && OutErrors.Len() > 0) - { - OutResults.Append(OutErrors); - OutErrors.Empty(); - } - - return ReturnCode == ExpectedReturnCode; -} - -// Basic parsing or results & errors from the Git command line process -static bool RunCommandInternal(const FString& InCommand, const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray& InParameters, const TArray& InFiles, TArray& OutResults, TArray& OutErrorMessages) -{ - bool bResult; - FString Results; - FString Errors; - - bResult = RunCommandInternalRaw(InCommand, InPathToGitBinary, InRepositoryRoot, InParameters, InFiles, Results, Errors); - Results.ParseIntoArray(OutResults, TEXT("\n"), true); - Errors.ParseIntoArray(OutErrorMessages, TEXT("\n"), true); - - return bResult; -} - -FString FindGitBinaryPath() -{ -#if PLATFORM_WINDOWS - // 1) First of all, look into standard install directories - // NOTE using only "git" (or "git.exe") relying on the "PATH" envvar does not always work as expected, depending on the installation: - // If the PATH is set with "git/cmd" instead of "git/bin", - // "git.exe" launch "git/cmd/git.exe" that redirect to "git/bin/git.exe" and ExecProcess() is unable to catch its outputs streams. - // First check the 64-bit program files directory: - FString GitBinaryPath(TEXT("C:/Program Files/Git/bin/git.exe")); - bool bFound = CheckGitAvailability(GitBinaryPath); - if(!bFound) - { - // otherwise check the 32-bit program files directory. - GitBinaryPath = TEXT("C:/Program Files (x86)/Git/bin/git.exe"); - bFound = CheckGitAvailability(GitBinaryPath); - } - if(!bFound) - { - // else the install dir for the current user: C:\Users\UserName\AppData\Local\Programs\Git\cmd - const FString AppDataLocalPath = FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")); - GitBinaryPath = FString::Printf(TEXT("%s/Programs/Git/cmd/git.exe"), *AppDataLocalPath); - bFound = CheckGitAvailability(GitBinaryPath); - } - - // 2) Else, look for the version of Git bundled with SmartGit "Installer with JRE" - if(!bFound) - { - GitBinaryPath = TEXT("C:/Program Files (x86)/SmartGit/git/bin/git.exe"); - bFound = CheckGitAvailability(GitBinaryPath); - if (!bFound) - { - // If git is not found in "git/bin/" subdirectory, try the "bin/" path that was in use before - GitBinaryPath = TEXT("C:/Program Files (x86)/SmartGit/bin/git.exe"); - bFound = CheckGitAvailability(GitBinaryPath); - } - } - - // 3) Else, look for the local_git provided by SourceTree - if(!bFound) - { - // C:\Users\UserName\AppData\Local\Atlassian\SourceTree\git_local\bin - const FString AppDataLocalPath = FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")); - GitBinaryPath = FString::Printf(TEXT("%s/Atlassian/SourceTree/git_local/bin/git.exe"), *AppDataLocalPath); - bFound = CheckGitAvailability(GitBinaryPath); - } - - // 4) Else, look for the PortableGit provided by GitHub Desktop - if(!bFound) - { - // The latest GitHub Desktop adds its binaries into the local appdata directory: - // C:\Users\UserName\AppData\Local\GitHub\PortableGit_c2ba306e536fdf878271f7fe636a147ff37326ad\cmd - const FString AppDataLocalPath = FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")); - const FString SearchPath = FString::Printf(TEXT("%s/GitHub/PortableGit_*"), *AppDataLocalPath); - TArray PortableGitFolders; - IFileManager::Get().FindFiles(PortableGitFolders, *SearchPath, false, true); - if(PortableGitFolders.Num() > 0) - { - // FindFiles just returns directory names, so we need to prepend the root path to get the full path. - GitBinaryPath = FString::Printf(TEXT("%s/GitHub/%s/cmd/git.exe"), *AppDataLocalPath, *(PortableGitFolders.Last())); // keep only the last PortableGit found - bFound = CheckGitAvailability(GitBinaryPath); - if (!bFound) - { - // If Portable git is not found in "cmd/" subdirectory, try the "bin/" path that was in use before - GitBinaryPath = FString::Printf(TEXT("%s/GitHub/%s/bin/git.exe"), *AppDataLocalPath, *(PortableGitFolders.Last())); // keep only the last PortableGit found - bFound = CheckGitAvailability(GitBinaryPath); - } - } - } - - // 5) Else, look for the version of Git bundled with Tower - if (!bFound) - { - GitBinaryPath = TEXT("C:/Program Files (x86)/fournova/Tower/vendor/Git/bin/git.exe"); - bFound = CheckGitAvailability(GitBinaryPath); - } - -#elif PLATFORM_MAC - // 1) First of all, look for the version of git provided by official git - FString GitBinaryPath = TEXT("/usr/local/git/bin/git"); - bool bFound = CheckGitAvailability(GitBinaryPath); - - // 2) Else, look for the version of git provided by Homebrew - if (!bFound) - { - GitBinaryPath = TEXT("/usr/local/bin/git"); - bFound = CheckGitAvailability(GitBinaryPath); - } - - // 3) Else, look for the version of git provided by MacPorts - if (!bFound) - { - GitBinaryPath = TEXT("/opt/local/bin/git"); - bFound = CheckGitAvailability(GitBinaryPath); - } - - // 4) Else, look for the version of git provided by Command Line Tools - if (!bFound) - { - GitBinaryPath = TEXT("/usr/bin/git"); - bFound = CheckGitAvailability(GitBinaryPath); - } - - { - SCOPED_AUTORELEASE_POOL; - NSWorkspace* SharedWorkspace = [NSWorkspace sharedWorkspace]; - - // 5) Else, look for the version of local_git provided by SmartGit - if (!bFound) - { - NSURL* AppURL = [SharedWorkspace URLForApplicationWithBundleIdentifier:@"com.syntevo.smartgit"]; - if (AppURL != nullptr) - { - NSBundle* Bundle = [NSBundle bundleWithURL:AppURL]; - GitBinaryPath = FString::Printf(TEXT("%s/git/bin/git"), *FString([Bundle resourcePath])); - bFound = CheckGitAvailability(GitBinaryPath); - } - } - - // 6) Else, look for the version of local_git provided by SourceTree - if (!bFound) - { - NSURL* AppURL = [SharedWorkspace URLForApplicationWithBundleIdentifier:@"com.torusknot.SourceTreeNotMAS"]; - if (AppURL != nullptr) - { - NSBundle* Bundle = [NSBundle bundleWithURL:AppURL]; - GitBinaryPath = FString::Printf(TEXT("%s/git_local/bin/git"), *FString([Bundle resourcePath])); - bFound = CheckGitAvailability(GitBinaryPath); - } - } - - // 7) Else, look for the version of local_git provided by GitHub Desktop - if (!bFound) - { - NSURL* AppURL = [SharedWorkspace URLForApplicationWithBundleIdentifier:@"com.github.GitHubClient"]; - if (AppURL != nullptr) - { - NSBundle* Bundle = [NSBundle bundleWithURL:AppURL]; - GitBinaryPath = FString::Printf(TEXT("%s/app/git/bin/git"), *FString([Bundle resourcePath])); - bFound = CheckGitAvailability(GitBinaryPath); - } - } - - // 8) Else, look for the version of local_git provided by Tower2 - if (!bFound) - { - NSURL* AppURL = [SharedWorkspace URLForApplicationWithBundleIdentifier:@"com.fournova.Tower2"]; - if (AppURL != nullptr) - { - NSBundle* Bundle = [NSBundle bundleWithURL:AppURL]; - GitBinaryPath = FString::Printf(TEXT("%s/git/bin/git"), *FString([Bundle resourcePath])); - bFound = CheckGitAvailability(GitBinaryPath); - } - } - } - -#else - FString GitBinaryPath = TEXT("/usr/bin/git"); - bool bFound = CheckGitAvailability(GitBinaryPath); -#endif - - if(bFound) - { - FPaths::MakePlatformFilename(GitBinaryPath); - } - else - { - // If we did not find a path to Git, set it empty - GitBinaryPath.Empty(); - } - - return GitBinaryPath; -} - -bool CheckGitAvailability(const FString& InPathToGitBinary, FGitVersion *OutVersion) -{ - FString InfoMessages; - FString ErrorMessages; - bool bGitAvailable = RunCommandInternalRaw(TEXT("version"), InPathToGitBinary, FString(), TArray(), TArray(), InfoMessages, ErrorMessages); - if(bGitAvailable) - { - if(!InfoMessages.Contains("git")) - { - bGitAvailable = false; - } - else if(OutVersion) - { - ParseGitVersion(InfoMessages, OutVersion); - FindGitCapabilities(InPathToGitBinary, OutVersion); - FindGitLfsCapabilities(InPathToGitBinary, OutVersion); - } - } - - return bGitAvailable; -} - -void ParseGitVersion(const FString& InVersionString, FGitVersion *OutVersion) -{ - // Parse "git version 2.11.0.windows.3" into the string tokens "git", "version", "2.11.0.windows.3" - TArray TokenizedString; - InVersionString.ParseIntoArrayWS(TokenizedString); - - // Select the string token containing the version "2.11.0.windows.3" - const FString* TokenVersionStringPtr = TokenizedString.FindByPredicate([](FString& s) { return TChar::IsDigit(s[0]); }); - if(TokenVersionStringPtr) - { - // Parse the version into its numerical components - TArray ParsedVersionString; - TokenVersionStringPtr->ParseIntoArray(ParsedVersionString, TEXT(".")); - if(ParsedVersionString.Num() >= 3) - { - if(ParsedVersionString[0].IsNumeric() && ParsedVersionString[1].IsNumeric() && ParsedVersionString[2].IsNumeric()) - { - OutVersion->Major = FCString::Atoi(*ParsedVersionString[0]); - OutVersion->Minor = FCString::Atoi(*ParsedVersionString[1]); - OutVersion->Patch = FCString::Atoi(*ParsedVersionString[2]); - if(ParsedVersionString.Num() >= 5) - { - if((ParsedVersionString[3] == TEXT("windows")) && ParsedVersionString[4].IsNumeric()) - { - OutVersion->Windows = FCString::Atoi(*ParsedVersionString[4]); - } - } - UE_LOG(LogSourceControl, Log, TEXT("Git version %d.%d.%d(%d)"), OutVersion->Major, OutVersion->Minor, OutVersion->Patch, OutVersion->Windows); - } - } - } -} - -void FindGitCapabilities(const FString& InPathToGitBinary, FGitVersion *OutVersion) -{ - FString InfoMessages; - FString ErrorMessages; - RunCommandInternalRaw(TEXT("cat-file -h"), InPathToGitBinary, FString(), TArray(), TArray(), InfoMessages, ErrorMessages, 129); - if (InfoMessages.Contains("--filters")) - { - OutVersion->bHasCatFileWithFilters = true; - } -} - -void FindGitLfsCapabilities(const FString& InPathToGitBinary, FGitVersion *OutVersion) -{ - FString InfoMessages; - FString ErrorMessages; - bool bGitLfsAvailable = RunCommandInternalRaw(TEXT("lfs version"), InPathToGitBinary, FString(), TArray(), TArray(), InfoMessages, ErrorMessages); - if(bGitLfsAvailable) - { - OutVersion->bHasGitLfs = true; - - if(InfoMessages.Compare(TEXT("git-lfs/2.0.0")) >= 0) - { - OutVersion->bHasGitLfsLocking = true; // Git LFS File Locking workflow introduced in "git-lfs/2.0.0" - } - UE_LOG(LogSourceControl, Log, TEXT("%s"), *InfoMessages); - } -} - -// Find the root of the Git repository, looking from the provided path and upward in its parent directories. -bool FindRootDirectory(const FString& InPath, FString& OutRepositoryRoot) -{ - bool bFound = false; - FString PathToGitSubdirectory; - OutRepositoryRoot = InPath; - - auto TrimTrailing = [](FString& Str, const TCHAR Char) - { - int32 Len = Str.Len(); - while(Len && Str[Len - 1] == Char) - { - Str = Str.LeftChop(1); - Len = Str.Len(); - } - }; - - TrimTrailing(OutRepositoryRoot, '\\'); - TrimTrailing(OutRepositoryRoot, '/'); - - while(!bFound && !OutRepositoryRoot.IsEmpty()) - { - // Look for the ".git" subdirectory (or file) present at the root of every Git repository - PathToGitSubdirectory = OutRepositoryRoot / TEXT(".git"); - bFound = IFileManager::Get().DirectoryExists(*PathToGitSubdirectory) || IFileManager::Get().FileExists(*PathToGitSubdirectory); - if(!bFound) - { - int32 LastSlashIndex; - if(OutRepositoryRoot.FindLastChar('/', LastSlashIndex)) - { - OutRepositoryRoot = OutRepositoryRoot.Left(LastSlashIndex); - } - else - { - OutRepositoryRoot.Empty(); - } - } - } - if(!bFound) - { - OutRepositoryRoot = InPath; // If not found, return the provided dir as best possible root. - } - return bFound; -} - -void GetUserConfig(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutUserName, FString& OutUserEmail) -{ - bool bResults; - TArray InfoMessages; - TArray ErrorMessages; - TArray Parameters; - Parameters.Add(TEXT("user.name")); - bResults = RunCommandInternal(TEXT("config"), InPathToGitBinary, InRepositoryRoot, Parameters, TArray(), InfoMessages, ErrorMessages); - if(bResults && InfoMessages.Num() > 0) - { - OutUserName = InfoMessages[0]; - } - - Parameters.Reset(); - Parameters.Add(TEXT("user.email")); - InfoMessages.Reset(); - bResults &= RunCommandInternal(TEXT("config"), InPathToGitBinary, InRepositoryRoot, Parameters, TArray(), InfoMessages, ErrorMessages); - if(bResults && InfoMessages.Num() > 0) - { - OutUserEmail = InfoMessages[0]; - } -} - -bool GetBranchName(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutBranchName) -{ - bool bResults; - TArray InfoMessages; - TArray ErrorMessages; - TArray Parameters; - Parameters.Add(TEXT("--short")); - Parameters.Add(TEXT("--quiet")); // no error message while in detached HEAD - Parameters.Add(TEXT("HEAD")); - bResults = RunCommandInternal(TEXT("symbolic-ref"), InPathToGitBinary, InRepositoryRoot, Parameters, TArray(), InfoMessages, ErrorMessages); - if(bResults && InfoMessages.Num() > 0) - { - OutBranchName = InfoMessages[0]; - } - else - { - Parameters.Reset(); - Parameters.Add(TEXT("-1")); - Parameters.Add(TEXT("--format=\"%h\"")); // no error message while in detached HEAD - bResults = RunCommandInternal(TEXT("log"), InPathToGitBinary, InRepositoryRoot, Parameters, TArray(), InfoMessages, ErrorMessages); - if(bResults && InfoMessages.Num() > 0) - { - OutBranchName = "HEAD detached at "; - OutBranchName += InfoMessages[0]; - } - else - { - bResults = false; - } - } - - return bResults; -} - -bool GetCommitInfo(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutCommitId, FString& OutCommitSummary) -{ - bool bResults; - TArray InfoMessages; - TArray ErrorMessages; - TArray Parameters; - Parameters.Add(TEXT("-1")); - Parameters.Add(TEXT("--format=\"%H %s\"")); - bResults = RunCommandInternal(TEXT("log"), InPathToGitBinary, InRepositoryRoot, Parameters, TArray(), InfoMessages, ErrorMessages); - if(bResults && InfoMessages.Num() > 0) - { - OutCommitId = InfoMessages[0].Left(40); - OutCommitSummary = InfoMessages[0].RightChop(41); - } - - return bResults; -} - -bool GetRemoteUrl(const FString& InPathToGitBinary, const FString& InRepositoryRoot, FString& OutRemoteUrl) -{ - TArray InfoMessages; - TArray ErrorMessages; - TArray Parameters; - Parameters.Add(TEXT("get-url")); - Parameters.Add(TEXT("origin")); - const bool bResults = RunCommandInternal(TEXT("remote"), InPathToGitBinary, InRepositoryRoot, Parameters, TArray(), InfoMessages, ErrorMessages); - if (bResults && InfoMessages.Num() > 0) - { - OutRemoteUrl = InfoMessages[0]; - } - - return bResults; -} - -bool RunCommand(const FString& InCommand, const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray& InParameters, const TArray& InFiles, TArray& OutResults, TArray& OutErrorMessages) -{ - bool bResult = true; - - if(InFiles.Num() > GitSourceControlConstants::MaxFilesPerBatch) - { - // Batch files up so we dont exceed command-line limits - int32 FileCount = 0; - while(FileCount < InFiles.Num()) - { - TArray FilesInBatch; - for(int32 FileIndex = 0; FileCount < InFiles.Num() && FileIndex < GitSourceControlConstants::MaxFilesPerBatch; FileIndex++, FileCount++) - { - FilesInBatch.Add(InFiles[FileCount]); - } - - TArray BatchResults; - TArray BatchErrors; - bResult &= RunCommandInternal(InCommand, InPathToGitBinary, InRepositoryRoot, InParameters, FilesInBatch, BatchResults, BatchErrors); - OutResults += BatchResults; - OutErrorMessages += BatchErrors; - } - } - else - { - bResult &= RunCommandInternal(InCommand, InPathToGitBinary, InRepositoryRoot, InParameters, InFiles, OutResults, OutErrorMessages); - } - - return bResult; -} - -// Run a Git "commit" command by batches -bool RunCommit(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray& InParameters, const TArray& InFiles, TArray& OutResults, TArray& OutErrorMessages) -{ - bool bResult = true; - - if(InFiles.Num() > GitSourceControlConstants::MaxFilesPerBatch) - { - // Batch files up so we dont exceed command-line limits - int32 FileCount = 0; - { - TArray FilesInBatch; - for(int32 FileIndex = 0; FileIndex < GitSourceControlConstants::MaxFilesPerBatch; FileIndex++, FileCount++) - { - FilesInBatch.Add(InFiles[FileCount]); - } - // First batch is a simple "git commit" command with only the first files - bResult &= RunCommandInternal(TEXT("commit"), InPathToGitBinary, InRepositoryRoot, InParameters, FilesInBatch, OutResults, OutErrorMessages); - } - - TArray Parameters; - for(const auto& Parameter : InParameters) - { - Parameters.Add(Parameter); - } - Parameters.Add(TEXT("--amend")); - - while(FileCount < InFiles.Num()) - { - TArray FilesInBatch; - for(int32 FileIndex = 0; FileCount < InFiles.Num() && FileIndex < GitSourceControlConstants::MaxFilesPerBatch; FileIndex++, FileCount++) - { - FilesInBatch.Add(InFiles[FileCount]); - } - // Next batches "amend" the commit with some more files - TArray BatchResults; - TArray BatchErrors; - bResult &= RunCommandInternal(TEXT("commit"), InPathToGitBinary, InRepositoryRoot, Parameters, FilesInBatch, BatchResults, BatchErrors); - OutResults += BatchResults; - OutErrorMessages += BatchErrors; - } - } - else - { - bResult = RunCommandInternal(TEXT("commit"), InPathToGitBinary, InRepositoryRoot, InParameters, InFiles, OutResults, OutErrorMessages); - } - - return bResult; -} - -/** - * Parse informations on a file locked with Git LFS - * - * Example output of "git lfs locks" -Content\ThirdPersonBP\Blueprints\ThirdPersonCharacter.uasset SRombauts ID:891 -Content\ThirdPersonBP\Blueprints\ThirdPersonGameMode.uasset SRombauts ID:896 - */ -class FGitLfsLocksParser -{ -public: - FGitLfsLocksParser(const FString& InRepositoryRoot, const FString& InStatus, const bool bAbsolutePaths = true) - { - TArray Informations; - InStatus.ParseIntoArray(Informations, TEXT("\t"), true); - if(Informations.Num() >= 3) - { - Informations[0].TrimEndInline(); // Trim whitespace from the end of the filename - Informations[1].TrimEndInline(); // Trim whitespace from the end of the username - if (bAbsolutePaths) - LocalFilename = FPaths::ConvertRelativePathToFull(InRepositoryRoot, Informations[0]); - else - LocalFilename = Informations[0]; - LockUser = MoveTemp(Informations[1]); - } - } - - FString LocalFilename; ///< Filename on disk - FString LockUser; ///< Name of user who has file locked -}; - -/** - * @brief Extract the relative filename from a Git status result. - * - * Examples of status results: -M Content/Textures/T_Perlin_Noise_M.uasset -R Content/Textures/T_Perlin_Noise_M.uasset -> Content/Textures/T_Perlin_Noise_M2.uasset -?? Content/Materials/M_Basic_Wall.uasset -!! BasicCode.sln - * - * @param[in] InResult One line of status - * @return Relative filename extracted from the line of status - * - * @see FGitStatusFileMatcher and StateFromGitStatus() - */ -static FString FilenameFromGitStatus(const FString& InResult) -{ - int32 RenameIndex; - if(InResult.FindLastChar('>', RenameIndex)) - { - // Extract only the second part of a rename "from -> to" - return InResult.RightChop(RenameIndex + 2); - } - else - { - // Extract the relative filename from the Git status result (after the 2 letters status and 1 space) - return InResult.RightChop(3); - } -} - -/** Match the relative filename of a Git status result with a provided absolute filename */ -class FGitStatusFileMatcher -{ -public: - FGitStatusFileMatcher(const FString& InAbsoluteFilename) - : AbsoluteFilename(InAbsoluteFilename) - { - } - - bool operator()(const FString& InResult) const - { - return AbsoluteFilename.Contains(FilenameFromGitStatus(InResult)); - } - -private: - const FString& AbsoluteFilename; -}; - -/** - * Extract and interpret the file state from the given Git status result. - * @see http://git-scm.com/docs/git-status - * ' ' = unmodified - * 'M' = modified - * 'A' = added - * 'D' = deleted - * 'R' = renamed - * 'C' = copied - * 'U' = updated but unmerged - * '?' = unknown/untracked - * '!' = ignored -*/ -class FGitStatusParser -{ -public: - FGitStatusParser(const FString& InResult) - { - TCHAR IndexState = InResult[0]; - TCHAR WCopyState = InResult[1]; - if( (IndexState == 'U' || WCopyState == 'U') - || (IndexState == 'A' && WCopyState == 'A') - || (IndexState == 'D' && WCopyState == 'D')) - { - // "Unmerged" conflict cases are generally marked with a "U", - // but there are also the special cases of both "A"dded, or both "D"eleted - State = EWorkingCopyState::Conflicted; - } - else if(IndexState == 'A') - { - State = EWorkingCopyState::Added; - } - else if(IndexState == 'D') - { - State = EWorkingCopyState::Deleted; - } - else if(WCopyState == 'D') - { - State = EWorkingCopyState::Missing; - } - else if(IndexState == 'M' || WCopyState == 'M') - { - State = EWorkingCopyState::Modified; - } - else if(IndexState == 'R') - { - State = EWorkingCopyState::Renamed; - } - else if(IndexState == 'C') - { - State = EWorkingCopyState::Copied; - } - else if(IndexState == '?' || WCopyState == '?') - { - State = EWorkingCopyState::NotControlled; - } - else if(IndexState == '!' || WCopyState == '!') - { - State = EWorkingCopyState::Ignored; - } - else - { - // Unmodified never yield a status - State = EWorkingCopyState::Unknown; - } - } - - EWorkingCopyState::Type State; -}; - -/** - * Extract the status of a unmerged (conflict) file - * - * Example output of git ls-files --unmerged Content/Blueprints/BP_Test.uasset -100644 d9b33098273547b57c0af314136f35b494e16dcb 1 Content/Blueprints/BP_Test.uasset -100644 a14347dc3b589b78fb19ba62a7e3982f343718bc 2 Content/Blueprints/BP_Test.uasset -100644 f3137a7167c840847cd7bd2bf07eefbfb2d9bcd2 3 Content/Blueprints/BP_Test.uasset - * - * 1: The "common ancestor" of the file (the version of the file that both the current and other branch originated from). - * 2: The version from the current branch (the master branch in this case). - * 3: The version from the other branch (the test branch) -*/ -class FGitConflictStatusParser -{ -public: - /** Parse the unmerge status: extract the base SHA1 identifier of the file */ - FGitConflictStatusParser(const TArray& InResults) - { - const FString& FirstResult = InResults[0]; // 1: The common ancestor of merged branches - CommonAncestorFileId = FirstResult.Mid(7, 40); - } - - FString CommonAncestorFileId; ///< SHA1 Id of the file (warning: not the commit Id) -}; - -/** Execute a command to get the details of a conflict */ -static void RunGetConflictStatus(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InFile, FGitSourceControlState& InOutFileState) -{ - TArray ErrorMessages; - TArray Results; - TArray Files; - Files.Add(InFile); - TArray Parameters; - Parameters.Add(TEXT("--unmerged")); - bool bResult = RunCommandInternal(TEXT("ls-files"), InPathToGitBinary, InRepositoryRoot, Parameters, Files, Results, ErrorMessages); - if(bResult && Results.Num() == 3) - { - // Parse the unmerge status: extract the base revision (or the other branch?) - FGitConflictStatusParser ConflictStatus(Results); - InOutFileState.PendingMergeBaseFileHash = ConflictStatus.CommonAncestorFileId; - } -} - -/// Convert filename relative to the repository root to absolute path (inplace) -void AbsoluteFilenames(const FString& InRepositoryRoot, TArray& InFileNames) -{ - for(auto& FileName : InFileNames) - { - FileName = FPaths::ConvertRelativePathToFull(InRepositoryRoot, FileName); - } -} - -/** Run a 'git ls-files' command to get all files tracked by Git recursively in a directory. - * - * Called in case of a "directory status" (no file listed in the command) when using the "Submit to Source Control" menu. -*/ -static bool ListFilesInDirectoryRecurse(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InDirectory, TArray& OutFiles) -{ - TArray ErrorMessages; - TArray Directory; - Directory.Add(InDirectory); - const bool bResult = RunCommandInternal(TEXT("ls-files"), InPathToGitBinary, InRepositoryRoot, TArray(), Directory, OutFiles, ErrorMessages); - AbsoluteFilenames(InRepositoryRoot, OutFiles); - return bResult; -} - -/** Parse the array of strings results of a 'git status' command for a provided list of files all in a common directory - * - * Called in case of a normal refresh of status on a list of assets in a the Content Browser (or user selected "Refresh" context menu). - * - * Example git status results: -M Content/Textures/T_Perlin_Noise_M.uasset -R Content/Textures/T_Perlin_Noise_M.uasset -> Content/Textures/T_Perlin_Noise_M2.uasset -?? Content/Materials/M_Basic_Wall.uasset -!! BasicCode.sln -*/ -static void ParseFileStatusResult(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool InUsingLfsLocking, const TArray& InFiles, const TMap& InLockedFiles, const TArray& InResults, TArray& OutStates) -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const FString LfsUserName = GitSourceControl.AccessSettings().GetLfsUserName(); - const FDateTime Now = FDateTime::Now(); - - // Iterate on all files explicitly listed in the command - for(const auto& File : InFiles) - { - FGitSourceControlState FileState(File, InUsingLfsLocking); - // Search the file in the list of status - int32 IdxResult = InResults.IndexOfByPredicate(FGitStatusFileMatcher(File)); - if(IdxResult != INDEX_NONE) - { - // File found in status results; only the case for "changed" files - FGitStatusParser StatusParser(InResults[IdxResult]); - // TODO LFS Debug log - UE_LOG(LogSourceControl, Log, TEXT("Status(%s) = '%s' => %d"), *File, *InResults[IdxResult], static_cast(StatusParser.State)); - - FileState.WorkingCopyState = StatusParser.State; - if(FileState.IsConflicted()) - { - // In case of a conflict (unmerged file) get the base revision to merge - RunGetConflictStatus(InPathToGitBinary, InRepositoryRoot, File, FileState); - } - } - else - { - // File not found in status - if(FPaths::FileExists(File)) - { - // usually means the file is unchanged, - FileState.WorkingCopyState = EWorkingCopyState::Unchanged; - // TODO LFS Debug log - UE_LOG(LogSourceControl, Log, TEXT("Status(%s) not found but exists => unchanged"), *File); - } - else - { - // but also the case for newly created content: there is no file on disk until the content is saved for the first time - FileState.WorkingCopyState = EWorkingCopyState::NotControlled; - // TODO LFS Debug log - UE_LOG(LogSourceControl, Log, TEXT("Status(%s) not found and does not exists => new/not controled"), *File); - } - } - if(InLockedFiles.Contains(File)) - { - FileState.LockUser = InLockedFiles[File]; - if(LfsUserName == FileState.LockUser) - { - FileState.LockState = ELockState::Locked; - } - else - { - FileState.LockState = ELockState::LockedOther; - } - // TODO LFS Debug log - UE_LOG(LogSourceControl, Log, TEXT("Status(%s) Locked by '%s'"), *File, *FileState.LockUser); - } - else - { - FileState.LockState = ELockState::NotLocked; - // TODO LFS Debug log - if (InUsingLfsLocking) - { - UE_LOG(LogSourceControl, Log, TEXT("Status(%s) Not Locked"), *File); - } - } - FileState.TimeStamp = Now; - OutStates.Add(FileState); - } -} - -/** Parse the array of strings results of a 'git status' command for a directory - * - * Called in case of a "directory status" (no file listed in the command) ONLY to detect Deleted/Missing/Untracked files - * since those files are not listed by the 'git ls-files' command. - * - * @see #ParseFileStatusResult() above for an example of a 'git status' results -*/ -static void ParseDirectoryStatusResult(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool InUsingLfsLocking, const TArray& InResults, TArray& OutStates) -{ - // Iterate on each line of result of the status command - for(const FString& Result : InResults) - { - const FString RelativeFilename = FilenameFromGitStatus(Result); - const FString File = FPaths::ConvertRelativePathToFull(InRepositoryRoot, RelativeFilename); - - FGitSourceControlState FileState(File, InUsingLfsLocking); - FGitStatusParser StatusParser(Result); - if((EWorkingCopyState::Deleted == StatusParser.State) || (EWorkingCopyState::Missing == StatusParser.State) || (EWorkingCopyState::NotControlled == StatusParser.State)) - { - FileState.WorkingCopyState = StatusParser.State; - FileState.TimeStamp.Now(); - OutStates.Add(MoveTemp(FileState)); - } - } -} - -/** - * @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 Source 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") - */ -static void ParseStatusResults(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool InUsingLfsLocking, const TArray& InFiles, const TMap& InLockedFiles, const TArray& InResults, TArray& OutStates) -{ - if((InFiles.Num() == 1) && FPaths::DirectoryExists(InFiles[0])) - { - // 1) Special case for "status" of a directory: requires to get the list of files by ourselves. - // (this is triggered by the "Submit to Source Control" menu) - // TODO LFS Debug Log - UE_LOG(LogSourceControl, Log, TEXT("ParseStatusResults: 1) Special case for status of a directory (%s)"), *InFiles[0]); - TArray Files; - const FString& Directory = InFiles[0]; - const bool bResult = ListFilesInDirectoryRecurse(InPathToGitBinary, InRepositoryRoot, Directory, Files); - if(bResult) - { - ParseFileStatusResult(InPathToGitBinary, InRepositoryRoot, InUsingLfsLocking, Files, InLockedFiles, InResults, OutStates); - } - // The above cannot detect deleted assets since there is no file left to enumerate (either by the Content Browser or by git ls-files) - // => so we also parse the status results to explicitly look for Deleted/Missing assets - ParseDirectoryStatusResult(InPathToGitBinary, InRepositoryRoot, InUsingLfsLocking, InResults, OutStates); - } - else - { - // 2) General case for one or more files in the same directory. - // TODO LFS Debug Log - UE_LOG(LogSourceControl, Log, TEXT("ParseStatusResults: 2) General case for one or more files (%s, ...)"), *InFiles[0]); - ParseFileStatusResult(InPathToGitBinary, InRepositoryRoot, InUsingLfsLocking, InFiles, InLockedFiles, InResults, OutStates); - } -} - -bool GetAllLocks(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool bAbsolutePaths, TArray& OutErrorMessages, TMap& OutLocks) -{ - TArray Results; - TArray ErrorMessages; - const bool bResult = RunCommand(TEXT("lfs locks"), InPathToGitBinary, InRepositoryRoot, TArray(), TArray(), Results, ErrorMessages); - for(const FString& Result : Results) - { - FGitLfsLocksParser LockFile(InRepositoryRoot, Result, bAbsolutePaths); - // TODO LFS Debug log - UE_LOG(LogSourceControl, Log, TEXT("LockedFile(%s, %s)"), *LockFile.LocalFilename, *LockFile.LockUser); - OutLocks.Add(MoveTemp(LockFile.LocalFilename), MoveTemp(LockFile.LockUser)); - } - - return bResult; -} - -// Run a batch of Git "status" command to update status of given files and/or directories. -bool RunUpdateStatus(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool InUsingLfsLocking, const TArray& InFiles, TArray& OutErrorMessages, TArray& OutStates) -{ - bool bResults = true; - TMap LockedFiles; - - // 0) Issue a "git lfs locks" command at the root of the repository - if(InUsingLfsLocking) - { - TArray ErrorMessages; - GetAllLocks(InPathToGitBinary, InRepositoryRoot, true, ErrorMessages, LockedFiles); - } - - // Git status does not show any "untracked files" when called with files from different subdirectories! (issue #3) - // 1) So here we group files by path (ie. by subdirectory) - TMap> GroupOfFiles; - for(const auto& File : InFiles) - { - const FString Path = FPaths::GetPath(*File); - TArray* Group = GroupOfFiles.Find(Path); - if(Group != nullptr) - { - Group->Add(File); - } - else - { - TArray NewGroup; - NewGroup.Add(File); - GroupOfFiles.Add(Path, NewGroup); - } - } - - // Get the current branch name, since we need origin of current branch - FString BranchName; - GitSourceControlUtils::GetBranchName(InPathToGitBinary, InRepositoryRoot, BranchName); - - TArray Parameters; - Parameters.Add(TEXT("--porcelain")); - Parameters.Add(TEXT("--ignored")); - - // 2) then we can batch git status operation by subdirectory - for(const auto& Files : GroupOfFiles) - { - // "git status" can only detect renamed and deleted files when it operate on a folder, so use one folder path for all files in a directory - const FString Path = FPaths::GetPath(*Files.Value[0]); - TArray OnePath; - // Only one file: optim very useful for the .uproject file at the root to avoid parsing the whole repository - // (works only if the file exists) - if((Files.Value.Num() == 1) && (FPaths::FileExists(Files.Value[0]))) - { - OnePath.Add(Files.Value[0]); - } - else - { - OnePath.Add(Path); - } - { - TArray Results; - TArray ErrorMessages; - const bool bResult = RunCommand(TEXT("status"), InPathToGitBinary, InRepositoryRoot, Parameters, OnePath, Results, ErrorMessages); - OutErrorMessages.Append(ErrorMessages); - if(bResult) - { - ParseStatusResults(InPathToGitBinary, InRepositoryRoot, InUsingLfsLocking, Files.Value, LockedFiles, Results, OutStates); - } - } - - if (!BranchName.IsEmpty()) - { - // Using git diff, we can obtain a list of files that were modified between our current origin and HEAD. Assumes that fetch has been run to get accurate info. - // TODO: should do a fetch (at least periodically). - TArray Results; - TArray ErrorMessages; - TArray ParametersLsRemote; - ParametersLsRemote.Add(TEXT("origin")); - ParametersLsRemote.Add(BranchName); - const bool bResultLsRemote = RunCommand(TEXT("ls-remote"), InPathToGitBinary, InRepositoryRoot, ParametersLsRemote, OnePath, Results, ErrorMessages); - // If the command is successful and there is only 1 line on the output the branch exists on remote - const bool bDiffAgainstRemote = bResultLsRemote && Results.Num(); - - Results.Reset(); - ErrorMessages.Reset(); - TArray ParametersLog; - ParametersLog.Add(TEXT("--pretty=")); // this omits the commit lines, just gets us files - ParametersLog.Add(TEXT("--name-only")); - ParametersLog.Add(bDiffAgainstRemote ? TEXT("HEAD..HEAD@{upstream}") : BranchName); - const bool bResultDiff = RunCommand(TEXT("log"), InPathToGitBinary, InRepositoryRoot, ParametersLog, OnePath, Results, ErrorMessages); - OutErrorMessages.Append(ErrorMessages); - if (bResultDiff) - { - for (const FString& NewerFileName : Results) - { - const FString NewerFilePath = FPaths::ConvertRelativePathToFull(InRepositoryRoot, NewerFileName); - - // Find existing corresponding file state to update it (not found would mean new file or not in the current path) - if (FGitSourceControlState* FileStatePtr = OutStates.FindByPredicate([NewerFilePath](FGitSourceControlState& FileState) { return FileState.LocalFilename == NewerFilePath; })) - { - FileStatePtr->bNewerVersionOnServer = true; - } - } - } - } - } - - return bResults; -} - -// Run a Git `cat-file --filters` command to dump the binary content of a revision into a file. -bool RunDumpToFile(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const FString& InParameter, const FString& InDumpFileName) -{ - int32 ReturnCode = -1; - FString FullCommand; - - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const FGitVersion& GitVersion = GitSourceControl.GetProvider().GetGitVersion(); - - if(!InRepositoryRoot.IsEmpty()) - { - // Specify the working copy (the root) of the git repository (before the command itself) - FullCommand = TEXT("-C \""); - FullCommand += InRepositoryRoot; - FullCommand += TEXT("\" "); - } - - // then the git command itself - if(GitVersion.bHasCatFileWithFilters) - { - // Newer versions (2.9.3.windows.2) support smudge/clean filters used by Git LFS, git-fat, git-annex, etc - FullCommand += TEXT("cat-file --filters "); - } - else - { - // Previous versions fall-back on "git show" like before - FullCommand += TEXT("show "); - } - - // Append to the command the parameter - FullCommand += InParameter; - - const bool bLaunchDetached = false; - const bool bLaunchHidden = true; - const bool bLaunchReallyHidden = bLaunchHidden; - - void* PipeRead = nullptr; - void* PipeWrite = nullptr; - - verify(FPlatformProcess::CreatePipe(PipeRead, PipeWrite)); - - UE_LOG(LogSourceControl, Log, TEXT("RunDumpToFile: 'git %s'"), *FullCommand); - - FString PathToGitOrEnvBinary = InPathToGitBinary; - #if PLATFORM_MAC - // The Cocoa application does not inherit shell environment variables, so add the path expected to have git-lfs to PATH - FString PathEnv = FPlatformMisc::GetEnvironmentVariable(TEXT("PATH")); - FString GitInstallPath = FPaths::GetPath(InPathToGitBinary); - - TArray PathArray; - PathEnv.ParseIntoArray(PathArray, FPlatformMisc::GetPathVarDelimiter()); - bool bHasGitInstallPath = false; - for (auto Path : PathArray) - { - if (GitInstallPath.Equals(Path, ESearchCase::CaseSensitive)) - { - bHasGitInstallPath = true; - break; - } - } - - if (!bHasGitInstallPath) - { - PathToGitOrEnvBinary = FString("/usr/bin/env"); - FullCommand = FString::Printf(TEXT("PATH=\"%s%s%s\" \"%s\" %s"), *GitInstallPath, FPlatformMisc::GetPathVarDelimiter(), *PathEnv, *InPathToGitBinary, *FullCommand); - } - #endif - - FProcHandle ProcessHandle = FPlatformProcess::CreateProc(*PathToGitOrEnvBinary, *FullCommand, bLaunchDetached, bLaunchHidden, bLaunchReallyHidden, nullptr, 0, *InRepositoryRoot, PipeWrite); - if(ProcessHandle.IsValid()) - { - FPlatformProcess::Sleep(0.01); - - TArray BinaryFileContent; - while(FPlatformProcess::IsProcRunning(ProcessHandle)) - { - TArray BinaryData; - FPlatformProcess::ReadPipeToArray(PipeRead, BinaryData); - if(BinaryData.Num() > 0) - { - BinaryFileContent.Append(MoveTemp(BinaryData)); - } - } - TArray BinaryData; - FPlatformProcess::ReadPipeToArray(PipeRead, BinaryData); - if(BinaryData.Num() > 0) - { - BinaryFileContent.Append(MoveTemp(BinaryData)); - } - - FPlatformProcess::GetProcReturnCode(ProcessHandle, &ReturnCode); - if(ReturnCode == 0) - { - // Save buffer into temp file - if(FFileHelper::SaveArrayToFile(BinaryFileContent, *InDumpFileName)) - { - UE_LOG(LogSourceControl, Log, TEXT("Writed '%s' (%do)"), *InDumpFileName, BinaryFileContent.Num()); - } - else - { - UE_LOG(LogSourceControl, Error, TEXT("Could not write %s"), *InDumpFileName); - ReturnCode = -1; - } - } - else - { - UE_LOG(LogSourceControl, Error, TEXT("DumpToFile: ReturnCode=%d"), ReturnCode); - } - - FPlatformProcess::CloseProc(ProcessHandle); - } - else - { - UE_LOG(LogSourceControl, Error, TEXT("Failed to launch 'git cat-file'")); - } - - FPlatformProcess::ClosePipe(PipeRead, PipeWrite); - - return (ReturnCode == 0); -} - -/** - * Translate file actions from the given Git log --name-status command to keywords used by the Editor UI. - * - * @see https://www.kernel.org/pub/software/scm/git/docs/git-log.html - * ' ' = unmodified - * 'M' = modified - * 'A' = added - * 'D' = deleted - * 'R' = renamed - * 'C' = copied - * 'T' = type changed - * 'U' = updated but unmerged - * 'X' = unknown - * 'B' = broken pairing - * - * @see SHistoryRevisionListRowContent::GenerateWidgetForColumn(): "add", "edit", "delete", "branch" and "integrate" (everything else is taken like "edit") -*/ -static FString LogStatusToString(TCHAR InStatus) -{ - switch(InStatus) - { - case TEXT(' '): - return FString("unmodified"); - case TEXT('M'): - return FString("modified"); - case TEXT('A'): // added: keyword "add" to display a specific icon instead of the default "edit" action one - return FString("add"); - case TEXT('D'): // deleted: keyword "delete" to display a specific icon instead of the default "edit" action one - return FString("delete"); - case TEXT('R'): // renamed keyword "branch" to display a specific icon instead of the default "edit" action one - return FString("branch"); - case TEXT('C'): // copied keyword "branch" to display a specific icon instead of the default "edit" action one - return FString("branch"); - case TEXT('T'): - return FString("type changed"); - case TEXT('U'): - return FString("unmerged"); - case TEXT('X'): - return FString("unknown"); - case TEXT('B'): - return FString("broked pairing"); - } - - return FString(); -} - -/** - * Parse the array of strings results of a 'git log' command - * - * Example git log results: -commit 97a4e7626681895e073aaefd68b8ac087db81b0b -Author: Sébastien Rombauts -Date: 2014-2015-05-15 21:32:27 +0200 - - Another commit used to test History - - - with many lines - - some - - and strange characteres $*+ - -M Content/Blueprints/Blueprint_CeilingLight.uasset -R100 Content/Textures/T_Concrete_Poured_D.uasset Content/Textures/T_Concrete_Poured_D2.uasset - -commit 355f0df26ebd3888adbb558fd42bb8bd3e565000 -Author: Sébastien Rombauts -Date: 2014-2015-05-12 11:28:14 +0200 - - Testing git status, edit, and revert - -A Content/Blueprints/Blueprint_CeilingLight.uasset -C099 Content/Textures/T_Concrete_Poured_N.uasset Content/Textures/T_Concrete_Poured_N2.uasset -*/ -static void ParseLogResults(const TArray& InResults, TGitSourceControlHistory& OutHistory) -{ - TSharedRef SourceControlRevision = MakeShareable(new FGitSourceControlRevision); - for(const auto& Result : InResults) - { - if(Result.StartsWith(TEXT("commit "))) // Start of a new commit - { - // End of the previous commit - if(SourceControlRevision->RevisionNumber != 0) - { - OutHistory.Add(MoveTemp(SourceControlRevision)); - - SourceControlRevision = MakeShareable(new FGitSourceControlRevision); - } - SourceControlRevision->CommitId = Result.RightChop(7); // Full commit SHA1 hexadecimal string - SourceControlRevision->ShortCommitId = SourceControlRevision->CommitId.Left(8); // Short revision ; first 8 hex characters (max that can hold a 32 bit integer) - SourceControlRevision->CommitIdNumber = FParse::HexNumber(*SourceControlRevision->ShortCommitId); - SourceControlRevision->RevisionNumber = -1; // RevisionNumber will be set at the end, based off the index in the History - } - else if(Result.StartsWith(TEXT("Author: "))) // Author name & email - { - // Remove the 'email' part of the UserName - FString UserNameEmail = Result.RightChop(8); - int32 EmailIndex = 0; - if(UserNameEmail.FindLastChar('<', EmailIndex)) - { - SourceControlRevision->UserName = UserNameEmail.Left(EmailIndex - 1); - } - } - else if(Result.StartsWith(TEXT("Date: "))) // Commit date - { - FString Date = Result.RightChop(8); - SourceControlRevision->Date = FDateTime::FromUnixTimestamp(FCString::Atoi(*Date)); - } - // else if(Result.IsEmpty()) // empty line before/after commit message has already been taken care by FString::ParseIntoArray() - else if(Result.StartsWith(TEXT(" "))) // Multi-lines commit message - { - SourceControlRevision->Description += Result.RightChop(4); - SourceControlRevision->Description += TEXT("\n"); - } - else // Name of the file, starting with an uppercase status letter ("A"/"M"...) - { - const TCHAR Status = Result[0]; - SourceControlRevision->Action = LogStatusToString(Status); // Readable action string ("Added", Modified"...) instead of "A"/"M"... - // Take care of special case for Renamed/Copied file: extract the second filename after second tabulation - int32 IdxTab; - if(Result.FindLastChar('\t', IdxTab)) - { - SourceControlRevision->Filename = Result.RightChop(IdxTab + 1); // relative filename - } - } - } - // End of the last commit - if(SourceControlRevision->RevisionNumber != 0) - { - OutHistory.Add(MoveTemp(SourceControlRevision)); - } - - // Then set the revision number of each Revision based on its index (reverse order since the log starts with the most recent change) - for(int32 RevisionIndex = 0; RevisionIndex < OutHistory.Num(); RevisionIndex++) - { - const auto& SourceControlRevisionItem = OutHistory[RevisionIndex]; - SourceControlRevisionItem->RevisionNumber = OutHistory.Num() - RevisionIndex; - - // Special case of a move ("branch" in Perforce term): point to the previous change (so the next one in the order of the log) - if((SourceControlRevisionItem->Action == "branch") && (RevisionIndex < OutHistory.Num() - 1)) - { - SourceControlRevisionItem->BranchSource = OutHistory[RevisionIndex + 1]; - } - } -} - -/** - * Extract the SHA1 identifier and size of a blob (file) from a Git "ls-tree" command. - * - * Example output for the command git ls-tree --long 7fdaeb2 Content/Blueprints/BP_Test.uasset -100644 blob a14347dc3b589b78fb19ba62a7e3982f343718bc 70731 Content/Blueprints/BP_Test.uasset -*/ -class FGitLsTreeParser -{ -public: - /** Parse the unmerge status: extract the base SHA1 identifier of the file */ - FGitLsTreeParser(const TArray& InResults) - { - const FString& FirstResult = InResults[0]; - FileHash = FirstResult.Mid(12, 40); - int32 IdxTab; - if(FirstResult.FindChar('\t', IdxTab)) - { - const FString SizeString = FirstResult.Mid(53, IdxTab - 53); - FileSize = FCString::Atoi(*SizeString); - } - } - - FString FileHash; ///< SHA1 Id of the file (warning: not the commit Id) - int32 FileSize; ///< Size of the file (in bytes) -}; - -// 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) -{ - 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, InRepositoryRoot, 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, InRepositoryRoot, Parameters, Files, Results, OutErrorMessages); - if(bResults && Results.Num()) - { - FGitLsTreeParser LsTree(Results); - Revision->FileHash = LsTree.FileHash; - Revision->FileSize = LsTree.FileSize; - } - } - - return bResults; -} - -TArray RelativeFilenames(const TArray& InFileNames, const FString& InRelativeTo) -{ - TArray RelativeFiles; - FString RelativeTo = InRelativeTo; - - // Ensure that the path ends w/ '/' - if((RelativeTo.Len() > 0) && (RelativeTo.EndsWith(TEXT("/"), ESearchCase::CaseSensitive) == false) && (RelativeTo.EndsWith(TEXT("\\"), ESearchCase::CaseSensitive) == false)) - { - RelativeTo += TEXT("/"); - } - for(FString FileName : InFileNames) // string copy to be able to convert it inplace - { - if(FPaths::MakePathRelativeTo(FileName, *RelativeTo)) - { - RelativeFiles.Add(FileName); - } - } - - return RelativeFiles; -} - -TArray AbsoluteFilenames(const TArray& InFileNames, const FString& InRelativeTo) -{ - TArray AbsFiles; - - for(FString FileName : InFileNames) // string copy to be able to convert it inplace - { - AbsFiles.Add(FPaths::Combine(InRelativeTo, FileName)); - } - - return AbsFiles; -} - -bool UpdateCachedStates(const TArray& InStates) -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked( "GitSourceControl" ); - FGitSourceControlProvider& Provider = GitSourceControl.GetProvider(); - const bool bUsingGitLfsLocking = GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); - - // TODO without LFS : Workaround a bug with the Source Control Module not updating file state after a simple "Save" with no "Checkout" (when not using File Lock) - const FDateTime Now = bUsingGitLfsLocking ? FDateTime::Now() : FDateTime(); - - for(const auto& InState : InStates) - { - TSharedRef State = Provider.GetStateInternal(InState.LocalFilename); - *State = InState; - State->TimeStamp = Now; - } - - return (InStates.Num() > 0); -} - -/** - * Helper struct for RemoveRedundantErrors() - */ -struct FRemoveRedundantErrors -{ - FRemoveRedundantErrors(const FString& InFilter) - : Filter(InFilter) - { - } - - bool operator()(const FString& String) const - { - if(String.Contains(Filter)) - { - return true; - } - - return false; - } - - /** The filter string we try to identify in the reported error */ - FString Filter; -}; - -void RemoveRedundantErrors(FGitSourceControlCommand& InCommand, const FString& InFilter) -{ - bool bFoundRedundantError = false; - for(auto Iter(InCommand.ErrorMessages.CreateConstIterator()); Iter; Iter++) - { - if(Iter->Contains(InFilter)) - { - InCommand.InfoMessages.Add(*Iter); - bFoundRedundantError = true; - } - } - - InCommand.ErrorMessages.RemoveAll( FRemoveRedundantErrors(InFilter) ); - - // if we have no error messages now, assume success! - if(bFoundRedundantError && InCommand.ErrorMessages.Num() == 0 && !InCommand.bCommandSuccessful) - { - InCommand.bCommandSuccessful = true; - } -} - -} diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.h deleted file mode 100644 index 971b433..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.h +++ /dev/null @@ -1,220 +0,0 @@ -// 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 "GitSourceControlState.h" - -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; - -namespace GitSourceControlUtils -{ - -/** - * 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 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); - -/** - * 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 - */ -bool RunCommand(const FString& InCommand, const FString& InPathToGitBinary, const FString& InRepositoryRoot, const TArray& InParameters, const TArray& InFiles, TArray& OutResults, TArray& OutErrorMessages); - -/** - * 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& InParameters, const TArray& InFiles, TArray& OutResults, TArray& OutErrorMessages); - -/** - * 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 - * @returns true if the command succeeded and returned no errors - */ -bool RunUpdateStatus(const FString& InPathToGitBinary, const FString& InRepositoryRoot, const bool InUsingLfsLocking, const TArray& InFiles, TArray& OutErrorMessages, TArray& OutStates); - -/** - * 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& 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 RelativeFilenames(const TArray& 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 AbsoluteFilenames(const TArray& InFileNames, const FString& InRelativeTo); - -/** - * Helper function for various commands to update cached states. - * @returns true if any states were updated - */ -bool UpdateCachedStates(const TArray& InStates); - -/** - * 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); - -/** - * Run 'git lfs locks" to extract all lock information for all files in the repository - * - * @param InPathToGitBinary The path to the Git binary - * @param InRepositoryRoot The Git repository from where to run the command - usually the Game directory - * @param bAbsolutePaths Whether to report absolute filenames, false for repo-relative - * @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& InPathToGitBinary, const FString& InRepositoryRoot, const bool bAbsolutePaths, TArray& OutErrorMessages, TMap& OutLocks); - -} diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/IGitSourceControlWorker.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/IGitSourceControlWorker.h deleted file mode 100644 index fb2a01a..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/IGitSourceControlWorker.h +++ /dev/null @@ -1,30 +0,0 @@ -// 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" - -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 FGitSourceControlWorkerRef; diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.cpp b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.cpp deleted file mode 100644 index ce1d9ab..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.cpp +++ /dev/null @@ -1,750 +0,0 @@ -// 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) - -#include "SGitSourceControlSettings.h" - -#include "Fonts/SlateFontInfo.h" -#include "Misc/App.h" -#include "Misc/FileHelper.h" -#include "Misc/Paths.h" -#include "Modules/ModuleManager.h" -#include "Styling/SlateTypes.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Text/STextBlock.h" -#include "Widgets/Input/SButton.h" -#include "Widgets/Input/SCheckBox.h" -#include "Widgets/Input/SEditableTextBox.h" -#include "Widgets/Input/SFilePathPicker.h" -#include "Widgets/Input/SMultiLineEditableTextBox.h" -#include "Widgets/Layout/SBorder.h" -#include "Widgets/Layout/SSeparator.h" -#include "Widgets/Notifications/SNotificationList.h" -#include "Framework/Notifications/NotificationManager.h" -#include "EditorDirectories.h" -#include "EditorStyleSet.h" -#include "SourceControlOperations.h" -#include "GitSourceControlModule.h" -#include "GitSourceControlUtils.h" - -#define LOCTEXT_NAMESPACE "SGitSourceControlSettings" - -void SGitSourceControlSettings::Construct(const FArguments& InArgs) -{ - const FSlateFontInfo Font = FEditorStyle::GetFontStyle(TEXT("SourceControl.LoginWindow.Font")); - - bAutoCreateGitIgnore = true; - bAutoCreateReadme = true; - bAutoCreateGitAttributes = false; - bAutoInitialCommit = true; - - InitialCommitMessage = LOCTEXT("InitialCommitMessage", "Initial commit"); - - const FText FileFilterType = NSLOCTEXT("GitSourceControl", "Executables", "Executables"); -#if PLATFORM_WINDOWS - const FString FileFilterText = FString::Printf(TEXT("%s (*.exe)|*.exe"), *FileFilterType.ToString()); -#else - const FString FileFilterText = FString::Printf(TEXT("%s"), *FileFilterType.ToString()); -#endif - - ReadmeContent = FText::FromString(FString(TEXT("# ")) + FApp::GetProjectName() + "\n\nDeveloped with Unreal Engine 4\n"); - - ChildSlot - [ - SNew(SBorder) - .BorderImage( FEditorStyle::GetBrush("DetailsView.CategoryBottom")) - .Padding(FMargin(0.0f, 3.0f, 0.0f, 0.0f)) - [ - SNew(SVerticalBox) - // Path to the Git command line executable - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .ToolTipText(LOCTEXT("BinaryPathLabel_Tooltip", "Path to Git binary")) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BinaryPathLabel", "Git Path")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - [ - SNew(SFilePathPicker) - .BrowseButtonImage(FEditorStyle::GetBrush("PropertyWindow.Button_Ellipsis")) - .BrowseButtonStyle(FEditorStyle::Get(), "HoverHintOnly") - .BrowseDirectory(FEditorDirectories::Get().GetLastDirectory(ELastDirectory::GENERIC_OPEN)) - .BrowseTitle(LOCTEXT("BinaryPathBrowseTitle", "File picker...")) - .FilePath(this, &SGitSourceControlSettings::GetBinaryPathString) - .FileTypeFilter(FileFilterText) - .OnPathPicked(this, &SGitSourceControlSettings::OnBinaryPathPicked) - ] - ] - // Root of the local repository - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .ToolTipText(LOCTEXT("RepositoryRootLabel_Tooltip", "Path to the root of the Git repository")) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("RepositoryRootLabel", "Root of the repository")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - [ - SNew(STextBlock) - .Text(this, &SGitSourceControlSettings::GetPathToRepositoryRoot) - .Font(Font) - ] - ] - // User Name - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .ToolTipText(LOCTEXT("GitUserName_Tooltip", "User name configured for the Git repository")) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("GitUserName", "User Name")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - [ - SNew(STextBlock) - .Text(this, &SGitSourceControlSettings::GetUserName) - .Font(Font) - ] - ] - // User e-mail - +SVerticalBox::Slot() - .FillHeight(1.0f) - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .ToolTipText(LOCTEXT("GitUserEmail_Tooltip", "User e-mail configured for the Git repository")) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("GitUserEmail", "E-Mail")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - [ - SNew(STextBlock) - .Text(this, &SGitSourceControlSettings::GetUserEmail) - .Font(Font) - ] - ] - // Separator - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SSeparator) - ] - // Explanation text - +SVerticalBox::Slot() - .FillHeight(1.0f) - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - .HAlign(HAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("RepositoryNotFound", "Current Project is not contained in a Git Repository. Fill the form below to initialize a new Repository.")) - .ToolTipText(LOCTEXT("RepositoryNotFound_Tooltip", "No Repository found at the level or above the current Project")) - .Font(Font) - ] - ] - // Option to configure the URL of the default remote 'origin' - // TODO: option to configure the name of the remote instead of the default origin - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - .ToolTipText(LOCTEXT("ConfigureOrigin_Tooltip", "Configure the URL of the default remote 'origin'")) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("ConfigureOrigin", "URL of the remote server 'origin'")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SEditableTextBox) - .Text(this, &SGitSourceControlSettings::GetRemoteUrl) - .OnTextCommitted(this, &SGitSourceControlSettings::OnRemoteUrlCommited) - .Font(Font) - ] - ] - // Option to add a proper .gitignore file (true by default) - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - .ToolTipText(LOCTEXT("CreateGitIgnore_Tooltip", "Create and add a standard '.gitignore' file")) - +SHorizontalBox::Slot() - .FillWidth(0.1f) - [ - SNew(SCheckBox) - .IsChecked(ECheckBoxState::Checked) - .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedCreateGitIgnore) - ] - +SHorizontalBox::Slot() - .FillWidth(2.9f) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("CreateGitIgnore", "Add a .gitignore file")) - .Font(Font) - ] - ] - // Option to add a README.md file with custom content - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - .ToolTipText(LOCTEXT("CreateReadme_Tooltip", "Add a README.md file")) - +SHorizontalBox::Slot() - .FillWidth(0.1f) - [ - SNew(SCheckBox) - .IsChecked(ECheckBoxState::Checked) - .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedCreateReadme) - ] - +SHorizontalBox::Slot() - .FillWidth(0.9f) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("CreateReadme", "Add a basic README.md file")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - .Padding(2.0f) - [ - SNew(SMultiLineEditableTextBox) - .Text(this, &SGitSourceControlSettings::GetReadmeContent) - .OnTextCommitted(this, &SGitSourceControlSettings::OnReadmeContentCommited) - .IsEnabled(this, &SGitSourceControlSettings::GetAutoCreateReadme) - .SelectAllTextWhenFocused(true) - .Font(Font) - ] - ] - // Option to add a proper .gitattributes file for Git LFS (false by default) - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - .ToolTipText(LOCTEXT("CreateGitAttributes_Tooltip", "Create and add a '.gitattributes' file to enable Git LFS for the whole 'Content/' directory (needs Git LFS extensions to be installed).")) - +SHorizontalBox::Slot() - .FillWidth(0.1f) - [ - SNew(SCheckBox) - .IsChecked(ECheckBoxState::Unchecked) - .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedCreateGitAttributes) - .IsEnabled(this, &SGitSourceControlSettings::CanInitializeGitLfs) - ] - +SHorizontalBox::Slot() - .FillWidth(2.9f) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("CreateGitAttributes", "Add a .gitattributes file to enable Git LFS")) - .Font(Font) - ] - ] - // Option to use the Git LFS File Locking workflow (false by default) - // Enabled even after init to switch it off in case of no network - // TODO LFS turning it off afterwards does not work because all files are readonly ! - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .ToolTipText(LOCTEXT("UseGitLfsLocking_Tooltip", "Uses Git LFS 2 File Locking workflow (CheckOut and Commit/Push).")) - +SHorizontalBox::Slot() - .FillWidth(0.1f) - [ - SNew(SCheckBox) - .IsChecked(SGitSourceControlSettings::IsUsingGitLfsLocking()) - .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedUseGitLfsLocking) - .IsEnabled(this, &SGitSourceControlSettings::CanUseGitLfsLocking) - ] - +SHorizontalBox::Slot() - .FillWidth(0.9f) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("UseGitLfsLocking", "Uses Git LFS 2 File Locking workflow")) - .Font(Font) - ] - // Username credential used to access the Git LFS 2 File Locks server - +SHorizontalBox::Slot() - .FillWidth(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SEditableTextBox) - .Text(this, &SGitSourceControlSettings::GetLfsUserName) - .OnTextCommitted(this, &SGitSourceControlSettings::OnLfsUserNameCommited) - .IsEnabled(this, &SGitSourceControlSettings::GetIsUsingGitLfsLocking) - .HintText(LOCTEXT("LfsUserName_Hint", "Username to lock files on the LFS server")) - .Font(Font) - ] - ] - // Option to Make the initial Git commit with custom message - +SVerticalBox::Slot() - .AutoHeight() - .Padding(2.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - .ToolTipText(LOCTEXT("InitialGitCommit_Tooltip", "Make the initial Git commit")) - +SHorizontalBox::Slot() - .FillWidth(0.1f) - [ - SNew(SCheckBox) - .IsChecked(ECheckBoxState::Checked) - .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedInitialCommit) - ] - +SHorizontalBox::Slot() - .FillWidth(0.9f) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("InitialGitCommit", "Make the initial Git commit")) - .Font(Font) - ] - +SHorizontalBox::Slot() - .FillWidth(2.0f) - .Padding(2.0f) - [ - SNew(SMultiLineEditableTextBox) - .Text(this, &SGitSourceControlSettings::GetInitialCommitMessage) - .OnTextCommitted(this, &SGitSourceControlSettings::OnInitialCommitMessageCommited) - .IsEnabled(this, &SGitSourceControlSettings::GetAutoInitialCommit) - .SelectAllTextWhenFocused(true) - .Font(Font) - ] - ] - // Button to initialize the project with Git, create .gitignore/.gitattributes files, and make the first commit) - +SVerticalBox::Slot() - .FillHeight(2.5f) - .Padding(4.0f) - .VAlign(VAlign_Center) - [ - SNew(SHorizontalBox) - .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(SButton) - .Text(LOCTEXT("GitInitRepository", "Initialize project with Git")) - .ToolTipText(LOCTEXT("GitInitRepository_Tooltip", "Initialize current project as a new Git repository")) - .OnClicked(this, &SGitSourceControlSettings::OnClickedInitializeGitRepository) - .IsEnabled(this, &SGitSourceControlSettings::CanInitializeGitRepository) - .HAlign(HAlign_Center) - .ContentPadding(6) - ] - ] - ] - ]; -} - -SGitSourceControlSettings::~SGitSourceControlSettings() -{ - RemoveInProgressNotification(); -} - -FString SGitSourceControlSettings::GetBinaryPathString() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - return GitSourceControl.AccessSettings().GetBinaryPath(); -} - -void SGitSourceControlSettings::OnBinaryPathPicked( const FString& PickedPath ) const -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - FString PickedFullPath = FPaths::ConvertRelativePathToFull(PickedPath); - const bool bChanged = GitSourceControl.AccessSettings().SetBinaryPath(PickedFullPath); - if(bChanged) - { - // Re-Check provided git binary path for each change - GitSourceControl.GetProvider().CheckGitAvailability(); - if(GitSourceControl.GetProvider().IsGitAvailable()) - { - GitSourceControl.SaveSettings(); - } - } -} - -FText SGitSourceControlSettings::GetPathToRepositoryRoot() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - return FText::FromString(GitSourceControl.GetProvider().GetPathToRepositoryRoot()); -} - -FText SGitSourceControlSettings::GetUserName() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - return FText::FromString(GitSourceControl.GetProvider().GetUserName()); -} - -FText SGitSourceControlSettings::GetUserEmail() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - return FText::FromString(GitSourceControl.GetProvider().GetUserEmail()); -} - -EVisibility SGitSourceControlSettings::MustInitializeGitRepository() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const bool bGitAvailable = GitSourceControl.GetProvider().IsGitAvailable(); - const bool bGitRepositoryFound = GitSourceControl.GetProvider().IsEnabled(); - return (bGitAvailable && !bGitRepositoryFound) ? EVisibility::Visible : EVisibility::Collapsed; -} - -bool SGitSourceControlSettings::CanInitializeGitRepository() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const bool bGitAvailable = GitSourceControl.GetProvider().IsGitAvailable(); - const bool bGitRepositoryFound = GitSourceControl.GetProvider().IsEnabled(); - const FString LfsUserName = GitSourceControl.AccessSettings().GetLfsUserName(); - const bool bIsUsingGitLfsLocking = GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); - const bool bGitLfsConfigOk = !bIsUsingGitLfsLocking || !LfsUserName.IsEmpty(); - const bool bInitialCommitConfigOk = !bAutoInitialCommit || !InitialCommitMessage.IsEmpty(); - return (bGitAvailable && !bGitRepositoryFound && bGitLfsConfigOk && bInitialCommitConfigOk); -} - -bool SGitSourceControlSettings::CanInitializeGitLfs() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - const bool bGitLfsAvailable = GitSourceControl.GetProvider().GetGitVersion().bHasGitLfs; - return bGitLfsAvailable; -} - -bool SGitSourceControlSettings::CanUseGitLfsLocking() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const bool bGitLfsLockingAvailable = GitSourceControl.GetProvider().GetGitVersion().bHasGitLfsLocking; - // TODO LFS SRombauts : check if .gitattributes file is present and if Content/ is already tracked! - const bool bGitAttributesCreated = true; - return (bGitLfsLockingAvailable && (bAutoCreateGitAttributes || bGitAttributesCreated)); -} - -FReply SGitSourceControlSettings::OnClickedInitializeGitRepository() -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); - const FString PathToProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); - TArray InfoMessages; - TArray ErrorMessages; - - // 1.a. Synchronous (very quick) "git init" operation: initialize a Git local repository with a .git/ subdirectory - GitSourceControlUtils::RunCommand(TEXT("init"), PathToGitBinary, PathToProjectDir, TArray(), TArray(), InfoMessages, ErrorMessages); - // 1.b. Synchronous (very quick) "git remote add" operation: configure the URL of the default remote server 'origin' if specified - if(!RemoteUrl.IsEmpty()) - { - TArray Parameters; - Parameters.Add(TEXT("add origin")); - Parameters.Add(RemoteUrl.ToString()); - GitSourceControlUtils::RunCommand(TEXT("remote"), PathToGitBinary, PathToProjectDir, Parameters, TArray(), InfoMessages, ErrorMessages); - } - - // Check the new repository status to enable connection (branch, user e-mail) - GitSourceControl.GetProvider().CheckRepositoryStatus(PathToGitBinary); - if(GitSourceControl.GetProvider().IsAvailable()) - { - // List of files to add to Source Control (.uproject, Config/, Content/, Source/ files and .gitignore/.gitattributes if any) - TArray ProjectFiles; - ProjectFiles.Add(FPaths::GetProjectFilePath()); - ProjectFiles.Add(FPaths::ProjectConfigDir()); - ProjectFiles.Add(FPaths::ProjectContentDir()); - if (FPaths::DirectoryExists(FPaths::GameSourceDir())) - { - ProjectFiles.Add(FPaths::GameSourceDir()); - } - if(bAutoCreateGitIgnore) - { - // 2.a. Create a standard ".gitignore" file with common patterns for a typical Blueprint & C++ project - const FString GitIgnoreFilename = FPaths::Combine(FPaths::ProjectDir(), TEXT(".gitignore")); - const FString GitIgnoreContent = TEXT("Binaries\nDerivedDataCache\nIntermediate\nSaved\n.vscode\n.vs\n*.VC.db\n*.opensdf\n*.opendb\n*.sdf\n*.sln\n*.suo\n*.xcodeproj\n*.xcworkspace\n*.log"); - if(FFileHelper::SaveStringToFile(GitIgnoreContent, *GitIgnoreFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) - { - ProjectFiles.Add(GitIgnoreFilename); - } - } - if(bAutoCreateReadme) - { - // 2.b. Create a "README.md" file with a custom description - const FString ReadmeFilename = FPaths::Combine(FPaths::ProjectDir(), TEXT("README.md")); - if (FFileHelper::SaveStringToFile(ReadmeContent.ToString(), *ReadmeFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) - { - ProjectFiles.Add(ReadmeFilename); - } - } - if(bAutoCreateGitAttributes) - { - // 2.c. Synchronous (very quick) "lfs install" operation: needs only to be run once by user - GitSourceControlUtils::RunCommand(TEXT("lfs install"), PathToGitBinary, PathToProjectDir, TArray(), TArray(), InfoMessages, ErrorMessages); - - // 2.d. Create a ".gitattributes" file to enable Git LFS (Large File System) for the whole "Content/" subdir - const FString GitAttributesFilename = FPaths::Combine(FPaths::ProjectDir(), TEXT(".gitattributes")); - FString GitAttributesContent; - if(GitSourceControl.AccessSettings().IsUsingGitLfsLocking()) - { - // Git LFS 2.x File Locking mechanism - GitAttributesContent = TEXT("Content/** filter=lfs diff=lfs merge=lfs -text lockable\n"); - } - else - { - GitAttributesContent = TEXT("Content/** filter=lfs diff=lfs merge=lfs -text\n"); - } - if(FFileHelper::SaveStringToFile(GitAttributesContent, *GitAttributesFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) - { - ProjectFiles.Add(GitAttributesFilename); - } - } - - // 3. Add files to Source Control: launch an asynchronous MarkForAdd operation - LaunchMarkForAddOperation(ProjectFiles); - - // 4. The CheckIn will follow, at completion of the MarkForAdd operation - } - return FReply::Handled(); -} - -// Launch an asynchronous "MarkForAdd" operation and start an ongoing notification -void SGitSourceControlSettings::LaunchMarkForAddOperation(const TArray& InFiles) -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - TSharedRef MarkForAddOperation = ISourceControlOperation::Create(); - ECommandResult::Type Result = GitSourceControl.GetProvider().Execute(MarkForAddOperation, InFiles, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateSP(this, &SGitSourceControlSettings::OnSourceControlOperationComplete)); - if (Result == ECommandResult::Succeeded) - { - DisplayInProgressNotification(MarkForAddOperation); - } - else - { - DisplayFailureNotification(MarkForAddOperation); - } -} - -// Launch an asynchronous "CheckIn" operation and start another ongoing notification -void SGitSourceControlSettings::LaunchCheckInOperation() -{ - TSharedRef CheckInOperation = ISourceControlOperation::Create(); - CheckInOperation->SetDescription(InitialCommitMessage); - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - ECommandResult::Type Result = GitSourceControl.GetProvider().Execute(CheckInOperation, TArray(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateSP(this, &SGitSourceControlSettings::OnSourceControlOperationComplete)); - if (Result == ECommandResult::Succeeded) - { - DisplayInProgressNotification(CheckInOperation); - } - else - { - DisplayFailureNotification(CheckInOperation); - } -} - -/// Delegate called when a source control operation has completed: launch the next one and manage notifications -void SGitSourceControlSettings::OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult) -{ - RemoveInProgressNotification(); - - // Report result with a notification - if (InResult == ECommandResult::Succeeded) - { - DisplaySuccessNotification(InOperation); - } - else - { - DisplayFailureNotification(InOperation); - } - - if ((InOperation->GetName() == "MarkForAdd") && (InResult == ECommandResult::Succeeded) && bAutoInitialCommit) - { - // 4. optional initial Asynchronous commit with custom message: launch a "CheckIn" Operation - LaunchCheckInOperation(); - } -} - - -// Display an ongoing notification during the whole operation -void SGitSourceControlSettings::DisplayInProgressNotification(const FSourceControlOperationRef& InOperation) -{ - FNotificationInfo Info(InOperation->GetInProgressString()); - Info.bFireAndForget = false; - Info.ExpireDuration = 0.0f; - Info.FadeOutDuration = 1.0f; - OperationInProgressNotification = FSlateNotificationManager::Get().AddNotification(Info); - if (OperationInProgressNotification.IsValid()) - { - OperationInProgressNotification.Pin()->SetCompletionState(SNotificationItem::CS_Pending); - } -} - -// Remove the ongoing notification at the end of the operation -void SGitSourceControlSettings::RemoveInProgressNotification() -{ - if (OperationInProgressNotification.IsValid()) - { - OperationInProgressNotification.Pin()->ExpireAndFadeout(); - OperationInProgressNotification.Reset(); - } -} - -// Display a temporary success notification at the end of the operation -void SGitSourceControlSettings::DisplaySuccessNotification(const FSourceControlOperationRef& InOperation) -{ - const FText NotificationText = FText::Format(LOCTEXT("InitialCommit_Success", "{0} operation was successfull!"), FText::FromName(InOperation->GetName())); - FNotificationInfo Info(NotificationText); - Info.bUseSuccessFailIcons = true; - Info.Image = FEditorStyle::GetBrush(TEXT("NotificationList.SuccessImage")); - FSlateNotificationManager::Get().AddNotification(Info); -} - -// Display a temporary failure notification at the end of the operation -void SGitSourceControlSettings::DisplayFailureNotification(const FSourceControlOperationRef& InOperation) -{ - const FText NotificationText = FText::Format(LOCTEXT("InitialCommit_Failure", "Error: {0} operation failed!"), FText::FromName(InOperation->GetName())); - FNotificationInfo Info(NotificationText); - Info.ExpireDuration = 8.0f; - FSlateNotificationManager::Get().AddNotification(Info); -} - -void SGitSourceControlSettings::OnCheckedCreateGitIgnore(ECheckBoxState NewCheckedState) -{ - bAutoCreateGitIgnore = (NewCheckedState == ECheckBoxState::Checked); -} - -void SGitSourceControlSettings::OnCheckedCreateReadme(ECheckBoxState NewCheckedState) -{ - bAutoCreateReadme = (NewCheckedState == ECheckBoxState::Checked); -} - -bool SGitSourceControlSettings::GetAutoCreateReadme() const -{ - return bAutoCreateReadme; -} - -void SGitSourceControlSettings::OnReadmeContentCommited(const FText& InText, ETextCommit::Type InCommitType) -{ - ReadmeContent = InText; -} - -FText SGitSourceControlSettings::GetReadmeContent() const -{ - return ReadmeContent; -} - -void SGitSourceControlSettings::OnCheckedCreateGitAttributes(ECheckBoxState NewCheckedState) -{ - bAutoCreateGitAttributes = (NewCheckedState == ECheckBoxState::Checked); -} - -void SGitSourceControlSettings::OnCheckedUseGitLfsLocking(ECheckBoxState NewCheckedState) -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - GitSourceControl.AccessSettings().SetUsingGitLfsLocking(NewCheckedState == ECheckBoxState::Checked); - GitSourceControl.AccessSettings().SaveSettings(); -} - -bool SGitSourceControlSettings::GetIsUsingGitLfsLocking() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - return GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); -} - -ECheckBoxState SGitSourceControlSettings::IsUsingGitLfsLocking() const -{ - return (GetIsUsingGitLfsLocking() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked); -} - -void SGitSourceControlSettings::OnLfsUserNameCommited(const FText& InText, ETextCommit::Type InCommitType) -{ - FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - GitSourceControl.AccessSettings().SetLfsUserName(InText.ToString()); - GitSourceControl.AccessSettings().SaveSettings(); -} - -FText SGitSourceControlSettings::GetLfsUserName() const -{ - const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); - return FText::FromString(GitSourceControl.AccessSettings().GetLfsUserName()); -} - -void SGitSourceControlSettings::OnCheckedInitialCommit(ECheckBoxState NewCheckedState) -{ - bAutoInitialCommit = (NewCheckedState == ECheckBoxState::Checked); -} - -bool SGitSourceControlSettings::GetAutoInitialCommit() const -{ - return bAutoInitialCommit; -} - -void SGitSourceControlSettings::OnInitialCommitMessageCommited(const FText& InText, ETextCommit::Type InCommitType) -{ - InitialCommitMessage = InText; -} - -FText SGitSourceControlSettings::GetInitialCommitMessage() const -{ - return InitialCommitMessage; -} - -void SGitSourceControlSettings::OnRemoteUrlCommited(const FText& InText, ETextCommit::Type InCommitType) -{ - RemoteUrl = InText; -} - -FText SGitSourceControlSettings::GetRemoteUrl() const -{ - return RemoteUrl; -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.h b/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.h deleted file mode 100644 index 38d4855..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.h +++ /dev/null @@ -1,98 +0,0 @@ -// 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 "Layout/Visibility.h" -#include "Input/Reply.h" -#include "Widgets/DeclarativeSyntaxSupport.h" -#include "Widgets/SCompoundWidget.h" -#include "SlateFwd.h" -#include "ISourceControlOperation.h" -#include "ISourceControlProvider.h" - -enum class ECheckBoxState : uint8; - -class SGitSourceControlSettings : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SGitSourceControlSettings) {} - - SLATE_END_ARGS() - -public: - - void Construct(const FArguments& InArgs); - - ~SGitSourceControlSettings(); - -private: - - /** Delegates to get Git binary path from/to settings */ - FString GetBinaryPathString() const; - void OnBinaryPathPicked(const FString & PickedPath) const; - - /** Delegate to get repository root, user name and email from provider */ - FText GetPathToRepositoryRoot() const; - FText GetUserName() const; - FText GetUserEmail() const; - - EVisibility MustInitializeGitRepository() const; - bool CanInitializeGitRepository() const; - bool CanInitializeGitLfs() const; - bool CanUseGitLfsLocking() const; - - /** Delegate to initialize a new Git repository */ - FReply OnClickedInitializeGitRepository(); - - void OnCheckedCreateGitIgnore(ECheckBoxState NewCheckedState); - bool bAutoCreateGitIgnore; - - /** Delegates to create a README.md file */ - void OnCheckedCreateReadme(ECheckBoxState NewCheckedState); - bool GetAutoCreateReadme() const; - bool bAutoCreateReadme; - void OnReadmeContentCommited(const FText& InText, ETextCommit::Type InCommitType); - FText GetReadmeContent() const; - FText ReadmeContent; - - void OnCheckedCreateGitAttributes(ECheckBoxState NewCheckedState); - bool bAutoCreateGitAttributes; - - void OnCheckedUseGitLfsLocking(ECheckBoxState NewCheckedState); - ECheckBoxState IsUsingGitLfsLocking() const; - bool GetIsUsingGitLfsLocking() const; - - void OnLfsUserNameCommited(const FText& InText, ETextCommit::Type InCommitType); - FText GetLfsUserName() const; - - void OnCheckedInitialCommit(ECheckBoxState NewCheckedState); - bool GetAutoInitialCommit() const; - bool bAutoInitialCommit; - void OnInitialCommitMessageCommited(const FText& InText, ETextCommit::Type InCommitType); - FText GetInitialCommitMessage() const; - FText InitialCommitMessage; - - void OnRemoteUrlCommited(const FText& InText, ETextCommit::Type InCommitType); - FText GetRemoteUrl() const; - FText RemoteUrl; - - /** Launch initial asynchronous add and commit operations */ - void LaunchMarkForAddOperation(const TArray& InFiles); - void LaunchCheckInOperation(); - - /** Delegate called when a source control operation has completed */ - void OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult); - - /** Asynchronous operation progress notifications */ - TWeakPtr OperationInProgressNotification; - - void DisplayInProgressNotification(const FSourceControlOperationRef& InOperation); - void RemoveInProgressNotification(); - void DisplaySuccessNotification(const FSourceControlOperationRef& InOperation); - void DisplayFailureNotification(const FSourceControlOperationRef& InOperation); -}; diff --git a/Plugins/UE4GitPlugin-2.17-beta/_config.yml b/Plugins/UE4GitPlugin-2.17-beta/_config.yml deleted file mode 100644 index f170406..0000000 --- a/Plugins/UE4GitPlugin-2.17-beta/_config.yml +++ /dev/null @@ -1,2 +0,0 @@ -show_downloads: true -theme: jekyll-theme-slate \ No newline at end of file