From f9b852da7724cbf0f372dd09d2de48b6a04d8234 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 17 Nov 2021 14:06:49 +0100 Subject: [PATCH] Decals Decals are currently quite slow. I'm fixing that with dynamic materials --- Config/steamvr_ue_editor_app.json | 2 +- .../Book/CoverImages/DecalInstance.uasset | 3 + Content/blueprints/BP_CollectableBook.uasset | 4 +- .../ProceduralDungeon.uplugin | 2 +- .../Private/DungeonGenerator.cpp | 49 +- .../Public/DungeonGenerator.h | 30 +- Plugins/UE4GitPlugin-2.17-beta/.gitbugtraq | 7 - Plugins/UE4GitPlugin-2.17-beta/.gitignore | 5 - .../GitSourceControl.uplugin | 25 - Plugins/UE4GitPlugin-2.17-beta/LICENSE.txt | 20 - Plugins/UE4GitPlugin-2.17-beta/README.md | 201 --- .../Resources/Icon128.png | Bin 3292 -> 0 bytes .../Screenshots/FileHistory.png | Bin 49797 -> 0 bytes .../Screenshots/Icons/Added.png | Bin 7009 -> 0 bytes .../Screenshots/Icons/Modified.png | Bin 7071 -> 0 bytes .../Screenshots/Icons/New.png | Bin 7537 -> 0 bytes .../Screenshots/Icons/Renamed.png | Bin 7297 -> 0 bytes .../Screenshots/Icons/Unchanged.png | Bin 6811 -> 0 bytes .../Screenshots/SourceControlLogin_Init.png | Bin 63505 -> 0 bytes .../Screenshots/SourceControlMenu.png | Bin 48179 -> 0 bytes .../SourceControlStatusTooltip.png | Bin 28518 -> 0 bytes .../Screenshots/SubmitFiles.png | Bin 42652 -> 0 bytes .../GitSourceControl.Build.cs | 33 - .../Private/GitSourceControlCommand.cpp | 65 - .../Private/GitSourceControlCommand.h | 92 - .../Private/GitSourceControlMenu.cpp | 515 ------ .../Private/GitSourceControlMenu.h | 61 - .../Private/GitSourceControlModule.cpp | 65 - .../Private/GitSourceControlModule.h | 114 -- .../Private/GitSourceControlOperations.cpp | 684 -------- .../Private/GitSourceControlOperations.h | 192 -- .../Private/GitSourceControlPrivatePCH.h | 15 - .../Private/GitSourceControlProvider.cpp | 467 ----- .../Private/GitSourceControlProvider.h | 210 --- .../Private/GitSourceControlRevision.cpp | 114 -- .../Private/GitSourceControlRevision.h | 76 - .../Private/GitSourceControlSettings.cpp | 90 - .../Private/GitSourceControlSettings.h | 49 - .../Private/GitSourceControlState.cpp | 386 ---- .../Private/GitSourceControlState.h | 117 -- .../Private/GitSourceControlUtils.cpp | 1556 ----------------- .../Private/GitSourceControlUtils.h | 220 --- .../Private/IGitSourceControlWorker.h | 30 - .../Private/SGitSourceControlSettings.cpp | 750 -------- .../Private/SGitSourceControlSettings.h | 98 -- Plugins/UE4GitPlugin-2.17-beta/_config.yml | 2 - 46 files changed, 81 insertions(+), 6268 deletions(-) create mode 100644 Content/Materials/Book/CoverImages/DecalInstance.uasset delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/.gitbugtraq delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/.gitignore delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/GitSourceControl.uplugin delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/LICENSE.txt delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/README.md delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Resources/Icon128.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/FileHistory.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Added.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Modified.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/New.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Renamed.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/Icons/Unchanged.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlLogin_Init.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlMenu.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/SourceControlStatusTooltip.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Screenshots/SubmitFiles.png delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/GitSourceControl.Build.cs delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlCommand.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlMenu.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlModule.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlOperations.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlPrivatePCH.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlProvider.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlRevision.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlSettings.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlState.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/GitSourceControlUtils.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/IGitSourceControlWorker.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.cpp delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/Source/GitSourceControl/Private/SGitSourceControlSettings.h delete mode 100644 Plugins/UE4GitPlugin-2.17-beta/_config.yml 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 3d9e8642c5bee71f8bc07775b40636a8797c9c30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3292 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

i%)n|dAoP|m44T%?cV1;;VPbEnixSI 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

ZJ<- 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 uOVGd000McNliru;sFL012~k|QrZ9j03B&m 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 JPDHLkV1hn8TE74Q 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 cfea784c07d03e5e4bccbbc0b53ceb280d4d7ee4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7537 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 Hu0mjfjp8jf 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 d66b81b3268731eab9c8f62e0251861235e87ee7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7297 zcmV-{9Dd`8P)VGd000McNliru;sFK~IU5mQLx=zX03B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*02}2=L_t(|+U;F=tX$Q7 z{?1)z=FQAoyEC{V^-S$NYq=3cpACLL`fqaLhHXCt!yQv0-Rd-0Et8* zI%t$q*tQMRG+~;i3p6of$wwHlIH1Kjj|$AuENH|uVcRw+r9w5S_}MelG+|j*4bxl# zj*5k75BZRSM(YQgisJpc2!ZOQ{WCfC})*NJ8B^ad~ zBs~wil!lh*1d$cz(m}vU@mn>S6*BS4+r5E^4uNBFtL$xtN+eDA#B%e5_s{;Kwu!>j zAVyE_Mga$5mS;Q-oT8K;#hm3i-2M2~D0Ln}X=V(R+Kt6&P6af~CXwhCiI4_BX}QB) zACo=W*|ts0OblSzjR{=-$(uonz&o$(#mIqP==xkx+d^r27>n1-*!ovDVk~h6n;%<* z|NFN=SOZx|daALE2=S`GfQCr|2|ywO)&v&#-BsO}i*hfs%qvWt!@RX7Ha>6_rsj;m zT3W!}+wMf)>Isw!6DZ|JAf?aZrY~QO)8rTii^rj+Wk?++sBJZYY0eGNK#0Vl8)*T9 zNR65azlL86qjj1?vExH+8@Z8wEZvgC^D2yoh z)I&F6A~}RHV*u17IPjAp%)HZwj;>_0K=T&@iLC-j{CY^M)Fuf(JR;xr-%N4#FXZ6P<(k7YM$*l_O+I9WV`5-q?g0f&Ef3YTu`L)Wq- z1`DTvB#@k!!9b}WGLdlnwNZ@xDua&hOF?9%+K$3yAVp&p5j%^a!j51>VtbLZ_NB3Z z?z-E@Y(}G@s#A%f7I-)c-MN1)7T!lT) zp2WoXG$;X1{(cPOKT9Liy8@b}V_9)6<|voJumRHs2KG(i^iK%VJxd@diP(x>ilVYe zECPvk@<2st9F4|N(TOf@WW;&yP`^=M8XtUb8Y!{_ir(SLEk?UprG6bB)n)|)Md|^BMu_=@vY;`OD}G)w zbstIy^mHHO;#HXXX#uIEj#9ZFfA+oGpf0sBU>pS{6qDzQII*LE&Uve#=oyb(qr0+7 zV_*dU7NGhj$cG&EJk$mkesD&gP<$1prHg`v?2oE{v(RIUiS;of&al7Wi!2Zbc7=ty6NiI*up zu!k|RcLs@(l}L2X5$J6iTY)I?Fe9;S7Kxa%3@WLFip2}M@!74LamAVyShR4y)9+;4 zHY7=MhD8vv9JX!a#!VZXZBw~v96vdT-~E0!Uf;D3<5Q*h`9+~vsZVmG^3;-|A=A4I z#?C2ZAVbM?07MdGVo2@v!tTO5Sd~(WQmKSoE(a^U7L?l6ZUPe1Zjpup=-#dl-1V7F zxM}m%SiEq7b8oE6f_Yt{E(``3|4aZ-HVnM+)*k%&wcU98y`w$}k9rsxoD!qLAqLbZ zijW{r??)z+fv)S(d#=NEEeS{(HwIQ;cLtxkV>7niay@!_x}4z;POq-FGnV_Cfy%Rw z->1kjZrXe;ZrFG=-rIi!|M}BjWAC9;9{G{_7)S!Z5IUwLLaM9|^;qde<*Fnkp3RB@ zmP80PZdif8{rVTt-IaA1LkMvOxQIaWvPrBqi3?Oh2(Dba0#~eAj^F)$H=g;AUt)Z! z7|}5Z>#8Awloyi@6@%vhMnVq{*Oo;hv!o_{|O{X!oNQEk|)v;s72qj7Y$aK+##e6mp?|9U(ljLhY^TdU{^-P zV-Mehgs!{T#DmrdU_2P2>x2za`{a$+;+BmoLBgJg#Cz^&Dyhl+Y4P%r;{B=u{M|S2 zMK+reIv2bhY8Gg`jT3?A1<|%`Jp8qLv1a)kpC{qdT&PP9sC#&QM5Ym8elW&Oa^~U7 ze{nNbeQcTImh-ByQI+TioS$9u0}TL7)5K%j??*Nzg;*{U!Jd;~Cnc`x504o=>M(An zCl>--@v-^%^sSpIqli*>#t3hNY(5z{s! zP74OC@)QY8!r%Pm=U`b@pxtT}cwsE#zu9lwwy|#Y3ani@57A~SgxR|Yjh-|Mv^aqI zw6zEYfJF8Bv2EVmK5V>ZMd>@Fil13!7Pe;jF6@xwYGRR$OUfQat#c_ zaFVsQZM%ddatvAloVSs9f#Lz=Sx70x!b|31-OBl~9E}>_NFyn+y3<%B(Af~>)8=+3 zv1s9Z;rwksVYNavvWCKcybR-k^r}xvDXzV8h1<=K06@)OO|aQ9Y?@zyO#<2(!8KPb zb=c+Cig`gc)3&h*|6aQlrW(B}lQX8K|U|uwJ*%U`4#6t{G3=#p>Tz(l0 z!*Bt{R!$HJlV?GrfUys&&#%FF0pst9KQEa~pgXIJ1rfnZj1F7_ppgjanIKJ`PAQl- zr^jjSUNyuEQzYa@0W+%;&b^;7z`SabXXWaZi|UB6NU)uY*#{Y6QlTfiO(H1(06=ed z2V_}xfx$W^`Ce%eK&ENB`o`keFUztV;Jpna2r!YI<3ExlVgB6iAYlb0`e)@R9)m?A z6EH^4bwG>paZs`y2`4t=RgEmmuD|_&W5V=H_y#!}A{O(co0gAm{%qNjvQpSoB^A|pyLudfTHX@U^qs4Pj6T%fUijBQLv zY@7$LM)`C6Qchm5ysQagSD068+ctDv!zJ_k@WHXOh_oDr&)CRX;7GHXM#5Dhsr_SW zYSIN51BVX*n6MN@aV3&@@c84sYSJrkKQ+j+N0j{#g(P0>pePDtS;pwdNB}!%7-DrM zui}8_v9IB^^5YQUbi**vc!4X50!k@lxiW24mgSn>YX)3UH;V`iufq|f3jJ8-^>SzH zwA~}$qPBaA*nrl_H1`aVU>DLY&{QggQmF)0Rcl5`@hh&0>=+LcKUg&^AsnXig1{2x zDET$Q*m-m3&d0>DL6EA=qQU66Ljp#D2{FOGKT@g1jfg347hr;`9bPXn|B5E*9Ei#~ZCxoeK(=^e4Vkks7ernRstMtTmq}eut_;|DmHpSg~XQG)?mr zI-i5&8Ri9yw|_w_3u2$2`_BO4LF0pJ062bf5c#4VNdlz0{#Lk+i*MhWH0RnsJ2F|2 zFgZDeOeS5!G~2eJD2gk$$E!&%P;5WD4zqhAhKYa@F^^{y^K@BU#vBoLoi}$Mgxl8% z3y@Z*^-ge?_0n0DVA(>3Ngs1Czjcwrp z;u*(hMfi*f|2xb{;S<0-SfV~RKJ7*b!EgWPO-ziQg`|u@OJt!XGms=Tf;SO`Z9ZOl zGZMyO+lbH5=AxCr;iG4;dgbDpjJv9;u4JqvNl+A}#`)mYr5|8?riE?uX8!rc5!6A- z_Q|r0TrP*5f7lC3Z5YLAn58^QGvm-xUC@%9kR%0R)12H3N(ZF6nir>qFNR4-Ft{g% zOe|Sca9G3(k_k|jWyrGZ#6?V1iY6I~01HZQiAH4c>d)&%vVEGS;g_$zhVjXnYNEJO zk6A9jGKwe{rjSVYK})2;tzzseBT5_a^&4lJ7qH-B&?MeJau#b>F2Kypj7vbOsyaz! z#yB3lAhwBE=CxlU;6#H~y*=KNB;nl9F#hn)f!U=wxY=KnQW&K?Y|DUc^+Hc|g)X#N zJJf)-3bgw3k9--NRIzY!8tIM%^7*`D*Aj_@!!$)vT&hykJHWh229kF^TM*0q!T=G= z6h*;{FaHk3k~O=MH?4h6%PgUgtMswW0vBgNnPzACg&jvEuzS>xp_C~2!~PL$x@sO2 zSwcRacVZ&OHdR%Hpz$mdF)R!;zsVO~TgEaWi9`aweSH@`ICj;H-uLK{gt*2}cUUI;|=ZtU7~44bZ5?xcvAL&Af`t4VJ@B3B2DmI-~dv=M771YHzqcjagO;lG)r4fVamUhjMh_=i>*fvUeOih*W z&btS(e%(rx%Vk)W<)}zrO)}7U73l?z1=vB%W83+_T;!Cnx=)@Oz^{I@3#M6Bi-ga> zlA2M4vt7kDETaU=ELA~kV#2YhHVq`Q&9f~NMllDYG>xOD${++-bNNz~N+rirV|hpx z2Jj(($RY7g{|!qjdjorx)i+HOilX4m;930Qm#?ExGH1QT>e^tGfyYoIo@F~JK&b`W zHtXyRZ31Y4AtQ;eDs9VvX%t|a2B>Y}$nkTSDHO4O?TX6#+NvoerY2d-7li-}AU+ov zHYt%G%qj}#4<7EvYdiO#STd{c#)A7bWYq185Vp`8;o~vMGZ7_tpj`Zg0~;Fm7f zt+>s$U3H%;s%qfu1ZHOT;mWnkkV+$;Nvvg zwz22E1K6|g2rRq88p2JAgcGnkzJ3)v;(EIkkt9g63L+^@RfkRK1oaQK32G1WO#2~h zf;@AfDaIzI@z&e#W971im_N4{mSv$-DnZxvn)r>+ONyKlZ?G)_j2~S!J~4^id)~*% z)5BbQBcMcbM#ZxiPf_P}S;W~uxz*%kS%s`uP|E35i5s_Ljm5pWDVKfrRiX`C7u#PJgY$W0fj zGgqptt*#G8XcZ~~M^RGdfH=T0J4Zoe1*)Egn#e%WQnU9GflOZ9NK0&*FKQH_Q+|j9 zRZqc6W?`8{lnaxvsa<^?BC6juOp{{Z%rMRj4xy_vgE@WO=<3Wu*EMLG=GZt@RRh%{ z^9)O+62`_SFf=@hfipuW8)n5}AOwI^u}hqn=vu3o&Puj|8F5}PL@6LES~csRg__7f zk`*@vYhL?88`hKxT3U#JZ5s4cmt)gPGn25X!8N1^PX*x6~1hy2u1iKlykKs#K@+(m(cOPpO(L1@gCtd6y;z!=fLSg$?;f2U(<}k7K|o+y zHm0ZZ$W7;~jGzwV+<`o++=CR@N9+bw?d)i40^_ z_mKx}Mmuj7X!Qa%TVz#7BGU(1O+e97DCNgdF6LlcWiIznbqr`#by2D6ATS4pqHLG~ zJ0Z`q2#igV>zjc_2;mqf#u2amXbV?Yq+QZbtHD8MpGuq^|WTA;QKP}_e# zCgFN432`xt5D7$*9lI(iS|vNLCZMQE$Z7(zqJcj>EFNg21vC#jR+2Sn za>bplq`yp9mI2!=!!paTE%t3xK(wt{AUVL8(@_CQ0+E#JyQekCRj?#kg)FNevI0V6 zCuNi2bX$<9PMGG!p=-r!tD~Gi>jAm(II^jbkn;{2r*qG%DI-n3D2WCpy#UmS zS*g^xOlSiSOwZ$kSFHMGkS! zmRi97@S|xR*QC+lr;%}|=i#wqyy=jhchFk#)bW%Zy%25+iFkjsxs6_lE#vc=%8CO# zi692r1eslU&s93VEQ(|QXiRz000S5|trFLWUPL|Db3ge!+U5M0p%>PC1cK{!2?wp! zZ&zxU7O6)<2nPTAa^Wi8L2q@ByUnle3+mHGTr$;fm7uU~e(%;{WMQit#R5&1W$3!@ ztXIVg(r>{`(eHY}jRIV~z(xTi?9Eeg!SH(xnCP1j;>-|=3>ur2l1L_S0;M?E+HUs(p#C`YO2US(^-h1!;@!B-jt=`kqgX^xl4ku2W z001Ns32fiK9WTE4BDQbe4pmjLb?a6DKsK92E|LE7A{=q zFlOJreOR<;k#D`njvYfjpU2LfI{^T1zWF9T@rh6Taf0^aAO9F*V`Io>v$*^2yK&oX zx8bFiUP4b#4`f-!=FOX(V+RKZYtCmYd^4tf;R|2D%{SldtTcZ7_;F#q<#O4@7Q--* zNF;pg?ds~n=;&zMrUAggg9jZj(&;qTty_neUU~`R;>6zymmY_AFj_;f28S_wV12{{DVE`Q(!r9v;TK@4j1O z)53~hy;DX@k}- bQOf@V;0h10;I+cb00000NkvXXu0mjf8Cj*i 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 17307adfb6d5daa20e1426b38d1e5fd8f047cd08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6811 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 JPDHLkV1iQC4F&)J 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 3b2f1c3b4f0e92384cdbf3213a047ad398301fdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63505 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 LZCz%0>Bj#7TGc%WEu`||w%dRJyv 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 z

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 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 2545aae4ed5dd285083584da075633e97ba53bde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42652 zcmd43WmJ@HyFZM9bW2Hth=ib^w4k)4($XQNbTNwp zexAMev)8-U{`P)wQ8U+^*BQt0tIMF5iqd%4cd*gW(C}W!NGPMBU5B8dT`R%71%A`} z_`(%@x#plOErwRmOZ^-CaMSF$!gDmV;s~5`Lv-*nmaWWd2Q)PN7Sz9MFqZ-Sqgf&Vd8MifQYe@e2a}qNa1A`&f&FlQ@LigSqRvEr{`Rtjq#OETWKud1R z>m_%diD4!@djm)NSmOI&{YUzI=ox>wyIB>FLpEo_ZaqvkGgs+AVEL?mj~VHmJnf*U zp{7xH;H+`bke638eN=x@KRe~NOw)%v)kn% zd`!=Tq9WT0H zirGLTANJmTm|@sgpy_RfWA=05`nQ3@{7u8!HXD~qHkYPEm^>Vo2&)Pwq0P#4vj7@r z^K(Auaf|F*cGTi^hMtv*dI-zXpNub~BXQvyog%jOR}|@-A0lj~i&~0y5e((5=X3=DhPOl%7soh9-Y$s-EN^ zpPG=sR9~DJ#=@}@lwoumB^l?cnz{+H5y)Tnn9NkD^-WZSHn_}GbiAnvOjTIMeK2{W zzS$TA__@rv%0{m8s4R+~H)LCuc#Y(Hp3(U`^=H{45%oCT*xlt1nQ-3?WtqZKPI6v@ z54hke&WW0z4$n4S%$_q&5FEM`OUTbxJs~qSP_q1L5w`6^5iLvZKjzcBVds_=N0`zl zima_!jMkp`$v0g~;p?-PFrEJsQZZHCfpkssw5k4`bxU2Z(i)lKu_?gi4v$SWfZT@E zY}I?Hh#cf7RXshL7$_al^LS#iRd;0lIhe1*CgSPg5A^cJh0d?!tjhx_mr#~-I^yi^ z54!47x>@pZUwTS)|5?EHFQz13rfhxAkttm>o-u5NH&1dxnP(}u;H;l_RenTDh22@S zuG!#qSucj&6WD1bVHenLyfJAedN%fE;u;3^Ot|3ceV1k|7LH~+7|n9Hz*1}*|LIEl z)1A}&*QFC3kxY}|?MutU<1N}zX;DekMx^*HwsTO60b%}ktAjZ&0(k;Td&Tva3|jiX zgx8LxGN_wUTNA>18Mi39g-9{f?B08+N(p{fz7?9g%MiXcSkJQ> zO=pBCFWC|se;`UaVmVF8O4kl^jkS?MT_p4T8=GkBoM7D+uH*9;Cj#I#Y5~U-1l>3c zoZ)<;uj_x_AA0C4?AJPOG3hjNkoNF)#7kyP1y999ez-#H!zLvagWH465u%sJQWG%I z%N@zNj-$O4q^J1j_KZ1-BuO3P3asI*v>}PN_|%RELmD+9T=kvef^a-{?096f_3K2kMq- zHWz)!b0t2n(|yU^f|e!>y7fK&{a@lCQ>RA>!5+G1P zCxk2z1*>-it0!DHsF*(Kd-(v_C%h+rG*5>dy_lT7NNUicH@<0xX=rZhZWvas%*NL7o<2_#n!NUr#}fiXKvINR-f;7PsY^mXTEXc zKpyBL7twR=erLv<@_Q=K;u03wc83bMpu*L3oc<2)HC z_wOkDN4}kPft_QeW;Mt&A7mllCSv-sTvW4@;@GAq_EF2zy&fKs$v*Ex9(_U!`JQp! z_r?%v!DB&#uZyp*W9VRwnq05u9Ap7E4B8Gj$#P#68qOEFyret67E@VQrk=rk&ZUq1 zxI7u+hG%$5anhcE2`X05sb!LIi+zHN5e-k5_OQFtY{e(n;>k@W!Ojw_8u#qm@84%>NF@=;j7=l^BK97yLmN2mzb!boBgUr1 z@Z5N(zZZ3U(&w`|aDL>2tT>w^Z&;ZZe?txv1O)}0v zeLF`Ofv%s~#s2)N@vhkL+VKUVKkqQVW`+ES!@13rBh23zn3cJxPENjfZnpnnF;D*@ z_Y7>Xl`|o0-B+~fBtFM2bmvz2$cqN>iEUiovyz-%chjqcFS=wf_by)ZKi_WUbmNJ4 z2i26f>#=50BujA1_CtcR2=m3DH>-`hg`&ulzRe((oqHBA?JbIvxzc26@tls z8@PK{mNJBx+o%=u=%CPN@`$tItao<;kzv0#z zKjHHU?9tmx8rLy&d5UnFrm(Me1pT_oV$`3`zps&?=l+}dXuE~rjOYdq!aP0fe8N+L zohjazLHIXf3VC_t@uBPE=gf3d-~GiHeP?a!s?5FszepwD?HdHbGw6&2wE3mIf}2qq zdOHrsohj&%K@dzCS}l4XL4#6sO+rX*@;%&aCNl;CMb*O;k(kJE6{V*`UUMWVwnvjL zDK!gUlLD=d_tH!E7Y_4J{Es3_aluqw)1$+fPHLiM5+jC(=_~h)AaGe8T6dTOuXY8K^t9L z9w!Nxc>?bebf}Pr(%N@0)DsxHqgAx=zp{NEE`vAkiaE=!e-eE5Tghita0l$(;TA7h$op_lrtCr(fx+(EEvLvGwI(K1=P~<8mq8lN?euabMbQ z5Df+i?tQ;WzFs?;P-SN5mh=qf`UmMr1*vy_D5{+cW+x*FWB!G)JNt1^hP~U5TyJH% zFxZP%GjPV{*6(JbH%pgH1v_+J8p=@rkE9fHWg}!o=W7j@c{QLhKAo82hMy)(pSSlp z6(zM2a#z4C-9V%8YBG>bahwgnmMtQmXH09!^S{lX&pApS{R+ePF@|$?QaV*kL`t70J^Vfa_Scva)$!C({qp6a{$*RlbqD1cU(%Q{e8KiVfU|+{-B$`r zZiq7Zx)`qgHGOH->n2Px;!Rz;iJFZi;gzkurm;m47*xLzgs|gZ^;tuzPL6i3xdg85$ldE99YM&8bVBV3W=nNQwY*KioWI`)a2<)`pAc0k6Ai|9=$lkm=X zmu5;nW0WI&q%?-$QD3wqIcV_p!8dybA=b9&Pn&E-O*e^)C`UXEEw@b_J= zXOvINg44^Z+Dvhn$Z$0hh(e$FrbP)D+_r4DDQ+X9m119Q6fQZ=y(Z65f$bcp@Jt|+ zKEuxTf9#HlVIzqz_%T9{dg9#AHYzTJvVPyJVpgJ)3J`GFobWf#)PG~B$bQIpzH&ww zG~sUc9BM?8o*g(0&~0x_=OCJtmRsz5P(>y~y%}SPqKlI_?rA*;wg2t#x}&9kC*?BY z_cdWOx#_M?TJknCSO%-UVS_Ok(1O}s+36ka`i<}Z6pkASVi0AFb?%>^C>EZp?y@&Q zX~>(L;g%ms8RUHYtf3b2I(G}9{jwsp*p4*u#6E2gy+SU7T}dGKbjV2{nCgr<_bZ}S zMy;f`6DXc1iTc#1u%9X+_m#ZAIYp_d?^JxREbE)R+w7+DK1iKb&~-V<#gneX zxi%*LRbPbX0+UEMIy_i`_bD1&*3Hw53ND-7`?2FLjSBHPfU)`wuKl+;1>7jrLICVVaGjIscjsSS71bI2Puh4M$O+szdzw-aT8U5`f>`Nc!WKO35C*DJ7me2IM~ z2FDHwR2LKr`JTQ&Ms75K+kP!n>KEmB^FCU?)}3TcJV|yMHYv-J&U=@Wx>A~ybN0VJ z7>kDnEmRlnT{H7NT)>npd+x=Nq9|6U{7>`Y2;$v&e|zt#o91c@-DOb&L^9>QLSVH? z^y~H(nVV@*+^{gJ<>lypKJKk3fxOT!TGVMnyZSbD2cBBm%L~t>74^jhPnDX-!r|l3 zKizuLeQ=AF*?JpP9c$%@^%>7<{B;I7q&oUT{Cb!&o%MsRlDVA;C0~u1{sG=Hs z-nVftkH5&6LJwb(o#{5_YlOwbm$}ucXmWi|ctelS=HnAVu6ZRddJ=E-a ze_bVS``r(@R0geiOkaAcSQ1;ba06EXG6=4|wxD&2Us}{ixi4G7iF!|8AgMd9Sj=t< zdmOR6!SZ)nD$Ak|0o<$4E(8Jv@ebspG(P@!T>=FVOB9OLS&KWN^0=S8?Yy%hDOe?M z4}Yi;Yuta8AbY3fok|{k*diQv%J#iJ!*JGX_*+FRX_42`7kgL|+OgBYOdNQHFOLC6 zJ3`W(#bDJ;)Th~s(6r;26BYK^(ZyKDL3gC#C*%j?@P6^XjRWk|TNlcw zEYh<2na_}!`**(Y9c8+ZP^$^wM4aWbeg-H1nv1TgSe^*mJ#ZY!WZ3-~5MCY1)$sOk3K$*KWVr^=rjGGs_2nG6P7$BQecn!(oEuA& zyG1M(5D=b!14FEG&pyN1n{9{trjq~6!0kaCF?iRzA~GTf z8CLlkj=KGdi-&ik6F);@N=)6Z81R7hpwp-&DyK4xV*i06;MGJ2Fl3L#q627Gk z$*;bejGC8)$MpBn8~*npq${w78KJg9HYFBn}SpSQ9`Wa+(rP=>u!X`eyd9_-td zEZV*GG2i?NX-YHq4QgrATU%&MT;6yj#gD#y&DFtnwqzYOJHVvTzmDkp}PUg$$K}FmbG=x9D>lORRHI4kQD}4D@MK=3c)YS$P z)AcLBk^DilR&hHc#LryjzJ&O;4feqIg)oCBY?ACG0d8D*UZv`Pq|)T;lR=jfm;oQl zl%c!TzN)5TSHIuCD{P?7p;a;dHt)^7e|{BONft7!dh73xw}>HE-=(dw5<`T2qsBG= zTyg4In%dQOj!zXN^XjPB%ZdrFURc-E_~C|!9&{5(8JjGkCoi}Q_{IQMtffgg9n78p zg6TR+$gP0UdfXjwW`~SV>aVc_IT!PqWLgw?brvuF^V?oPGIK0!xUBX#S&W&qs+b%m zl$pG*rLx0Xf854ZEsig6mr_~}^SLV^_Y-Owx{ILd*Ss08|K8<%r3_!*1RTBqv_O*l zyE0bPyAT_%?n#Hfr`CYQ*kF!g(7HCl4$R=PN%eqbv&?sdRPXpqdmTAVpY*!a(#<(0 z8LdGo6#bWfl<1=FF-`OptCm6$FA2nBvnm?}kfDJTZ@xvn(vYH6fF?Wi3Ty(Q)&T_v zg@DpRWdS4=nWKv>pXs`jrGzUjAVJuzguqNl%8ILU=6b^<0U)iC zr?%?PteG}vJ`p%ipG_TkPSCha%%X%;Nr`tH1}KJpbiUVjwvTDOo-E{2aJCcSv%cos z{h;FUE-&czqd$M-`DI$t3#*aMrkwPcO^_%F&t5o>=(@P=DCy;+fNp(3>SXIvK-xFg z^pVv^G$yadiZ|@MiZ^PeFADn3F3+}HRI=oEW-av>9j+#^p8huE(=EKyJaatscRcXm z6$#(WIWz`egLnalEOPT0UFWz2fR-YR_qVH-lO3dKoW4cW?DYywD)3E0JDl}T`(q|} z5tj3ub^FR(&_IMU!s_C|Nok)5!UhI$>n9H2zq{#6qG*e-X{bx!F$?G7VbP+bk(!`! zo%x`5U%4GzbZw7~#^MQKWMF?HUR8+CX8jm7hK2_Z4t_uifzkQYdMw46 zeX%nT>lh~BpFtC4q|AC=yIH+funA_{g+TPYh#;gw2*@B4+BS8INT0*f6sJOfUgx9T zVh>S0%#J0+vjpRjbh26+sRv;!rolSeZZ5l49HZ^&?`$)xj;J<4rkv?yIb# z{qdzA9_N6G`z22B;0t-H$PZ%3v$%n5r6Z(6S`^%(i=d)~EQ>|;MCWRo7g7kj+KLx( zGHQu8jmtyF6Nk5*WY48BAH`GPq}tLng=jK|I!Uq0Nw6axK^nm*LeKrvqoov~o8bJ3 z*z|9wV00AJpT2F-lLl{A-|ZUv%pMXsY`g!J=(SXaF`a6Vx@VYAmSWQC$XYlQb!MVO z$WcXOgI#HqUb>Nu4n7q zDPK<@8hUOfOb_gO2uxbP^0(3rVA-cdMZS=_&q^>0^He5*2tRWI!t9A{X^VkJ`K+i| zBm!A#)_1rjKxJa=HP7o28?8MlPgj(^N*VVvNWGsYhHNMFBG9HZX@l@<&x^8JymGTQq^>L2 zJLJKLZ+O1TU1Eddh#Qrd&pKeJu~~{^@;qtEy7kQ5LuZF-JX zZk$$DI7_M+*r+=3MV8~CAF32btDcsbo2W)sm4;gH#^-x%)$zqyzmWV>w&if%LCVyT zq!3{{vd<`cGN1)L3TENpJL&zRz zLCxXH)!*shkt?0wdLPSwjkmQ59DZ3O_SX25Quy0{zo>upW3v2jd8WZkP|ekwI|kd^ z=`Oc1^hf)AE**}5VT6MkEat15r;+GSgW> zIA@kbTQTll&2h~p*`&4ZXlhM~I9)nIv7Jhdv6PKdnjNuEnkj-k zW}09%aiw|dgjYs|% z7``s7JB&X&Ul!dOer=Z2*1z~;t?*JYFYVwH-TC*kCIaswnd1)_v@7$d)laJ_V$S3V z(@b#Ifzo;(&sq;C@Ex#x-tt@)3Tf2^?ZtZKSE@Bor(9IB6?G-vsJBge9ZgyT@ms_a zG+h@i&;$mo#H_1--{S*1YQ=g}_o}j!-QN~J4h#lOe8_7jM1TJTmx{MXiLm_m z8y+YHg+ep$Z+Hr`bRRV!FE)2zbVqB)hM@CUSkfskrVDM=4up&Wp>t{jI-q#z{Det{ z?zj=d^At=LBHQ?;#l^wM(V=D8kosmPOUnN2;HQV{cSJmo8QsJ=hO-nBmS%nKlMxPw z;%Zgf_?#i8ZfPr>eDahA4w-3B?5vkUZ4@bjG+I)p_b8{D%ITb#<&$RMEwjSBc`v z67QlW1w6n$ccj%;790pHvw%sieP$4!7^y#e;{3L5ugf0*tXzZ*ojSA>Z_NrmY$V#bMD!JW0geyLC3(}vk)8# z>!qH!FvlEY<_Qe7a=*{ChO(Dr?TwvFJ+)%<~Ibj0w)o zq|;seK&F&lW7tV0@am`oj+8a9Z*76PmU2YEBE7^Z0z8(m^H4TAU8fZl$V1)lb@*al3d#(M|~UOvuluQi@UtpHeG5(*dyUSX_8Vjuw+|PSz`qy{5OA z@NB4K>=|`>WoQv2wSe=y)jTh0OlKQ4O&EmEdDQT-Kqiq>SiRZnrHMA?@6N9wMQ2+L z(*X>&0JwhiH^&>`D;0vmno*k(U)v~*(kTAh9;VA3AvP^DaAwLU?B%#@!G6T|*-Zq{ zD*_zRJu;$@)QyI|zf*fOF9;+C$Qg5(2&7KG%t$5USdvv$JwCZY@xS zE}ciRB{(`P^}OaNkfruFd7fhq*_ETX^(~;%P!*5xqp!vodc}l&2%RL0{%WYq-P$;F z6Nx14wa^7W6|vQ0ZZ+p5V}`@LS5BTcm?{D{Y!h@6g1gTV4Hw#c_6r^Dg6SC`zMx&= z?VRs>_C3z(7MPp!V!X%*y2|gklJ9aR4Gw(`L9-P6^nf+V!uR77b}hG(r)9;4a+YXJ z+=<^S(XlYAj3b^7!yXw?B-e^Xz9xmq$nl?=t6GsXQ}Z?puoj2g{$ST~;6-C;N^+U1 zV)!nQW3O!bm{k!+O~mLn9#UINv6lDk?cg+5$1v?5qk=w{=L?;Dt%{rF;kG8)&0@uk z_aDu+el2LFvl|P1Zfzk7T>Fr=179!=A%2wK$WNPol&p=Jr8p6Y92?AwlGCAx!nr#S zJf02b@80-K0#SqUsYr|L1SuWQ4==T-m*rIUD8uj2vd@bAD0$$!zi?8w<<*Diu0P(- z7TuVahYlD2dpm&I^c}g3uLYB2XL&5e8=Gxr2&#tNyl6HUlj#A3wioJJ6W4( z=}Ga>y~5w=F)I?h~DgD7I#B5`Wf`>S*EXc#`7*u>2oQO zRL$?yQt>~4v1G#o3J)*W=j??lv9!d|0>QK=`89r-bGhfl;%-X;pV?|CoOs~kZao6L za4i$#0uJ*fIk_0rp#c*kKoyJ8ope*m0mM5zeG-ZFhqC6l6M}{Zi-|Ws<(89+x$iEB zGDflTPjnVJNxTc1WcA3$1>hv91%)LD^Pe(m#l0&B9Fc>#z4Mx(1q%tS$XrI#z-4x# zeK5IXbU9nNc2cP9xJq4U>|;dxj08;PKEaC}$A?Or*?N4pQ!M96JbQ0R+}P8ibDg=? z(2Ka?dd%#7fdB0{kc4|rr^>6dGs(4?mEgG?Z9WhBH4hoskA$Cy&^03Q#5$gD_MJ>m zvZI`&N-4|4ZYOyijwxIiniw;kT`xAO&@ir94IHlAWXJ^dS9s4&CQ7#vbL#!I8|R*y zwA>{E5H;SVF78}U%B=1vU3ZQJ3HA(2e$a7iZRZ(ru63&%p_;e}I4zdS||NN+5 z>Eiyl7V?Hs=A>7jR2PgY81$NM&)@$uR1(xsTl8Ak{<+hes5|}q_YGcGZ;*jvqmXZ) zNE>SW{{Zw9BUBF~hCF!aacEi5jLm*`-u(+oQUj-UORl}F8HcNOX73LI{`2V{P@le^ zV=npdL1_>76(Bx4hKX+8Q%YVZ$GPLA*ml4?`8EcH{#UQ2$BR>o z$mrY$ggYhyw*8%@lv1x*ZEb8p0s*P7u(SS{`fs6Ya@9kesG$yC9fWErXful0^G-Sr zw4YA8OmCo)7U*-EHCvk~b34Tw*vC#R$*ySxbRwRWH2`BxfL^>Bt?T@n>x~TT&|$gP zX|1rKUa0~^Q>p;{?hl}I{Po-#=r>m1902L6PI4Y+)xI|iGAngdf{s75y$*&Z2Xj

t9%!`Rn1R|rO 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
2|)7r88DLi}e;-kqq(@gRIGY!Q-ti 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 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