From 2a625969993c1d4c0446fa2916d268e58d23a4bb Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 Feb 2024 15:08:37 +0800 Subject: [PATCH] refactor<*>: rewrite all with AvaloniaUI --- .gitignore | 1 - README.md | 5 +- src/App.axaml | 25 + src/App.axaml.cs | 158 ++ src/App.manifest | 24 +- src/App.xaml | 15 - src/App.xaml.cs | 99 -- src/Avalonia.Controls.DataGrid.patch | 81 + src/BuildWindows.bat | 1 + src/Commands/Add.cs | 33 +- src/Commands/Apply.cs | 9 +- src/Commands/Archive.cs | 23 +- src/Commands/AssumeUnchanged.cs | 30 +- src/Commands/Blame.cs | 97 +- src/Commands/Branch.cs | 53 +- src/Commands/Checkout.cs | 20 +- src/Commands/CherryPick.cs | 9 +- src/Commands/Clean.cs | 12 +- src/Commands/Clone.cs | 30 +- src/Commands/Command.cs | 129 +- src/Commands/Commit.cs | 11 +- src/Commands/CommitChanges.cs | 39 - src/Commands/CommitRangeChanges.cs | 39 - src/Commands/CompareRevisions.cs | 38 + src/Commands/Config.cs | 51 +- src/Commands/Diff.cs | 146 +- src/Commands/Discard.cs | 19 +- src/Commands/Fetch.cs | 77 +- src/Commands/FormatPatch.cs | 13 +- src/Commands/GC.cs | 20 +- src/Commands/GetRepositoryRootPath.cs | 17 - src/Commands/GitFlow.cs | 34 +- src/Commands/Init.cs | 12 +- src/Commands/IsBinary.cs | 18 + src/Commands/IsBinaryFile.cs | 18 - src/Commands/IsLFSFiltered.cs | 15 + src/Commands/LFS.cs | 39 +- src/Commands/LocalChanges.cs | 67 - src/Commands/Merge.cs | 22 +- src/Commands/MergeTool.cs | 41 + src/Commands/Pull.cs | 38 +- src/Commands/Push.cs | 31 +- .../{Branches.cs => QueryBranches.cs} | 25 +- src/Commands/QueryCommitChanges.cs | 38 + src/Commands/{Commits.cs => QueryCommits.cs} | 19 +- src/Commands/QueryFileContent.cs | 27 +- src/Commands/QueryFileSize.cs | 29 + src/Commands/QueryFileSizeChange.cs | 50 - src/Commands/QueryGitDir.cs | 13 +- src/Commands/QueryLFSObject.cs | 28 - src/Commands/QueryLFSObjectChange.cs | 41 - src/Commands/QueryLocalChanges.cs | 66 + src/Commands/{Remotes.cs => QueryRemotes.cs} | 23 +- src/Commands/QueryRepositoryRootPath.cs | 15 + ...sionObjects.cs => QueryRevisionObjects.cs} | 14 +- src/Commands/QueryStashChanges.cs | 37 + src/Commands/{Stashes.cs => QueryStashes.cs} | 39 +- src/Commands/QuerySubmodules.cs | 27 + src/Commands/QueryTags.cs | 34 + src/Commands/Rebase.cs | 9 +- src/Commands/Remote.cs | 9 +- src/Commands/Reset.cs | 31 +- src/Commands/Revert.cs | 9 +- src/Commands/Reword.cs | 16 - src/Commands/SaveChangesAsPatch.cs | 43 + src/Commands/SaveChangesToPatch.cs | 26 - src/Commands/SaveRevisionFile.cs | 74 +- src/Commands/Stash.cs | 94 +- src/Commands/StashChanges.cs | 38 - src/Commands/SubTree.cs | 43 - src/Commands/Submodule.cs | 33 +- src/Commands/Submodules.cs | 28 - src/Commands/Tag.cs | 39 +- src/Commands/Tags.cs | 37 - src/Commands/Version.cs | 22 +- src/Converters/BookmarkConverters.cs | 12 + src/Converters/BoolConverters.cs | 8 + src/Converters/BranchConverters.cs | 8 + src/Converters/ChangeViewModeConverters.cs | 28 + src/Converters/DecoratorTypeConverters.cs | 34 + src/Converters/IntConverters.cs | 27 + src/Converters/LauncherPageConverters.cs | 28 + src/Converters/ListConverters.cs | 12 + src/Converters/PathConverters.cs | 18 + src/Converters/StringConverters.cs | 56 + src/Converters/WindowStateConverters.cs | 36 + src/FodyWeavers.xml | 3 - src/FodyWeavers.xsd | 141 -- src/Models/ApplyWhiteSpaceMode.cs | 13 + src/Models/AvatarManager.cs | 120 ++ src/Models/Blame.cs | 17 + src/Models/BlameLine.cs | 12 - src/Models/Bookmarks.cs | 22 + src/Models/Branch.cs | 5 +- src/Models/BranchTreeNode.cs | 152 ++ src/Models/CRLFMode.cs | 21 + src/Models/CRLFOption.cs | 25 - src/Models/Change.cs | 59 +- src/Models/Commit.cs | 26 +- src/Models/CommitGraph.cs | 205 +++ src/Models/CrashInfo.cs | 31 - src/Models/Decorator.cs | 16 +- src/Models/DiffOption.cs | 95 ++ src/Models/DiffResult.cs | 56 + src/Models/ExecutableFinder.cs | 26 - src/Models/ExternalMergeTools.cs | 36 + src/Models/FileSizeChange.cs | 9 - src/Models/GitFlow.cs | 8 +- src/Models/InstalledFonts.cs | 42 - src/Models/LFSChange.cs | 10 - src/Models/LFSObject.cs | 9 +- src/Models/Locale.cs | 33 - src/Models/Locales.cs | 18 + src/Models/MergeOption.cs | 25 - src/Models/MergeTool.cs | 134 -- src/Models/Notification.cs | 10 + src/Models/Object.cs | 8 +- src/Models/Preference.cs | 297 ---- src/Models/Remote.cs | 29 +- src/Models/Repository.cs | 146 -- src/Models/ResetMode.cs | 27 - src/Models/RevisionFile.cs | 17 + src/Models/Stash.cs | 7 +- src/Models/StatisticSample.cs | 19 - src/Models/SubTree.cs | 10 - src/Models/Tag.cs | 6 +- src/Models/TextChanges.cs | 61 - .../{TextCompare.cs => TextInlineChange.cs} | 71 +- src/Models/TextLine.cs | 9 - src/Models/Theme.cs | 38 - src/Models/User.cs | 9 +- src/Models/Watcher.cs | 359 ++--- src/Models/WhitespaceOption.cs | 25 - src/Native/MacOS.cs | 45 + src/Native/OS.cs | 79 + src/Native/Windows.cs | 105 ++ src/Resources/Controls.xaml | 25 - src/Resources/Converters.xaml | 14 - src/Resources/Fonts.axaml | 6 + src/Resources/Fonts/JetBrainsMono-Bold.ttf | Bin 0 -> 277828 bytes src/Resources/Fonts/JetBrainsMono-Italic.ttf | Bin 0 -> 276840 bytes src/Resources/Fonts/JetBrainsMono-Regular.ttf | Bin 0 -> 273900 bytes src/Resources/Icons.axaml | 84 ++ src/Resources/Icons.xaml | 76 - src/Resources/Locales/en_US.axaml | 523 +++++++ src/Resources/Locales/en_US.xaml | 549 ------- src/Resources/Locales/zh_CN.axaml | 522 +++++++ src/Resources/Locales/zh_CN.xaml | 548 ------- src/Resources/Styles.axaml | 897 +++++++++++ src/Resources/Styles/Button.xaml | 44 - src/Resources/Styles/CheckBox.xaml | 42 - src/Resources/Styles/ComboBox.xaml | 81 - src/Resources/Styles/ContextMenu.xaml | 113 -- src/Resources/Styles/DataGrid.xaml | 57 - src/Resources/Styles/HyperLink.xaml | 12 - src/Resources/Styles/IconButton.xaml | 46 - src/Resources/Styles/ListBox.xaml | 26 - src/Resources/Styles/ListView.xaml | 111 -- src/Resources/Styles/Path.xaml | 9 - src/Resources/Styles/RadioButton.xaml | 52 - src/Resources/Styles/ScrollBar.xaml | 86 -- src/Resources/Styles/ScrollViewer.xaml | 52 - src/Resources/Styles/Slider.xaml | 125 -- src/Resources/Styles/TabControl.xaml | 116 -- src/Resources/Styles/TextBlock.xaml | 53 - src/Resources/Styles/TextBox.xaml | 107 -- src/Resources/Styles/ToggleButton.xaml | 183 --- src/Resources/Styles/Tooltip.xaml | 22 - src/Resources/Styles/Tree.xaml | 135 -- src/Resources/Styles/Window.xaml | 40 - src/Resources/Themes.axaml | 95 ++ src/Resources/Themes/Dark.xaml | 30 - src/Resources/Themes/Light.xaml | 30 - src/SourceGit.csproj | 64 +- src/SourceGit.sln | 25 + src/ViewModels/AddRemote.cs | 82 ++ src/ViewModels/AddSubmodule.cs | 61 + src/ViewModels/Apply.cs | 65 + src/ViewModels/Archive.cs | 56 + src/ViewModels/AssumeUnchangedManager.cs | 30 + src/ViewModels/Blame.cs | 48 + src/ViewModels/Checkout.cs | 28 + src/ViewModels/CherryPick.cs | 34 + src/ViewModels/Cleanup.cs | 30 + src/ViewModels/ClearStashes.cs | 21 + src/ViewModels/Clone.cs | 111 ++ src/ViewModels/CommitDetail.cs | 408 +++++ src/ViewModels/CreateBranch.cs | 111 ++ src/ViewModels/CreateGroup.cs | 32 + src/ViewModels/CreateTag.cs | 64 + src/ViewModels/DeleteBranch.cs | 32 + src/ViewModels/DeleteRemote.cs | 28 + src/ViewModels/DeleteRepositoryNode.cs | 22 + src/ViewModels/DeleteSubmodule.cs | 28 + src/ViewModels/DeleteTag.cs | 35 + src/ViewModels/DiffContext.cs | 105 ++ src/ViewModels/Discard.cs | 50 + src/ViewModels/DropStash.cs | 22 + src/ViewModels/EditRemote.cs | 100 ++ src/ViewModels/EditRepositoryNode.cs | 55 + src/ViewModels/FastForwardWithoutCheckout.cs | 34 + src/ViewModels/Fetch.cs | 54 + src/ViewModels/FileHistories.cs | 66 + src/ViewModels/FileTreeNode.cs | 170 +++ src/ViewModels/GitFlowFinish.cs | 48 + src/ViewModels/GitFlowStart.cs | 67 + src/ViewModels/Histories.cs | 473 ++++++ src/ViewModels/Init.cs | 41 + src/ViewModels/InitGitFlow.cs | 96 ++ src/ViewModels/Launcher.cs | 154 ++ src/ViewModels/LauncherPage.cs | 53 + src/ViewModels/Merge.cs | 64 + src/ViewModels/Popup.cs | 51 + src/ViewModels/PopupHost.cs | 75 + src/ViewModels/Preference.cs | 258 ++++ src/ViewModels/PruneRemote.cs | 28 + src/ViewModels/Pull.cs | 129 ++ src/ViewModels/Push.cs | 174 +++ src/ViewModels/PushTag.cs | 39 + src/ViewModels/Rebase.cs | 50 + src/ViewModels/RenameBranch.cs | 53 + src/ViewModels/Repository.cs | 1082 ++++++++++++++ src/ViewModels/RepositoryConfigure.cs | 69 + src/ViewModels/RepositoryNode.cs | 73 + src/ViewModels/Reset.cs | 66 + src/ViewModels/Revert.cs | 34 + src/ViewModels/RevisionCompare.cs | 172 +++ src/ViewModels/Reword.cs | 39 + src/ViewModels/Squash.cs | 43 + src/ViewModels/StashChanges.cs | 55 + src/ViewModels/StashesPage.cs | 105 ++ src/ViewModels/TwoSideTextDiff.cs | 52 + src/ViewModels/Welcome.cs | 102 ++ src/ViewModels/WorkingCopy.cs | 600 ++++++++ src/Views/About.axaml | 87 ++ src/Views/About.axaml.cs | 39 + src/Views/About.xaml | 123 -- src/Views/About.xaml.cs | 48 - src/Views/AddRemote.axaml | 57 + src/Views/AddRemote.axaml.cs | 22 + src/Views/AddSubmodule.axaml | 43 + src/Views/AddSubmodule.axaml.cs | 9 + src/Views/Apply.axaml | 58 + src/Views/Apply.axaml.cs | 24 + src/Views/Archive.axaml | 63 + src/Views/Archive.axaml.cs | 22 + src/Views/AssumeUnchanged.xaml | 100 -- src/Views/AssumeUnchanged.xaml.cs | 64 - src/Views/AssumeUnchangedManager.axaml | 105 ++ src/Views/AssumeUnchangedManager.axaml.cs | 14 + src/Views/Avatar.cs | 122 ++ src/Views/Blame.axaml | 76 + src/Views/Blame.axaml.cs | 261 ++++ src/Views/Blame.xaml | 192 --- src/Views/Blame.xaml.cs | 173 --- src/Views/CaptionButtons.axaml | 23 + src/Views/CaptionButtons.axaml.cs | 33 + src/Views/CaptionButtonsMacOS.axaml | 32 + src/Views/CaptionButtonsMacOS.axaml.cs | 33 + src/Views/ChangeStatusIcon.cs | 112 ++ src/Views/ChangeViewModeSwitcher.axaml | 35 + src/Views/ChangeViewModeSwitcher.axaml.cs | 23 + src/Views/Checkout.axaml | 19 + src/Views/Checkout.axaml.cs | 9 + src/Views/CherryPick.axaml | 32 + src/Views/CherryPick.axaml.cs | 9 + src/Views/Cleanup.axaml | 19 + src/Views/Cleanup.axaml.cs | 9 + src/Views/ClearStashes.axaml | 18 + src/Views/ClearStashes.axaml.cs | 9 + src/Views/Clone.axaml | 54 + src/Views/Clone.axaml.cs | 33 + src/Views/Clone.xaml | 261 ---- src/Views/Clone.xaml.cs | 131 -- src/Views/CommitBaseInfo.axaml | 115 ++ src/Views/CommitBaseInfo.axaml.cs | 17 + src/Views/CommitChanges.axaml | 183 +++ src/Views/CommitChanges.axaml.cs | 39 + src/Views/CommitDetail.axaml | 76 + src/Views/CommitDetail.axaml.cs | 28 + src/Views/ConfirmDialog.xaml | 78 - src/Views/ConfirmDialog.xaml.cs | 49 - src/Views/Controls/Avatar.cs | 250 ---- src/Views/Controls/Badge.cs | 52 - src/Views/Controls/ChangeDisplaySwitcher.cs | 105 -- src/Views/Controls/ChangeStatusIcon.cs | 106 -- src/Views/Controls/Chart.cs | 180 --- src/Views/Controls/CommitGraph.cs | 341 ----- src/Views/Controls/HighlightableTextBlock.cs | 85 -- src/Views/Controls/IconButton.cs | 46 - src/Views/Controls/Loading.cs | 51 - src/Views/Controls/PageContainer.cs | 66 - src/Views/Controls/PopupWidget.cs | 102 -- src/Views/Controls/TextEdit.cs | 92 -- src/Views/Controls/Tree.cs | 246 ---- src/Views/Controls/TreeItem.cs | 40 - src/Views/Controls/Window.cs | 97 -- src/Views/Converters/BoolToCollapsed.cs | 18 - src/Views/Converters/BranchToName.cs | 19 - src/Views/Converters/FontFamilyName.cs | 20 - src/Views/Converters/IntToBookmarkBrush.cs | 40 - src/Views/Converters/InverseBool.cs | 17 - src/Views/Converters/PureFileName.cs | 21 - src/Views/Converters/PureFolderName.cs | 21 - .../Converters/WindowStateToTitleBarHeight.cs | 22 - src/Views/CreateBranch.axaml | 74 + src/Views/CreateBranch.axaml.cs | 9 + src/Views/CreateGroup.axaml | 17 + src/Views/CreateGroup.axaml.cs | 9 + src/Views/CreateTag.axaml | 62 + src/Views/CreateTag.axaml.cs | 9 + src/Views/DeleteBranch.axaml | 20 + src/Views/DeleteBranch.axaml.cs | 9 + src/Views/DeleteRemote.axaml | 19 + src/Views/DeleteRemote.axaml.cs | 9 + src/Views/DeleteRepositoryNode.axaml | 44 + src/Views/DeleteRepositoryNode.axaml.cs | 9 + src/Views/DeleteSubmodule.axaml | 26 + src/Views/DeleteSubmodule.axaml.cs | 9 + src/Views/DeleteTag.axaml | 29 + src/Views/DeleteTag.axaml.cs | 9 + src/Views/DiffView.axaml | 120 ++ src/Views/DiffView.axaml.cs | 9 + src/Views/Discard.axaml | 45 + src/Views/Discard.axaml.cs | 9 + src/Views/DropStash.axaml | 29 + src/Views/DropStash.axaml.cs | 9 + src/Views/EditRemote.axaml | 57 + src/Views/EditRemote.axaml.cs | 22 + src/Views/EditRepositoryDisplayName.xaml | 77 - src/Views/EditRepositoryDisplayName.xaml.cs | 35 - src/Views/EditRepositoryNode.axaml | 54 + src/Views/EditRepositoryNode.axaml.cs | 10 + src/Views/Extensions.cs | 12 + src/Views/FastForwardWithoutCheckout.axaml | 22 + src/Views/FastForwardWithoutCheckout.axaml.cs | 9 + src/Views/Fetch.axaml | 45 + src/Views/Fetch.axaml.cs | 9 + src/Views/FileHistories.axaml | 113 ++ src/Views/FileHistories.axaml.cs | 19 + src/Views/FileHistories.xaml | 124 -- src/Views/FileHistories.xaml.cs | 65 - src/Views/GitFlowFinish.axaml | 39 + src/Views/GitFlowFinish.axaml.cs | 9 + src/Views/GitFlowStart.axaml | 37 + src/Views/GitFlowStart.axaml.cs | 9 + src/Views/Histories.axaml | 177 +++ src/Views/Histories.axaml.cs | 222 +++ src/Views/Init.axaml | 25 + src/Views/Init.axaml.cs | 10 + src/Views/InitGitFlow.axaml | 83 ++ src/Views/InitGitFlow.axaml.cs | 9 + src/Views/Launcher.axaml | 342 +++++ src/Views/Launcher.axaml.cs | 174 +++ src/Views/Launcher.xaml | 74 - src/Views/Launcher.xaml.cs | 230 --- src/Views/Merge.axaml | 52 + src/Views/Merge.axaml.cs | 9 + src/Views/Popups/AddSubTree.xaml | 86 -- src/Views/Popups/AddSubTree.xaml.cs | 57 - src/Views/Popups/AddSubmodule.xaml | 69 - src/Views/Popups/AddSubmodule.xaml.cs | 40 - src/Views/Popups/Apply.xaml | 89 -- src/Views/Popups/Apply.xaml.cs | 50 - src/Views/Popups/Archive.xaml | 72 - src/Views/Popups/Archive.xaml.cs | 83 -- src/Views/Popups/Checkout.xaml | 10 - src/Views/Popups/Checkout.xaml.cs | 34 - src/Views/Popups/CherryPick.xaml | 44 - src/Views/Popups/CherryPick.xaml.cs | 36 - src/Views/Popups/Cleanup.xaml | 12 - src/Views/Popups/Cleanup.xaml.cs | 34 - src/Views/Popups/Configure.xaml | 78 - src/Views/Popups/Configure.xaml.cs | 52 - src/Views/Popups/CreateBranch.xaml | 76 - src/Views/Popups/CreateBranch.xaml.cs | 91 -- src/Views/Popups/CreateTag.xaml | 71 - src/Views/Popups/CreateTag.xaml.cs | 57 - src/Views/Popups/DeleteBranch.xaml | 33 - src/Views/Popups/DeleteBranch.xaml.cs | 53 - src/Views/Popups/DeleteRemote.xaml | 33 - src/Views/Popups/DeleteRemote.xaml.cs | 33 - src/Views/Popups/DeleteSubmodule.xaml | 33 - src/Views/Popups/DeleteSubmodule.xaml.cs | 33 - src/Views/Popups/DeleteTag.xaml | 41 - src/Views/Popups/DeleteTag.xaml.cs | 35 - src/Views/Popups/Discard.xaml | 39 - src/Views/Popups/Discard.xaml.cs | 47 - src/Views/Popups/EditSubTree.xaml | 54 - src/Views/Popups/EditSubTree.xaml.cs | 34 - .../Popups/FastForwardWithoutCheckout.xaml | 10 - .../Popups/FastForwardWithoutCheckout.xaml.cs | 46 - src/Views/Popups/Fetch.xaml | 52 - src/Views/Popups/Fetch.xaml.cs | 49 - src/Views/Popups/GitFlowFinish.xaml | 41 - src/Views/Popups/GitFlowFinish.xaml.cs | 59 - src/Views/Popups/GitFlowStart.xaml | 41 - src/Views/Popups/GitFlowStart.xaml.cs | 62 - src/Views/Popups/Init.xaml | 43 - src/Views/Popups/Init.xaml.cs | 33 - src/Views/Popups/InitGitFlow.xaml | 102 -- src/Views/Popups/InitGitFlow.xaml.cs | 50 - src/Views/Popups/Merge.xaml | 71 - src/Views/Popups/Merge.xaml.cs | 35 - src/Views/Popups/Prune.xaml | 10 - src/Views/Popups/Prune.xaml.cs | 30 - src/Views/Popups/Pull.xaml | 87 -- src/Views/Popups/Pull.xaml.cs | 72 - src/Views/Popups/Push.xaml | 96 -- src/Views/Popups/Push.xaml.cs | 108 -- src/Views/Popups/PushTag.xaml | 53 - src/Views/Popups/PushTag.xaml.cs | 38 - src/Views/Popups/Rebase.xaml | 57 - src/Views/Popups/Rebase.xaml.cs | 50 - src/Views/Popups/Remote.xaml | 90 -- src/Views/Popups/Remote.xaml.cs | 104 -- src/Views/Popups/RenameBranch.xaml | 55 - src/Views/Popups/RenameBranch.xaml.cs | 40 - src/Views/Popups/Reset.xaml | 81 - src/Views/Popups/Reset.xaml.cs | 36 - src/Views/Popups/Revert.xaml | 44 - src/Views/Popups/Revert.xaml.cs | 36 - src/Views/Popups/Reword.xaml | 62 - src/Views/Popups/Reword.xaml.cs | 42 - src/Views/Popups/Squash.xaml | 79 - src/Views/Popups/Squash.xaml.cs | 43 - src/Views/Popups/Stash.xaml | 38 - src/Views/Popups/Stash.xaml.cs | 52 - src/Views/Popups/StashDropConfirm.xaml | 33 - src/Views/Popups/StashDropConfirm.xaml.cs | 33 - src/Views/Popups/SubTreePull.xaml | 76 - src/Views/Popups/SubTreePull.xaml.cs | 43 - src/Views/Popups/SubTreePush.xaml | 68 - src/Views/Popups/SubTreePush.xaml.cs | 41 - src/Views/Popups/UnlinkSubTree.xaml | 40 - src/Views/Popups/UnlinkSubTree.xaml.cs | 34 - src/Views/Preference.axaml | 312 ++++ src/Views/Preference.axaml.cs | 135 ++ src/Views/Preference.xaml | 433 ------ src/Views/Preference.xaml.cs | 173 --- src/Views/PruneRemote.axaml | 19 + src/Views/PruneRemote.axaml.cs | 9 + src/Views/Pull.axaml | 74 + src/Views/Pull.axaml.cs | 9 + src/Views/Push.axaml | 84 ++ src/Views/Push.axaml.cs | 9 + src/Views/PushTag.axaml | 46 + src/Views/PushTag.axaml.cs | 9 + src/Views/Rebase.axaml | 53 + src/Views/Rebase.axaml.cs | 9 + src/Views/RenameBranch.axaml | 37 + src/Views/RenameBranch.axaml.cs | 9 + src/Views/Repository.axaml | 453 ++++++ src/Views/Repository.axaml.cs | 193 +++ src/Views/RepositoryConfigure.axaml | 67 + src/Views/RepositoryConfigure.axaml.cs | 9 + src/Views/Reset.axaml | 55 + src/Views/Reset.axaml.cs | 9 + src/Views/Revert.axaml | 32 + src/Views/Revert.axaml.cs | 9 + src/Views/RevisionCompare.axaml | 225 +++ src/Views/RevisionCompare.axaml.cs | 49 + src/Views/RevisionFiles.axaml | 119 ++ src/Views/RevisionFiles.axaml.cs | 124 ++ src/Views/Reword.axaml | 36 + src/Views/Reword.axaml.cs | 9 + src/Views/Squash.axaml | 46 + src/Views/Squash.axaml.cs | 9 + src/Views/StashChanges.axaml | 27 + src/Views/StashChanges.axaml.cs | 9 + src/Views/StashesPage.axaml | 151 ++ src/Views/StashesPage.axaml.cs | 16 + src/Views/Statistics.xaml | 67 - src/Views/Statistics.xaml.cs | 133 -- src/Views/TextDiffView.axaml | 56 + src/Views/TextDiffView.axaml.cs | 628 ++++++++ src/Views/Validations/ArchiveFile.cs | 12 - src/Views/Validations/BranchName.cs | 28 - src/Views/Validations/CloneDir.cs | 13 - src/Views/Validations/CommitMessage.cs | 13 - src/Views/Validations/GitURL.cs | 35 - src/Views/Validations/LocalRepositoryName.cs | 16 - src/Views/Validations/PatchFile.cs | 13 - src/Views/Validations/RelativePath.cs | 16 - src/Views/Validations/RemoteName.cs | 31 - src/Views/Validations/Required.cs | 13 - src/Views/Validations/TagName.cs | 26 - src/Views/Welcome.axaml | 158 ++ src/Views/Welcome.axaml.cs | 210 +++ src/Views/Widgets/Bookmark.xaml | 13 - src/Views/Widgets/Bookmark.xaml.cs | 33 - src/Views/Widgets/CommitChanges.xaml | 190 --- src/Views/Widgets/CommitChanges.xaml.cs | 367 ----- src/Views/Widgets/CommitDetail.xaml | 237 --- src/Views/Widgets/CommitDetail.xaml.cs | 219 --- src/Views/Widgets/Dashboard.xaml | 604 -------- src/Views/Widgets/Dashboard.xaml.cs | 1309 ----------------- src/Views/Widgets/DiffViewer.xaml | 148 -- src/Views/Widgets/DiffViewer.xaml.cs | 631 -------- src/Views/Widgets/Exceptions.xaml | 58 - src/Views/Widgets/Exceptions.xaml.cs | 46 - src/Views/Widgets/Histories.xaml | 200 --- src/Views/Widgets/Histories.xaml.cs | 637 -------- src/Views/Widgets/PageTabBar.xaml | 153 -- src/Views/Widgets/PageTabBar.xaml.cs | 412 ------ src/Views/Widgets/RevisionCompare.xaml | 104 -- src/Views/Widgets/RevisionCompare.xaml.cs | 40 - src/Views/Widgets/RevisionFiles.xaml | 146 -- src/Views/Widgets/RevisionFiles.xaml.cs | 355 ----- src/Views/Widgets/Stashes.xaml | 157 -- src/Views/Widgets/Stashes.xaml.cs | 110 -- src/Views/Widgets/StatisticsPage.xaml | 74 - src/Views/Widgets/StatisticsPage.xaml.cs | 24 - src/Views/Widgets/Welcome.xaml | 170 --- src/Views/Widgets/Welcome.xaml.cs | 292 ---- src/Views/Widgets/WorkingCopy.xaml | 267 ---- src/Views/Widgets/WorkingCopy.xaml.cs | 398 ----- src/Views/Widgets/WorkingCopyChanges.xaml | 137 -- src/Views/Widgets/WorkingCopyChanges.xaml.cs | 1045 ------------- src/Views/WorkingCopy.axaml | 400 +++++ src/Views/WorkingCopy.axaml.cs | 388 +++++ 521 files changed, 19780 insertions(+), 23244 deletions(-) create mode 100644 src/App.axaml create mode 100644 src/App.axaml.cs delete mode 100644 src/App.xaml delete mode 100644 src/App.xaml.cs create mode 100644 src/Avalonia.Controls.DataGrid.patch create mode 100644 src/BuildWindows.bat delete mode 100644 src/Commands/CommitChanges.cs delete mode 100644 src/Commands/CommitRangeChanges.cs create mode 100644 src/Commands/CompareRevisions.cs delete mode 100644 src/Commands/GetRepositoryRootPath.cs create mode 100644 src/Commands/IsBinary.cs delete mode 100644 src/Commands/IsBinaryFile.cs create mode 100644 src/Commands/IsLFSFiltered.cs delete mode 100644 src/Commands/LocalChanges.cs create mode 100644 src/Commands/MergeTool.cs rename src/Commands/{Branches.cs => QueryBranches.cs} (80%) create mode 100644 src/Commands/QueryCommitChanges.cs rename src/Commands/{Commits.cs => QueryCommits.cs} (92%) create mode 100644 src/Commands/QueryFileSize.cs delete mode 100644 src/Commands/QueryFileSizeChange.cs delete mode 100644 src/Commands/QueryLFSObject.cs delete mode 100644 src/Commands/QueryLFSObjectChange.cs create mode 100644 src/Commands/QueryLocalChanges.cs rename src/Commands/{Remotes.cs => QueryRemotes.cs} (54%) create mode 100644 src/Commands/QueryRepositoryRootPath.cs rename src/Commands/{RevisionObjects.cs => QueryRevisionObjects.cs} (77%) create mode 100644 src/Commands/QueryStashChanges.cs rename src/Commands/{Stashes.cs => QueryStashes.cs} (52%) create mode 100644 src/Commands/QuerySubmodules.cs create mode 100644 src/Commands/QueryTags.cs delete mode 100644 src/Commands/Reword.cs create mode 100644 src/Commands/SaveChangesAsPatch.cs delete mode 100644 src/Commands/SaveChangesToPatch.cs delete mode 100644 src/Commands/StashChanges.cs delete mode 100644 src/Commands/SubTree.cs delete mode 100644 src/Commands/Submodules.cs delete mode 100644 src/Commands/Tags.cs create mode 100644 src/Converters/BookmarkConverters.cs create mode 100644 src/Converters/BoolConverters.cs create mode 100644 src/Converters/BranchConverters.cs create mode 100644 src/Converters/ChangeViewModeConverters.cs create mode 100644 src/Converters/DecoratorTypeConverters.cs create mode 100644 src/Converters/IntConverters.cs create mode 100644 src/Converters/LauncherPageConverters.cs create mode 100644 src/Converters/ListConverters.cs create mode 100644 src/Converters/PathConverters.cs create mode 100644 src/Converters/StringConverters.cs create mode 100644 src/Converters/WindowStateConverters.cs delete mode 100644 src/FodyWeavers.xml delete mode 100644 src/FodyWeavers.xsd create mode 100644 src/Models/ApplyWhiteSpaceMode.cs create mode 100644 src/Models/AvatarManager.cs create mode 100644 src/Models/Blame.cs delete mode 100644 src/Models/BlameLine.cs create mode 100644 src/Models/Bookmarks.cs create mode 100644 src/Models/BranchTreeNode.cs create mode 100644 src/Models/CRLFMode.cs delete mode 100644 src/Models/CRLFOption.cs create mode 100644 src/Models/CommitGraph.cs delete mode 100644 src/Models/CrashInfo.cs create mode 100644 src/Models/DiffOption.cs create mode 100644 src/Models/DiffResult.cs delete mode 100644 src/Models/ExecutableFinder.cs create mode 100644 src/Models/ExternalMergeTools.cs delete mode 100644 src/Models/FileSizeChange.cs delete mode 100644 src/Models/InstalledFonts.cs delete mode 100644 src/Models/LFSChange.cs delete mode 100644 src/Models/Locale.cs create mode 100644 src/Models/Locales.cs delete mode 100644 src/Models/MergeOption.cs delete mode 100644 src/Models/MergeTool.cs create mode 100644 src/Models/Notification.cs delete mode 100644 src/Models/Preference.cs delete mode 100644 src/Models/Repository.cs delete mode 100644 src/Models/ResetMode.cs create mode 100644 src/Models/RevisionFile.cs delete mode 100644 src/Models/StatisticSample.cs delete mode 100644 src/Models/SubTree.cs delete mode 100644 src/Models/TextChanges.cs rename src/Models/{TextCompare.cs => TextInlineChange.cs} (85%) delete mode 100644 src/Models/TextLine.cs delete mode 100644 src/Models/Theme.cs delete mode 100644 src/Models/WhitespaceOption.cs create mode 100644 src/Native/MacOS.cs create mode 100644 src/Native/OS.cs create mode 100644 src/Native/Windows.cs delete mode 100644 src/Resources/Controls.xaml delete mode 100644 src/Resources/Converters.xaml create mode 100644 src/Resources/Fonts.axaml create mode 100644 src/Resources/Fonts/JetBrainsMono-Bold.ttf create mode 100644 src/Resources/Fonts/JetBrainsMono-Italic.ttf create mode 100644 src/Resources/Fonts/JetBrainsMono-Regular.ttf create mode 100644 src/Resources/Icons.axaml delete mode 100644 src/Resources/Icons.xaml create mode 100644 src/Resources/Locales/en_US.axaml delete mode 100644 src/Resources/Locales/en_US.xaml create mode 100644 src/Resources/Locales/zh_CN.axaml delete mode 100644 src/Resources/Locales/zh_CN.xaml create mode 100644 src/Resources/Styles.axaml delete mode 100644 src/Resources/Styles/Button.xaml delete mode 100644 src/Resources/Styles/CheckBox.xaml delete mode 100644 src/Resources/Styles/ComboBox.xaml delete mode 100644 src/Resources/Styles/ContextMenu.xaml delete mode 100644 src/Resources/Styles/DataGrid.xaml delete mode 100644 src/Resources/Styles/HyperLink.xaml delete mode 100644 src/Resources/Styles/IconButton.xaml delete mode 100644 src/Resources/Styles/ListBox.xaml delete mode 100644 src/Resources/Styles/ListView.xaml delete mode 100644 src/Resources/Styles/Path.xaml delete mode 100644 src/Resources/Styles/RadioButton.xaml delete mode 100644 src/Resources/Styles/ScrollBar.xaml delete mode 100644 src/Resources/Styles/ScrollViewer.xaml delete mode 100644 src/Resources/Styles/Slider.xaml delete mode 100644 src/Resources/Styles/TabControl.xaml delete mode 100644 src/Resources/Styles/TextBlock.xaml delete mode 100644 src/Resources/Styles/TextBox.xaml delete mode 100644 src/Resources/Styles/ToggleButton.xaml delete mode 100644 src/Resources/Styles/Tooltip.xaml delete mode 100644 src/Resources/Styles/Tree.xaml delete mode 100644 src/Resources/Styles/Window.xaml create mode 100644 src/Resources/Themes.axaml delete mode 100644 src/Resources/Themes/Dark.xaml delete mode 100644 src/Resources/Themes/Light.xaml create mode 100644 src/SourceGit.sln create mode 100644 src/ViewModels/AddRemote.cs create mode 100644 src/ViewModels/AddSubmodule.cs create mode 100644 src/ViewModels/Apply.cs create mode 100644 src/ViewModels/Archive.cs create mode 100644 src/ViewModels/AssumeUnchangedManager.cs create mode 100644 src/ViewModels/Blame.cs create mode 100644 src/ViewModels/Checkout.cs create mode 100644 src/ViewModels/CherryPick.cs create mode 100644 src/ViewModels/Cleanup.cs create mode 100644 src/ViewModels/ClearStashes.cs create mode 100644 src/ViewModels/Clone.cs create mode 100644 src/ViewModels/CommitDetail.cs create mode 100644 src/ViewModels/CreateBranch.cs create mode 100644 src/ViewModels/CreateGroup.cs create mode 100644 src/ViewModels/CreateTag.cs create mode 100644 src/ViewModels/DeleteBranch.cs create mode 100644 src/ViewModels/DeleteRemote.cs create mode 100644 src/ViewModels/DeleteRepositoryNode.cs create mode 100644 src/ViewModels/DeleteSubmodule.cs create mode 100644 src/ViewModels/DeleteTag.cs create mode 100644 src/ViewModels/DiffContext.cs create mode 100644 src/ViewModels/Discard.cs create mode 100644 src/ViewModels/DropStash.cs create mode 100644 src/ViewModels/EditRemote.cs create mode 100644 src/ViewModels/EditRepositoryNode.cs create mode 100644 src/ViewModels/FastForwardWithoutCheckout.cs create mode 100644 src/ViewModels/Fetch.cs create mode 100644 src/ViewModels/FileHistories.cs create mode 100644 src/ViewModels/FileTreeNode.cs create mode 100644 src/ViewModels/GitFlowFinish.cs create mode 100644 src/ViewModels/GitFlowStart.cs create mode 100644 src/ViewModels/Histories.cs create mode 100644 src/ViewModels/Init.cs create mode 100644 src/ViewModels/InitGitFlow.cs create mode 100644 src/ViewModels/Launcher.cs create mode 100644 src/ViewModels/LauncherPage.cs create mode 100644 src/ViewModels/Merge.cs create mode 100644 src/ViewModels/Popup.cs create mode 100644 src/ViewModels/PopupHost.cs create mode 100644 src/ViewModels/Preference.cs create mode 100644 src/ViewModels/PruneRemote.cs create mode 100644 src/ViewModels/Pull.cs create mode 100644 src/ViewModels/Push.cs create mode 100644 src/ViewModels/PushTag.cs create mode 100644 src/ViewModels/Rebase.cs create mode 100644 src/ViewModels/RenameBranch.cs create mode 100644 src/ViewModels/Repository.cs create mode 100644 src/ViewModels/RepositoryConfigure.cs create mode 100644 src/ViewModels/RepositoryNode.cs create mode 100644 src/ViewModels/Reset.cs create mode 100644 src/ViewModels/Revert.cs create mode 100644 src/ViewModels/RevisionCompare.cs create mode 100644 src/ViewModels/Reword.cs create mode 100644 src/ViewModels/Squash.cs create mode 100644 src/ViewModels/StashChanges.cs create mode 100644 src/ViewModels/StashesPage.cs create mode 100644 src/ViewModels/TwoSideTextDiff.cs create mode 100644 src/ViewModels/Welcome.cs create mode 100644 src/ViewModels/WorkingCopy.cs create mode 100644 src/Views/About.axaml create mode 100644 src/Views/About.axaml.cs delete mode 100644 src/Views/About.xaml delete mode 100644 src/Views/About.xaml.cs create mode 100644 src/Views/AddRemote.axaml create mode 100644 src/Views/AddRemote.axaml.cs create mode 100644 src/Views/AddSubmodule.axaml create mode 100644 src/Views/AddSubmodule.axaml.cs create mode 100644 src/Views/Apply.axaml create mode 100644 src/Views/Apply.axaml.cs create mode 100644 src/Views/Archive.axaml create mode 100644 src/Views/Archive.axaml.cs delete mode 100644 src/Views/AssumeUnchanged.xaml delete mode 100644 src/Views/AssumeUnchanged.xaml.cs create mode 100644 src/Views/AssumeUnchangedManager.axaml create mode 100644 src/Views/AssumeUnchangedManager.axaml.cs create mode 100644 src/Views/Avatar.cs create mode 100644 src/Views/Blame.axaml create mode 100644 src/Views/Blame.axaml.cs delete mode 100644 src/Views/Blame.xaml delete mode 100644 src/Views/Blame.xaml.cs create mode 100644 src/Views/CaptionButtons.axaml create mode 100644 src/Views/CaptionButtons.axaml.cs create mode 100644 src/Views/CaptionButtonsMacOS.axaml create mode 100644 src/Views/CaptionButtonsMacOS.axaml.cs create mode 100644 src/Views/ChangeStatusIcon.cs create mode 100644 src/Views/ChangeViewModeSwitcher.axaml create mode 100644 src/Views/ChangeViewModeSwitcher.axaml.cs create mode 100644 src/Views/Checkout.axaml create mode 100644 src/Views/Checkout.axaml.cs create mode 100644 src/Views/CherryPick.axaml create mode 100644 src/Views/CherryPick.axaml.cs create mode 100644 src/Views/Cleanup.axaml create mode 100644 src/Views/Cleanup.axaml.cs create mode 100644 src/Views/ClearStashes.axaml create mode 100644 src/Views/ClearStashes.axaml.cs create mode 100644 src/Views/Clone.axaml create mode 100644 src/Views/Clone.axaml.cs delete mode 100644 src/Views/Clone.xaml delete mode 100644 src/Views/Clone.xaml.cs create mode 100644 src/Views/CommitBaseInfo.axaml create mode 100644 src/Views/CommitBaseInfo.axaml.cs create mode 100644 src/Views/CommitChanges.axaml create mode 100644 src/Views/CommitChanges.axaml.cs create mode 100644 src/Views/CommitDetail.axaml create mode 100644 src/Views/CommitDetail.axaml.cs delete mode 100644 src/Views/ConfirmDialog.xaml delete mode 100644 src/Views/ConfirmDialog.xaml.cs delete mode 100644 src/Views/Controls/Avatar.cs delete mode 100644 src/Views/Controls/Badge.cs delete mode 100644 src/Views/Controls/ChangeDisplaySwitcher.cs delete mode 100644 src/Views/Controls/ChangeStatusIcon.cs delete mode 100644 src/Views/Controls/Chart.cs delete mode 100644 src/Views/Controls/CommitGraph.cs delete mode 100644 src/Views/Controls/HighlightableTextBlock.cs delete mode 100644 src/Views/Controls/IconButton.cs delete mode 100644 src/Views/Controls/Loading.cs delete mode 100644 src/Views/Controls/PageContainer.cs delete mode 100644 src/Views/Controls/PopupWidget.cs delete mode 100644 src/Views/Controls/TextEdit.cs delete mode 100644 src/Views/Controls/Tree.cs delete mode 100644 src/Views/Controls/TreeItem.cs delete mode 100644 src/Views/Controls/Window.cs delete mode 100644 src/Views/Converters/BoolToCollapsed.cs delete mode 100644 src/Views/Converters/BranchToName.cs delete mode 100644 src/Views/Converters/FontFamilyName.cs delete mode 100644 src/Views/Converters/IntToBookmarkBrush.cs delete mode 100644 src/Views/Converters/InverseBool.cs delete mode 100644 src/Views/Converters/PureFileName.cs delete mode 100644 src/Views/Converters/PureFolderName.cs delete mode 100644 src/Views/Converters/WindowStateToTitleBarHeight.cs create mode 100644 src/Views/CreateBranch.axaml create mode 100644 src/Views/CreateBranch.axaml.cs create mode 100644 src/Views/CreateGroup.axaml create mode 100644 src/Views/CreateGroup.axaml.cs create mode 100644 src/Views/CreateTag.axaml create mode 100644 src/Views/CreateTag.axaml.cs create mode 100644 src/Views/DeleteBranch.axaml create mode 100644 src/Views/DeleteBranch.axaml.cs create mode 100644 src/Views/DeleteRemote.axaml create mode 100644 src/Views/DeleteRemote.axaml.cs create mode 100644 src/Views/DeleteRepositoryNode.axaml create mode 100644 src/Views/DeleteRepositoryNode.axaml.cs create mode 100644 src/Views/DeleteSubmodule.axaml create mode 100644 src/Views/DeleteSubmodule.axaml.cs create mode 100644 src/Views/DeleteTag.axaml create mode 100644 src/Views/DeleteTag.axaml.cs create mode 100644 src/Views/DiffView.axaml create mode 100644 src/Views/DiffView.axaml.cs create mode 100644 src/Views/Discard.axaml create mode 100644 src/Views/Discard.axaml.cs create mode 100644 src/Views/DropStash.axaml create mode 100644 src/Views/DropStash.axaml.cs create mode 100644 src/Views/EditRemote.axaml create mode 100644 src/Views/EditRemote.axaml.cs delete mode 100644 src/Views/EditRepositoryDisplayName.xaml delete mode 100644 src/Views/EditRepositoryDisplayName.xaml.cs create mode 100644 src/Views/EditRepositoryNode.axaml create mode 100644 src/Views/EditRepositoryNode.axaml.cs create mode 100644 src/Views/Extensions.cs create mode 100644 src/Views/FastForwardWithoutCheckout.axaml create mode 100644 src/Views/FastForwardWithoutCheckout.axaml.cs create mode 100644 src/Views/Fetch.axaml create mode 100644 src/Views/Fetch.axaml.cs create mode 100644 src/Views/FileHistories.axaml create mode 100644 src/Views/FileHistories.axaml.cs delete mode 100644 src/Views/FileHistories.xaml delete mode 100644 src/Views/FileHistories.xaml.cs create mode 100644 src/Views/GitFlowFinish.axaml create mode 100644 src/Views/GitFlowFinish.axaml.cs create mode 100644 src/Views/GitFlowStart.axaml create mode 100644 src/Views/GitFlowStart.axaml.cs create mode 100644 src/Views/Histories.axaml create mode 100644 src/Views/Histories.axaml.cs create mode 100644 src/Views/Init.axaml create mode 100644 src/Views/Init.axaml.cs create mode 100644 src/Views/InitGitFlow.axaml create mode 100644 src/Views/InitGitFlow.axaml.cs create mode 100644 src/Views/Launcher.axaml create mode 100644 src/Views/Launcher.axaml.cs delete mode 100644 src/Views/Launcher.xaml delete mode 100644 src/Views/Launcher.xaml.cs create mode 100644 src/Views/Merge.axaml create mode 100644 src/Views/Merge.axaml.cs delete mode 100644 src/Views/Popups/AddSubTree.xaml delete mode 100644 src/Views/Popups/AddSubTree.xaml.cs delete mode 100644 src/Views/Popups/AddSubmodule.xaml delete mode 100644 src/Views/Popups/AddSubmodule.xaml.cs delete mode 100644 src/Views/Popups/Apply.xaml delete mode 100644 src/Views/Popups/Apply.xaml.cs delete mode 100644 src/Views/Popups/Archive.xaml delete mode 100644 src/Views/Popups/Archive.xaml.cs delete mode 100644 src/Views/Popups/Checkout.xaml delete mode 100644 src/Views/Popups/Checkout.xaml.cs delete mode 100644 src/Views/Popups/CherryPick.xaml delete mode 100644 src/Views/Popups/CherryPick.xaml.cs delete mode 100644 src/Views/Popups/Cleanup.xaml delete mode 100644 src/Views/Popups/Cleanup.xaml.cs delete mode 100644 src/Views/Popups/Configure.xaml delete mode 100644 src/Views/Popups/Configure.xaml.cs delete mode 100644 src/Views/Popups/CreateBranch.xaml delete mode 100644 src/Views/Popups/CreateBranch.xaml.cs delete mode 100644 src/Views/Popups/CreateTag.xaml delete mode 100644 src/Views/Popups/CreateTag.xaml.cs delete mode 100644 src/Views/Popups/DeleteBranch.xaml delete mode 100644 src/Views/Popups/DeleteBranch.xaml.cs delete mode 100644 src/Views/Popups/DeleteRemote.xaml delete mode 100644 src/Views/Popups/DeleteRemote.xaml.cs delete mode 100644 src/Views/Popups/DeleteSubmodule.xaml delete mode 100644 src/Views/Popups/DeleteSubmodule.xaml.cs delete mode 100644 src/Views/Popups/DeleteTag.xaml delete mode 100644 src/Views/Popups/DeleteTag.xaml.cs delete mode 100644 src/Views/Popups/Discard.xaml delete mode 100644 src/Views/Popups/Discard.xaml.cs delete mode 100644 src/Views/Popups/EditSubTree.xaml delete mode 100644 src/Views/Popups/EditSubTree.xaml.cs delete mode 100644 src/Views/Popups/FastForwardWithoutCheckout.xaml delete mode 100644 src/Views/Popups/FastForwardWithoutCheckout.xaml.cs delete mode 100644 src/Views/Popups/Fetch.xaml delete mode 100644 src/Views/Popups/Fetch.xaml.cs delete mode 100644 src/Views/Popups/GitFlowFinish.xaml delete mode 100644 src/Views/Popups/GitFlowFinish.xaml.cs delete mode 100644 src/Views/Popups/GitFlowStart.xaml delete mode 100644 src/Views/Popups/GitFlowStart.xaml.cs delete mode 100644 src/Views/Popups/Init.xaml delete mode 100644 src/Views/Popups/Init.xaml.cs delete mode 100644 src/Views/Popups/InitGitFlow.xaml delete mode 100644 src/Views/Popups/InitGitFlow.xaml.cs delete mode 100644 src/Views/Popups/Merge.xaml delete mode 100644 src/Views/Popups/Merge.xaml.cs delete mode 100644 src/Views/Popups/Prune.xaml delete mode 100644 src/Views/Popups/Prune.xaml.cs delete mode 100644 src/Views/Popups/Pull.xaml delete mode 100644 src/Views/Popups/Pull.xaml.cs delete mode 100644 src/Views/Popups/Push.xaml delete mode 100644 src/Views/Popups/Push.xaml.cs delete mode 100644 src/Views/Popups/PushTag.xaml delete mode 100644 src/Views/Popups/PushTag.xaml.cs delete mode 100644 src/Views/Popups/Rebase.xaml delete mode 100644 src/Views/Popups/Rebase.xaml.cs delete mode 100644 src/Views/Popups/Remote.xaml delete mode 100644 src/Views/Popups/Remote.xaml.cs delete mode 100644 src/Views/Popups/RenameBranch.xaml delete mode 100644 src/Views/Popups/RenameBranch.xaml.cs delete mode 100644 src/Views/Popups/Reset.xaml delete mode 100644 src/Views/Popups/Reset.xaml.cs delete mode 100644 src/Views/Popups/Revert.xaml delete mode 100644 src/Views/Popups/Revert.xaml.cs delete mode 100644 src/Views/Popups/Reword.xaml delete mode 100644 src/Views/Popups/Reword.xaml.cs delete mode 100644 src/Views/Popups/Squash.xaml delete mode 100644 src/Views/Popups/Squash.xaml.cs delete mode 100644 src/Views/Popups/Stash.xaml delete mode 100644 src/Views/Popups/Stash.xaml.cs delete mode 100644 src/Views/Popups/StashDropConfirm.xaml delete mode 100644 src/Views/Popups/StashDropConfirm.xaml.cs delete mode 100644 src/Views/Popups/SubTreePull.xaml delete mode 100644 src/Views/Popups/SubTreePull.xaml.cs delete mode 100644 src/Views/Popups/SubTreePush.xaml delete mode 100644 src/Views/Popups/SubTreePush.xaml.cs delete mode 100644 src/Views/Popups/UnlinkSubTree.xaml delete mode 100644 src/Views/Popups/UnlinkSubTree.xaml.cs create mode 100644 src/Views/Preference.axaml create mode 100644 src/Views/Preference.axaml.cs delete mode 100644 src/Views/Preference.xaml delete mode 100644 src/Views/Preference.xaml.cs create mode 100644 src/Views/PruneRemote.axaml create mode 100644 src/Views/PruneRemote.axaml.cs create mode 100644 src/Views/Pull.axaml create mode 100644 src/Views/Pull.axaml.cs create mode 100644 src/Views/Push.axaml create mode 100644 src/Views/Push.axaml.cs create mode 100644 src/Views/PushTag.axaml create mode 100644 src/Views/PushTag.axaml.cs create mode 100644 src/Views/Rebase.axaml create mode 100644 src/Views/Rebase.axaml.cs create mode 100644 src/Views/RenameBranch.axaml create mode 100644 src/Views/RenameBranch.axaml.cs create mode 100644 src/Views/Repository.axaml create mode 100644 src/Views/Repository.axaml.cs create mode 100644 src/Views/RepositoryConfigure.axaml create mode 100644 src/Views/RepositoryConfigure.axaml.cs create mode 100644 src/Views/Reset.axaml create mode 100644 src/Views/Reset.axaml.cs create mode 100644 src/Views/Revert.axaml create mode 100644 src/Views/Revert.axaml.cs create mode 100644 src/Views/RevisionCompare.axaml create mode 100644 src/Views/RevisionCompare.axaml.cs create mode 100644 src/Views/RevisionFiles.axaml create mode 100644 src/Views/RevisionFiles.axaml.cs create mode 100644 src/Views/Reword.axaml create mode 100644 src/Views/Reword.axaml.cs create mode 100644 src/Views/Squash.axaml create mode 100644 src/Views/Squash.axaml.cs create mode 100644 src/Views/StashChanges.axaml create mode 100644 src/Views/StashChanges.axaml.cs create mode 100644 src/Views/StashesPage.axaml create mode 100644 src/Views/StashesPage.axaml.cs delete mode 100644 src/Views/Statistics.xaml delete mode 100644 src/Views/Statistics.xaml.cs create mode 100644 src/Views/TextDiffView.axaml create mode 100644 src/Views/TextDiffView.axaml.cs delete mode 100644 src/Views/Validations/ArchiveFile.cs delete mode 100644 src/Views/Validations/BranchName.cs delete mode 100644 src/Views/Validations/CloneDir.cs delete mode 100644 src/Views/Validations/CommitMessage.cs delete mode 100644 src/Views/Validations/GitURL.cs delete mode 100644 src/Views/Validations/LocalRepositoryName.cs delete mode 100644 src/Views/Validations/PatchFile.cs delete mode 100644 src/Views/Validations/RelativePath.cs delete mode 100644 src/Views/Validations/RemoteName.cs delete mode 100644 src/Views/Validations/Required.cs delete mode 100644 src/Views/Validations/TagName.cs create mode 100644 src/Views/Welcome.axaml create mode 100644 src/Views/Welcome.axaml.cs delete mode 100644 src/Views/Widgets/Bookmark.xaml delete mode 100644 src/Views/Widgets/Bookmark.xaml.cs delete mode 100644 src/Views/Widgets/CommitChanges.xaml delete mode 100644 src/Views/Widgets/CommitChanges.xaml.cs delete mode 100644 src/Views/Widgets/CommitDetail.xaml delete mode 100644 src/Views/Widgets/CommitDetail.xaml.cs delete mode 100644 src/Views/Widgets/Dashboard.xaml delete mode 100644 src/Views/Widgets/Dashboard.xaml.cs delete mode 100644 src/Views/Widgets/DiffViewer.xaml delete mode 100644 src/Views/Widgets/DiffViewer.xaml.cs delete mode 100644 src/Views/Widgets/Exceptions.xaml delete mode 100644 src/Views/Widgets/Exceptions.xaml.cs delete mode 100644 src/Views/Widgets/Histories.xaml delete mode 100644 src/Views/Widgets/Histories.xaml.cs delete mode 100644 src/Views/Widgets/PageTabBar.xaml delete mode 100644 src/Views/Widgets/PageTabBar.xaml.cs delete mode 100644 src/Views/Widgets/RevisionCompare.xaml delete mode 100644 src/Views/Widgets/RevisionCompare.xaml.cs delete mode 100644 src/Views/Widgets/RevisionFiles.xaml delete mode 100644 src/Views/Widgets/RevisionFiles.xaml.cs delete mode 100644 src/Views/Widgets/Stashes.xaml delete mode 100644 src/Views/Widgets/Stashes.xaml.cs delete mode 100644 src/Views/Widgets/StatisticsPage.xaml delete mode 100644 src/Views/Widgets/StatisticsPage.xaml.cs delete mode 100644 src/Views/Widgets/Welcome.xaml delete mode 100644 src/Views/Widgets/Welcome.xaml.cs delete mode 100644 src/Views/Widgets/WorkingCopy.xaml delete mode 100644 src/Views/Widgets/WorkingCopy.xaml.cs delete mode 100644 src/Views/Widgets/WorkingCopyChanges.xaml delete mode 100644 src/Views/Widgets/WorkingCopyChanges.xaml.cs create mode 100644 src/Views/WorkingCopy.axaml create mode 100644 src/Views/WorkingCopy.axaml.cs diff --git a/.gitignore b/.gitignore index f348333a..1742b876 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ .vscode bin obj -publish *.user diff --git a/README.md b/README.md index ca35f786..878c1abe 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # SourceGit -Opensouce Git GUI client for Windows. +Opensouce Git GUI client. ## High-lights * Opensource/Free -* Light-weight * Fast * English/简体中文 * Build-in light/dark themes @@ -29,8 +28,6 @@ Opensouce Git GUI client for Windows. Pre-build Binaries:[Releases](https://github.com/sourcegit-scm/sourcegit/releases) -> NOTE: You need install Git first. - ## Screen Shots * Drak Theme diff --git a/src/App.axaml b/src/App.axaml new file mode 100644 index 00000000..b9124294 --- /dev/null +++ b/src/App.axaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App.axaml.cs b/src/App.axaml.cs new file mode 100644 index 00000000..924d79b5 --- /dev/null +++ b/src/App.axaml.cs @@ -0,0 +1,158 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Data.Core.Plugins; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Styling; +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Text; + +namespace SourceGit { + public partial class App : Application { + + [STAThread] + public static void Main(string[] args) { + try { + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } catch (Exception ex) { + var builder = new StringBuilder(); + builder.Append("Crash: "); + builder.Append(ex.Message); + builder.Append("\n\n"); + builder.Append("----------------------------\n"); + builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); + builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); + builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); + builder.Append($"Source: {ex.Source}\n"); + builder.Append($"---------------------------\n\n"); + builder.Append(ex.StackTrace); + + var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); + var file = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); + file = Path.Combine(file, $"crash_{time}.log"); + File.WriteAllText(file, builder.ToString()); + } + } + + public static AppBuilder BuildAvaloniaApp() { + var builder = AppBuilder.Configure(); + builder.UsePlatformDetect(); + + if (OperatingSystem.IsWindows()) { + builder.With(new FontManagerOptions() { + FontFallbacks = [ + new FontFallback { FontFamily = new FontFamily("Microsoft YaHei UI") } + ] + }); + } else if (OperatingSystem.IsMacOS()) { + builder.With(new FontManagerOptions() { + FontFallbacks = [ + new FontFallback { FontFamily = new FontFamily("PingFang SC") } + ] + }); + builder.With(new MacOSPlatformOptions() { + DisableDefaultApplicationMenuItems = true, + DisableNativeMenus = true, + }); + } + + builder.LogToTrace(); + return builder; + } + + public static void RaiseException(string context, string message) { + if (Current is App app && app._notificationReceiver != null) { + var ctx = context.Replace('\\', '/'); + var notice = new Models.Notification() { IsError = true, Message = message }; + app._notificationReceiver.OnReceiveNotification(ctx, notice); + } + } + + public static void SendNotification(string context, string message) { + if (Current is App app && app._notificationReceiver != null) { + var ctx = context.Replace('\\', '/'); + var notice = new Models.Notification() { IsError = false, Message = message }; + app._notificationReceiver.OnReceiveNotification(ctx, notice); + } + } + + public static void SetLocale(string localeKey) { + var app = Current as App; + var targetLocale = app.Resources[localeKey] as ResourceDictionary; + if (targetLocale == null || targetLocale == app._activeLocale) { + return; + } + + if (app._activeLocale != null) { + app.Resources.MergedDictionaries.Remove(app._activeLocale); + } + + app.Resources.MergedDictionaries.Add(targetLocale); + app._activeLocale = targetLocale; + } + + public static void SetTheme(string theme) { + if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase)) { + App.Current.RequestedThemeVariant = ThemeVariant.Light; + } else if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase)) { + App.Current.RequestedThemeVariant = ThemeVariant.Dark; + } else { + App.Current.RequestedThemeVariant = ThemeVariant.Default; + } + } + + public static async void CopyText(string data) { + if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + if (desktop.MainWindow.Clipboard is { } clipbord) { + await clipbord.SetTextAsync(data); + } + } + } + + public static string Text(string key, params object[] args) { + var fmt = Current.FindResource($"Text.{key}") as string; + if (string.IsNullOrWhiteSpace(fmt)) return $"Text.{key}"; + return string.Format(fmt, args); + } + + public static TopLevel GetTopLevel() { + if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + return desktop.MainWindow; + } + return null; + } + + public static void Quit() { + if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + desktop.MainWindow.Close(); + desktop.Shutdown(); + } + } + + public override void Initialize() { + AvaloniaXamlLoader.Load(this); + + SetLocale(ViewModels.Preference.Instance.Locale); + SetTheme(ViewModels.Preference.Instance.Theme); + } + + public override void OnFrameworkInitializationCompleted() { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + BindingPlugins.DataValidators.RemoveAt(0); + + var launcher = new Views.Launcher(); + _notificationReceiver = launcher; + desktop.MainWindow = launcher; + } + + base.OnFrameworkInitializationCompleted(); + } + + private ResourceDictionary _activeLocale = null; + private Models.INotificationReceiver _notificationReceiver = null; + } +} \ No newline at end of file diff --git a/src/App.manifest b/src/App.manifest index 5a6db8f3..b3bc3bdf 100644 --- a/src/App.manifest +++ b/src/App.manifest @@ -1,14 +1,18 @@  - - - - - - - - PerMonitorV2 - true - + + + + + + + + + + diff --git a/src/App.xaml b/src/App.xaml deleted file mode 100644 index 1f48956b..00000000 --- a/src/App.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/App.xaml.cs b/src/App.xaml.cs deleted file mode 100644 index 16637cd1..00000000 --- a/src/App.xaml.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.IO; -using System.Windows; - -namespace SourceGit { - - /// - /// 程序入口. - /// - public partial class App : Application { - public static event Action ExceptionRaised; - - /// - /// 读取本地化字串 - /// - /// 本地化字串的Key - /// 可选格式化参数 - /// 本地化字串 - public static string Text(string key, params object[] args) { - var data = Current.FindResource($"Text.{key}") as string; - if (string.IsNullOrEmpty(data)) return $"Text.{key}"; - return string.Format(data, args); - } - - /// - /// 触发错误 - /// - /// 错误上下文 - /// 错误内容 - public static void Exception(string ctx, string detail) { - ExceptionRaised?.Invoke(ctx, detail); - } - - /// - /// 启动. - /// - /// - /// - protected override void OnStartup(StartupEventArgs e) { - base.OnStartup(e); - - // 崩溃文件生成 - AppDomain.CurrentDomain.UnhandledException += (_, ev) => Models.CrashInfo.Create(ev.ExceptionObject as Exception); - - // 创建必要目录 - if (!Directory.Exists(Views.Controls.Avatar.CACHE_PATH)) { - Directory.CreateDirectory(Views.Controls.Avatar.CACHE_PATH); - } - - Models.Theme.Change(); - Models.Locale.Change(); - - // 如果启动命令中指定了路径,打开指定目录的仓库 - var launcher = new Views.Launcher(); - if (Models.Preference.Instance.IsReady) { - if (e.Args.Length > 0) { - var repo = Models.Preference.Instance.FindRepository(e.Args[0]); - if (repo == null) { - var path = new Commands.GetRepositoryRootPath(e.Args[0]).Result(); - if (path != null) { - var gitDir = new Commands.QueryGitDir(path).Result(); - repo = Models.Preference.Instance.AddRepository(path, gitDir); - } - } - - if (repo != null) Models.Watcher.Open(repo); - } else if (Models.Preference.Instance.Restore.IsEnabled) { - var restore = Models.Preference.Instance.Restore; - var actived = null as Models.Repository; - if (restore.Opened.Count > 0) { - foreach (var path in restore.Opened) { - if (!Directory.Exists(path)) continue; - var repo = Models.Preference.Instance.FindRepository(path); - if (repo != null) Models.Watcher.Open(repo); - if (path == restore.Actived) actived = repo; - } - - if (actived != null) Models.Watcher.Open(actived); - } - } - } - - // 主界面显示 - MainWindow = launcher; - MainWindow.Show(); - } - - /// - /// 后台运行 - /// - /// - /// - protected override void OnDeactivated(EventArgs e) { - base.OnDeactivated(e); - GC.Collect(); - Models.Preference.Save(); - } - } -} diff --git a/src/Avalonia.Controls.DataGrid.patch b/src/Avalonia.Controls.DataGrid.patch new file mode 100644 index 00000000..415c537f --- /dev/null +++ b/src/Avalonia.Controls.DataGrid.patch @@ -0,0 +1,81 @@ +diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs +index e4573c3759a8a1eeece45c8bacb4fa853201f8e7..aa29066173e03092b61477985aed73beb08ec8fc 100644 +--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs ++++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs +@@ -716,6 +716,16 @@ public DataGridRowDetailsVisibilityMode RowDetailsVisibilityMode + set { SetValue(RowDetailsVisibilityModeProperty, value); } + } + ++ public static readonly RoutedEvent DisplayRegionChangedEvent = RoutedEvent.Register( ++ nameof(DisplayRegionChanged), ++ RoutingStrategies.Bubble); ++ ++ public event EventHandler DisplayRegionChanged ++ { ++ add => AddHandler(DisplayRegionChangedEvent, value); ++ remove => RemoveHandler(DisplayRegionChangedEvent, value); ++ } ++ + static DataGrid() + { + AffectsMeasure( +@@ -2428,6 +2438,11 @@ protected virtual void OnUnloadingRow(DataGridRowEventArgs e) + } + } + ++ protected virtual void OnDisplayRegionChanged() ++ { ++ RaiseEvent(new RoutedEventArgs(DisplayRegionChangedEvent)); ++ } ++ + /// + /// Comparator class so we can sort list by the display index + /// +@@ -3879,6 +3894,7 @@ private void InvalidateColumnHeadersMeasure() + { + EnsureColumnHeadersVisibility(); + _columnHeadersPresenter.InvalidateMeasure(); ++ OnDisplayRegionChanged(); + } + } + +@@ -3903,6 +3919,8 @@ private void InvalidateRowsMeasure(bool invalidateIndividualElements) + element.InvalidateMeasure(); + } + } ++ ++ OnDisplayRegionChanged(); + } + } + +@@ -6211,5 +6229,30 @@ protected virtual void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventA + { + AutoGeneratingColumn?.Invoke(this, e); + } ++ ++ public Vector GetDisplayOffset() ++ { ++ // Has bug when using arrow keys via keyboard. ++ // return new Vector(_horizontalOffset, _verticalOffset); ++ ++ double startX = 0; ++ double startY = 0; ++ ++ foreach (var child in _rowsPresenter.Children) ++ { ++ var row = child as DataGridRow; ++ if (row.Slot >= 0 && row.Bounds.Top <= 0 && row.Bounds.Top > -RowHeight) ++ { ++ var testY = RowHeight * row.Index - row.Bounds.Top; ++ if (startY < testY) ++ { ++ startY = testY; ++ startX = row.Bounds.Left; ++ } ++ } ++ } ++ ++ return new Vector(startX, startY); ++ } + } + } diff --git a/src/BuildWindows.bat b/src/BuildWindows.bat new file mode 100644 index 00000000..8ec72502 --- /dev/null +++ b/src/BuildWindows.bat @@ -0,0 +1 @@ +dotnet publish -c Release -r win-x64 -p:PublishAot=true -p:PublishTrimmed=true -p:TrimMode=link --self-contained \ No newline at end of file diff --git a/src/Commands/Add.cs b/src/Commands/Add.cs index fb98cf87..c83c330d 100644 --- a/src/Commands/Add.cs +++ b/src/Commands/Add.cs @@ -1,27 +1,24 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text; namespace SourceGit.Commands { - /// - /// `git add`命令 - /// public class Add : Command { - public Add(string repo) { - Cwd = repo; - Args = "add ."; - } + public Add(string repo, List changes = null) { + WorkingDirectory = repo; + Context = repo; - public Add(string repo, List paths) { - StringBuilder builder = new StringBuilder(); - builder.Append("add --"); - foreach (var p in paths) { - builder.Append(" \""); - builder.Append(p); - builder.Append("\""); + if (changes == null || changes.Count == 0) { + Args = "add ."; + } else { + var builder = new StringBuilder(); + builder.Append("add --"); + foreach (var c in changes) { + builder.Append(" \""); + builder.Append(c.Path); + builder.Append("\""); + } + Args = builder.ToString(); } - - Cwd = repo; - Args = builder.ToString(); } } } diff --git a/src/Commands/Apply.cs b/src/Commands/Apply.cs index f62f113c..3619d963 100644 --- a/src/Commands/Apply.cs +++ b/src/Commands/Apply.cs @@ -1,11 +1,8 @@ -namespace SourceGit.Commands { - /// - /// 应用Patch - /// +namespace SourceGit.Commands { public class Apply : Command { - public Apply(string repo, string file, bool ignoreWhitespace, string whitespaceMode) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = "apply "; if (ignoreWhitespace) Args += "--ignore-whitespace "; else Args += $"--whitespace={whitespaceMode} "; diff --git a/src/Commands/Archive.cs b/src/Commands/Archive.cs index b4153e34..ac748653 100644 --- a/src/Commands/Archive.cs +++ b/src/Commands/Archive.cs @@ -1,22 +1,19 @@ -using System; +using System; namespace SourceGit.Commands { - - /// - /// 存档命令 - /// public class Archive : Command { - private Action handler; - - public Archive(string repo, string revision, string to, Action onProgress) { - Cwd = repo; - Args = $"archive --format=zip --verbose --output=\"{to}\" {revision}"; + public Archive(string repo, string revision, string saveTo, Action outputHandler) { + WorkingDirectory = repo; + Context = repo; + Args = $"archive --format=zip --verbose --output=\"{saveTo}\" {revision}"; TraitErrorAsOutput = true; - handler = onProgress; + _outputHandler = outputHandler; } - public override void OnReadline(string line) { - handler?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler; } } diff --git a/src/Commands/AssumeUnchanged.cs b/src/Commands/AssumeUnchanged.cs index 9a0af3d9..d7dc4c06 100644 --- a/src/Commands/AssumeUnchanged.cs +++ b/src/Commands/AssumeUnchanged.cs @@ -2,59 +2,59 @@ using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// 查看、添加或移除忽略变更文件 - /// public class AssumeUnchanged { - private string repo; - class ViewCommand : Command { private static readonly Regex REG = new Regex(@"^(\w)\s+(.+)$"); - private List outs = new List(); public ViewCommand(string repo) { - Cwd = repo; + WorkingDirectory = repo; Args = "ls-files -v"; + RaiseError = false; } public List Result() { Exec(); - return outs; + return _outs; } - public override void OnReadline(string line) { + protected override void OnReadline(string line) { var match = REG.Match(line); if (!match.Success) return; if (match.Groups[1].Value == "h") { - outs.Add(match.Groups[2].Value); + _outs.Add(match.Groups[2].Value); } } + + private List _outs = new List(); } class ModCommand : Command { public ModCommand(string repo, string file, bool bAdd) { var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged"; - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = $"update-index {mode} -- \"{file}\""; } } public AssumeUnchanged(string repo) { - this.repo = repo; + _repo = repo; } public List View() { - return new ViewCommand(repo).Result(); + return new ViewCommand(_repo).Result(); } public void Add(string file) { - new ModCommand(repo, file, true).Exec(); + new ModCommand(_repo, file, true).Exec(); } public void Remove(string file) { - new ModCommand(repo, file, false).Exec(); + new ModCommand(_repo, file, false).Exec(); } + + private string _repo; } } diff --git a/src/Commands/Blame.cs b/src/Commands/Blame.cs index 571ff17e..908019f9 100644 --- a/src/Commands/Blame.cs +++ b/src/Commands/Blame.cs @@ -1,77 +1,90 @@ -using System; -using System.Collections.Generic; +using System; +using System.IO; +using System.Text; using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// 逐行追溯 - /// public class Blame : Command { private static readonly Regex REG_FORMAT = new Regex(@"^\^?([0-9a-f]+)\s+.*\((.*)\s+(\d+)\s+[\-\+]?\d+\s+\d+\) (.*)"); private static readonly DateTime UTC_START = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); - private Data data = new Data(); - private bool needUnifyCommitSHA = false; - private int minSHALen = 0; - - public class Data { - public List Lines = new List(); - public bool IsBinary = false; - } - public Blame(string repo, string file, string revision) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = $"blame -t {revision} -- \"{file}\""; + RaiseError = false; + + _result.File = file; } - public Data Result() { - Exec(); + public Models.BlameData Result() { + var succ = Exec(); + if (!succ) { + return new Models.BlameData(); + } - if (needUnifyCommitSHA) { - foreach (var line in data.Lines) { - if (line.CommitSHA.Length > minSHALen) { - line.CommitSHA = line.CommitSHA.Substring(0, minSHALen); + if (_needUnifyCommitSHA) { + foreach (var line in _result.LineInfos) { + if (line.CommitSHA.Length > _minSHALen) { + line.CommitSHA = line.CommitSHA.Substring(0, _minSHALen); } } } - return data; + _result.Content = _content.ToString(); + return _result; } - public override void OnReadline(string line) { - if (data.IsBinary) return; + protected override void OnReadline(string line) { + if (_result.IsBinary) return; if (string.IsNullOrEmpty(line)) return; if (line.IndexOf('\0') >= 0) { - data.IsBinary = true; - data.Lines.Clear(); + _result.IsBinary = true; + _result.LineInfos.Clear(); return; } var match = REG_FORMAT.Match(line); if (!match.Success) return; + _content.AppendLine(match.Groups[4].Value); + var commit = match.Groups[1].Value; - var author = match.Groups[2].Value; - var timestamp = int.Parse(match.Groups[3].Value); - var content = match.Groups[4].Value; - var when = UTC_START.AddSeconds(timestamp).ToString("yyyy/MM/dd"); + if (commit == _lastSHA) { + var info = new Models.BlameLineInfo() { + CommitSHA = commit, + Author = string.Empty, + Time = string.Empty, + }; - var blameLine = new Models.BlameLine() { - LineNumber = $"{data.Lines.Count + 1}", - CommitSHA = commit, - Author = author, - Time = when, - Content = content, - }; + _result.LineInfos.Add(info); + } else { + var author = match.Groups[2].Value; + var timestamp = int.Parse(match.Groups[3].Value); + var when = UTC_START.AddSeconds(timestamp).ToString("yyyy/MM/dd"); - if (line[0] == '^') { - needUnifyCommitSHA = true; - if (minSHALen == 0) minSHALen = commit.Length; - else if (commit.Length < minSHALen) minSHALen = commit.Length; + var blameLine = new Models.BlameLineInfo() { + IsFirstInGroup = true, + CommitSHA = commit, + Author = author, + Time = when, + }; + + _lastSHA = commit; + _result.LineInfos.Add(blameLine); } - data.Lines.Add(blameLine); + if (line[0] == '^') { + _needUnifyCommitSHA = true; + _minSHALen = Math.Min(_minSHALen, commit.Length); + } } + + private Models.BlameData _result = new Models.BlameData(); + private StringBuilder _content = new StringBuilder(); + private string _lastSHA = string.Empty; + private bool _needUnifyCommitSHA = false; + private int _minSHALen = 64; } } diff --git a/src/Commands/Branch.cs b/src/Commands/Branch.cs index 387892ba..f109d1e4 100644 --- a/src/Commands/Branch.cs +++ b/src/Commands/Branch.cs @@ -1,38 +1,39 @@ -namespace SourceGit.Commands { - /// - /// 分支相关操作 - /// - class Branch : Command { - private string target = null; - - public Branch(string repo, string branch) { - Cwd = repo; - target = branch; +namespace SourceGit.Commands { + public static class Branch { + public static bool Create(string repo, string name, string basedOn) { + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.Context = repo; + cmd.Args = $"branch {name} {basedOn}"; + return cmd.Exec(); } - public void Create(string basedOn) { - Args = $"branch {target} {basedOn}"; - Exec(); + public static bool Rename(string repo, string name, string to) { + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.Context = repo; + cmd.Args = $"branch -M {name} {to}"; + return cmd.Exec(); } - public void Rename(string to) { - Args = $"branch -M {target} {to}"; - Exec(); - } - - public void SetUpstream(string upstream) { - Args = $"branch {target} "; + public static bool SetUpstream(string repo, string name, string upstream) { + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.Context = repo; if (string.IsNullOrEmpty(upstream)) { - Args += "--unset-upstream"; + cmd.Args = $"branch {name} --unset-upstream"; } else { - Args += $"-u {upstream}"; + cmd.Args = $"branch {name} -u {upstream}"; } - Exec(); + return cmd.Exec(); } - public void Delete() { - Args = $"branch -D {target}"; - Exec(); + public static bool Delete(string repo, string name) { + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.Context = repo; + cmd.Args = $"branch -D {name}"; + return cmd.Exec(); } } } diff --git a/src/Commands/Checkout.cs b/src/Commands/Checkout.cs index 1f169992..14121fab 100644 --- a/src/Commands/Checkout.cs +++ b/src/Commands/Checkout.cs @@ -1,29 +1,25 @@ -using System; +using System; using System.Collections.Generic; using System.Text; namespace SourceGit.Commands { - /// - /// 检出 - /// public class Checkout : Command { - private Action handler = null; - public Checkout(string repo) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; } public bool Branch(string branch, Action onProgress) { Args = $"checkout --progress {branch}"; TraitErrorAsOutput = true; - handler = onProgress; + _outputHandler = onProgress; return Exec(); } public bool Branch(string branch, string basedOn, Action onProgress) { Args = $"checkout --progress -b {branch} {basedOn}"; TraitErrorAsOutput = true; - handler = onProgress; + _outputHandler = onProgress; return Exec(); } @@ -54,8 +50,10 @@ namespace SourceGit.Commands { return Exec(); } - public override void OnReadline(string line) { - handler?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler; } } diff --git a/src/Commands/CherryPick.cs b/src/Commands/CherryPick.cs index ca939e76..6160ebac 100644 --- a/src/Commands/CherryPick.cs +++ b/src/Commands/CherryPick.cs @@ -1,12 +1,9 @@ -namespace SourceGit.Commands { - /// - /// 遴选命令 - /// +namespace SourceGit.Commands { public class CherryPick : Command { - public CherryPick(string repo, string commit, bool noCommit) { var mode = noCommit ? "-n" : "--ff"; - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = $"cherry-pick {mode} {commit}"; } } diff --git a/src/Commands/Clean.cs b/src/Commands/Clean.cs index 38a9a477..56a56a6c 100644 --- a/src/Commands/Clean.cs +++ b/src/Commands/Clean.cs @@ -1,14 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text; namespace SourceGit.Commands { - /// - /// 清理指令 - /// public class Clean : Command { - public Clean(string repo) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = "clean -qfd"; } @@ -21,7 +18,8 @@ namespace SourceGit.Commands { builder.Append("\""); } - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = builder.ToString(); } } diff --git a/src/Commands/Clone.cs b/src/Commands/Clone.cs index fefc0f3e..a6228e20 100644 --- a/src/Commands/Clone.cs +++ b/src/Commands/Clone.cs @@ -1,24 +1,18 @@ -using System; +using System; namespace SourceGit.Commands { - - /// - /// 克隆 - /// public class Clone : Command { - private Action handler = null; - private Action onError = null; + private Action _notifyProgress; - public Clone(string path, string url, string localName, string sshKey, string extraArgs, Action outputHandler, Action errHandler) { - Cwd = path; + public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs, Action ouputHandler) { + Context = ctx; + WorkingDirectory = path; TraitErrorAsOutput = true; - handler = outputHandler; - onError = errHandler; if (string.IsNullOrEmpty(sshKey)) { - Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; - } else { Args = "-c credential.helper=manager "; + } else { + Args = $"-c core.sshCommand=\"ssh -i '{sshKey}'\" "; } Args += "clone --progress --verbose --recurse-submodules "; @@ -26,14 +20,12 @@ namespace SourceGit.Commands { if (!string.IsNullOrEmpty(extraArgs)) Args += $"{extraArgs} "; Args += $"{url} "; if (!string.IsNullOrEmpty(localName)) Args += localName; + + _notifyProgress = ouputHandler; } - public override void OnReadline(string line) { - handler?.Invoke(line); - } - - public override void OnException(string message) { - onError?.Invoke(message); + protected override void OnReadline(string line) { + _notifyProgress?.Invoke(line); } } } diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index aac9bdff..9e55a581 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -1,3 +1,4 @@ +using Avalonia.Threading; using System; using System.Collections.Generic; using System.Diagnostics; @@ -5,60 +6,27 @@ using System.Text; using System.Text.RegularExpressions; namespace SourceGit.Commands { - - /// - /// 用于取消命令执行的上下文对象 - /// - public class Context { - public bool IsCancelRequested { get; set; } = false; - } - - /// - /// 命令接口 - /// public class Command { - private static readonly Regex PROGRESS_REG = new Regex(@"\d+%"); - - /// - /// 读取全部输出时的结果 - /// - public class ReadToEndResult { - public bool IsSuccess { get; set; } - public string Output { get; set; } - public string Error { get; set; } + public class CancelToken { + public bool Requested { get; set; } = false; } - /// - /// 上下文 - /// - public Context Ctx { get; set; } = null; + public class ReadToEndResult { + public bool IsSuccess { get; set; } + public string StdOut { get; set; } + public string StdErr { get; set; } + } - /// - /// 运行路径 - /// - public string Cwd { get; set; } = ""; - - /// - /// 参数 - /// - public string Args { get; set; } = ""; - - /// - /// 是否忽略错误 - /// - public bool DontRaiseError { get; set; } = false; - - /// - /// 使用标准错误输出 - /// + public string Context { get; set; } = string.Empty; + public CancelToken Cancel { get; set; } = null; + public string WorkingDirectory { get; set; } = null; + public string Args { get; set; } = string.Empty; + public bool RaiseError { get; set; } = true; public bool TraitErrorAsOutput { get; set; } = false; - /// - /// 运行 - /// public bool Exec() { var start = new ProcessStartInfo(); - start.FileName = Models.Preference.Instance.Git.Path; + start.FileName = Native.OS.GitExecutableFile; start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.UseShellExecute = false; start.CreateNoWindow = true; @@ -67,49 +35,53 @@ namespace SourceGit.Commands { start.StandardOutputEncoding = Encoding.UTF8; start.StandardErrorEncoding = Encoding.UTF8; - if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd; + if (!string.IsNullOrEmpty(WorkingDirectory)) start.WorkingDirectory = WorkingDirectory; var errs = new List(); var proc = new Process() { StartInfo = start }; var isCancelled = false; - proc.OutputDataReceived += (o, e) => { - if (Ctx != null && Ctx.IsCancelRequested) { + proc.OutputDataReceived += (_, e) => { + if (Cancel != null && Cancel.Requested) { isCancelled = true; proc.CancelErrorRead(); proc.CancelOutputRead(); - if (!proc.HasExited) proc.Kill(); + if (!proc.HasExited) proc.Kill(true); return; } - if (e.Data == null) return; - OnReadline(e.Data); + if (e.Data != null) OnReadline(e.Data); }; - proc.ErrorDataReceived += (o, e) => { - if (Ctx != null && Ctx.IsCancelRequested) { + + proc.ErrorDataReceived += (_, e) => { + if (Cancel != null && Cancel.Requested) { isCancelled = true; proc.CancelErrorRead(); proc.CancelOutputRead(); - if (!proc.HasExited) proc.Kill(); + if (!proc.HasExited) proc.Kill(true); return; } if (string.IsNullOrEmpty(e.Data)) return; if (TraitErrorAsOutput) OnReadline(e.Data); - // 错误信息中忽略进度相关的输出 + // Ignore progress messages if (e.Data.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal)) return; if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal)) return; if (e.Data.StartsWith("remote: Compressing objects:", StringComparison.Ordinal)) return; if (e.Data.StartsWith("Filtering content:", StringComparison.Ordinal)) return; - if (PROGRESS_REG.IsMatch(e.Data)) return; + if (_progressRegex.IsMatch(e.Data)) return; errs.Add(e.Data); }; try { proc.Start(); } catch (Exception e) { - if (!DontRaiseError) OnException(e.Message); + if (RaiseError) { + Dispatcher.UIThread.Invoke(() => { + App.RaiseException(Context, e.Message); + }); + } return false; } @@ -121,19 +93,20 @@ namespace SourceGit.Commands { proc.Close(); if (!isCancelled && exitCode != 0 && errs.Count > 0) { - if (!DontRaiseError) OnException(string.Join("\n", errs)); + if (RaiseError) { + Dispatcher.UIThread.Invoke(() => { + App.RaiseException(Context, string.Join("\n", errs)); + }); + } return false; } else { return true; } } - /// - /// 直接读取全部标准输出 - /// public ReadToEndResult ReadToEnd() { var start = new ProcessStartInfo(); - start.FileName = Models.Preference.Instance.Git.Path; + start.FileName = Native.OS.GitExecutableFile; start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.UseShellExecute = false; start.CreateNoWindow = true; @@ -142,22 +115,23 @@ namespace SourceGit.Commands { start.StandardOutputEncoding = Encoding.UTF8; start.StandardErrorEncoding = Encoding.UTF8; - if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd; + if (!string.IsNullOrEmpty(WorkingDirectory)) start.WorkingDirectory = WorkingDirectory; var proc = new Process() { StartInfo = start }; try { proc.Start(); } catch (Exception e) { return new ReadToEndResult() { - Output = string.Empty, - Error = e.Message, IsSuccess = false, + StdOut = string.Empty, + StdErr = e.Message, }; } - var rs = new ReadToEndResult(); - rs.Output = proc.StandardOutput.ReadToEnd(); - rs.Error = proc.StandardError.ReadToEnd(); + var rs = new ReadToEndResult() { + StdOut = proc.StandardOutput.ReadToEnd(), + StdErr = proc.StandardError.ReadToEnd(), + }; proc.WaitForExit(); rs.IsSuccess = proc.ExitCode == 0; @@ -166,19 +140,8 @@ namespace SourceGit.Commands { return rs; } - /// - /// 调用Exec时的读取函数 - /// - /// - public virtual void OnReadline(string line) { - } + protected virtual void OnReadline(string line) { } - /// - /// 默认异常处理函数 - /// - /// - public virtual void OnException(string message) { - App.Exception(Cwd, message); - } + private static readonly Regex _progressRegex = new Regex(@"\d+%"); } -} +} \ No newline at end of file diff --git a/src/Commands/Commit.cs b/src/Commands/Commit.cs index a3dde9a9..2733590c 100644 --- a/src/Commands/Commit.cs +++ b/src/Commands/Commit.cs @@ -1,17 +1,16 @@ -using System.IO; +using System.IO; namespace SourceGit.Commands { - /// - /// `git commit`命令 - /// public class Commit : Command { - public Commit(string repo, string message, bool amend) { + public Commit(string repo, string message, bool amend, bool allowEmpty = false) { var file = Path.GetTempFileName(); File.WriteAllText(file, message); - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = $"commit --file=\"{file}\""; if (amend) Args += " --amend --no-edit"; + if (allowEmpty) Args += " --allow-empty"; } } } diff --git a/src/Commands/CommitChanges.cs b/src/Commands/CommitChanges.cs deleted file mode 100644 index defcbff2..00000000 --- a/src/Commands/CommitChanges.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Commands { - - /// - /// 取得一个提交的变更列表 - /// - public class CommitChanges : Command { - private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); - private List changes = new List(); - - public CommitChanges(string cwd, string commit) { - Cwd = cwd; - Args = $"show --name-status {commit}"; - } - - public List Result() { - Exec(); - return changes; - } - - public override void OnReadline(string line) { - var match = REG_FORMAT.Match(line); - if (!match.Success) return; - - var change = new Models.Change() { Path = match.Groups[2].Value }; - var status = match.Groups[1].Value; - - switch (status[0]) { - case 'M': change.Set(Models.Change.Status.Modified); changes.Add(change); break; - case 'A': change.Set(Models.Change.Status.Added); changes.Add(change); break; - case 'D': change.Set(Models.Change.Status.Deleted); changes.Add(change); break; - case 'R': change.Set(Models.Change.Status.Renamed); changes.Add(change); break; - case 'C': change.Set(Models.Change.Status.Copied); changes.Add(change); break; - } - } - } -} diff --git a/src/Commands/CommitRangeChanges.cs b/src/Commands/CommitRangeChanges.cs deleted file mode 100644 index 05cc778e..00000000 --- a/src/Commands/CommitRangeChanges.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Commands { - - /// - /// 对比两个提交间的变更 - /// - public class CommitRangeChanges : Command { - private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); - private List changes = new List(); - - public CommitRangeChanges(string cwd, string start, string end) { - Cwd = cwd; - Args = $"diff --name-status {start} {end}"; - } - - public List Result() { - Exec(); - return changes; - } - - public override void OnReadline(string line) { - var match = REG_FORMAT.Match(line); - if (!match.Success) return; - - var change = new Models.Change() { Path = match.Groups[2].Value }; - var status = match.Groups[1].Value; - - switch (status[0]) { - case 'M': change.Set(Models.Change.Status.Modified); changes.Add(change); break; - case 'A': change.Set(Models.Change.Status.Added); changes.Add(change); break; - case 'D': change.Set(Models.Change.Status.Deleted); changes.Add(change); break; - case 'R': change.Set(Models.Change.Status.Renamed); changes.Add(change); break; - case 'C': change.Set(Models.Change.Status.Copied); changes.Add(change); break; - } - } - } -} diff --git a/src/Commands/CompareRevisions.cs b/src/Commands/CompareRevisions.cs new file mode 100644 index 00000000..2b10adab --- /dev/null +++ b/src/Commands/CompareRevisions.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class CompareRevisions : Command { + private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); + + public CompareRevisions(string repo, string start, string end) { + WorkingDirectory = repo; + Context = repo; + Args = $"diff --name-status {start} {end}"; + } + + public List Result() { + Exec(); + _changes.Sort((l, r) => l.Path.CompareTo(r.Path)); + return _changes; + } + + protected override void OnReadline(string line) { + var match = REG_FORMAT.Match(line); + if (!match.Success) return; + + var change = new Models.Change() { Path = match.Groups[2].Value }; + var status = match.Groups[1].Value; + + switch (status[0]) { + case 'M': change.Set(Models.ChangeState.Modified); _changes.Add(change); break; + case 'A': change.Set(Models.ChangeState.Added); _changes.Add(change); break; + case 'D': change.Set(Models.ChangeState.Deleted); _changes.Add(change); break; + case 'R': change.Set(Models.ChangeState.Renamed); _changes.Add(change); break; + case 'C': change.Set(Models.ChangeState.Copied); _changes.Add(change); break; + } + } + + private List _changes = new List(); + } +} diff --git a/src/Commands/Config.cs b/src/Commands/Config.cs index f77216dd..4a0ebb30 100644 --- a/src/Commands/Config.cs +++ b/src/Commands/Config.cs @@ -1,32 +1,55 @@ +using System; +using System.Collections.Generic; + namespace SourceGit.Commands { - /// - /// config命令 - /// - public class Config : Command { + public class Config : Command { + public Config(string repository) { + WorkingDirectory = repository; + Context = repository; + RaiseError = false; + } - public Config() { } + public Dictionary ListAll() { + Args = "config -l"; - public Config(string repo) { - Cwd = repo; + var output = ReadToEnd(); + var rs = new Dictionary(); + if (output.IsSuccess) { + var lines = output.StdOut.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) { + var idx = line.IndexOf('='); + if (idx != -1) { + var key = line.Substring(0, idx).Trim(); + var val = line.Substring(idx+1).Trim(); + if (rs.ContainsKey(key)) { + rs[key] = val; + } else { + rs.Add(key, val); + } + } + } + } + + return rs; } public string Get(string key) { Args = $"config {key}"; - return ReadToEnd().Output.Trim(); + return ReadToEnd().StdOut.Trim(); } - public bool Set(string key, string val, bool allowEmpty = false) { - if (!allowEmpty && string.IsNullOrEmpty(val)) { - if (string.IsNullOrEmpty(Cwd)) { + public bool Set(string key, string value, bool allowEmpty = false) { + if (!allowEmpty && string.IsNullOrWhiteSpace(value)) { + if (string.IsNullOrEmpty(WorkingDirectory)) { Args = $"config --global --unset {key}"; } else { Args = $"config --unset {key}"; } } else { - if (string.IsNullOrEmpty(Cwd)) { - Args = $"config --global {key} \"{val}\""; + if (string.IsNullOrWhiteSpace(WorkingDirectory)) { + Args = $"config --global {key} \"{value}\""; } else { - Args = $"config {key} \"{val}\""; + Args = $"config {key} \"{value}\""; } } diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index 4e4e641a..983b60c1 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -1,111 +1,147 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// Diff命令(用于文件文件比对) - /// public class Diff : Command { private static readonly Regex REG_INDICATOR = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@"); - private Models.TextChanges changes = new Models.TextChanges(); - private List deleted = new List(); - private List added = new List(); - private int oldLine = 0; - private int newLine = 0; - private int lineIndex = 0; + private static readonly string PREFIX_LFS = " version https://git-lfs.github.com/spec/"; - public Diff(string repo, string args) { - Cwd = repo; - Args = $"diff --ignore-cr-at-eol --unified=4 {args}"; + public Diff(string repo, Models.DiffOption opt) { + WorkingDirectory = repo; + Context = repo; + Args = $"diff --ignore-cr-at-eol --unified=4 {opt}"; } - public Models.TextChanges Result() { + public Models.DiffResult Result() { Exec(); - ProcessChanges(); - if (changes.IsBinary) changes.Lines.Clear(); - lineIndex = 0; - return changes; + + if (_result.IsBinary || _result.IsLFS) { + _result.TextDiff = null; + } else { + ProcessInlineHighlights(); + + if (_result.TextDiff.Lines.Count == 0) { + _result.TextDiff = null; + } else { + _result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine); + } + } + + return _result; } - public override void OnReadline(string line) { - if (changes.IsBinary) return; + protected override void OnReadline(string line) { + if (_result.IsBinary) return; - if (changes.Lines.Count == 0) { + if (_result.IsLFS) { + var ch = line[0]; + if (ch == '-') { + line = line.Substring(1); + if (line.StartsWith("oid sha256:")) { + _result.LFSDiff.Old.Oid = line.Substring(11); + } else if (line.StartsWith("size ")) { + _result.LFSDiff.Old.Size = long.Parse(line.Substring(5)); + } + } else if (ch == '+') { + line = line.Substring(1); + if (line.StartsWith("oid sha256:")) { + _result.LFSDiff.New.Oid = line.Substring(11); + } else if (line.StartsWith("size ")) { + _result.LFSDiff.New.Size = long.Parse(line.Substring(5)); + } + } else if (line.StartsWith(" size ")) { + _result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.Substring(6)); + } + return; + } + + if (_result.TextDiff.Lines.Count == 0) { var match = REG_INDICATOR.Match(line); if (!match.Success) { - if (line.StartsWith("Binary", StringComparison.Ordinal)) changes.IsBinary = true; + if (line.StartsWith("Binary", StringComparison.Ordinal)) _result.IsBinary = true; return; } - oldLine = int.Parse(match.Groups[1].Value); - newLine = int.Parse(match.Groups[2].Value); - changes.Lines.Add(new Models.TextChanges.Line(lineIndex++, Models.TextChanges.LineMode.Indicator, line, "", "")); + _oldLine = int.Parse(match.Groups[1].Value); + _newLine = int.Parse(match.Groups[2].Value); + _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, "", "")); } else { if (line.Length == 0) { - ProcessChanges(); - changes.Lines.Add(new Models.TextChanges.Line(lineIndex++, Models.TextChanges.LineMode.Normal, "", $"{oldLine}", $"{newLine}")); - oldLine++; - newLine++; + ProcessInlineHighlights(); + _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", $"{_oldLine}", $"{_newLine}")); + _oldLine++; + _newLine++; return; } var ch = line[0]; if (ch == '-') { - deleted.Add(new Models.TextChanges.Line(lineIndex++, Models.TextChanges.LineMode.Deleted, line.Substring(1), $"{oldLine}", "")); - oldLine++; + _deleted.Add(new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), $"{_oldLine}", "")); + _oldLine++; } else if (ch == '+') { - added.Add(new Models.TextChanges.Line(lineIndex++, Models.TextChanges.LineMode.Added, line.Substring(1), "", $"{newLine}")); - newLine++; + _added.Add(new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), "", $"{_newLine}")); + _newLine++; } else if (ch != '\\') { - ProcessChanges(); + ProcessInlineHighlights(); var match = REG_INDICATOR.Match(line); if (match.Success) { - oldLine = int.Parse(match.Groups[1].Value); - newLine = int.Parse(match.Groups[2].Value); - changes.Lines.Add(new Models.TextChanges.Line(lineIndex++, Models.TextChanges.LineMode.Indicator, line, "", "")); + _oldLine = int.Parse(match.Groups[1].Value); + _newLine = int.Parse(match.Groups[2].Value); + _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, "", "")); } else { - changes.Lines.Add(new Models.TextChanges.Line(lineIndex++, Models.TextChanges.LineMode.Normal, line.Substring(1), $"{oldLine}", $"{newLine}")); - oldLine++; - newLine++; + if (line.StartsWith(PREFIX_LFS)) { + _result.IsLFS = true; + _result.LFSDiff = new Models.LFSDiff(); + return; + } + + _result.TextDiff.Lines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), $"{_oldLine}", $"{_newLine}")); + _oldLine++; + _newLine++; } } } } - private void ProcessChanges() { - if (deleted.Any()) { - if (added.Count == deleted.Count) { - for (int i = added.Count - 1; i >= 0; i--) { - var left = deleted[i]; - var right = added[i]; + private void ProcessInlineHighlights() { + if (_deleted.Count > 0) { + if (_added.Count == _deleted.Count) { + for (int i = _added.Count - 1; i >= 0; i--) { + var left = _deleted[i]; + var right = _added[i]; if (left.Content.Length > 1024 || right.Content.Length > 1024) continue; - var chunks = Models.TextCompare.Process(left.Content, right.Content); + var chunks = Models.TextInlineChange.Compare(left.Content, right.Content); if (chunks.Count > 4) continue; foreach (var chunk in chunks) { if (chunk.DeletedCount > 0) { - left.Highlights.Add(new Models.TextChanges.HighlightRange(chunk.DeletedStart, chunk.DeletedCount)); + left.Highlights.Add(new Models.TextInlineRange(chunk.DeletedStart, chunk.DeletedCount)); } if (chunk.AddedCount > 0) { - right.Highlights.Add(new Models.TextChanges.HighlightRange(chunk.AddedStart, chunk.AddedCount)); + right.Highlights.Add(new Models.TextInlineRange(chunk.AddedStart, chunk.AddedCount)); } } } } - changes.Lines.AddRange(deleted); - deleted.Clear(); + _result.TextDiff.Lines.AddRange(_deleted); + _deleted.Clear(); } - if (added.Any()) { - changes.Lines.AddRange(added); - added.Clear(); + if (_added.Count > 0) { + _result.TextDiff.Lines.AddRange(_added); + _added.Clear(); } } + + private Models.DiffResult _result = new Models.DiffResult() { TextDiff = new Models.TextDiff() }; + private List _deleted = new List(); + private List _added = new List(); + private int _oldLine = 0; + private int _newLine = 0; } } diff --git a/src/Commands/Discard.cs b/src/Commands/Discard.cs index dd35d0ca..0480c7e6 100644 --- a/src/Commands/Discard.cs +++ b/src/Commands/Discard.cs @@ -1,28 +1,19 @@ -using System; +using System; using System.Collections.Generic; namespace SourceGit.Commands { - /// - /// 忽略变更 - /// - public class Discard { - private string repo = null; - - public Discard(string repo) { - this.repo = repo; - } - - public void Whole() { + public static class Discard { + public static void All(string repo) { new Reset(repo, "HEAD", "--hard").Exec(); new Clean(repo).Exec(); } - public void Changes(List changes) { + public static void Changes(string repo, List changes) { var needClean = new List(); var needCheckout = new List(); foreach (var c in changes) { - if (c.WorkTree == Models.Change.Status.Untracked || c.WorkTree == Models.Change.Status.Added) { + if (c.WorkTree == Models.ChangeState.Untracked || c.WorkTree == Models.ChangeState.Added) { needClean.Add(c.Path); } else { needCheckout.Add(c.Path); diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs index ad3925cc..ad36051f 100644 --- a/src/Commands/Fetch.cs +++ b/src/Commands/Fetch.cs @@ -1,17 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Threading; +using System; namespace SourceGit.Commands { - - /// - /// 拉取 - /// public class Fetch : Command { - private Action handler = null; - public Fetch(string repo, string remote, bool prune, Action outputHandler) { - Cwd = repo; + _outputHandler = outputHandler; + WorkingDirectory = repo; + Context = repo; TraitErrorAsOutput = true; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); @@ -24,12 +18,12 @@ namespace SourceGit.Commands { Args += "fetch --progress --verbose "; if (prune) Args += "--prune "; Args += remote; - handler = outputHandler; - AutoFetch.MarkFetched(repo); } public Fetch(string repo, string remote, string localBranch, string remoteBranch, Action outputHandler) { - Cwd = repo; + _outputHandler = outputHandler; + WorkingDirectory = repo; + Context = repo; TraitErrorAsOutput = true; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); @@ -40,63 +34,12 @@ namespace SourceGit.Commands { } Args += $"fetch --progress --verbose {remote} {remoteBranch}:{localBranch}"; - handler = outputHandler; } - public override void OnReadline(string line) { - handler?.Invoke(line); - } - } - - /// - /// 自动拉取(每隔10分钟) - /// - public class AutoFetch { - private static Dictionary jobs = new Dictionary(); - - private Fetch cmd = null; - private long nextFetchPoint = 0; - private Timer timer = null; - - public static void Start(string repo) { - if (!Models.Preference.Instance.Git.AutoFetchRemotes) return; - - // 只自动更新加入管理列表中的仓库(子模块等不自动更新) - var exists = Models.Preference.Instance.FindRepository(repo); - if (exists == null) return; - - var job = new AutoFetch(repo); - jobs.Add(repo, job); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } - public static void MarkFetched(string repo) { - if (!jobs.ContainsKey(repo)) return; - jobs[repo].nextFetchPoint = DateTime.Now.AddMinutes(10).ToFileTime(); - } - - public static void Stop(string repo) { - if (!jobs.ContainsKey(repo)) return; - - jobs[repo].timer.Dispose(); - jobs.Remove(repo); - } - - public AutoFetch(string repo) { - cmd = new Fetch(repo, "--all", true, null); - cmd.DontRaiseError = true; - - nextFetchPoint = DateTime.Now.AddMinutes(10).ToFileTime(); - timer = new Timer(OnTick, null, 60000, 10000); - } - - private void OnTick(object o) { - var now = DateTime.Now.ToFileTime(); - if (nextFetchPoint > now) return; - - Models.Watcher.SetEnabled(cmd.Cwd, false); - cmd.Exec(); - nextFetchPoint = DateTime.Now.AddMinutes(10).ToFileTime(); - Models.Watcher.SetEnabled(cmd.Cwd, true); - } + private Action _outputHandler; } } diff --git a/src/Commands/FormatPatch.cs b/src/Commands/FormatPatch.cs index af5fb624..c139c477 100644 --- a/src/Commands/FormatPatch.cs +++ b/src/Commands/FormatPatch.cs @@ -1,12 +1,9 @@ -namespace SourceGit.Commands { - /// - /// 将Commit另存为Patch文件 - /// +namespace SourceGit.Commands { public class FormatPatch : Command { - - public FormatPatch(string repo, string commit, string path) { - Cwd = repo; - Args = $"format-patch {commit} -1 -o \"{path}\""; + public FormatPatch(string repo, string commit, string saveTo) { + WorkingDirectory = repo; + Context = repo; + Args = $"format-patch {commit} -1 -o \"{saveTo}\""; } } } diff --git a/src/Commands/GC.cs b/src/Commands/GC.cs index ceadb0b7..0c9d5761 100644 --- a/src/Commands/GC.cs +++ b/src/Commands/GC.cs @@ -1,21 +1,19 @@ using System; namespace SourceGit.Commands { - /// - /// GC - /// public class GC : Command { - private Action handler; - - public GC(string repo, Action onProgress) { - Cwd = repo; - Args = "gc"; + public GC(string repo, Action outputHandler) { + _outputHandler = outputHandler; + WorkingDirectory = repo; + Context = repo; TraitErrorAsOutput = true; - handler = onProgress; + Args = "gc"; } - public override void OnReadline(string line) { - handler?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler; } } diff --git a/src/Commands/GetRepositoryRootPath.cs b/src/Commands/GetRepositoryRootPath.cs deleted file mode 100644 index c4dc6777..00000000 --- a/src/Commands/GetRepositoryRootPath.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SourceGit.Commands { - /// - /// 取得一个库的根路径 - /// - public class GetRepositoryRootPath : Command { - public GetRepositoryRootPath(string path) { - Cwd = path; - Args = "rev-parse --show-toplevel"; - } - - public string Result() { - var rs = ReadToEnd().Output; - if (string.IsNullOrEmpty(rs)) return null; - return rs.Trim(); - } - } -} diff --git a/src/Commands/GitFlow.cs b/src/Commands/GitFlow.cs index b6707b06..8aaa45b6 100644 --- a/src/Commands/GitFlow.cs +++ b/src/Commands/GitFlow.cs @@ -1,24 +1,22 @@ -namespace SourceGit.Commands { - /// - /// Git-Flow命令 - /// - public class GitFlow : Command { +using System.Collections.Generic; +namespace SourceGit.Commands { + public class GitFlow : Command { public GitFlow(string repo) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; } - public bool Init(string master, string develop, string feature, string release, string hotfix, string version) { - var branches = new Branches(Cwd).Result(); + public bool Init(List branches, string master, string develop, string feature, string release, string hotfix, string version) { var current = branches.Find(x => x.IsCurrent); var masterBranch = branches.Find(x => x.Name == master); - if (masterBranch == null && current != null) new Branch(Cwd, develop).Create(current.Head); + if (masterBranch == null && current != null) Branch.Create(WorkingDirectory, master, current.Head); var devBranch = branches.Find(x => x.Name == develop); - if (devBranch == null && current != null) new Branch(Cwd, develop).Create(current.Head); + if (devBranch == null && current != null) Branch.Create(WorkingDirectory, develop, current.Head); - var cmd = new Config(Cwd); + var cmd = new Config(WorkingDirectory); cmd.Set("gitflow.branch.master", master); cmd.Set("gitflow.branch.develop", develop); cmd.Set("gitflow.prefix.feature", feature); @@ -32,7 +30,7 @@ namespace SourceGit.Commands { return Exec(); } - public void Start(Models.GitFlowBranchType type, string name) { + public bool Start(Models.GitFlowBranchType type, string name) { switch (type) { case Models.GitFlowBranchType.Feature: Args = $"flow feature start {name}"; @@ -44,13 +42,14 @@ namespace SourceGit.Commands { Args = $"flow hotfix start {name}"; break; default: - return; + App.RaiseException(Context, "Bad branch type!!!"); + return false; } - Exec(); + return Exec(); } - public void Finish(Models.GitFlowBranchType type, string name, bool keepBranch) { + public bool Finish(Models.GitFlowBranchType type, string name, bool keepBranch) { var option = keepBranch ? "-k" : string.Empty; switch (type) { case Models.GitFlowBranchType.Feature: @@ -63,10 +62,11 @@ namespace SourceGit.Commands { Args = $"flow hotfix finish {option} {name} -m \"HOTFIX_DONE\""; break; default: - return; + App.RaiseException(Context, "Bad branch type!!!"); + return false; } - Exec(); + return Exec(); } } } diff --git a/src/Commands/Init.cs b/src/Commands/Init.cs index 35dde5a2..f009ebe0 100644 --- a/src/Commands/Init.cs +++ b/src/Commands/Init.cs @@ -1,12 +1,8 @@ -namespace SourceGit.Commands { - - /// - /// 初始化Git仓库 - /// +namespace SourceGit.Commands { public class Init : Command { - - public Init(string workDir) { - Cwd = workDir; + public Init(string ctx, string dir) { + Context = ctx; + WorkingDirectory = dir; Args = "init -q"; } } diff --git a/src/Commands/IsBinary.cs b/src/Commands/IsBinary.cs new file mode 100644 index 00000000..d9096aa2 --- /dev/null +++ b/src/Commands/IsBinary.cs @@ -0,0 +1,18 @@ +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class IsBinary : Command { + private static readonly Regex REG_TEST = new Regex(@"^\-\s+\-\s+.*$"); + + public IsBinary(string repo, string commit, string path) { + WorkingDirectory = repo; + Context = repo; + Args = $"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {commit} --numstat -- \"{path}\""; + RaiseError = false; + } + + public bool Result() { + return REG_TEST.IsMatch(ReadToEnd().StdOut); + } + } +} diff --git a/src/Commands/IsBinaryFile.cs b/src/Commands/IsBinaryFile.cs deleted file mode 100644 index 68cbff62..00000000 --- a/src/Commands/IsBinaryFile.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.RegularExpressions; - -namespace SourceGit.Commands { - /// - /// 查询指定版本下的某文件是否是二进制文件 - /// - public class IsBinaryFile : Command { - private static readonly Regex REG_TEST = new Regex(@"^\-\s+\-\s+.*$"); - public IsBinaryFile(string repo, string commit, string path) { - Cwd = repo; - Args = $"diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 {commit} --numstat -- \"{path}\""; - } - - public bool Result() { - return REG_TEST.IsMatch(ReadToEnd().Output); - } - } -} diff --git a/src/Commands/IsLFSFiltered.cs b/src/Commands/IsLFSFiltered.cs new file mode 100644 index 00000000..39e24654 --- /dev/null +++ b/src/Commands/IsLFSFiltered.cs @@ -0,0 +1,15 @@ +namespace SourceGit.Commands { + public class IsLFSFiltered : Command { + public IsLFSFiltered(string repo, string path) { + WorkingDirectory = repo; + Context = repo; + Args = $"check-attr -a -z \"{path}\""; + RaiseError = false; + } + + public bool Result() { + var rs = ReadToEnd(); + return rs.IsSuccess && rs.StdOut.Contains("filter\0lfs"); + } + } +} diff --git a/src/Commands/LFS.cs b/src/Commands/LFS.cs index 87d3e378..d632a25b 100644 --- a/src/Commands/LFS.cs +++ b/src/Commands/LFS.cs @@ -1,51 +1,40 @@ -using System; +using System; using System.IO; namespace SourceGit.Commands { - /// - /// LFS相关 - /// public class LFS { - private string repo; - - private class PruneCmd : Command { - private Action handler; - + class PruneCmd : Command { public PruneCmd(string repo, Action onProgress) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = "lfs prune"; TraitErrorAsOutput = true; - handler = onProgress; + _outputHandler = onProgress; } - public override void OnReadline(string line) { - handler?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler; } public LFS(string repo) { - this.repo = repo; + _repo = repo; } public bool IsEnabled() { - var path = Path.Combine(repo, ".git", "hooks", "pre-push"); + var path = Path.Combine(_repo, ".git", "hooks", "pre-push"); if (!File.Exists(path)) return false; var content = File.ReadAllText(path); return content.Contains("git lfs pre-push"); } - public bool IsFiltered(string path) { - var cmd = new Command(); - cmd.Cwd = repo; - cmd.Args = $"check-attr -a -z \"{path}\""; - - var rs = cmd.ReadToEnd(); - return rs.Output.Contains("filter\0lfs"); + public void Prune(Action outputHandler) { + new PruneCmd(_repo, outputHandler).Exec(); } - public void Prune(Action onProgress) { - new PruneCmd(repo, onProgress).Exec(); - } + private string _repo; } } diff --git a/src/Commands/LocalChanges.cs b/src/Commands/LocalChanges.cs deleted file mode 100644 index edb156db..00000000 --- a/src/Commands/LocalChanges.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Commands { - /// - /// 取得本地工作副本变更 - /// - public class LocalChanges : Command { - private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); - private static readonly string[] UNTRACKED = new string[] { "no", "all" }; - private List changes = new List(); - - public LocalChanges(string path, bool includeUntracked = true) { - Cwd = path; - Args = $"status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain"; - } - - public List Result() { - Exec(); - return changes; - } - - public override void OnReadline(string line) { - var match = REG_FORMAT.Match(line); - if (!match.Success) return; - if (line.EndsWith("/", StringComparison.Ordinal)) return; // Ignore changes with git-worktree - - var change = new Models.Change() { Path = match.Groups[2].Value }; - var status = match.Groups[1].Value; - - switch (status) { - case " M": change.Set(Models.Change.Status.None, Models.Change.Status.Modified); break; - case " A": change.Set(Models.Change.Status.None, Models.Change.Status.Added); break; - case " D": change.Set(Models.Change.Status.None, Models.Change.Status.Deleted); break; - case " R": change.Set(Models.Change.Status.None, Models.Change.Status.Renamed); break; - case " C": change.Set(Models.Change.Status.None, Models.Change.Status.Copied); break; - case "M": change.Set(Models.Change.Status.Modified, Models.Change.Status.None); break; - case "MM": change.Set(Models.Change.Status.Modified, Models.Change.Status.Modified); break; - case "MD": change.Set(Models.Change.Status.Modified, Models.Change.Status.Deleted); break; - case "A": change.Set(Models.Change.Status.Added, Models.Change.Status.None); break; - case "AM": change.Set(Models.Change.Status.Added, Models.Change.Status.Modified); break; - case "AD": change.Set(Models.Change.Status.Added, Models.Change.Status.Deleted); break; - case "D": change.Set(Models.Change.Status.Deleted, Models.Change.Status.None); break; - case "R": change.Set(Models.Change.Status.Renamed, Models.Change.Status.None); break; - case "RM": change.Set(Models.Change.Status.Renamed, Models.Change.Status.Modified); break; - case "RD": change.Set(Models.Change.Status.Renamed, Models.Change.Status.Deleted); break; - case "C": change.Set(Models.Change.Status.Copied, Models.Change.Status.None); break; - case "CM": change.Set(Models.Change.Status.Copied, Models.Change.Status.Modified); break; - case "CD": change.Set(Models.Change.Status.Copied, Models.Change.Status.Deleted); break; - case "DR": change.Set(Models.Change.Status.Deleted, Models.Change.Status.Renamed); break; - case "DC": change.Set(Models.Change.Status.Deleted, Models.Change.Status.Copied); break; - case "DD": change.Set(Models.Change.Status.Deleted, Models.Change.Status.Deleted); break; - case "AU": change.Set(Models.Change.Status.Added, Models.Change.Status.Unmerged); break; - case "UD": change.Set(Models.Change.Status.Unmerged, Models.Change.Status.Deleted); break; - case "UA": change.Set(Models.Change.Status.Unmerged, Models.Change.Status.Added); break; - case "DU": change.Set(Models.Change.Status.Deleted, Models.Change.Status.Unmerged); break; - case "AA": change.Set(Models.Change.Status.Added, Models.Change.Status.Added); break; - case "UU": change.Set(Models.Change.Status.Unmerged, Models.Change.Status.Unmerged); break; - case "??": change.Set(Models.Change.Status.Untracked, Models.Change.Status.Untracked); break; - default: return; - } - - changes.Add(change); - } - } -} diff --git a/src/Commands/Merge.cs b/src/Commands/Merge.cs index a44c72a1..9854e8c5 100644 --- a/src/Commands/Merge.cs +++ b/src/Commands/Merge.cs @@ -1,21 +1,19 @@ -using System; +using System; namespace SourceGit.Commands { - /// - /// 合并分支 - /// public class Merge : Command { - private Action handler = null; - - public Merge(string repo, string source, string mode, Action onProgress) { - Cwd = repo; - Args = $"merge --progress {source} {mode}"; + public Merge(string repo, string source, string mode, Action outputHandler) { + _outputHandler = outputHandler; + WorkingDirectory = repo; + Context = repo; TraitErrorAsOutput = true; - handler = onProgress; + Args = $"merge --progress {source} {mode}"; } - public override void OnReadline(string line) { - handler?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler = null; } } diff --git a/src/Commands/MergeTool.cs b/src/Commands/MergeTool.cs new file mode 100644 index 00000000..4d02f029 --- /dev/null +++ b/src/Commands/MergeTool.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace SourceGit.Commands { + public static class MergeTool { + public static bool OpenForMerge(string repo, string tool, string mergeCmd, string file) { + if (string.IsNullOrWhiteSpace(tool) || string.IsNullOrWhiteSpace(mergeCmd)) { + App.RaiseException(repo, "Invalid external merge tool settings!"); + return false; + } + + if (!File.Exists(tool)) { + App.RaiseException(repo, $"Can NOT found external merge tool in '{tool}'!"); + return false; + } + + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.RaiseError = false; + cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{tool}\\\" {mergeCmd}\" -c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true mergetool --tool=sourcegit \"{file}\""; + return cmd.Exec(); + } + + public static bool OpenForDiff(string repo, string tool, string diffCmd, Models.DiffOption option) { + if (string.IsNullOrWhiteSpace(tool) || string.IsNullOrWhiteSpace(diffCmd)) { + App.RaiseException(repo, "Invalid external merge tool settings!"); + return false; + } + + if (!File.Exists(tool)) { + App.RaiseException(repo, $"Can NOT found external merge tool in '{tool}'!"); + return false; + } + + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.RaiseError = false; + cmd.Args = $"-c difftool.sourcegit.cmd=\"\\\"{tool}\\\" {diffCmd}\" difftool --tool=sourcegit --no-prompt {option}"; + return cmd.Exec(); + } + } +} diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs index e2ab522a..f9112206 100644 --- a/src/Commands/Pull.cs +++ b/src/Commands/Pull.cs @@ -1,19 +1,12 @@ -using System; +using System; namespace SourceGit.Commands { - - /// - /// 拉回 - /// public class Pull : Command { - private Action handler = null; - private bool needStash = false; - - public Pull(string repo, string remote, string branch, bool useRebase, bool autoStash, Action onProgress) { - Cwd = repo; + public Pull(string repo, string remote, string branch, bool useRebase, Action outputHandler) { + _outputHandler = outputHandler; + WorkingDirectory = repo; + Context = repo; TraitErrorAsOutput = true; - handler = onProgress; - needStash = autoStash; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); if (!string.IsNullOrEmpty(sshKey)) { @@ -27,25 +20,10 @@ namespace SourceGit.Commands { Args += $"{remote} {branch}"; } - public bool Run() { - if (needStash) { - var changes = new LocalChanges(Cwd).Result(); - if (changes.Count > 0) { - if (!new Stash(Cwd).Push(changes, "PULL_AUTO_STASH", true)) { - return false; - } - } else { - needStash = false; - } - } - - var succ = Exec(); - if (succ && needStash) new Stash(Cwd).Pop("stash@{0}"); - return succ; + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } - public override void OnReadline(string line) { - handler?.Invoke(line); - } + private Action _outputHandler; } } diff --git a/src/Commands/Push.cs b/src/Commands/Push.cs index 7784c581..8839666b 100644 --- a/src/Commands/Push.cs +++ b/src/Commands/Push.cs @@ -1,16 +1,12 @@ -using System; +using System; namespace SourceGit.Commands { - /// - /// 推送 - /// public class Push : Command { - private Action handler = null; - public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool force, bool track, Action onProgress) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; TraitErrorAsOutput = true; - handler = onProgress; + _outputHandler = onProgress; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); if (!string.IsNullOrEmpty(sshKey)) { @@ -28,8 +24,16 @@ namespace SourceGit.Commands { Args += $"{remote} {local}:{remoteBranch}"; } + /// + /// Only used to delete a remote branch!!!!!! + /// + /// + /// + /// public Push(string repo, string remote, string branch) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; + TraitErrorAsOutput = true; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); if (!string.IsNullOrEmpty(sshKey)) { @@ -42,7 +46,8 @@ namespace SourceGit.Commands { } public Push(string repo, string remote, string tag, bool isDelete) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); if (!string.IsNullOrEmpty(sshKey)) { @@ -56,8 +61,10 @@ namespace SourceGit.Commands { Args += $"{remote} refs/tags/{tag}"; } - public override void OnReadline(string line) { - handler?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler = null; } } diff --git a/src/Commands/Branches.cs b/src/Commands/QueryBranches.cs similarity index 80% rename from src/Commands/Branches.cs rename to src/Commands/QueryBranches.cs index 8dfdfae1..8c2db9a2 100644 --- a/src/Commands/Branches.cs +++ b/src/Commands/QueryBranches.cs @@ -1,31 +1,26 @@ -using System; +using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// 解析所有的分支 - /// - public class Branches : Command { + public class QueryBranches : Command { private static readonly string PREFIX_LOCAL = "refs/heads/"; private static readonly string PREFIX_REMOTE = "refs/remotes/"; - private static readonly string CMD = "branch -l --all -v --format=\"%(refname)$%(objectname)$%(HEAD)$%(upstream)$%(upstream:track)\""; private static readonly Regex REG_AHEAD = new Regex(@"ahead (\d+)"); private static readonly Regex REG_BEHIND = new Regex(@"behind (\d+)"); - private List loaded = new List(); - - public Branches(string path) { - Cwd = path; - Args = CMD; + public QueryBranches(string repo) { + WorkingDirectory = repo; + Context = repo; + Args = "branch -l --all -v --format=\"%(refname)$%(objectname)$%(HEAD)$%(upstream)$%(upstream:track)\""; } public List Result() { Exec(); - return loaded; + return _branches; } - public override void OnReadline(string line) { + protected override void OnReadline(string line) { var parts = line.Split('$'); if (parts.Length != 5) return; @@ -55,7 +50,7 @@ namespace SourceGit.Commands { branch.Upstream = parts[3]; branch.UpstreamTrackStatus = ParseTrackStatus(parts[4]); - loaded.Add(branch); + _branches.Add(branch); } private string ParseTrackStatus(string data) { @@ -75,5 +70,7 @@ namespace SourceGit.Commands { return track.Trim(); } + + private List _branches = new List(); } } diff --git a/src/Commands/QueryCommitChanges.cs b/src/Commands/QueryCommitChanges.cs new file mode 100644 index 00000000..7b252efd --- /dev/null +++ b/src/Commands/QueryCommitChanges.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class QueryCommitChanges : Command { + private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); + + public QueryCommitChanges(string repo, string commitSHA) { + WorkingDirectory = repo; + Context = repo; + Args = $"show --name-status {commitSHA}"; + } + + public List Result() { + Exec(); + _changes.Sort((l, r) => l.Path.CompareTo(r.Path)); + return _changes; + } + + protected override void OnReadline(string line) { + var match = REG_FORMAT.Match(line); + if (!match.Success) return; + + var change = new Models.Change() { Path = match.Groups[2].Value }; + var status = match.Groups[1].Value; + + switch (status[0]) { + case 'M': change.Set(Models.ChangeState.Modified); _changes.Add(change); break; + case 'A': change.Set(Models.ChangeState.Added); _changes.Add(change); break; + case 'D': change.Set(Models.ChangeState.Deleted); _changes.Add(change); break; + case 'R': change.Set(Models.ChangeState.Renamed); _changes.Add(change); break; + case 'C': change.Set(Models.ChangeState.Copied); _changes.Add(change); break; + } + } + + private List _changes = new List(); + } +} diff --git a/src/Commands/Commits.cs b/src/Commands/QueryCommits.cs similarity index 92% rename from src/Commands/Commits.cs rename to src/Commands/QueryCommits.cs index f70528db..0d6c31e2 100644 --- a/src/Commands/Commits.cs +++ b/src/Commands/QueryCommits.cs @@ -1,13 +1,8 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; namespace SourceGit.Commands { - - /// - /// 取得提交列表 - /// - public class Commits : Command { + public class QueryCommits : Command { private static readonly string GPGSIG_START = "gpgsig -----BEGIN PGP SIGNATURE-----"; private static readonly string GPGSIG_END = " -----END PGP SIGNATURE-----"; @@ -17,8 +12,8 @@ namespace SourceGit.Commands { private bool isHeadFounded = false; private bool findFirstMerged = true; - public Commits(string path, string limits, bool needFindHead = true) { - Cwd = path; + public QueryCommits(string repo, string limits, bool needFindHead = true) { + WorkingDirectory = repo; Args = "log --date-order --decorate=full --pretty=raw " + limits; findFirstMerged = needFindHead; } @@ -38,7 +33,7 @@ namespace SourceGit.Commands { return commits; } - public override void OnReadline(string line) { + protected override void OnReadline(string line) { if (isSkipingGpgsig) { if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal)) isSkipingGpgsig = false; return; @@ -137,10 +132,10 @@ namespace SourceGit.Commands { } private void MarkFirstMerged() { - Args = $"log --since=\"{commits.Last().CommitterTimeStr}\" --format=\"%H\""; + Args = $"log --since=\"{commits[commits.Count - 1].CommitterTimeStr}\" --format=\"%H\""; var rs = ReadToEnd(); - var shas = rs.Output.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); + var shas = rs.StdOut.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if (shas.Length == 0) return; var set = new HashSet(); diff --git a/src/Commands/QueryFileContent.cs b/src/Commands/QueryFileContent.cs index 04eab006..172134c5 100644 --- a/src/Commands/QueryFileContent.cs +++ b/src/Commands/QueryFileContent.cs @@ -1,26 +1,23 @@ -using System.Collections.Generic; +using System.Text; namespace SourceGit.Commands { - /// - /// 取得指定提交下的某文件内容 - /// public class QueryFileContent : Command { - private List lines = new List(); - private int added = 0; - - public QueryFileContent(string repo, string commit, string path) { - Cwd = repo; - Args = $"show {commit}:\"{path}\""; + public QueryFileContent(string repo, string revision, string file) { + WorkingDirectory = repo; + Context = repo; + Args = $"show {revision}:\"{file}\""; } - public List Result() { + public string Result() { Exec(); - return lines; + return _builder.ToString(); } - public override void OnReadline(string line) { - added++; - lines.Add(new Models.TextLine() { Number = added, Data = line }); + protected override void OnReadline(string line) { + _builder.Append(line); + _builder.Append('\n'); } + + private StringBuilder _builder = new StringBuilder(); } } diff --git a/src/Commands/QueryFileSize.cs b/src/Commands/QueryFileSize.cs new file mode 100644 index 00000000..e16bf9e6 --- /dev/null +++ b/src/Commands/QueryFileSize.cs @@ -0,0 +1,29 @@ +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class QueryFileSize : Command { + private static readonly Regex REG_FORMAT = new Regex(@"^\d+\s+\w+\s+[0-9a-f]+\s+(\d+)\s+.*$"); + + public QueryFileSize(string repo, string file, string revision) { + WorkingDirectory = repo; + Context = repo; + Args = $"ls-tree {revision} -l -- {file}"; + } + + public long Result() { + if (_result != 0) return _result; + + var rs = ReadToEnd(); + if (rs.IsSuccess) { + var match = REG_FORMAT.Match(rs.StdOut); + if (match.Success) { + return long.Parse(match.Groups[1].Value); + } + } + + return 0; + } + + private long _result = 0; + } +} diff --git a/src/Commands/QueryFileSizeChange.cs b/src/Commands/QueryFileSizeChange.cs deleted file mode 100644 index 70792e2a..00000000 --- a/src/Commands/QueryFileSizeChange.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.IO; - -namespace SourceGit.Commands { - /// - /// 查询文件大小变化 - /// - public class QueryFileSizeChange { - - class QuerySizeCmd : Command { - public QuerySizeCmd(string repo, string path, string revision) { - Cwd = repo; - Args = $"cat-file -s {revision}:\"{path}\""; - } - - public long Result() { - string data = ReadToEnd().Output; - long size; - if (!long.TryParse(data, out size)) size = 0; - return size; - } - } - - private Models.FileSizeChange change = new Models.FileSizeChange(); - - public QueryFileSizeChange(string repo, string[] revisions, string path, string orgPath) { - if (revisions.Length == 0) { - change.NewSize = new FileInfo(Path.Combine(repo, path)).Length; - change.OldSize = new QuerySizeCmd(repo, path, "HEAD").Result(); - } else if (revisions.Length == 1) { - change.NewSize = new QuerySizeCmd(repo, path, "HEAD").Result(); - if (string.IsNullOrEmpty(orgPath)) { - change.OldSize = new QuerySizeCmd(repo, path, revisions[0]).Result(); - } else { - change.OldSize = new QuerySizeCmd(repo, orgPath, revisions[0]).Result(); - } - } else { - change.NewSize = new QuerySizeCmd(repo, path, revisions[1]).Result(); - if (string.IsNullOrEmpty(orgPath)) { - change.OldSize = new QuerySizeCmd(repo, path, revisions[0]).Result(); - } else { - change.OldSize = new QuerySizeCmd(repo, orgPath, revisions[0]).Result(); - } - } - } - - public Models.FileSizeChange Result() { - return change; - } - } -} diff --git a/src/Commands/QueryGitDir.cs b/src/Commands/QueryGitDir.cs index af45273d..dcdc216b 100644 --- a/src/Commands/QueryGitDir.cs +++ b/src/Commands/QueryGitDir.cs @@ -1,23 +1,20 @@ -using System.IO; +using System.IO; namespace SourceGit.Commands { - - /// - /// 取得GitDir - /// public class QueryGitDir : Command { public QueryGitDir(string workDir) { - Cwd = workDir; + WorkingDirectory = workDir; Args = "rev-parse --git-dir"; + RaiseError = false; } public string Result() { - var rs = ReadToEnd().Output; + var rs = ReadToEnd().StdOut; if (string.IsNullOrEmpty(rs)) return null; rs = rs.Trim(); if (Path.IsPathRooted(rs)) return rs; - return Path.GetFullPath(Path.Combine(Cwd, rs)); + return Path.GetFullPath(Path.Combine(WorkingDirectory, rs)); } } } diff --git a/src/Commands/QueryLFSObject.cs b/src/Commands/QueryLFSObject.cs deleted file mode 100644 index 8db8bbe0..00000000 --- a/src/Commands/QueryLFSObject.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace SourceGit.Commands { - /// - /// 取得一个LFS对象的信息 - /// - public class QueryLFSObject : Command { - private Models.LFSObject obj = new Models.LFSObject(); - - public QueryLFSObject(string repo, string commit, string path) { - Cwd = repo; - Args = $"show {commit}:\"{path}\""; - } - - public Models.LFSObject Result() { - Exec(); - return obj; - } - - public override void OnReadline(string line) { - if (line.StartsWith("oid sha256:", StringComparison.Ordinal)) { - obj.OID = line.Substring(11).Trim(); - } else if (line.StartsWith("size")) { - obj.Size = int.Parse(line.Substring(4).Trim()); - } - } - } -} diff --git a/src/Commands/QueryLFSObjectChange.cs b/src/Commands/QueryLFSObjectChange.cs deleted file mode 100644 index 5f9e1631..00000000 --- a/src/Commands/QueryLFSObjectChange.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace SourceGit.Commands { - /// - /// 查询LFS对象变更 - /// - public class QueryLFSObjectChange : Command { - private Models.LFSChange change = new Models.LFSChange(); - - public QueryLFSObjectChange(string repo, string args) { - Cwd = repo; - Args = $"diff --ignore-cr-at-eol {args}"; - } - - public Models.LFSChange Result() { - Exec(); - return change; - } - - public override void OnReadline(string line) { - var ch = line[0]; - if (ch == '-') { - if (change.Old == null) change.Old = new Models.LFSObject(); - line = line.Substring(1); - if (line.StartsWith("oid sha256:")) { - change.Old.OID = line.Substring(11); - } else if (line.StartsWith("size ")) { - change.Old.Size = int.Parse(line.Substring(5)); - } - } else if (ch == '+') { - if (change.New == null) change.New = new Models.LFSObject(); - line = line.Substring(1); - if (line.StartsWith("oid sha256:")) { - change.New.OID = line.Substring(11); - } else if (line.StartsWith("size ")) { - change.New.Size = int.Parse(line.Substring(5)); - } - } else if (line.StartsWith(" size ")) { - change.New.Size = change.Old.Size = int.Parse(line.Substring(6)); - } - } - } -} diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs new file mode 100644 index 00000000..260885d5 --- /dev/null +++ b/src/Commands/QueryLocalChanges.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class QueryLocalChanges : Command { + private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); + private static readonly string[] UNTRACKED = [ "no", "all" ]; + + public QueryLocalChanges(string repo, bool includeUntracked = true) { + WorkingDirectory = repo; + Context = repo; + Args = $"status -u{UNTRACKED[includeUntracked ? 1 : 0]} --ignore-submodules=dirty --porcelain"; + } + + public List Result() { + Exec(); + return _changes; + } + + protected override void OnReadline(string line) { + var match = REG_FORMAT.Match(line); + if (!match.Success) return; + if (line.EndsWith("/", StringComparison.Ordinal)) return; // Ignore changes with git-worktree + + var change = new Models.Change() { Path = match.Groups[2].Value }; + var status = match.Groups[1].Value; + + switch (status) { + case " M": change.Set(Models.ChangeState.None, Models.ChangeState.Modified); break; + case " A": change.Set(Models.ChangeState.None, Models.ChangeState.Added); break; + case " D": change.Set(Models.ChangeState.None, Models.ChangeState.Deleted); break; + case " R": change.Set(Models.ChangeState.None, Models.ChangeState.Renamed); break; + case " C": change.Set(Models.ChangeState.None, Models.ChangeState.Copied); break; + case "M": change.Set(Models.ChangeState.Modified, Models.ChangeState.None); break; + case "MM": change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified); break; + case "MD": change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted); break; + case "A": change.Set(Models.ChangeState.Added, Models.ChangeState.None); break; + case "AM": change.Set(Models.ChangeState.Added, Models.ChangeState.Modified); break; + case "AD": change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted); break; + case "D": change.Set(Models.ChangeState.Deleted, Models.ChangeState.None); break; + case "R": change.Set(Models.ChangeState.Renamed, Models.ChangeState.None); break; + case "RM": change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified); break; + case "RD": change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted); break; + case "C": change.Set(Models.ChangeState.Copied, Models.ChangeState.None); break; + case "CM": change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified); break; + case "CD": change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted); break; + case "DR": change.Set(Models.ChangeState.Deleted, Models.ChangeState.Renamed); break; + case "DC": change.Set(Models.ChangeState.Deleted, Models.ChangeState.Copied); break; + case "DD": change.Set(Models.ChangeState.Deleted, Models.ChangeState.Deleted); break; + case "AU": change.Set(Models.ChangeState.Added, Models.ChangeState.Unmerged); break; + case "UD": change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Deleted); break; + case "UA": change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Added); break; + case "DU": change.Set(Models.ChangeState.Deleted, Models.ChangeState.Unmerged); break; + case "AA": change.Set(Models.ChangeState.Added, Models.ChangeState.Added); break; + case "UU": change.Set(Models.ChangeState.Unmerged, Models.ChangeState.Unmerged); break; + case "??": change.Set(Models.ChangeState.Untracked, Models.ChangeState.Untracked); break; + default: return; + } + + _changes.Add(change); + } + + private List _changes = new List(); + } +} diff --git a/src/Commands/Remotes.cs b/src/Commands/QueryRemotes.cs similarity index 54% rename from src/Commands/Remotes.cs rename to src/Commands/QueryRemotes.cs index 1866b7f2..e8e21b4a 100644 --- a/src/Commands/Remotes.cs +++ b/src/Commands/QueryRemotes.cs @@ -1,25 +1,22 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// 获取远程列表 - /// - public class Remotes : Command { + public class QueryRemotes : Command { private static readonly Regex REG_REMOTE = new Regex(@"^([\w\.\-]+)\s*(\S+).*$"); - private List loaded = new List(); - public Remotes(string repo) { - Cwd = repo; + public QueryRemotes(string repo) { + WorkingDirectory = repo; + Context = repo; Args = "remote -v"; } public List Result() { Exec(); - return loaded; + return _loaded; } - public override void OnReadline(string line) { + protected override void OnReadline(string line) { var match = REG_REMOTE.Match(line); if (!match.Success) return; @@ -28,8 +25,10 @@ namespace SourceGit.Commands { URL = match.Groups[2].Value, }; - if (loaded.Find(x => x.Name == remote.Name) != null) return; - loaded.Add(remote); + if (_loaded.Find(x => x.Name == remote.Name) != null) return; + _loaded.Add(remote); } + + private List _loaded = new List(); } } diff --git a/src/Commands/QueryRepositoryRootPath.cs b/src/Commands/QueryRepositoryRootPath.cs new file mode 100644 index 00000000..51a1a15a --- /dev/null +++ b/src/Commands/QueryRepositoryRootPath.cs @@ -0,0 +1,15 @@ +namespace SourceGit.Commands { + public class QueryRepositoryRootPath : Command { + public QueryRepositoryRootPath(string path) { + WorkingDirectory = path; + Args = "rev-parse --show-toplevel"; + RaiseError = false; + } + + public string Result() { + var rs = ReadToEnd().StdOut; + if (string.IsNullOrEmpty(rs)) return null; + return rs.Trim(); + } + } +} diff --git a/src/Commands/RevisionObjects.cs b/src/Commands/QueryRevisionObjects.cs similarity index 77% rename from src/Commands/RevisionObjects.cs rename to src/Commands/QueryRevisionObjects.cs index 25a190ec..b0c48f2a 100644 --- a/src/Commands/RevisionObjects.cs +++ b/src/Commands/QueryRevisionObjects.cs @@ -1,16 +1,14 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// 取出指定Revision下的文件列表 - /// - public class RevisionObjects : Command { + public class QueryRevisionObjects : Command { private static readonly Regex REG_FORMAT = new Regex(@"^\d+\s+(\w+)\s+([0-9a-f]+)\s+(.*)$"); private List objects = new List(); - public RevisionObjects(string cwd, string sha) { - Cwd = cwd; + public QueryRevisionObjects(string repo, string sha) { + WorkingDirectory = repo; + Context = repo; Args = $"ls-tree -r {sha}"; } @@ -19,7 +17,7 @@ namespace SourceGit.Commands { return objects; } - public override void OnReadline(string line) { + protected override void OnReadline(string line) { var match = REG_FORMAT.Match(line); if (!match.Success) return; diff --git a/src/Commands/QueryStashChanges.cs b/src/Commands/QueryStashChanges.cs new file mode 100644 index 00000000..c2d50d45 --- /dev/null +++ b/src/Commands/QueryStashChanges.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class QueryStashChanges : Command { + private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); + + public QueryStashChanges(string repo, string sha) { + WorkingDirectory = repo; + Context = repo; + Args = $"diff --name-status --pretty=format: {sha}^ {sha}"; + } + + public List Result() { + Exec(); + return _changes; + } + + protected override void OnReadline(string line) { + var match = REG_FORMAT.Match(line); + if (!match.Success) return; + + var change = new Models.Change() { Path = match.Groups[2].Value }; + var status = match.Groups[1].Value; + + switch (status[0]) { + case 'M': change.Set(Models.ChangeState.Modified); _changes.Add(change); break; + case 'A': change.Set(Models.ChangeState.Added); _changes.Add(change); break; + case 'D': change.Set(Models.ChangeState.Deleted); _changes.Add(change); break; + case 'R': change.Set(Models.ChangeState.Renamed); _changes.Add(change); break; + case 'C': change.Set(Models.ChangeState.Copied); _changes.Add(change); break; + } + } + + private List _changes = new List(); + } +} diff --git a/src/Commands/Stashes.cs b/src/Commands/QueryStashes.cs similarity index 52% rename from src/Commands/Stashes.cs rename to src/Commands/QueryStashes.cs index 5668ff64..7e159500 100644 --- a/src/Commands/Stashes.cs +++ b/src/Commands/QueryStashes.cs @@ -1,48 +1,47 @@ -using System; +using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace SourceGit.Commands { - /// - /// 解析当前仓库中的贮藏 - /// - public class Stashes : Command { + public class QueryStashes : Command { private static readonly Regex REG_STASH = new Regex(@"^Reflog: refs/(stash@\{\d+\}).*$"); - private List parsed = new List(); - private Models.Stash current = null; - - public Stashes(string path) { - Cwd = path; + + public QueryStashes(string repo) { + WorkingDirectory = repo; + Context = repo; Args = "stash list --pretty=raw"; } public List Result() { Exec(); - if (current != null) parsed.Add(current); - return parsed; + if (_current != null) _stashes.Add(_current); + return _stashes; } - public override void OnReadline(string line) { + protected override void OnReadline(string line) { if (line.StartsWith("commit ", StringComparison.Ordinal)) { - if (current != null && !string.IsNullOrEmpty(current.Name)) parsed.Add(current); - current = new Models.Stash() { SHA = line.Substring(7, 8) }; + if (_current != null && !string.IsNullOrEmpty(_current.Name)) _stashes.Add(_current); + _current = new Models.Stash() { SHA = line.Substring(7, 8) }; return; } - if (current == null) return; + if (_current == null) return; if (line.StartsWith("Reflog: refs/stash@", StringComparison.Ordinal)) { var match = REG_STASH.Match(line); - if (match.Success) current.Name = match.Groups[1].Value; + if (match.Success) _current.Name = match.Groups[1].Value; } else if (line.StartsWith("Reflog message: ", StringComparison.Ordinal)) { - current.Message = line.Substring(16); + _current.Message = line.Substring(16); } else if (line.StartsWith("author ", StringComparison.Ordinal)) { Models.User user = Models.User.Invalid; ulong time = 0; Models.Commit.ParseUserAndTime(line.Substring(7), ref user, ref time); - current.Author = user; - current.Time = time; + _current.Author = user; + _current.Time = time; } } + + private List _stashes = new List(); + private Models.Stash _current = null; } } diff --git a/src/Commands/QuerySubmodules.cs b/src/Commands/QuerySubmodules.cs new file mode 100644 index 00000000..0013ff49 --- /dev/null +++ b/src/Commands/QuerySubmodules.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Commands { + public class QuerySubmodules : Command { + private readonly Regex REG_FORMAT = new Regex(@"^[\-\+ ][0-9a-f]+\s(.*)\s\(.*\)$"); + + public QuerySubmodules(string repo) { + WorkingDirectory = repo; + Context = repo; + Args = "submodule status"; + } + + public List Result() { + Exec(); + return _submodules; + } + + protected override void OnReadline(string line) { + var match = REG_FORMAT.Match(line); + if (!match.Success) return; + _submodules.Add(match.Groups[1].Value); + } + + private List _submodules = new List(); + } +} diff --git a/src/Commands/QueryTags.cs b/src/Commands/QueryTags.cs new file mode 100644 index 00000000..470301a3 --- /dev/null +++ b/src/Commands/QueryTags.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace SourceGit.Commands { + public class QueryTags : Command { + public QueryTags(string repo) { + Context = repo; + WorkingDirectory = repo; + Args = "for-each-ref --sort=-creatordate --format=\"$%(refname:short)$%(objectname)$%(*objectname)\" refs/tags"; + } + + public List Result() { + Exec(); + return _loaded; + } + + protected override void OnReadline(string line) { + var subs = line.Split(new char[] { '$' }, StringSplitOptions.RemoveEmptyEntries); + if (subs.Length == 2) { + _loaded.Add(new Models.Tag() { + Name = subs[0], + SHA = subs[1], + }); + } else if (subs.Length == 3) { + _loaded.Add(new Models.Tag() { + Name = subs[0], + SHA = subs[2], + }); + } + } + + private List _loaded = new List(); + } +} diff --git a/src/Commands/Rebase.cs b/src/Commands/Rebase.cs index 791233af..88d304aa 100644 --- a/src/Commands/Rebase.cs +++ b/src/Commands/Rebase.cs @@ -1,11 +1,8 @@ -namespace SourceGit.Commands { - /// - /// 变基命令 - /// +namespace SourceGit.Commands { public class Rebase : Command { - public Rebase(string repo, string basedOn, bool autoStash) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = "rebase "; if (autoStash) Args += "--autostash "; Args += basedOn; diff --git a/src/Commands/Remote.cs b/src/Commands/Remote.cs index 09326afd..1a526045 100644 --- a/src/Commands/Remote.cs +++ b/src/Commands/Remote.cs @@ -1,11 +1,8 @@ -namespace SourceGit.Commands { - /// - /// 远程操作 - /// +namespace SourceGit.Commands { public class Remote : Command { - public Remote(string repo) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; } public bool Add(string name, string url) { diff --git a/src/Commands/Reset.cs b/src/Commands/Reset.cs index b60ca174..445890bd 100644 --- a/src/Commands/Reset.cs +++ b/src/Commands/Reset.cs @@ -1,33 +1,32 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text; namespace SourceGit.Commands { - /// - /// 重置命令 - /// public class Reset : Command { - public Reset(string repo) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = "reset"; } - public Reset(string repo, string revision, string mode) { - Cwd = repo; - Args = $"reset {mode} {revision}"; - } + public Reset(string repo, List changes) { + WorkingDirectory = repo; + Context = repo; - public Reset(string repo, List files) { - Cwd = repo; - - StringBuilder builder = new StringBuilder(); + var builder = new StringBuilder(); builder.Append("reset --"); - foreach (var f in files) { + foreach (var c in changes) { builder.Append(" \""); - builder.Append(f); + builder.Append(c.Path); builder.Append("\""); } Args = builder.ToString(); } + + public Reset(string repo, string revision, string mode) { + WorkingDirectory = repo; + Context = repo; + Args = $"reset {mode} {revision}"; + } } } diff --git a/src/Commands/Revert.cs b/src/Commands/Revert.cs index 2a656fc8..6b9549f5 100644 --- a/src/Commands/Revert.cs +++ b/src/Commands/Revert.cs @@ -1,11 +1,8 @@ -namespace SourceGit.Commands { - /// - /// 撤销提交 - /// +namespace SourceGit.Commands { public class Revert : Command { - public Revert(string repo, string commit, bool autoCommit) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; Args = $"revert {commit} --no-edit"; if (!autoCommit) Args += " --no-commit"; } diff --git a/src/Commands/Reword.cs b/src/Commands/Reword.cs deleted file mode 100644 index 3296c37c..00000000 --- a/src/Commands/Reword.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.IO; - -namespace SourceGit.Commands { - /// - /// 编辑HEAD的提交信息 - /// - public class Reword : Command { - public Reword(string repo, string msg) { - var tmp = Path.GetTempFileName(); - File.WriteAllText(tmp, msg); - - Cwd = repo; - Args = $"commit --amend --allow-empty --file=\"{tmp}\""; - } - } -} diff --git a/src/Commands/SaveChangesAsPatch.cs b/src/Commands/SaveChangesAsPatch.cs new file mode 100644 index 00000000..fa8203ac --- /dev/null +++ b/src/Commands/SaveChangesAsPatch.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace SourceGit.Commands { + public static class SaveChangesAsPatch { + public static bool Exec(string repo, List changes, bool isUnstaged, string saveTo) { + using (var sw = File.Create(saveTo)) { + foreach (var change in changes) { + if (!ProcessSingleChange(repo, new Models.DiffOption(change, isUnstaged), sw)) return false; + } + } + + return true; + } + + private static bool ProcessSingleChange(string repo, Models.DiffOption opt, FileStream writer) { + var starter = new ProcessStartInfo(); + starter.WorkingDirectory = repo; + starter.FileName = Native.OS.GitExecutableFile; + starter.Arguments = $"diff --ignore-cr-at-eol --unified=4 {opt}"; + starter.UseShellExecute = false; + starter.CreateNoWindow = true; + starter.WindowStyle = ProcessWindowStyle.Hidden; + starter.RedirectStandardOutput = true; + + try { + var proc = new Process() { StartInfo = starter }; + proc.Start(); + proc.StandardOutput.BaseStream.CopyTo(writer); + proc.WaitForExit(); + var rs = proc.ExitCode == 0; + proc.Close(); + + return rs; + } catch (Exception e) { + App.RaiseException(repo, "Save change to patch failed: " + e.Message); + return false; + } + } + } +} diff --git a/src/Commands/SaveChangesToPatch.cs b/src/Commands/SaveChangesToPatch.cs deleted file mode 100644 index b40b9bee..00000000 --- a/src/Commands/SaveChangesToPatch.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.IO; - -namespace SourceGit.Commands { - /// - /// 将Changes保存到文件流中 - /// - public class SaveChangeToStream : Command { - private StreamWriter writer = null; - - public SaveChangeToStream(string repo, Models.Change change, StreamWriter to) { - Cwd = repo; - if (change.WorkTree == Models.Change.Status.Added || change.WorkTree == Models.Change.Status.Untracked) { - Args = $"diff --no-index --no-ext-diff --find-renames -- /dev/null \"{change.Path}\""; - } else { - var pathspec = $"\"{change.Path}\""; - if (!string.IsNullOrEmpty(change.OriginalPath)) pathspec = $"\"{change.OriginalPath}\" \"{change.Path}\""; - Args = $"diff --binary --no-ext-diff --find-renames --full-index -- {pathspec}"; - } - writer = to; - } - - public override void OnReadline(string line) { - writer.WriteLine(line); - } - } -} diff --git a/src/Commands/SaveRevisionFile.cs b/src/Commands/SaveRevisionFile.cs index a83fc618..61f49df1 100644 --- a/src/Commands/SaveRevisionFile.cs +++ b/src/Commands/SaveRevisionFile.cs @@ -1,44 +1,60 @@ +using System; using System.Diagnostics; using System.IO; namespace SourceGit.Commands { - /// - /// 保存指定版本的文件 - /// - public class SaveRevisionFile { - private string cwd = ""; - private string bat = ""; - - public SaveRevisionFile(string repo, string path, string sha, string saveTo) { - var tmp = Path.GetTempFileName(); - var cmd = $"\"{Models.Preference.Instance.Git.Path}\" --no-pager "; - - var isLFS = new LFS(repo).IsFiltered(path); - if (isLFS) { - cmd += $"show {sha}:\"{path}\" > {tmp}.lfs\n"; - cmd += $"\"{Models.Preference.Instance.Git.Path}\" --no-pager lfs smudge < {tmp}.lfs > \"{saveTo}\"\n"; + public static class SaveRevisionFile { + public static void Run(string repo, string revision, string file, string saveTo) { + var isLFSFiltered = new IsLFSFiltered(repo, file).Result(); + if (isLFSFiltered) { + var tmpFile = saveTo + ".tmp"; + if (ExecCmd(repo, $"show {revision}:\"{file}\"", tmpFile)) { + ExecCmd(repo, $"lfs smudge", saveTo, tmpFile); + } + File.Delete(tmpFile); } else { - cmd += $"show {sha}:\"{path}\" > \"{saveTo}\"\n"; + ExecCmd(repo, $"show {revision}:\"{file}\"", saveTo); } - - cwd = repo; - bat = tmp + ".bat"; - - File.WriteAllText(bat, cmd); } - - public void Exec() { + + private static bool ExecCmd(string repo, string args, string outputFile, string inputFile = null) { var starter = new ProcessStartInfo(); - starter.FileName = bat; - starter.WorkingDirectory = cwd; + starter.WorkingDirectory = repo; + starter.FileName = Native.OS.GitExecutableFile; + starter.Arguments = args; + starter.UseShellExecute = false; starter.CreateNoWindow = true; starter.WindowStyle = ProcessWindowStyle.Hidden; + starter.RedirectStandardInput = true; + starter.RedirectStandardOutput = true; + starter.RedirectStandardError = true; - var proc = Process.Start(starter); - proc.WaitForExit(); - proc.Close(); + using (var sw = File.OpenWrite(outputFile)) { + try { + var proc = new Process() { StartInfo = starter }; + proc.Start(); - File.Delete(bat); + if (inputFile != null) { + using (StreamReader sr = new StreamReader(inputFile)) { + while (true) { + var line = sr.ReadLine(); + if (line == null) break; + proc.StandardInput.WriteLine(line); + } + } + } + + proc.StandardOutput.BaseStream.CopyTo(sw); + proc.WaitForExit(); + var rs = proc.ExitCode == 0; + proc.Close(); + + return rs; + } catch (Exception e) { + App.RaiseException(repo, "Save file failed: " + e.Message); + return false; + } + } } } } diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs index 0a923f2c..720129a7 100644 --- a/src/Commands/Stash.cs +++ b/src/Commands/Stash.cs @@ -1,68 +1,49 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; namespace SourceGit.Commands { - /// - /// 单个贮藏相关操作 - /// public class Stash : Command { - public Stash(string repo) { - Cwd = repo; + WorkingDirectory = repo; + Context = repo; } - public bool Push(List changes, string message, bool bFull) { - if (bFull) { - var needAdd = new List(); - foreach (var c in changes) { - if (c.WorkTree == Models.Change.Status.Added || c.WorkTree == Models.Change.Status.Untracked) { - needAdd.Add(c.Path); - if (needAdd.Count > 10) { - new Add(Cwd, needAdd).Exec(); - needAdd.Clear(); - } + public bool Push(string message) { + Args = $"stash push -m \"{message}\""; + return Exec(); + } + + public bool Push(List changes, string message) { + var temp = Path.GetTempFileName(); + var stream = new FileStream(temp, FileMode.Create); + var writer = new StreamWriter(stream); + + var needAdd = new List(); + foreach (var c in changes) { + writer.WriteLine(c.Path); + + if (c.WorkTree == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked) { + needAdd.Add(c); + if (needAdd.Count > 10) { + new Add(WorkingDirectory, needAdd).Exec(); + needAdd.Clear(); } } + } + if (needAdd.Count > 0) { + new Add(WorkingDirectory, needAdd).Exec(); + needAdd.Clear(); + } - if (needAdd.Count > 0) { - new Add(Cwd, needAdd).Exec(); - needAdd.Clear(); - } + writer.Flush(); + stream.Flush(); + writer.Close(); + stream.Close(); - Args = $"stash push -m \"{message}\""; - return Exec(); - } else { - var temp = Path.GetTempFileName(); - var stream = new FileStream(temp, FileMode.Create); - var writer = new StreamWriter(stream); - - var needAdd = new List(); - foreach (var c in changes) { - writer.WriteLine(c.Path); - - if (c.WorkTree == Models.Change.Status.Added || c.WorkTree == Models.Change.Status.Untracked) { - needAdd.Add(c.Path); - if (needAdd.Count > 10) { - new Add(Cwd, needAdd).Exec(); - needAdd.Clear(); - } - } - } - if (needAdd.Count > 0) { - new Add(Cwd, needAdd).Exec(); - needAdd.Clear(); - } - - writer.Flush(); - stream.Flush(); - writer.Close(); - stream.Close(); - - Args = $"stash push -m \"{message}\" --pathspec-from-file=\"{temp}\""; - var succ = Exec(); - File.Delete(temp); - return succ; - } + Args = $"stash push -m \"{message}\" --pathspec-from-file=\"{temp}\""; + var succ = Exec(); + File.Delete(temp); + return succ; } public bool Apply(string name) { @@ -79,5 +60,10 @@ namespace SourceGit.Commands { Args = $"stash drop -q {name}"; return Exec(); } + + public bool Clear() { + Args = "stash clear"; + return Exec(); + } } } diff --git a/src/Commands/StashChanges.cs b/src/Commands/StashChanges.cs deleted file mode 100644 index 459a3776..00000000 --- a/src/Commands/StashChanges.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Commands { - /// - /// 查看Stash中的修改 - /// - public class StashChanges : Command { - private static readonly Regex REG_FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); - private List changes = new List(); - - public StashChanges(string repo, string sha) { - Cwd = repo; - Args = $"diff --name-status --pretty=format: {sha}^ {sha}"; - } - - public List Result() { - Exec(); - return changes; - } - - public override void OnReadline(string line) { - var match = REG_FORMAT.Match(line); - if (!match.Success) return; - - var change = new Models.Change() { Path = match.Groups[2].Value }; - var status = match.Groups[1].Value; - - switch (status[0]) { - case 'M': change.Set(Models.Change.Status.Modified); changes.Add(change); break; - case 'A': change.Set(Models.Change.Status.Added); changes.Add(change); break; - case 'D': change.Set(Models.Change.Status.Deleted); changes.Add(change); break; - case 'R': change.Set(Models.Change.Status.Renamed); changes.Add(change); break; - case 'C': change.Set(Models.Change.Status.Copied); changes.Add(change); break; - } - } - } -} diff --git a/src/Commands/SubTree.cs b/src/Commands/SubTree.cs deleted file mode 100644 index 62f632fe..00000000 --- a/src/Commands/SubTree.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.IO; - -namespace SourceGit.Commands { - /// - /// 子树相关操作 - /// - public class SubTree : Command { - private Action handler = null; - - public SubTree(string repo) { - Cwd = repo; - TraitErrorAsOutput = true; - } - - public override void OnReadline(string line) { - handler?.Invoke(line); - } - - public bool Add(string prefix, string source, string revision, bool squash, Action onProgress) { - var path = Path.Combine(Cwd, prefix); - if (Directory.Exists(path)) return true; - - handler = onProgress; - Args = $"subtree add --prefix=\"{prefix}\" {source} {revision}"; - if (squash) Args += " --squash"; - return Exec(); - } - - public void Pull(string prefix, string source, string branch, bool squash, Action onProgress) { - handler = onProgress; - Args = $"subtree pull --prefix=\"{prefix}\" {source} {branch}"; - if (squash) Args += " --squash"; - Exec(); - } - - public void Push(string prefix, string source, string branch, Action onProgress) { - handler = onProgress; - Args = $"subtree push --prefix=\"{prefix}\" {source} {branch}"; - Exec(); - } - } -} diff --git a/src/Commands/Submodule.cs b/src/Commands/Submodule.cs index bf3ba87d..a6aeee83 100644 --- a/src/Commands/Submodule.cs +++ b/src/Commands/Submodule.cs @@ -1,25 +1,22 @@ -using System; +using System; namespace SourceGit.Commands { - /// - /// 子模块 - /// public class Submodule : Command { - private Action onProgress = null; - - public Submodule(string cwd) { - Cwd = cwd; + public Submodule(string repo) { + WorkingDirectory = repo; + Context = repo; } - public bool Add(string url, string path, bool recursive, Action handler) { - Args = $"submodule add {url} {path}"; - onProgress = handler; + public bool Add(string url, string relativePath, bool recursive, Action outputHandler) { + _outputHandler = outputHandler; + Args = $"submodule add {url} {relativePath}"; if (!Exec()) return false; if (recursive) { - Args = $"submodule update --init --recursive -- {path}"; + Args = $"submodule update --init --recursive -- {relativePath}"; return Exec(); } else { + Args = $"submodule update --init -- {relativePath}"; return true; } } @@ -29,16 +26,18 @@ namespace SourceGit.Commands { return Exec(); } - public bool Delete(string path) { - Args = $"submodule deinit -f {path}"; + public bool Delete(string relativePath) { + Args = $"submodule deinit -f {relativePath}"; if (!Exec()) return false; - Args = $"rm -rf {path}"; + Args = $"rm -rf {relativePath}"; return Exec(); } - public override void OnReadline(string line) { - onProgress?.Invoke(line); + protected override void OnReadline(string line) { + _outputHandler?.Invoke(line); } + + private Action _outputHandler; } } diff --git a/src/Commands/Submodules.cs b/src/Commands/Submodules.cs deleted file mode 100644 index 4daf69f6..00000000 --- a/src/Commands/Submodules.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Commands { - /// - /// 获取子模块列表 - /// - public class Submodules : Command { - private readonly Regex REG_FORMAT = new Regex(@"^[\-\+ ][0-9a-f]+\s(.*)\s\(.*\)$"); - private List modules = new List(); - - public Submodules(string repo) { - Cwd = repo; - Args = "submodule status"; - } - - public List Result() { - Exec(); - return modules; - } - - public override void OnReadline(string line) { - var match = REG_FORMAT.Match(line); - if (!match.Success) return; - modules.Add(match.Groups[1].Value); - } - } -} diff --git a/src/Commands/Tag.cs b/src/Commands/Tag.cs index 88942878..d0ab3bcb 100644 --- a/src/Commands/Tag.cs +++ b/src/Commands/Tag.cs @@ -1,38 +1,35 @@ +using System.Collections.Generic; using System.IO; namespace SourceGit.Commands { - - /// - /// 标签相关指令 - /// - public class Tag : Command { - - public Tag(string repo) { - Cwd = repo; - } - - public bool Add(string name, string basedOn, string message) { - Args = $"tag -a {name} {basedOn} "; + public static class Tag { + public static bool Add(string repo, string name, string basedOn, string message) { + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.Context = repo; + cmd.Args = $"tag -a {name} {basedOn} "; if (!string.IsNullOrEmpty(message)) { string tmp = Path.GetTempFileName(); File.WriteAllText(tmp, message); - Args += $"-F \"{tmp}\""; + cmd.Args += $"-F \"{tmp}\""; } else { - Args += $"-m {name}"; + cmd.Args += $"-m {name}"; } - return Exec(); + return cmd.Exec(); } - public bool Delete(string name, bool push) { - Args = $"tag --delete {name}"; - if (!Exec()) return false; + public static bool Delete(string repo, string name, List remotes) { + var cmd = new Command(); + cmd.WorkingDirectory = repo; + cmd.Context = repo; + cmd.Args = $"tag --delete {name}"; + if (!cmd.Exec()) return false; - if (push) { - var remotes = new Remotes(Cwd).Result(); + if (remotes != null) { foreach (var r in remotes) { - new Push(Cwd, r.Name, name, true).Exec(); + new Push(repo, r.Name, name, true).Exec(); } } diff --git a/src/Commands/Tags.cs b/src/Commands/Tags.cs deleted file mode 100644 index 738c86db..00000000 --- a/src/Commands/Tags.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SourceGit.Commands { - /// - /// 解析所有的Tags - /// - public class Tags : Command { - public static readonly string CMD = "for-each-ref --sort=-creatordate --format=\"$%(refname:short)$%(objectname)$%(*objectname)\" refs/tags"; - private List loaded = new List(); - - public Tags(string path) { - Cwd = path; - Args = CMD; - } - - public List Result() { - Exec(); - return loaded; - } - - public override void OnReadline(string line) { - var subs = line.Split(new char[] { '$' }, StringSplitOptions.RemoveEmptyEntries); - if (subs.Length == 2) { - loaded.Add(new Models.Tag() { - Name = subs[0], - SHA = subs[1], - }); - } else if (subs.Length == 3) { - loaded.Add(new Models.Tag() { - Name = subs[0], - SHA = subs[2], - }); - } - } - } -} diff --git a/src/Commands/Version.cs b/src/Commands/Version.cs index 1911f34a..f4da1767 100644 --- a/src/Commands/Version.cs +++ b/src/Commands/Version.cs @@ -1,18 +1,14 @@ -using System; - -namespace SourceGit.Commands { - /// - /// 检测git是否可用,并获取git版本信息 - /// +namespace SourceGit.Commands { public class Version : Command { - const string GitVersionPrefix = "git version "; + public Version() { + Args = "-v"; + RaiseError = false; + } + public string Query() { - Args = $"--version"; - var result = ReadToEnd(); - if (!result.IsSuccess || string.IsNullOrEmpty(result.Output)) return null; - var version = result.Output.Trim(); - if (!version.StartsWith(GitVersionPrefix, StringComparison.Ordinal)) return null; - return version.Substring(GitVersionPrefix.Length); + var rs = ReadToEnd(); + if (!rs.IsSuccess || string.IsNullOrWhiteSpace(rs.StdOut)) return string.Empty; + return rs.StdOut.Trim().Substring("git version ".Length); } } } diff --git a/src/Converters/BookmarkConverters.cs b/src/Converters/BookmarkConverters.cs new file mode 100644 index 00000000..1eca567b --- /dev/null +++ b/src/Converters/BookmarkConverters.cs @@ -0,0 +1,12 @@ +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace SourceGit.Converters { + public static class BookmarkConverters { + public static FuncValueConverter ToBrush = + new FuncValueConverter(bookmark => Models.Bookmarks.Brushes[bookmark]); + + public static FuncValueConverter ToStrokeThickness = + new FuncValueConverter(bookmark => bookmark == 0 ? 1.0 : 0); + } +} diff --git a/src/Converters/BoolConverters.cs b/src/Converters/BoolConverters.cs new file mode 100644 index 00000000..65593f8f --- /dev/null +++ b/src/Converters/BoolConverters.cs @@ -0,0 +1,8 @@ +using Avalonia.Data.Converters; + +namespace SourceGit.Converters { + public static class BoolConverters { + public static FuncValueConverter ToCommitOpacity = + new FuncValueConverter(x => x ? 1 : 0.5); + } +} diff --git a/src/Converters/BranchConverters.cs b/src/Converters/BranchConverters.cs new file mode 100644 index 00000000..c067a481 --- /dev/null +++ b/src/Converters/BranchConverters.cs @@ -0,0 +1,8 @@ +using Avalonia.Data.Converters; + +namespace SourceGit.Converters { + public static class BranchConverters { + public static FuncValueConverter ToName = + new FuncValueConverter(v => v.IsLocal ? v.Name : $"{v.Remote}/{v.Name}"); + } +} diff --git a/src/Converters/ChangeViewModeConverters.cs b/src/Converters/ChangeViewModeConverters.cs new file mode 100644 index 00000000..8bfb0898 --- /dev/null +++ b/src/Converters/ChangeViewModeConverters.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace SourceGit.Converters { + public static class ChangeViewModeConverters { + public static FuncValueConverter ToIcon = + new FuncValueConverter(v => { + switch (v) { + case Models.ChangeViewMode.List: + return App.Current?.FindResource("Icons.List") as StreamGeometry; + case Models.ChangeViewMode.Grid: + return App.Current?.FindResource("Icons.Grid") as StreamGeometry; + default: + return App.Current?.FindResource("Icons.Tree") as StreamGeometry; + } + }); + + public static FuncValueConverter IsList = + new FuncValueConverter(v => v == Models.ChangeViewMode.List); + + public static FuncValueConverter IsGrid = + new FuncValueConverter(v => v == Models.ChangeViewMode.Grid); + + public static FuncValueConverter IsTree = + new FuncValueConverter(v => v == Models.ChangeViewMode.Tree); + } +} diff --git a/src/Converters/DecoratorTypeConverters.cs b/src/Converters/DecoratorTypeConverters.cs new file mode 100644 index 00000000..2e9460c9 --- /dev/null +++ b/src/Converters/DecoratorTypeConverters.cs @@ -0,0 +1,34 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace SourceGit.Converters { + public static class DecoratorTypeConverters { + public static FuncValueConverter ToBackground = + new FuncValueConverter(v => { + if (v == Models.DecoratorType.Tag) return Models.DecoratorResources.Backgrounds[0]; + return Models.DecoratorResources.Backgrounds[1]; + }); + + public static FuncValueConverter ToIcon = + new FuncValueConverter(v => { + var key = "Icons.Tag"; + switch (v) { + case Models.DecoratorType.CurrentBranchHead: + key = "Icons.Check"; + break; + case Models.DecoratorType.RemoteBranchHead: + key = "Icons.Remote"; + break; + case Models.DecoratorType.LocalBranchHead: + key = "Icons.Branch"; + break; + default: + break; + } + + return Application.Current?.FindResource(key) as StreamGeometry; + }); + } +} diff --git a/src/Converters/IntConverters.cs b/src/Converters/IntConverters.cs new file mode 100644 index 00000000..95f4a57a --- /dev/null +++ b/src/Converters/IntConverters.cs @@ -0,0 +1,27 @@ +using Avalonia.Data.Converters; +using System; +using System.Globalization; + +namespace SourceGit.Converters { + public static class IntConverters { + public static FuncValueConverter IsGreaterThanZero = + new FuncValueConverter(v => v > 0); + + public static FuncValueConverter IsZero = + new FuncValueConverter(v => v == 0); + + public class NotEqualConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + int v = (int)value; + int target = (int)parameter; + return v != target; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } + + public static NotEqualConverter NotEqual = new NotEqualConverter(); + } +} diff --git a/src/Converters/LauncherPageConverters.cs b/src/Converters/LauncherPageConverters.cs new file mode 100644 index 00000000..6ba2a0a4 --- /dev/null +++ b/src/Converters/LauncherPageConverters.cs @@ -0,0 +1,28 @@ +using Avalonia.Collections; +using Avalonia.Data.Converters; +using System.Collections.Generic; + +namespace SourceGit.Converters { + public static class LauncherPageConverters { + public static FuncMultiValueConverter ToTabSeperatorVisible = + new FuncMultiValueConverter(v => { + if (v == null) return false; + + var array = new List(); + array.AddRange(v); + if (array.Count != 3) return false; + + var self = array[0] as ViewModels.LauncherPage; + if (self == null) return false; + + var selected = array[1] as ViewModels.LauncherPage; + var collections = array[2] as AvaloniaList; + + if (selected != null && collections != null && (self == selected || collections.IndexOf(self) + 1 == collections.IndexOf(selected))) { + return false; + } else { + return true; + } + }); + } +} diff --git a/src/Converters/ListConverters.cs b/src/Converters/ListConverters.cs new file mode 100644 index 00000000..bf52a281 --- /dev/null +++ b/src/Converters/ListConverters.cs @@ -0,0 +1,12 @@ +using Avalonia.Data.Converters; +using System.Collections; + +namespace SourceGit.Converters { + public static class ListConverters { + public static FuncValueConverter ToCount = + new FuncValueConverter(v => $" ({v.Count})"); + + public static FuncValueConverter IsNotNullOrEmpty = + new FuncValueConverter(v => v != null && v.Count > 0); + } +} diff --git a/src/Converters/PathConverters.cs b/src/Converters/PathConverters.cs new file mode 100644 index 00000000..e1de3795 --- /dev/null +++ b/src/Converters/PathConverters.cs @@ -0,0 +1,18 @@ +using Avalonia.Data.Converters; +using System.IO; + +namespace SourceGit.Converters { + public static class PathConverters { + public static FuncValueConverter PureFileName = + new FuncValueConverter(fullpath => Path.GetFileName(fullpath) ?? ""); + + public static FuncValueConverter PureDirectoryName = + new FuncValueConverter(fullpath => Path.GetDirectoryName(fullpath) ?? ""); + + public static FuncValueConverter TruncateIfTooLong = + new FuncValueConverter(fullpath => { + if (fullpath.Length <= 50) return fullpath; + return fullpath.Substring(0, 20) + ".../" + Path.GetFileName(fullpath); + }); + } +} diff --git a/src/Converters/StringConverters.cs b/src/Converters/StringConverters.cs new file mode 100644 index 00000000..0f305700 --- /dev/null +++ b/src/Converters/StringConverters.cs @@ -0,0 +1,56 @@ +using Avalonia.Data.Converters; +using Avalonia.Styling; +using System; +using System.Globalization; + +namespace SourceGit.Converters { + public static class StringConverters { + public class ToLocaleConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + return Models.Locale.Supported.Find(x => x.Key == value as string); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + return (value as Models.Locale).Key; + } + } + + public static ToLocaleConverter ToLocale = new ToLocaleConverter(); + + public class ToThemeConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + var theme = (string)value; + if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase)) { + return ThemeVariant.Light; + } else if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase)) { + return ThemeVariant.Dark; + } else { + return ThemeVariant.Default; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + var theme = (ThemeVariant)value; + return theme.Key; + } + } + + public static ToThemeConverter ToTheme = new ToThemeConverter(); + + public class FormatByResourceKeyConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + var key = parameter as string; + return App.Text(key, value); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } + + public static FormatByResourceKeyConverter FormatByResourceKey = new FormatByResourceKeyConverter(); + + public static FuncValueConverter ToShortSHA = + new FuncValueConverter(v => v.Length > 10 ? v.Substring(0, 10) : v); + } +} diff --git a/src/Converters/WindowStateConverters.cs b/src/Converters/WindowStateConverters.cs new file mode 100644 index 00000000..67e5a3be --- /dev/null +++ b/src/Converters/WindowStateConverters.cs @@ -0,0 +1,36 @@ +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media; +using Avalonia; +using System.Runtime.InteropServices; + +namespace SourceGit.Converters { + public static class WindowStateConverters { + public static FuncValueConverter ToContentMargin = + new FuncValueConverter(state => { + if (state == WindowState.Maximized && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + return new Thickness(6); + } else { + return new Thickness(0); + } + }); + + public static FuncValueConverter ToTitleBarHeight = + new FuncValueConverter(state => { + if (state == WindowState.Maximized && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + return new GridLength(30); + } else { + return new GridLength(38); + } + }); + + public static FuncValueConverter ToMaxOrRestoreIcon = + new FuncValueConverter(state => { + if (state == WindowState.Maximized) { + return Application.Current?.FindResource("Icons.Window.Restore") as StreamGeometry; + } else { + return Application.Current?.FindResource("Icons.Window.Maximize") as StreamGeometry; + } + }); + } +} diff --git a/src/FodyWeavers.xml b/src/FodyWeavers.xml deleted file mode 100644 index 5029e706..00000000 --- a/src/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/FodyWeavers.xsd b/src/FodyWeavers.xsd deleted file mode 100644 index 05e92c11..00000000 --- a/src/FodyWeavers.xsd +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks - - - - - A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. - - - - - A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks - - - - - A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. - - - - - A list of unmanaged 32 bit assembly names to include, delimited with line breaks. - - - - - A list of unmanaged 64 bit assembly names to include, delimited with line breaks. - - - - - The order of preloaded assemblies, delimited with line breaks. - - - - - - This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. - - - - - Controls if .pdbs for reference assemblies are also embedded. - - - - - Controls if runtime assemblies are also embedded. - - - - - Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. - - - - - Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. - - - - - As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. - - - - - Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. - - - - - Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. - - - - - A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | - - - - - A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. - - - - - A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | - - - - - A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. - - - - - A list of unmanaged 32 bit assembly names to include, delimited with |. - - - - - A list of unmanaged 64 bit assembly names to include, delimited with |. - - - - - The order of preloaded assemblies, delimited with |. - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/src/Models/ApplyWhiteSpaceMode.cs b/src/Models/ApplyWhiteSpaceMode.cs new file mode 100644 index 00000000..b35fa5f2 --- /dev/null +++ b/src/Models/ApplyWhiteSpaceMode.cs @@ -0,0 +1,13 @@ +namespace SourceGit.Models { + public class ApplyWhiteSpaceMode { + public string Name { get; set; } + public string Desc { get; set; } + public string Arg { get; set; } + + public ApplyWhiteSpaceMode(string n, string d, string a) { + Name = App.Text(n); + Desc = App.Text(d); + Arg = a; + } + } +} diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs new file mode 100644 index 00000000..b5f90ae0 --- /dev/null +++ b/src/Models/AvatarManager.cs @@ -0,0 +1,120 @@ +using Avalonia.Media.Imaging; +using Avalonia.Threading; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace SourceGit.Models { + public interface IAvatarHost { + void OnAvatarResourceReady(string md5, Bitmap bitmap); + } + + public static class AvatarManager { + static AvatarManager() { + _storePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SourceGit", "avatars"); + if (!Directory.Exists(_storePath)) Directory.CreateDirectory(_storePath); + + Task.Run(() => { + while (true) { + var md5 = null as string; + + lock (_synclock) { + foreach (var one in _requesting) { + md5 = one; + break; + } + } + + if (md5 == null) { + Thread.Sleep(100); + continue; + } + + var localFile = Path.Combine(_storePath, md5); + var img = null as Bitmap; + try { + var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) }; + var task = client.GetAsync($"https://cravatar.cn/avatar/{md5}?d=404"); + task.Wait(); + + var rsp = task.Result; + if (rsp.IsSuccessStatusCode) { + using (var stream = rsp.Content.ReadAsStream()) { + using (var writer = File.OpenWrite(localFile)) { + stream.CopyTo(writer); + } + } + + using (var reader = File.OpenRead(localFile)) { + img = Bitmap.DecodeToWidth(reader, 128); + } + } + } catch { } + + lock (_synclock) { + _requesting.Remove(md5); + } + + Dispatcher.UIThread.InvokeAsync(() => { + if (_resources.ContainsKey(md5)) _resources[md5] = img; + else _resources.Add(md5, img); + if (img != null) NotifyResourceReady(md5, img); + }); + } + }); + } + + public static void Subscribe(IAvatarHost host) { + _avatars.Add(new WeakReference(host)); + } + + public static Bitmap Request(string md5, bool forceRefetch = false) { + if (forceRefetch) { + if (_resources.ContainsKey(md5)) _resources.Remove(md5); + } else { + if (_resources.ContainsKey(md5)) return _resources[md5]; + + var localFile = Path.Combine(_storePath, md5); + if (File.Exists(localFile)) { + try { + using (var stream = File.OpenRead(localFile)) { + var img = Bitmap.DecodeToWidth(stream, 128); + _resources.Add(md5, img); + return img; + } + } catch { } + } + } + + lock (_synclock) { + if (!_requesting.Contains(md5)) _requesting.Add(md5); + } + + return null; + } + + private static void NotifyResourceReady(string md5, Bitmap bitmap) { + List> invalids = new List>(); + foreach (var avatar in _avatars) { + IAvatarHost retrived = null; + if (avatar.TryGetTarget(out retrived)) { + retrived.OnAvatarResourceReady(md5, bitmap); + break; + } else { + invalids.Add(avatar); + } + } + + foreach (var invalid in invalids) _avatars.Remove(invalid); + } + + private static object _synclock = new object(); + private static string _storePath = string.Empty; + private static List> _avatars = new List>(); + private static Dictionary _resources = new Dictionary(); + private static HashSet _requesting = new HashSet(); + } +} diff --git a/src/Models/Blame.cs b/src/Models/Blame.cs new file mode 100644 index 00000000..d9d3c9a4 --- /dev/null +++ b/src/Models/Blame.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace SourceGit.Models { + public class BlameLineInfo { + public bool IsFirstInGroup { get; set; } = false; + public string CommitSHA { get; set; } = string.Empty; + public string Author { get; set; } = string.Empty; + public string Time { get; set; } = string.Empty; + } + + public class BlameData { + public string File { get; set; } = string.Empty; + public List LineInfos { get; set; } = new List(); + public string Content { get; set; } = string.Empty; + public bool IsBinary { get; set; } = false; + } +} diff --git a/src/Models/BlameLine.cs b/src/Models/BlameLine.cs deleted file mode 100644 index e97a4f87..00000000 --- a/src/Models/BlameLine.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SourceGit.Models { - /// - /// 追溯中的行信息 - /// - public class BlameLine { - public string LineNumber { get; set; } - public string CommitSHA { get; set; } - public string Author { get; set; } - public string Time { get; set; } - public string Content { get; set; } - } -} diff --git a/src/Models/Bookmarks.cs b/src/Models/Bookmarks.cs new file mode 100644 index 00000000..5c5e522c --- /dev/null +++ b/src/Models/Bookmarks.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace SourceGit.Models { + public static class Bookmarks { + public static readonly Avalonia.Media.IBrush[] Brushes = [ + Avalonia.Media.Brushes.Transparent, + Avalonia.Media.Brushes.Red, + Avalonia.Media.Brushes.Orange, + Avalonia.Media.Brushes.Gold, + Avalonia.Media.Brushes.ForestGreen, + Avalonia.Media.Brushes.DarkCyan, + Avalonia.Media.Brushes.DeepSkyBlue, + Avalonia.Media.Brushes.Purple, + ]; + + public static readonly List Supported = new List(); + + static Bookmarks() { + for (int i = 0; i < Brushes.Length; i++) Supported.Add(i); + } + } +} diff --git a/src/Models/Branch.cs b/src/Models/Branch.cs index 9f88c4c8..f1041839 100644 --- a/src/Models/Branch.cs +++ b/src/Models/Branch.cs @@ -1,7 +1,4 @@ -namespace SourceGit.Models { - /// - /// 分支数据 - /// +namespace SourceGit.Models { public class Branch { public string Name { get; set; } public string FullName { get; set; } diff --git a/src/Models/BranchTreeNode.cs b/src/Models/BranchTreeNode.cs new file mode 100644 index 00000000..1635ff6e --- /dev/null +++ b/src/Models/BranchTreeNode.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; + +namespace SourceGit.Models { + public enum BranchTreeNodeType { + Remote, + Folder, + Branch, + } + + public class BranchTreeNode { + public string Name { get; set; } + public BranchTreeNodeType Type { get; set; } + public object Backend { get; set; } + public bool IsExpanded { get; set; } + public List Children { get; set; } = new List(); + + public bool IsUpstreamTrackStatusVisible { + get => IsBranch && !string.IsNullOrEmpty((Backend as Branch).UpstreamTrackStatus); + } + + public string UpstreamTrackStatus { + get => Type == BranchTreeNodeType.Branch ? (Backend as Branch).UpstreamTrackStatus : ""; + } + + public bool IsRemote { + get => Type == BranchTreeNodeType.Remote; + } + + public bool IsFolder { + get => Type == BranchTreeNodeType.Folder; + } + + public bool IsBranch { + get => Type == BranchTreeNodeType.Branch; + } + + public bool IsCurrent { + get => IsBranch && (Backend as Branch).IsCurrent; + } + + public class Builder { + public List Locals => _locals; + public List Remotes => _remotes; + + public void Run(List branches, List remotes) { + foreach (var remote in remotes) { + var path = $"remote/{remote.Name}"; + var node = new BranchTreeNode() { + Name = remote.Name, + Type = BranchTreeNodeType.Remote, + Backend = remote, + IsExpanded = _expanded.Contains(path), + }; + + _maps.Add(path, node); + _remotes.Add(node); + } + + foreach (var branch in branches) { + if (branch.IsLocal) { + MakeBranchNode(branch, _locals, "local"); + } else { + var remote = _remotes.Find(x => x.Name == branch.Remote); + if (remote != null) MakeBranchNode(branch, remote.Children, $"remote/{remote.Name}"); + } + } + + SortNodes(_locals); + SortNodes(_remotes); + } + + public void CollectExpandedNodes(List nodes, bool isLocal) { + CollectExpandedNodes(nodes, isLocal ? "local" : "remote"); + } + + private void CollectExpandedNodes(List nodes, string prefix) { + foreach (var node in nodes) { + var path = prefix + "/" + node.Name; + if (node.Type != BranchTreeNodeType.Branch && node.IsExpanded) _expanded.Add(path); + CollectExpandedNodes(node.Children, path); + } + } + + private void MakeBranchNode(Branch branch, List roots, string prefix) { + var subs = branch.Name.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + if (subs.Length == 1) { + var node = new BranchTreeNode() { + Name = subs[0], + Type = BranchTreeNodeType.Branch, + Backend = branch, + IsExpanded = false, + }; + roots.Add(node); + return; + } + + BranchTreeNode lastFolder = null; + string path = prefix; + for (int i = 0; i < subs.Length - 1; i++) { + path = string.Concat(path, "/", subs[i]); + if (_maps.ContainsKey(path)) { + lastFolder = _maps[path]; + } else if (lastFolder == null) { + lastFolder = new BranchTreeNode() { + Name = subs[i], + Type = BranchTreeNodeType.Folder, + IsExpanded = _expanded.Contains(path), + }; + roots.Add(lastFolder); + _maps.Add(path, lastFolder); + } else { + var folder = new BranchTreeNode() { + Name = subs[i], + Type = BranchTreeNodeType.Folder, + IsExpanded = _expanded.Contains(path), + }; + _maps.Add(path, folder); + lastFolder.Children.Add(folder); + lastFolder = folder; + } + } + + var last = new BranchTreeNode() { + Name = subs[subs.Length - 1], + Type = BranchTreeNodeType.Branch, + Backend = branch, + IsExpanded = false, + }; + lastFolder.Children.Add(last); + } + + private void SortNodes(List nodes) { + nodes.Sort((l, r) => { + if (l.Type == r.Type) { + return l.Name.CompareTo(r.Name); + } else { + return (int)(l.Type) - (int)(r.Type); + } + }); + + foreach (var node in nodes) SortNodes(node.Children); + } + + private List _locals = new List(); + private List _remotes = new List(); + private HashSet _expanded = new HashSet(); + private Dictionary _maps = new Dictionary(); + } + } +} diff --git a/src/Models/CRLFMode.cs b/src/Models/CRLFMode.cs new file mode 100644 index 00000000..a75c247c --- /dev/null +++ b/src/Models/CRLFMode.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace SourceGit.Models { + public class CRLFMode { + public string Name { get; set; } + public string Value { get; set; } + public string Desc { get; set; } + + public static List Supported = new List() { + new CRLFMode("TRUE", "true", "Commit as LF, checkout as CRLF"), + new CRLFMode("INPUT", "input", "Only convert for commit"), + new CRLFMode("FALSE", "false", "Do NOT convert"), + }; + + public CRLFMode(string name, string value, string desc) { + Name = name; + Value = value; + Desc = desc; + } + } +} diff --git a/src/Models/CRLFOption.cs b/src/Models/CRLFOption.cs deleted file mode 100644 index 059b5060..00000000 --- a/src/Models/CRLFOption.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; - -namespace SourceGit.Models { - - /// - /// 自动换行处理方式 - /// - public class CRLFOption { - public string Display { get; set; } - public string Value { get; set; } - public string Desc { get; set; } - - public static List Supported = new List() { - new CRLFOption("TRUE", "true", "Commit as LF, checkout as CRLF"), - new CRLFOption("INPUT", "input", "Only convert for commit"), - new CRLFOption("FALSE", "false", "Do NOT convert"), - }; - - public CRLFOption(string display, string value, string desc) { - Display = display; - Value = value; - Desc = desc; - } - } -} diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 2bfc2c91..15596a13 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -1,52 +1,41 @@ -namespace SourceGit.Models { +namespace SourceGit.Models { + public enum ChangeViewMode { + List, + Grid, + Tree, + } + + public enum ChangeState { + None, + Modified, + Added, + Deleted, + Renamed, + Copied, + Unmerged, + Untracked + } - /// - /// Git变更 - /// public class Change { - - /// - /// 显示模式 - /// - public enum DisplayMode { - Tree, - List, - Grid, - } - - /// - /// 变更状态码 - /// - public enum Status { - None, - Modified, - Added, - Deleted, - Renamed, - Copied, - Unmerged, - Untracked, - } - - public Status Index { get; set; } - public Status WorkTree { get; set; } = Status.None; + public ChangeState Index { get; set; } + public ChangeState WorkTree { get; set; } = ChangeState.None; public string Path { get; set; } = ""; public string OriginalPath { get; set; } = ""; public bool IsConflit { get { - if (Index == Status.Unmerged || WorkTree == Status.Unmerged) return true; - if (Index == Status.Added && WorkTree == Status.Added) return true; - if (Index == Status.Deleted && WorkTree == Status.Deleted) return true; + if (Index == ChangeState.Unmerged || WorkTree == ChangeState.Unmerged) return true; + if (Index == ChangeState.Added && WorkTree == ChangeState.Added) return true; + if (Index == ChangeState.Deleted && WorkTree == ChangeState.Deleted) return true; return false; } } - public void Set(Status index, Status workTree = Status.None) { + public void Set(ChangeState index, ChangeState workTree = ChangeState.None) { Index = index; WorkTree = workTree; - if (index == Status.Renamed || workTree == Status.Renamed) { + if (index == ChangeState.Renamed || workTree == ChangeState.Renamed) { var idx = Path.IndexOf('\t'); if (idx >= 0) { OriginalPath = Path.Substring(0, idx); diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 141f6b9e..36261cf4 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -1,16 +1,10 @@ +using Avalonia; using System; using System.Collections.Generic; -using System.Windows; namespace SourceGit.Models { - /// - /// 提交记录 - /// public class Commit { - private static readonly DateTime UTC_START = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); - public string SHA { get; set; } = string.Empty; - public string ShortSHA => SHA.Substring(0, 8); public User Author { get; set; } = User.Invalid; public ulong AuthorTime { get; set; } = 0; public User Committer { get; set; } = User.Invalid; @@ -23,10 +17,18 @@ namespace SourceGit.Models { public bool IsMerged { get; set; } = false; public Thickness Margin { get; set; } = new Thickness(0); - public string AuthorTimeStr => UTC_START.AddSeconds(AuthorTime).ToString("yyyy-MM-dd HH:mm:ss"); - public string CommitterTimeStr => UTC_START.AddSeconds(CommitterTime).ToString("yyyy-MM-dd HH:mm:ss"); - public string AuthorTimeShortStr => UTC_START.AddSeconds(AuthorTime).ToString("yyyy/MM/dd"); - public string CommitterTimeShortStr => UTC_START.AddSeconds(CommitterTime).ToString("yyyy/MM/dd"); + public string AuthorTimeStr => _utcStart.AddSeconds(AuthorTime).ToString("yyyy/MM/dd HH:mm:ss"); + public string CommitterTimeStr => _utcStart.AddSeconds(CommitterTime).ToString("yyyy/MM/dd HH:mm:ss"); + public string AuthorTimeShortStr => _utcStart.AddSeconds(AuthorTime).ToString("yyyy/MM/dd"); + public string CommitterTimeShortStr => _utcStart.AddSeconds(CommitterTime).ToString("yyyy/MM/dd"); + + public bool IsCommitterVisible { + get => Author != Committer || AuthorTime != CommitterTime; + } + + public string FullMessage { + get => string.IsNullOrWhiteSpace(Message) ? Subject : $"{Subject}\n\n{Message}"; + } public static void ParseUserAndTime(string data, ref User user, ref ulong time) { var userEndIdx = data.IndexOf('>'); @@ -36,5 +38,7 @@ namespace SourceGit.Models { user = User.FindOrAdd(data.Substring(0, userEndIdx)); time = timeEndIdx < 0 ? 0 : ulong.Parse(data.Substring(userEndIdx + 2, timeEndIdx - userEndIdx - 2)); } + + private static readonly DateTime _utcStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); } } diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs new file mode 100644 index 00000000..cabaff14 --- /dev/null +++ b/src/Models/CommitGraph.cs @@ -0,0 +1,205 @@ +using Avalonia; +using System; +using System.Collections.Generic; + +namespace SourceGit.Models { + public class CommitGraph { + public class Path { + public List Points = new List(); + public int Color = 0; + } + + public class PathHelper { + public string Next; + public bool IsMerged; + public double LastX; + public double LastY; + public double EndY; + public Path Path; + + public PathHelper(string next, bool isMerged, int color, Point start) { + Next = next; + IsMerged = isMerged; + LastX = start.X; + LastY = start.Y; + EndY = LastY; + + Path = new Path(); + Path.Color = color; + Path.Points.Add(start); + } + + public PathHelper(string next, bool isMerged, int color, Point start, Point to) { + Next = next; + IsMerged = isMerged; + LastX = to.X; + LastY = to.Y; + EndY = LastY; + + Path = new Path(); + Path.Color = color; + Path.Points.Add(start); + Path.Points.Add(to); + } + + public void Add(double x, double y, double halfHeight, bool isEnd = false) { + if (x > LastX) { + Add(new Point(LastX, LastY)); + Add(new Point(x, y - halfHeight)); + if (isEnd) Add(new Point(x, y)); + } else if (x < LastX) { + if (y > LastY + halfHeight) Add(new Point(LastX, LastY + halfHeight)); + Add(new Point(x, y)); + } else if (isEnd) { + Add(new Point(x, y)); + } + + LastX = x; + LastY = y; + } + + private void Add(Point p) { + if (EndY < p.Y) { + Path.Points.Add(p); + EndY = p.Y; + } + } + } + + public class Link { + public Point Start; + public Point Control; + public Point End; + public int Color; + } + + public class Dot { + public Point Center; + public int Color; + } + + public List Paths { get; set; } = new List(); + public List Links { get; set; } = new List(); + public List Dots { get; set; } = new List(); + + public static CommitGraph Parse(List commits, double rowHeight, int colorCount) { + double UNIT_WIDTH = 12; + double HALF_WIDTH = 6; + double UNIT_HEIGHT = rowHeight; + double HALF_HEIGHT = rowHeight / 2; + + var temp = new CommitGraph(); + var unsolved = new List(); + var mapUnsolved = new Dictionary(); + var ended = new List(); + var offsetY = -HALF_HEIGHT; + var colorIdx = 0; + + foreach (var commit in commits) { + var major = null as PathHelper; + var isMerged = commit.IsMerged; + var oldCount = unsolved.Count; + + // Update current y offset + offsetY += UNIT_HEIGHT; + + // Find first curves that links to this commit and marks others that links to this commit ended. + double offsetX = -HALF_WIDTH; + foreach (var l in unsolved) { + if (l.Next == commit.SHA) { + if (major == null) { + offsetX += UNIT_WIDTH; + major = l; + + if (commit.Parents.Count > 0) { + major.Next = commit.Parents[0]; + if (!mapUnsolved.ContainsKey(major.Next)) mapUnsolved.Add(major.Next, major); + } else { + major.Next = "ENDED"; + ended.Add(l); + } + + major.Add(offsetX, offsetY, HALF_HEIGHT); + } else { + ended.Add(l); + } + + isMerged = isMerged || l.IsMerged; + } else { + if (!mapUnsolved.ContainsKey(l.Next)) mapUnsolved.Add(l.Next, l); + offsetX += UNIT_WIDTH; + l.Add(offsetX, offsetY, HALF_HEIGHT); + } + } + + // Create new curve for branch head + if (major == null && commit.Parents.Count > 0) { + offsetX += UNIT_WIDTH; + major = new PathHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY)); + unsolved.Add(major); + temp.Paths.Add(major.Path); + colorIdx = (colorIdx + 1) % colorCount; + } + + // Calculate link position of this commit. + Point position = new Point(offsetX, offsetY); + if (major != null) { + major.IsMerged = isMerged; + position = new Point(major.LastX, offsetY); + temp.Dots.Add(new Dot() { Center = position, Color = major.Path.Color }); + } else { + temp.Dots.Add(new Dot() { Center = position, Color = 0 }); + } + + // Deal with parents + for (int j = 1; j < commit.Parents.Count; j++) { + var parent = commit.Parents[j]; + if (mapUnsolved.ContainsKey(parent)) { + var l = mapUnsolved[parent]; + var link = new Link(); + + link.Start = position; + link.End = new Point(l.LastX, offsetY + HALF_HEIGHT); + link.Control = new Point(link.End.X, link.Start.Y); + link.Color = l.Path.Color; + temp.Links.Add(link); + } else { + offsetX += UNIT_WIDTH; + + // Create new curve for parent commit that not includes before + var l = new PathHelper(commit.Parents[j], isMerged, colorIdx, position, new Point(offsetX, position.Y + HALF_HEIGHT)); + unsolved.Add(l); + temp.Paths.Add(l.Path); + colorIdx = (colorIdx + 1) % colorCount; + } + } + + // Remove ended curves from unsolved + foreach (var l in ended) { + l.Add(position.X, position.Y, HALF_HEIGHT, true); + unsolved.Remove(l); + } + + // Margins & merge state (used by datagrid). + commit.IsMerged = isMerged; + commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH), 0, 0, 0); + + // Clean up + ended.Clear(); + mapUnsolved.Clear(); + } + + // Deal with curves haven't ended yet. + for (int i = 0; i < unsolved.Count; i++) { + var path = unsolved[i]; + var endY = (commits.Count - 0.5) * UNIT_HEIGHT; + + if (path.Path.Points.Count == 1 && path.Path.Points[0].Y == endY) continue; + path.Add((i + 0.5) * UNIT_WIDTH, endY + HALF_HEIGHT, HALF_HEIGHT, true); + } + unsolved.Clear(); + + return temp; + } + } +} diff --git a/src/Models/CrashInfo.cs b/src/Models/CrashInfo.cs deleted file mode 100644 index 02318bac..00000000 --- a/src/Models/CrashInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Reflection; -using System.IO; -using System.Text; -using System.Diagnostics; - -namespace SourceGit.Models { - /// - /// 崩溃日志生成 - /// - public class CrashInfo { - public static void Create(Exception e) { - var builder = new StringBuilder(); - builder.Append("Crash: "); - builder.Append(e.Message); - builder.Append("\n\n"); - builder.Append("----------------------------\n"); - builder.Append($"Windows OS: {Environment.OSVersion}\n"); - builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); - builder.Append($"Platform: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); - builder.Append($"Source: {e.Source}\n"); - builder.Append($"---------------------------\n\n"); - builder.Append(e.StackTrace); - - var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); - var file = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); - file = Path.Combine(file, $"sourcegit_crash_{time}.log"); - File.WriteAllText(file, builder.ToString()); - } - } -} diff --git a/src/Models/Decorator.cs b/src/Models/Decorator.cs index 4197b6d0..5c306b7f 100644 --- a/src/Models/Decorator.cs +++ b/src/Models/Decorator.cs @@ -1,8 +1,6 @@ -namespace SourceGit.Models { +using Avalonia.Media; - /// - /// 修饰类型 - /// +namespace SourceGit.Models { public enum DecoratorType { None, CurrentBranchHead, @@ -11,11 +9,15 @@ namespace SourceGit.Models { Tag, } - /// - /// 提交的附加修饰 - /// public class Decorator { public DecoratorType Type { get; set; } = DecoratorType.None; public string Name { get; set; } = ""; } + + public static class DecoratorResources { + public static readonly IBrush[] Backgrounds = [ + new SolidColorBrush(0xFF02C302), + new SolidColorBrush(0xFFFFB835), + ]; + } } diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs new file mode 100644 index 00000000..3a26145f --- /dev/null +++ b/src/Models/DiffOption.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Text; + +namespace SourceGit.Models { + public class DiffOption { + public List Revisions => _revisions; + public string Path => _path; + public string OrgPath => _orgPath; + + /// + /// Only used for working copy changes + /// + /// + /// + public DiffOption(Change change, bool isUnstaged) { + if (isUnstaged) { + switch (change.WorkTree) { + case ChangeState.Added: + case ChangeState.Untracked: + _extra = "--no-index"; + _path = change.Path; + _orgPath = "/dev/null"; + break; + default: + _path = change.Path; + _orgPath = change.OriginalPath; + break; + } + } else { + _extra = "--cached"; + _path = change.Path; + _orgPath = change.OriginalPath; + } + } + + /// + /// Only used for commit changes. + /// + /// + /// + public DiffOption(Commit commit, Change change) { + var baseRevision = commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : $"{commit.SHA}^"; + _revisions.Add(baseRevision); + _revisions.Add(commit.SHA); + _path = change.Path; + _orgPath = change.OriginalPath; + } + + /// + /// Diff with filepath. + /// + /// + /// + public DiffOption(Commit commit, string file) { + var baseRevision = commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : $"{commit.SHA}^"; + _revisions.Add(baseRevision); + _revisions.Add(commit.SHA); + _path = file; + } + + /// + /// Used to show differences between two revisions. + /// + /// + /// + /// + public DiffOption(string baseRevision, string targetRevision, Change change) { + _revisions.Add(baseRevision); + _revisions.Add(targetRevision); + _path = change.Path; + _orgPath = change.OriginalPath; + } + + /// + /// Converts to diff command arguments. + /// + /// + public override string ToString() { + var builder = new StringBuilder(); + if (!string.IsNullOrEmpty(_extra)) builder.Append($"{_extra} "); + foreach (var r in _revisions) builder.Append($"{r} "); + + builder.Append("-- "); + if (!string.IsNullOrEmpty(_orgPath)) builder.Append($"\"{_orgPath}\" "); + builder.Append($"\"{_path}\""); + + return builder.ToString(); + } + + private string _orgPath = string.Empty; + private string _path = string.Empty; + private string _extra = string.Empty; + private List _revisions = new List(); + } +} diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs new file mode 100644 index 00000000..debbd48d --- /dev/null +++ b/src/Models/DiffResult.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +namespace SourceGit.Models { + public enum TextDiffLineType { + None, + Normal, + Indicator, + Added, + Deleted, + } + + public class TextInlineRange { + public int Start { get; set; } + public int Count { get; set; } + public TextInlineRange(int p, int n) { Start = p; Count = n; } + } + + public class TextDiffLine { + public TextDiffLineType Type { get; set; } = TextDiffLineType.None; + public string Content { get; set; } = ""; + public string OldLine { get; set; } = ""; + public string NewLine { get; set; } = ""; + public List Highlights { get; set; } = new List(); + + public TextDiffLine() { } + public TextDiffLine(TextDiffLineType type, string content, string oldLine, string newLine) { + Type = type; + Content = content; + OldLine = oldLine; + NewLine = newLine; + } + } + + public class TextDiff { + public string File { get; set; } = string.Empty; + public List Lines { get; set; } = new List(); + public int MaxLineNumber = 0; + } + + public class LFSDiff { + public LFSObject Old { get; set; } = new LFSObject(); + public LFSObject New { get; set; } = new LFSObject(); + } + + public class BinaryDiff { + public long OldSize { get; set; } = 0; + public long NewSize { get; set; } = 0; + } + + public class DiffResult { + public bool IsBinary { get; set; } = false; + public bool IsLFS { get; set; } = false; + public TextDiff TextDiff { get; set; } = null; + public LFSDiff LFSDiff { get; set; } = null; + } +} diff --git a/src/Models/ExecutableFinder.cs b/src/Models/ExecutableFinder.cs deleted file mode 100644 index 7f36522e..00000000 --- a/src/Models/ExecutableFinder.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Runtime.InteropServices; -using System.Text; - -namespace SourceGit.Models { - /// - /// 用于在PATH中检测可执行文件 - /// - public class ExecutableFinder { - - // https://docs.microsoft.com/en-us/windows/desktop/api/shlwapi/nf-shlwapi-pathfindonpathw - // https://www.pinvoke.net/default.aspx/shlwapi.PathFindOnPath - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = false)] - private static extern bool PathFindOnPath([In, Out] StringBuilder pszFile, [In] string[] ppszOtherDirs); - - /// - /// 从PATH中找到可执行文件路径 - /// - /// - /// - public static string Find(string exec) { - var builder = new StringBuilder(exec, 259); - var rs = PathFindOnPath(builder, null); - return rs ? builder.ToString() : null; - } - } -} diff --git a/src/Models/ExternalMergeTools.cs b/src/Models/ExternalMergeTools.cs new file mode 100644 index 00000000..0df275e0 --- /dev/null +++ b/src/Models/ExternalMergeTools.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace SourceGit.Models { + public class ExternalMergeTools { + public int Type { get; set; } + public string Name { get; set; } + public string Exec { get; set; } + public string Cmd { get; set; } + public string DiffCmd { get; set; } + + public static List Supported; + + static ExternalMergeTools() { + if (OperatingSystem.IsWindows()) { + Supported = new List() { + new ExternalMergeTools(0, "Custom", "", "", ""), + new ExternalMergeTools(1, "Visual Studio Code", "Code.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(2, "Visual Studio 2017/2019", "vsDiffMerge.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\" /m", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(3, "Tortoise Merge", "TortoiseMerge.exe;TortoiseGitMerge.exe", "-base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"", "-base:\"$LOCAL\" -theirs:\"$REMOTE\""), + new ExternalMergeTools(4, "KDiff3", "kdiff3.exe", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(5, "Beyond Compare 4", "BComp.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), + new ExternalMergeTools(6, "WinMerge", "WinMergeU.exe", "-u -e \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", "-u -e \"$LOCAL\" \"$REMOTE\""), + }; + } + } + + public ExternalMergeTools(int type, string name, string exec, string cmd, string diffCmd) { + Type = type; + Name = name; + Exec = exec; + Cmd = cmd; + DiffCmd = diffCmd; + } + } +} diff --git a/src/Models/FileSizeChange.cs b/src/Models/FileSizeChange.cs deleted file mode 100644 index cced7392..00000000 --- a/src/Models/FileSizeChange.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SourceGit.Models { - /// - /// 文件大小变化 - /// - public class FileSizeChange { - public long OldSize = 0; - public long NewSize = 0; - } -} diff --git a/src/Models/GitFlow.cs b/src/Models/GitFlow.cs index 07c787df..dca2aa91 100644 --- a/src/Models/GitFlow.cs +++ b/src/Models/GitFlow.cs @@ -1,7 +1,4 @@ -namespace SourceGit.Models { - /// - /// GitFlow的分支类型 - /// +namespace SourceGit.Models { public enum GitFlowBranchType { None, Feature, @@ -9,9 +6,6 @@ namespace SourceGit.Models { Hotfix, } - /// - /// GitFlow相关设置 - /// public class GitFlow { public string Feature { get; set; } public string Release { get; set; } diff --git a/src/Models/InstalledFonts.cs b/src/Models/InstalledFonts.cs deleted file mode 100644 index 7855b493..00000000 --- a/src/Models/InstalledFonts.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Windows.Markup; -using System.Windows.Media; - -namespace SourceGit.Models { - public class InstalledFont { - public string Name { get; set; } - public int FamilyIndex { get; set; } - - public static List GetFonts { - get { - var fontList = new List(); - - var fontCollection = Fonts.SystemFontFamilies; - var familyCount = fontCollection.Count; - - for (int i = 0; i < familyCount; i++) { - var fontFamily = fontCollection.ElementAt(i); - var familyNames = fontFamily.FamilyNames; - - if (!familyNames.TryGetValue(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name), out var name)) { - if (!familyNames.TryGetValue(XmlLanguage.GetLanguage("en-us"), out name)) { - name = familyNames.FirstOrDefault().Value; - } - } - - fontList.Add(new InstalledFont() { - Name = name, - FamilyIndex = i - }); - } - - fontList.Sort((p, n) => string.Compare(p.Name, n.Name, StringComparison.Ordinal)); - - return fontList; - } - } - } -} diff --git a/src/Models/LFSChange.cs b/src/Models/LFSChange.cs deleted file mode 100644 index e77f78ac..00000000 --- a/src/Models/LFSChange.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SourceGit.Models { - /// - /// LFS对象变更 - /// - public class LFSChange { - public LFSObject Old; - public LFSObject New; - public bool IsValid => Old != null || New != null; - } -} diff --git a/src/Models/LFSObject.cs b/src/Models/LFSObject.cs index 4bbc08e6..13bf0b32 100644 --- a/src/Models/LFSObject.cs +++ b/src/Models/LFSObject.cs @@ -1,9 +1,6 @@ -namespace SourceGit.Models { - /// - /// LFS对象 - /// +namespace SourceGit.Models { public class LFSObject { - public string OID { get; set; } - public long Size { get; set; } + public string Oid { get; set; } = string.Empty; + public long Size { get; set; } = 0; } } diff --git a/src/Models/Locale.cs b/src/Models/Locale.cs deleted file mode 100644 index d3abb25c..00000000 --- a/src/Models/Locale.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SourceGit.Models { - - /// - /// 支持的语言 - /// - public class Locale { - public string Name { get; set; } - public string Resource { get; set; } - - public static List Supported = new List() { - new Locale("English", "en_US"), - new Locale("简体中文", "zh_CN"), - }; - - public Locale(string name, string res) { - Name = name; - Resource = res; - } - - public static void Change() { - var lang = Preference.Instance.General.Locale; - foreach (var rs in App.Current.Resources.MergedDictionaries) { - if (rs.Source != null && rs.Source.OriginalString.StartsWith("pack://application:,,,/Resources/Locales/", StringComparison.Ordinal)) { - rs.Source = new Uri($"pack://application:,,,/Resources/Locales/{lang}.xaml", UriKind.Absolute); - break; - } - } - } - } -} diff --git a/src/Models/Locales.cs b/src/Models/Locales.cs new file mode 100644 index 00000000..35bb7a29 --- /dev/null +++ b/src/Models/Locales.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace SourceGit.Models { + public class Locale { + public string Name { get; set; } + public string Key { get; set; } + + public static List Supported = new List() { + new Locale("English", "en_US"), + new Locale("简体中文", "zh_CN"), + }; + + public Locale(string name, string key) { + Name = name; + Key = key; + } + } +} diff --git a/src/Models/MergeOption.cs b/src/Models/MergeOption.cs deleted file mode 100644 index 778c1a12..00000000 --- a/src/Models/MergeOption.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; - -namespace SourceGit.Models { - /// - /// 合并方式 - /// - public class MergeOption { - public string Name { get; set; } - public string Desc { get; set; } - public string Arg { get; set; } - - public static List Supported = new List() { - new MergeOption("Default", "Fast-forward if possible", ""), - new MergeOption("No Fast-forward", "Always create a merge commit", "--no-ff"), - new MergeOption("Squash", "Use '--squash'", "--squash"), - new MergeOption("Don't commit", "Merge without commit", "--no-commit"), - }; - - public MergeOption(string n, string d, string a) { - Name = n; - Desc = d; - Arg = a; - } - } -} diff --git a/src/Models/MergeTool.cs b/src/Models/MergeTool.cs deleted file mode 100644 index 1f991023..00000000 --- a/src/Models/MergeTool.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.IO; - -namespace SourceGit.Models { - - /// - /// 外部合并工具 - /// - public class MergeTool { - public int Type { get; set; } - public string Name { get; set; } - public string Exec { get; set; } - public string Cmd { get; set; } - public string DiffCmd { get; set; } - public Func Finder { get; set; } - - public static List Supported = new List() { - new MergeTool(0, "--", "", "", "", () => ""), - new MergeTool(1, "Visual Studio Code", "Code.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\"", FindVSCode), - new MergeTool(2, "Visual Studio 2017/2019", "vsDiffMerge.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\" /m", "\"$LOCAL\" \"$REMOTE\"", FindVSMerge), - new MergeTool(3, "Tortoise Merge", "TortoiseMerge.exe;TortoiseGitMerge.exe", "-base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"", "-base:\"$LOCAL\" -theirs:\"$REMOTE\"", FindTortoiseMerge), - new MergeTool(4, "KDiff3", "kdiff3.exe", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\"", FindKDiff3), - new MergeTool(5, "Beyond Compare 4", "BComp.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\"", FindBCompare), - new MergeTool(6, "WinMerge", "WinMergeU.exe", "-u -e \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", "-u -e \"$LOCAL\" \"$REMOTE\"", FindWinMerge), - }; - - public MergeTool(int type, string name, string exec, string cmd, string diffCmd, Func finder) { - Type = type; - Name = name; - Exec = exec; - Cmd = cmd; - DiffCmd = diffCmd; - Finder = finder; - } - - private static string FindVSCode() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - return ""; - } - - private static string FindVSMerge() { - var dir = @"C:\Program Files (x86)\Microsoft Visual Studio"; - if (Directory.Exists($"{dir}\\2019")) { - dir += "\\2019"; - } else if (Directory.Exists($"{dir}\\2017")) { - dir += "\\2017"; - } else { - return ""; - } - - if (Directory.Exists($"{dir}\\Community")) { - dir += "\\Community"; - } else if (Directory.Exists($"{dir}\\Enterprise")) { - dir += "\\Enterprise"; - } else if (Directory.Exists($"{dir}\\Professional")) { - dir += "\\Professional"; - } else { - return ""; - } - - return $"{dir}\\Common7\\IDE\\CommonExtensions\\Microsoft\\TeamFoundation\\Team Explorer\\vsDiffMerge.exe"; - } - - private static string FindTortoiseMerge() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var tortoise = root.OpenSubKey("SOFTWARE\\TortoiseGit") ?? root.OpenSubKey("SOFTWARE\\TortoiseSVN"); - if (tortoise == null) return ""; - return tortoise.GetValue("TMergePath") as string; - } - - private static string FindKDiff3() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var kdiff = root.OpenSubKey(@"SOFTWARE\KDiff3\diff-ext"); - if (kdiff == null) return ""; - return kdiff.GetValue("diffcommand") as string; - } - - private static string FindBCompare() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var bc = root.OpenSubKey(@"SOFTWARE\Scooter Software\Beyond Compare"); - if (bc == null) return ""; - - var exec = bc.GetValue("ExePath") as string; - var dir = Path.GetDirectoryName(exec); - return $"{dir}\\BComp.exe"; - } - - private static string FindWinMerge() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.CurrentUser, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var merge = root.OpenSubKey(@"SOFTWARE\Thingamahoochie\WinMerge"); - if (merge == null) return ""; - - var exec = merge.GetValue("Executable") as string; - return exec; - } - } -} diff --git a/src/Models/Notification.cs b/src/Models/Notification.cs new file mode 100644 index 00000000..08c888b9 --- /dev/null +++ b/src/Models/Notification.cs @@ -0,0 +1,10 @@ +namespace SourceGit.Models { + public class Notification { + public bool IsError { get; set; } = false; + public string Message { get; set; } = string.Empty; + } + + public interface INotificationReceiver { + void OnReceiveNotification(string ctx, Notification notice); + } +} diff --git a/src/Models/Object.cs b/src/Models/Object.cs index 01552f8c..c2f27a31 100644 --- a/src/Models/Object.cs +++ b/src/Models/Object.cs @@ -1,7 +1,4 @@ -namespace SourceGit.Models { - /// - /// 提交中元素类型 - /// +namespace SourceGit.Models { public enum ObjectType { None, Blob, @@ -10,9 +7,6 @@ namespace SourceGit.Models { Commit, } - /// - /// Git提交中的元素 - /// public class Object { public string SHA { get; set; } public ObjectType Type { get; set; } diff --git a/src/Models/Preference.cs b/src/Models/Preference.cs deleted file mode 100644 index 353a26f8..00000000 --- a/src/Models/Preference.cs +++ /dev/null @@ -1,297 +0,0 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Windows; - -namespace SourceGit.Models { - - /// - /// 程序配置 - /// - public class Preference { - private static readonly string SAVE_PATH = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "SourceGit", - "preference_v4.json"); - private static Preference instance = null; - - /// - /// 起始页仓库列表排序方式 - /// - public enum SortMethod { - ByName, - ByRecentlyOpened, - ByBookmark, - } - - /// - /// 通用配置 - /// - public class GeneralInfo { - - /// - /// 显示语言 - /// - public string Locale { get; set; } = "en_US"; - - /// - /// 系统字体 - /// - public string FontFamilyWindowSetting { get; set; } = "Microsoft YaHei UI"; - - [JsonIgnore] - public string FontFamilyWindow { - get => FontFamilyWindowSetting + ",Microsoft YaHei UI"; - set => FontFamilyWindowSetting = value; - } - - /// - /// 用户字体(提交列表、提交日志、差异比较等) - /// - public string FontFamilyContentSetting { get; set; } = "Consolas"; - - [JsonIgnore] - public string FontFamilyContent { - get => FontFamilyContentSetting + ",Microsoft YaHei UI"; - set => FontFamilyContentSetting = value; - } - - /// - /// 是否启用深色主题 - /// - public bool UseDarkTheme { get; set; } = false; - - /// - /// 历史提交记录最多显示的条目数 - /// - public uint MaxHistoryCommits { get; set; } = 20000; - - /// - /// 起始页仓库列表排序规则 - /// - public SortMethod SortBy { get; set; } = SortMethod.ByName; - } - - /// - /// Git配置 - /// - public class GitInfo { - - /// - /// git.exe所在路径 - /// - public string Path { get; set; } - - /// - /// 默认克隆路径 - /// - public string DefaultCloneDir { get; set; } - - /// - /// 启用自动拉取远程变更(每10分钟一次) - /// - public bool AutoFetchRemotes { get; set; } = true; - - /// - /// 在本地变更列表中显示未跟踪文件 - /// - public bool IncludeUntrackedInWC { get; set; } = true; - } - - /// - /// 外部合并工具配置 - /// - public class MergeToolInfo { - /// - /// 合并工具类型 - /// - public int Type { get; set; } = 0; - - /// - /// 合并工具可执行文件路径 - /// - public string Path { get; set; } = ""; - } - - /// - /// 使用设置 - /// - public class WindowInfo { - - /// - /// 最近一次设置的宽度 - /// - public double Width { get; set; } = 800; - - /// - /// 最近一次设置的高度 - /// - public double Height { get; set; } = 600; - - /// - /// 保存上次关闭时是否最大化中 - /// - public WindowState State { get; set; } = WindowState.Normal; - - /// - /// 将提交信息面板与提交记录左右排布 - /// - public bool MoveCommitInfoRight { get; set; } = false; - - /// - /// 使用合并Diff视图 - /// - public bool UseCombinedDiff { get; set; } = false; - - /// - /// Pull时是否使用Rebase替换Merge - /// - public bool UseRebaseOnPull { get; set; } = true; - - /// - /// Pull时是否使用自动暂存 - /// - public bool UseAutoStashOnPull { get; set; } = true; - - /// - /// 未暂存视图中变更显示方式 - /// - public Change.DisplayMode ChangeInUnstaged { get; set; } = Change.DisplayMode.Tree; - - /// - /// 暂存视图中变更显示方式 - /// - public Change.DisplayMode ChangeInStaged { get; set; } = Change.DisplayMode.Tree; - - /// - /// 提交信息视图中变更显示方式 - /// - public Change.DisplayMode ChangeInCommitInfo { get; set; } = Change.DisplayMode.Tree; - } - - /// - /// 恢复上次打开的窗口 - /// - public class RestoreTabs { - - /// - /// 是否开启该功能 - /// - public bool IsEnabled { get; set; } = false; - - /// - /// 上次打开的仓库 - /// - public List Opened { get; set; } = new List(); - - /// - /// 最后浏览的仓库 - /// - public string Actived { get; set; } = null; - } - - /// - /// 全局配置 - /// - [JsonIgnore] - public static Preference Instance { - get { - if (instance == null) return Load(); - return instance; - } - } - - /// - /// 检测配置是否正常 - /// - [JsonIgnore] - public bool IsReady { - get => File.Exists(Git.Path) && new Commands.Version().Query() != null; - } - - #region DATA - public GeneralInfo General { get; set; } = new GeneralInfo(); - public GitInfo Git { get; set; } = new GitInfo(); - public MergeToolInfo MergeTool { get; set; } = new MergeToolInfo(); - public WindowInfo Window { get; set; } = new WindowInfo(); - public List Repositories { get; set; } = new List(); - public RestoreTabs Restore { get; set; } = new RestoreTabs(); - #endregion - - #region LOAD_SAVE - public static Preference Load() { - if (!File.Exists(SAVE_PATH)) { - instance = new Preference(); - } else { - try { - instance = JsonSerializer.Deserialize(File.ReadAllText(SAVE_PATH)); - } catch { - instance = new Preference(); - } - } - - if (!instance.IsReady) { - var reg = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - var git = reg.OpenSubKey("SOFTWARE\\GitForWindows"); - if (git != null) { - instance.Git.Path = Path.Combine(git.GetValue("InstallPath") as string, "bin", "git.exe"); - } - } - - return instance; - } - - public static void Save() { - var dir = Path.GetDirectoryName(SAVE_PATH); - if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); - var data = JsonSerializer.Serialize(instance, new JsonSerializerOptions() { WriteIndented = true }); - File.WriteAllText(SAVE_PATH, data); - } - #endregion - - #region METHOD_ON_REPOSITORIES - public Repository AddRepository(string path, string gitDir) { - var repo = FindRepository(path); - if (repo != null) return repo; - - var dir = new DirectoryInfo(path); - repo = new Repository() { - Path = dir.FullName, - GitDir = gitDir, - Name = dir.Name - }; - - Repositories.Add(repo); - Repositories.Sort((l, r) => l.Name.CompareTo(r.Name)); - return repo; - } - - public Repository FindRepository(string path) { - var dir = new DirectoryInfo(path); - foreach (var repo in Repositories) { - if (repo.Path == dir.FullName) return repo; - } - return null; - } - - public void RemoveRepository(string path) { - var dir = new DirectoryInfo(path); - var removedIdx = -1; - - for (int i = 0; i < Repositories.Count; i++) { - if (Repositories[i].Path == dir.FullName) { - removedIdx = i; - break; - } - } - - if (removedIdx >= 0) Repositories.RemoveAt(removedIdx); - } - #endregion - } -} \ No newline at end of file diff --git a/src/Models/Remote.cs b/src/Models/Remote.cs index a4455975..021ccc67 100644 --- a/src/Models/Remote.cs +++ b/src/Models/Remote.cs @@ -1,10 +1,31 @@ -namespace SourceGit.Models { +using System.Text.RegularExpressions; - /// - /// 远程 - /// +namespace SourceGit.Models { public class Remote { + private static readonly Regex[] URL_FORMATS = [ + new Regex(@"^http[s]?://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/[\w\-]+/[\w\-\.]+\.git$"), + new Regex(@"^[\w\-]+@[\w\.\-]+(\:[0-9]+)?:[\w\-]+/[\w\-\.]+\.git$"), + new Regex(@"^ssh://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/[\w\-]+/[\w\-\.]+\.git$"), + ]; + public string Name { get; set; } public string URL { get; set; } + + public static bool IsSSH(string url) { + if (string.IsNullOrWhiteSpace(url)) return false; + + for (int i = 1; i < URL_FORMATS.Length; i++) { + if (URL_FORMATS[i].IsMatch(url)) return true; + } + + return false; + } + + public static bool IsValidURL(string url) { + foreach (var fmt in URL_FORMATS) { + if (fmt.IsMatch(url)) return true; + } + return false; + } } } diff --git a/src/Models/Repository.cs b/src/Models/Repository.cs deleted file mode 100644 index 9f8cfedc..00000000 --- a/src/Models/Repository.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Text.Json.Serialization; - -namespace SourceGit.Models { - /// - /// 用于更新过滤器的参数 - /// - public class FilterUpdateParam { - /// - /// 是否是添加过滤的操作,false代表删除 - /// - public bool IsAdd = false; - - /// - /// 过滤内容 - /// - public string Name = ""; - } - - /// - /// 仓库 - /// - public class Repository { - - #region PROPERTIES_SAVED - public string Name { - get => name; - set { - if (name != value) { - name = value; - Watcher.NotifyDisplayNameChanged(this); - } - } - } - - public string Path { get; set; } = ""; - public string GitDir { get; set; } = ""; - public long LastOpenTime { get; set; } = 0; - public List SubTrees { get; set; } = new List(); - public List Filters { get; set; } = new List(); - public List CommitMessages { get; set; } = new List(); - - public int Bookmark { - get { return bookmark; } - set { - if (value != bookmark) { - bookmark = value; - Watcher.NotifyBookmarkChanged(this); - } - } - } - #endregion - - #region PROPERTIES_RUNTIME - [JsonIgnore] public List Remotes = new List(); - [JsonIgnore] public List Branches = new List(); - [JsonIgnore] public GitFlow GitFlow = new GitFlow(); - #endregion - - /// - /// 记录历史输入的提交信息 - /// - /// - public void PushCommitMessage(string message) { - if (string.IsNullOrEmpty(message)) return; - - int exists = CommitMessages.Count; - if (exists > 0) { - var last = CommitMessages[0]; - if (last == message) return; - } - - if (exists >= 10) { - CommitMessages.RemoveRange(9, exists - 9); - } - - CommitMessages.Insert(0, message); - } - - /// - /// 判断一个文件是否在GitDir中 - /// - /// - /// - public bool ExistsInGitDir(string file) { - if (string.IsNullOrEmpty(file)) return false; - string fullpath = System.IO.Path.Combine(GitDir, file); - return Directory.Exists(fullpath) || File.Exists(fullpath); - } - - /// - /// 更新提交记录过滤器 - /// - /// 更新参数 - /// 是否发生了变化 - public bool UpdateFilters(FilterUpdateParam param = null) { - lock (updateFilterLock) { - bool changed = false; - - // 填写了参数就仅增删 - if (param != null) { - if (param.IsAdd) { - if (!Filters.Contains(param.Name)) { - Filters.Add(param.Name); - changed = true; - } - } else { - if (Filters.Contains(param.Name)) { - Filters.Remove(param.Name); - changed = true; - } - } - - return changed; - } - - // 未填写参数就检测,去掉无效的过滤 - if (Filters.Count > 0) { - var invalidFilters = new List(); - var branches = new Commands.Branches(Path).Result(); - var tags = new Commands.Tags(Path).Result(); - - foreach (var filter in Filters) { - if (filter.StartsWith("refs/")) { - if (branches.FindIndex(b => b.FullName == filter) < 0) invalidFilters.Add(filter); - } else { - if (tags.FindIndex(t => t.Name == filter) < 0) invalidFilters.Add(filter); - } - } - - if (invalidFilters.Count > 0) { - foreach (var filter in invalidFilters) Filters.Remove(filter); - return true; - } - } - - return false; - } - } - - private readonly object updateFilterLock = new object(); - private string name = string.Empty; - private int bookmark = 0; - } -} diff --git a/src/Models/ResetMode.cs b/src/Models/ResetMode.cs deleted file mode 100644 index 4532bcf1..00000000 --- a/src/Models/ResetMode.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using System.Windows.Media; - -namespace SourceGit.Models { - /// - /// 重置方式 - /// - public class ResetMode { - public string Name { get; set; } - public string Desc { get; set; } - public string Arg { get; set; } - public Brush Color { get; set; } - - public static List Supported = new List() { - new ResetMode("Soft", "Keep all changes. Stage differences", "--soft", Brushes.Green), - new ResetMode("Mixed", "Keep all changes. Unstage differences", "--mixed", Brushes.Orange), - new ResetMode("Hard", "Discard all changes", "--hard", Brushes.Red), - }; - - public ResetMode(string n, string d, string a, Brush b) { - Name = n; - Desc = d; - Arg = a; - Color = b; - } - } -} diff --git a/src/Models/RevisionFile.cs b/src/Models/RevisionFile.cs new file mode 100644 index 00000000..9dd49545 --- /dev/null +++ b/src/Models/RevisionFile.cs @@ -0,0 +1,17 @@ +namespace SourceGit.Models { + public class RevisionBinaryFile { + } + + public class RevisionTextFile { + public string FileName { get; set; } + public string Content { get; set; } + } + + public class RevisionLFSObject { + public LFSObject Object { get; set; } + } + + public class RevisionSubmodule { + public string SHA { get; set; } + } +} diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs index f079277f..8b2973b9 100644 --- a/src/Models/Stash.cs +++ b/src/Models/Stash.cs @@ -1,9 +1,6 @@ -using System; +using System; namespace SourceGit.Models { - /// - /// 贮藏 - /// public class Stash { private static readonly DateTime UTC_START = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); @@ -13,6 +10,6 @@ namespace SourceGit.Models { public ulong Time { get; set; } = 0; public string Message { get; set; } = ""; - public string TimeStr => UTC_START.AddSeconds(Time).ToString("yyyy-MM-dd HH:mm:ss"); + public string TimeStr => UTC_START.AddSeconds(Time).ToString("yyyy/MM/dd HH:mm:ss"); } } diff --git a/src/Models/StatisticSample.cs b/src/Models/StatisticSample.cs deleted file mode 100644 index 1fa7283b..00000000 --- a/src/Models/StatisticSample.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace SourceGit.Models { - /// - /// 统计图表样品 - /// - public class StatisticSample { - /// - /// 在图表中的顺序 - /// - public int Index { get; set; } - /// - /// 样品名 - /// - public string Name { get; set; } - /// - /// 提交个数 - /// - public int Count { get; set; } - } -} diff --git a/src/Models/SubTree.cs b/src/Models/SubTree.cs deleted file mode 100644 index fdbad2fe..00000000 --- a/src/Models/SubTree.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SourceGit.Models { - /// - /// 子树 - /// - public class SubTree { - public string Prefix { get; set; } - public string Remote { get; set; } - public string Branch { get; set; } = "master"; - } -} diff --git a/src/Models/Tag.cs b/src/Models/Tag.cs index 20d6f09b..a6e10db4 100644 --- a/src/Models/Tag.cs +++ b/src/Models/Tag.cs @@ -1,10 +1,6 @@ -namespace SourceGit.Models { - /// - /// 标签 - /// +namespace SourceGit.Models { public class Tag { public string Name { get; set; } public string SHA { get; set; } - public bool IsFiltered { get; set; } } } diff --git a/src/Models/TextChanges.cs b/src/Models/TextChanges.cs deleted file mode 100644 index c563c5a7..00000000 --- a/src/Models/TextChanges.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Collections.Generic; - -namespace SourceGit.Models { - /// - /// Diff文本文件变化 - /// - public class TextChanges { - - public enum LineMode { - None, - Normal, - Indicator, - Added, - Deleted, - } - - public class HighlightRange { - public int Start { get; set; } - public int Count { get; set; } - public HighlightRange(int p, int n) { Start = p; Count = n; } - } - - public class Line { - public int Index { get; set; } = 0; - public LineMode Mode { get; set; } = LineMode.None; - public string Content { get; set; } = ""; - public string OldLine { get; set; } = ""; - public string NewLine { get; set; } = ""; - public List Highlights { get; set; } = new List(); - - public bool IsContent { - get { - return Mode == LineMode.Added - || Mode == LineMode.Deleted - || Mode == LineMode.Normal; - } - } - - public bool IsDifference { - get { - return Mode == LineMode.Added - || Mode == LineMode.Deleted - || Mode == LineMode.None; - } - } - - public Line() { } - - public Line(int index, LineMode mode, string content, string oldLine, string newLine) { - Index = index; - Mode = mode; - Content = content; - OldLine = oldLine; - NewLine = newLine; - } - } - - public bool IsBinary = false; - public List Lines = new List(); - } -} diff --git a/src/Models/TextCompare.cs b/src/Models/TextInlineChange.cs similarity index 85% rename from src/Models/TextCompare.cs rename to src/Models/TextInlineChange.cs index e59e3424..6ce4c374 100644 --- a/src/Models/TextCompare.cs +++ b/src/Models/TextInlineChange.cs @@ -1,34 +1,13 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace SourceGit.Models { + public class TextInlineChange { + public int DeletedStart { get; set; } + public int DeletedCount { get; set; } + public int AddedStart { get; set; } + public int AddedCount { get; set; } - /// - /// 字串差异对比,改写自DiffPlex - /// - public class TextCompare { - private static readonly HashSet SEPS = new HashSet(" \t+-*/=!,:;.'\"/?|&#@%`<>()[]{}\\".ToCharArray()); - - /// - /// 差异信息 - /// - public class Different { - public int DeletedStart { get; set; } - public int DeletedCount { get; set; } - public int AddedStart { get; set; } - public int AddedCount { get; set; } - - public Different(int dp, int dc, int ap, int ac) { - DeletedStart = dp; - DeletedCount = dc; - AddedStart = ap; - AddedCount = ac; - } - } - - /// - /// 分片 - /// - public class Chunk { + class Chunk { public int Hash; public bool Modified; public int Start; @@ -42,10 +21,7 @@ namespace SourceGit.Models { } } - /// - /// 区间修改状态 - /// - public enum Edit { + enum Edit { None, DeletedRight, DeletedLeft, @@ -53,10 +29,7 @@ namespace SourceGit.Models { AddedLeft, } - /// - /// 当前区间检测结果 - /// - public class EditResult { + class EditResult { public Edit State; public int DeleteStart; public int DeleteEnd; @@ -64,13 +37,14 @@ namespace SourceGit.Models { public int AddEnd; } - /// - /// 对比字串 - /// - /// - /// - /// - public static List Process(string oldValue, string newValue) { + public TextInlineChange(int dp, int dc, int ap, int ac) { + DeletedStart = dp; + DeletedCount = dc; + AddedStart = ap; + AddedCount = ac; + } + + public static List Compare(string oldValue, string newValue) { var hashes = new Dictionary(); var chunksOld = MakeChunks(hashes, oldValue); var chunksNew = MakeChunks(hashes, newValue); @@ -81,10 +55,10 @@ namespace SourceGit.Models { var reverse = new int[max]; CheckModified(chunksOld, 0, sizeOld, chunksNew, 0, sizeNew, forward, reverse); - var ret = new List(); + var ret = new List(); var posOld = 0; var posNew = 0; - var last = null as Different; + var last = null as TextInlineChange; do { while (posOld < sizeOld && posNew < sizeNew && !chunksOld[posOld].Modified && !chunksNew[posNew].Modified) { posOld++; @@ -100,7 +74,7 @@ namespace SourceGit.Models { if (countOld + countNew == 0) continue; - var diff = new Different( + var diff = new TextInlineChange( countOld > 0 ? chunksOld[beginOld].Start : 0, countOld, countNew > 0 ? chunksNew[beginNew].Start : 0, @@ -126,10 +100,11 @@ namespace SourceGit.Models { var start = 0; var size = text.Length; var chunks = new List(); + var delims = new HashSet(" \t+-*/=!,:;.'\"/?|&#@%`<>()[]{}\\".ToCharArray()); for (int i = 0; i < size; i++) { var ch = text[i]; - if (SEPS.Contains(ch)) { + if (delims.Contains(ch)) { if (start != i) AddChunk(chunks, hashes, text.Substring(start, i - start), start); AddChunk(chunks, hashes, text.Substring(i, 1), i); start = i + 1; @@ -190,7 +165,6 @@ namespace SourceGit.Models { for (int i = 0; i <= half; i++) { - // 正向 for (int j = -i; j <= i; j += 2) { var idx = j + half; int o, n; @@ -231,7 +205,6 @@ namespace SourceGit.Models { } } - // 反向 for (int j = -i; j <= i; j += 2) { var idx = j + half; int o, n; diff --git a/src/Models/TextLine.cs b/src/Models/TextLine.cs deleted file mode 100644 index 72084cc0..00000000 --- a/src/Models/TextLine.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SourceGit.Models { - /// - /// 文件中的一行内容 - /// - public class TextLine { - public int Number { get; set; } - public string Data { get; set; } - } -} diff --git a/src/Models/Theme.cs b/src/Models/Theme.cs deleted file mode 100644 index 7eafb22d..00000000 --- a/src/Models/Theme.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Windows; - -namespace SourceGit.Models { - /// - /// 主题 - /// - public static class Theme { - /// - /// 主题切换事件 - /// - public static event Action Changed; - - /// - /// 启用主题变化监听 - /// - /// - public static void AddListener(FrameworkElement elem, Action callback) { - elem.Loaded += (_, __) => Changed += callback; - elem.Unloaded += (_, __) => Changed -= callback; - } - - /// - /// 切换主题 - /// - public static void Change() { - var theme = Preference.Instance.General.UseDarkTheme ? "Dark" : "Light"; - foreach (var rs in App.Current.Resources.MergedDictionaries) { - if (rs.Source != null && rs.Source.OriginalString.StartsWith("pack://application:,,,/Resources/Themes/", StringComparison.Ordinal)) { - rs.Source = new Uri($"pack://application:,,,/Resources/Themes/{theme}.xaml", UriKind.Absolute); - break; - } - } - - Changed?.Invoke(); - } - } -} diff --git a/src/Models/User.cs b/src/Models/User.cs index 9af6bdcf..e5ed39ef 100644 --- a/src/Models/User.cs +++ b/src/Models/User.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace SourceGit.Models { - /// - /// Git用户 - /// public class User { public static User Invalid = new User(); public static Dictionary Caches = new Dictionary(); @@ -12,8 +9,8 @@ namespace SourceGit.Models { public string Email { get; set; } = string.Empty; public override bool Equals(object obj) { - if (obj == null || !(obj is User)) return false; - + if (obj == null || !(obj is User)) return false; + var other = obj as User; return Name == other.Name && Email == other.Email; } diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs index 090b512f..74441dc7 100644 --- a/src/Models/Watcher.cs +++ b/src/Models/Watcher.cs @@ -1,268 +1,167 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Threading; +using System.Threading.Tasks; namespace SourceGit.Models { + public interface IRepository { + string FullPath { get; set; } + string GitDir { get; set; } - /// - /// 文件系统更新监视 - /// - public class Watcher { - /// - /// 打开仓库事件 - /// - public static event Action Opened; + void RefreshBranches(); + void RefreshTags(); + void RefreshCommits(); + void RefreshSubmodules(); + void RefreshWorkingCopyChanges(); + void RefreshStashes(); + } - /// - /// 仓库的显示名变化了 - /// - public static event Action DisplayNameChanged; + public class Watcher : IDisposable { + public Watcher(IRepository repo) { + _repo = repo; - /// - /// 仓库的书签变化了 - /// - public static event Action BookmarkChanged; + _wcWatcher = new FileSystemWatcher(); + _wcWatcher.Path = _repo.FullPath; + _wcWatcher.Filter = "*"; + _wcWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.CreationTime; + _wcWatcher.IncludeSubdirectories = true; + _wcWatcher.Created += OnWorkingCopyChanged; + _wcWatcher.Renamed += OnWorkingCopyChanged; + _wcWatcher.Changed += OnWorkingCopyChanged; + _wcWatcher.Deleted += OnWorkingCopyChanged; + _wcWatcher.EnableRaisingEvents = true; - /// - /// 跳转到指定提交的事件 - /// - public event Action Navigate; - /// - /// 工作副本变更 - /// - public event Action WorkingCopyChanged; - /// - /// 分支数据变更 - /// - public event Action BranchChanged; - /// - /// 标签变更 - /// - public event Action TagChanged; - /// - /// 贮藏变更 - /// - public event Action StashChanged; - /// - /// 子模块变更 - /// - public event Action SubmoduleChanged; - /// - /// 树更新 - /// - public event Action SubTreeChanged; + _repoWatcher = new FileSystemWatcher(); + _repoWatcher.Path = _repo.GitDir; + _repoWatcher.Filter = "*"; + _repoWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName; + _repoWatcher.IncludeSubdirectories = true; + _repoWatcher.Created += OnRepositoryChanged; + _repoWatcher.Renamed += OnRepositoryChanged; + _repoWatcher.Changed += OnRepositoryChanged; + _repoWatcher.Deleted += OnRepositoryChanged; + _repoWatcher.EnableRaisingEvents = true; - /// - /// 打开仓库事件 - /// - /// - public static void Open(Repository repo) { - if (all.ContainsKey(repo.Path)) { - Opened?.Invoke(repo); - return; + _timer = new Timer(Tick, null, 100, 100); + } + + public void SetEnabled(bool enabled) { + if (enabled) { + if (_lockCount > 0) _lockCount--; + } else { + _lockCount++; } - - var watcher = new Watcher(); - watcher.Start(repo.Path, repo.GitDir); - all.Add(repo.Path, watcher); - repo.LastOpenTime = DateTime.Now.ToFileTime(); - - Opened?.Invoke(repo); } - /// - /// 停止指定的监视器 - /// - /// - public static void Close(string repoPath) { - if (!all.ContainsKey(repoPath)) return; - all[repoPath].Stop(); - all.Remove(repoPath); + public void MarkWorkingCopyRefreshed() { + _updateWC = 0; } - /// - /// 取得一个仓库的监视器 - /// - /// - /// - public static Watcher Get(string repoPath) { - if (all.ContainsKey(repoPath)) return all[repoPath]; - return null; + public void Dispose() { + _repoWatcher.EnableRaisingEvents = false; + _repoWatcher.Created -= OnRepositoryChanged; + _repoWatcher.Renamed -= OnRepositoryChanged; + _repoWatcher.Changed -= OnRepositoryChanged; + _repoWatcher.Deleted -= OnRepositoryChanged; + _repoWatcher.Dispose(); + _repoWatcher = null; + + _wcWatcher.EnableRaisingEvents = false; + _wcWatcher.Created -= OnWorkingCopyChanged; + _wcWatcher.Renamed -= OnWorkingCopyChanged; + _wcWatcher.Changed -= OnWorkingCopyChanged; + _wcWatcher.Deleted -= OnWorkingCopyChanged; + _wcWatcher.Dispose(); + _wcWatcher = null; + + _timer.Dispose(); + _timer = null; } - /// - /// 暂停或启用监听 - /// - /// - /// - public static void SetEnabled(string repoPath, bool enabled) { - if (all.ContainsKey(repoPath)) { - var watcher = all[repoPath]; - if (enabled) { - if (watcher.lockCount > 0) watcher.lockCount--; + private void Tick(object sender) { + if (_lockCount > 0) return; + + var now = DateTime.Now.ToFileTime(); + if (_updateBranch > 0 && now > _updateBranch) { + _updateBranch = 0; + _updateWC = 0; + + if (_updateTags > 0) { + _updateTags = 0; + Task.Run(() => { + _repo.RefreshTags(); + _repo.RefreshBranches(); + _repo.RefreshCommits(); + }); } else { - watcher.lockCount++; + Task.Run(() => { + _repo.RefreshBranches(); + _repo.RefreshCommits(); + }); } + + Task.Run(_repo.RefreshWorkingCopyChanges); } - } - /// - /// 通知仓库显示名变化 - /// - /// - public static void NotifyDisplayNameChanged(Repository repo) { - DisplayNameChanged?.Invoke(repo.Path, repo.Name); - } + if (_updateWC > 0 && now > _updateWC) { + _updateWC = 0; + Task.Run(_repo.RefreshWorkingCopyChanges); + } - /// - /// 通知仓库标签变化 - /// - /// - public static void NotifyBookmarkChanged(Repository repo) { - BookmarkChanged?.Invoke(repo.Path, repo.Bookmark); - } + if (_updateSubmodules > 0 && now > _updateSubmodules) { + _updateSubmodules = 0; + _repo.RefreshSubmodules(); + } - /// - /// 跳转到指定的提交 - /// - /// - public void NavigateTo(string commit) { - Navigate?.Invoke(commit); - } + if (_updateStashes > 0 && now > _updateStashes) { + _updateStashes = 0; + _repo.RefreshStashes(); + } - /// - /// 仅强制更新本地变化 - /// - public void RefreshWC() { - updateWC = 0; - WorkingCopyChanged?.Invoke(); - } - - /// - /// 通知更新子树列表 - /// - public void RefreshSubTrees() { - SubTreeChanged?.Invoke(); - } - - private void Start(string repo, string gitDir) { - wcWatcher = new FileSystemWatcher(); - wcWatcher.Path = repo; - wcWatcher.Filter = "*"; - wcWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.CreationTime; - wcWatcher.IncludeSubdirectories = true; - wcWatcher.Created += OnWorkingCopyChanged; - wcWatcher.Renamed += OnWorkingCopyChanged; - wcWatcher.Changed += OnWorkingCopyChanged; - wcWatcher.Deleted += OnWorkingCopyChanged; - wcWatcher.EnableRaisingEvents = true; - - repoWatcher = new FileSystemWatcher(); - repoWatcher.Path = gitDir; - repoWatcher.Filter = "*"; - repoWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName; - repoWatcher.IncludeSubdirectories = true; - repoWatcher.Created += OnRepositoryChanged; - repoWatcher.Renamed += OnRepositoryChanged; - repoWatcher.Changed += OnRepositoryChanged; - repoWatcher.Deleted += OnRepositoryChanged; - repoWatcher.EnableRaisingEvents = true; - - timer = new Timer(Tick, null, 100, 100); - } - - private void Stop() { - repoWatcher.EnableRaisingEvents = false; - repoWatcher.Dispose(); - repoWatcher = null; - - wcWatcher.EnableRaisingEvents = false; - wcWatcher.Dispose(); - wcWatcher = null; - - timer.Dispose(); - timer = null; - - Navigate = null; - WorkingCopyChanged = null; - BranchChanged = null; - TagChanged = null; - StashChanged = null; - SubmoduleChanged = null; - SubTreeChanged = null; + if (_updateTags > 0 && now > _updateTags) { + _updateTags = 0; + _repo.RefreshTags(); + _repo.RefreshCommits(); + } } private void OnRepositoryChanged(object o, FileSystemEventArgs e) { if (string.IsNullOrEmpty(e.Name)) return; - if (e.Name.StartsWith("modules", StringComparison.Ordinal)) { - updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime(); - } else if (e.Name.StartsWith("refs\\tags", StringComparison.Ordinal)) { - updateTags = DateTime.Now.AddSeconds(.5).ToFileTime(); - } else if (e.Name.StartsWith("refs\\stash", StringComparison.Ordinal)) { - updateStashes = DateTime.Now.AddSeconds(.5).ToFileTime(); - } else if (e.Name.Equals("HEAD", StringComparison.Ordinal) || - e.Name.StartsWith("refs\\heads\\", StringComparison.Ordinal) || - e.Name.StartsWith("refs\\remotes\\", StringComparison.Ordinal) || - e.Name.StartsWith("worktrees\\")) { - updateBranch = DateTime.Now.AddSeconds(.5).ToFileTime(); - } else if (e.Name.StartsWith("objects\\", StringComparison.Ordinal) || e.Name.Equals("index", StringComparison.Ordinal)) { - updateWC = DateTime.Now.AddSeconds(.5).ToFileTime(); + var name = e.Name.Replace("\\", "/"); + if (name.StartsWith("modules", StringComparison.Ordinal)) { + _updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime(); + } else if (name.StartsWith("refs/tags", StringComparison.Ordinal)) { + _updateTags = DateTime.Now.AddSeconds(.5).ToFileTime(); + } else if (name.StartsWith("refs/stash", StringComparison.Ordinal)) { + _updateStashes = DateTime.Now.AddSeconds(.5).ToFileTime(); + } else if (name.Equals("HEAD", StringComparison.Ordinal) || + name.StartsWith("refs/heads/", StringComparison.Ordinal) || + name.StartsWith("refs/remotes/", StringComparison.Ordinal) || + name.StartsWith("worktrees/")) { + _updateBranch = DateTime.Now.AddSeconds(.5).ToFileTime(); + } else if (name.StartsWith("objects/", StringComparison.Ordinal) || name.Equals("index", StringComparison.Ordinal)) { + _updateWC = DateTime.Now.AddSeconds(.5).ToFileTime(); } } private void OnWorkingCopyChanged(object o, FileSystemEventArgs e) { if (string.IsNullOrEmpty(e.Name)) return; - if (e.Name == ".git" || e.Name.StartsWith(".git\\", StringComparison.Ordinal)) return; - updateWC = DateTime.Now.AddSeconds(1).ToFileTime(); + var name = e.Name.Replace("\\", "/"); + if (name == ".git" || name.StartsWith(".git/", StringComparison.Ordinal)) return; + if (_updateWC == 0) _updateWC = DateTime.Now.AddSeconds(1).ToFileTime(); } - private void Tick(object sender) { - if (lockCount > 0) return; - - var now = DateTime.Now.ToFileTime(); - if (updateBranch > 0 && now > updateBranch) { - BranchChanged?.Invoke(); - WorkingCopyChanged?.Invoke(); - updateBranch = 0; - updateWC = 0; - } - - if (updateWC > 0 && now > updateWC) { - WorkingCopyChanged?.Invoke(); - updateWC = 0; - } - - if (updateSubmodules > 0 && now > updateSubmodules) { - SubmoduleChanged?.Invoke(); - updateSubmodules = 0; - } - - if (updateStashes > 0 && now > updateStashes) { - StashChanged?.Invoke(); - updateStashes = 0; - } - - if (updateTags > 0 && now > updateTags) { - TagChanged?.Invoke(); - updateTags = 0; - } - } - - #region PRIVATES - private static Dictionary all = new Dictionary(); - - private FileSystemWatcher repoWatcher = null; - private FileSystemWatcher wcWatcher = null; - private Timer timer = null; - private int lockCount = 0; - private long updateWC = 0; - private long updateBranch = 0; - private long updateSubmodules = 0; - private long updateStashes = 0; - private long updateTags = 0; - #endregion + private IRepository _repo = null; + private FileSystemWatcher _repoWatcher = null; + private FileSystemWatcher _wcWatcher = null; + private Timer _timer = null; + private int _lockCount = 0; + private long _updateWC = 0; + private long _updateBranch = 0; + private long _updateSubmodules = 0; + private long _updateStashes = 0; + private long _updateTags = 0; } } diff --git a/src/Models/WhitespaceOption.cs b/src/Models/WhitespaceOption.cs deleted file mode 100644 index 9e70437b..00000000 --- a/src/Models/WhitespaceOption.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; - -namespace SourceGit.Models { - /// - /// 应用补丁时空白字符的处理方式 - /// - public class WhitespaceOption { - public string Name { get; set; } - public string Desc { get; set; } - public string Arg { get; set; } - - public static List Supported = new List() { - new WhitespaceOption("Apply.NoWarn", "Apply.NoWarn.Desc", "nowarn"), - new WhitespaceOption("Apply.Warn", "Apply.Warn.Desc", "warn"), - new WhitespaceOption("Apply.Error", "Apply.Error.Desc", "error"), - new WhitespaceOption("Apply.ErrorAll", "Apply.ErrorAll.Desc", "error-all") - }; - - public WhitespaceOption(string n, string d, string a) { - Name = App.Text(n); - Desc = App.Text(d); - Arg = a; - } - } -} diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs new file mode 100644 index 00000000..0dc73802 --- /dev/null +++ b/src/Native/MacOS.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using System.IO; +using System.Runtime.Versioning; + +namespace SourceGit.Native { + [SupportedOSPlatform("macOS")] + internal class MacOS : OS.IBackend { + public string FindGitInstallDir() { + if (File.Exists("/usr/bin/git")) return "/usr/bin/git"; + return string.Empty; + } + + public string FindVSCode() { + if (File.Exists("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code")) { + return "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"; + } + + return string.Empty; + } + + public void OpenBrowser(string url) { + Process.Start("open", url); + } + + public void OpenInFileManager(string path, bool select) { + if (Directory.Exists(path)) { + Process.Start("open", path); + } else if (File.Exists(path)) { + Process.Start("open", $"\"{path}\" -a Finder"); + } + } + + public void OpenTerminal(string workdir) { + Process.Start(new ProcessStartInfo() { + WorkingDirectory = workdir, + FileName = "/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", + UseShellExecute = false, + }); + } + + public void OpenWithDefaultEditor(string file) { + Process.Start("open", file); + } + } +} diff --git a/src/Native/OS.cs b/src/Native/OS.cs new file mode 100644 index 00000000..f21558a4 --- /dev/null +++ b/src/Native/OS.cs @@ -0,0 +1,79 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace SourceGit.Native { + public static class OS { + public interface IBackend { + string FindGitInstallDir(); + string FindVSCode(); + + void OpenTerminal(string workdir); + void OpenInFileManager(string path, bool select); + void OpenBrowser(string url); + void OpenWithDefaultEditor(string file); + } + + public static string GitInstallDir { + get; + set; + } + + public static string GitExecutableFile { + get => Path.Combine(GitInstallDir, "bin", OperatingSystem.IsWindows() ? "git.exe" : "git"); + } + + public static string VSCodeExecutableFile { + get; + set; + } + + static OS() { + if (OperatingSystem.IsMacOS()) { + _backend = new MacOS(); + VSCodeExecutableFile = _backend.FindVSCode(); + } else if (OperatingSystem.IsWindows()) { + _backend = new Windows(); + VSCodeExecutableFile = _backend.FindVSCode(); + } else { + throw new Exception("Platform unsupported!!!"); + } + } + + public static string FindGitInstallDir() { + return _backend?.FindGitInstallDir(); + } + + public static void OpenInFileManager(string path, bool select = false) { + _backend?.OpenInFileManager(path, select); + } + + public static void OpenBrowser(string url) { + _backend?.OpenBrowser(url); + } + + public static void OpenTerminal(string workdir) { + _backend?.OpenTerminal(workdir); + } + + public static void OpenWithDefaultEditor(string file) { + _backend?.OpenWithDefaultEditor(file); + } + + public static void OpenInVSCode(string repo) { + if (string.IsNullOrEmpty(VSCodeExecutableFile)) { + App.RaiseException(repo, "Visual Studio Code can NOT be found in your system!!!"); + return; + } + + Process.Start(new ProcessStartInfo() { + WorkingDirectory = repo, + FileName = VSCodeExecutableFile, + Arguments = $"\"{repo}\"", + UseShellExecute = false, + }); + } + + private static IBackend _backend = null; + } +} diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs new file mode 100644 index 00000000..b4973baa --- /dev/null +++ b/src/Native/Windows.cs @@ -0,0 +1,105 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Text; + +namespace SourceGit.Native { + [SupportedOSPlatform("windows")] + internal class Windows : OS.IBackend { + [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = false)] + private static extern bool PathFindOnPath([In, Out] StringBuilder pszFile, [In] string[] ppszOtherDirs); + + public string FindGitInstallDir() { + var reg = Microsoft.Win32.RegistryKey.OpenBaseKey( + Microsoft.Win32.RegistryHive.LocalMachine, + Microsoft.Win32.RegistryView.Registry64); + + var git = reg.OpenSubKey("SOFTWARE\\GitForWindows"); + if (git != null) { + return git.GetValue("InstallPath") as string; + } + + var builder = new StringBuilder("git.exe", 259); + if (!PathFindOnPath(builder, null)) { + return null; + } + + var exePath = builder.ToString(); + if (string.IsNullOrEmpty(exePath)) return null; + + var binDir = Path.GetDirectoryName(exePath); + var bashPath = Path.Combine(binDir, "bash.exe"); + if (File.Exists(bashPath)) return Path.GetDirectoryName(binDir); + + binDir = Path.Combine(Path.GetDirectoryName(binDir), "bin"); + bashPath = Path.Combine(binDir, "bash.exe"); + if (File.Exists(bashPath)) return Path.GetDirectoryName(binDir); + + return null; + } + + public string FindVSCode() { + var root = Microsoft.Win32.RegistryKey.OpenBaseKey( + Microsoft.Win32.RegistryHive.LocalMachine, + Environment.Is64BitOperatingSystem ? Microsoft.Win32.RegistryView.Registry64 : Microsoft.Win32.RegistryView.Registry32); + + var vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1"); + if (vscode != null) { + return vscode.GetValue("DisplayIcon") as string; + } + + vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1"); + if (vscode != null) { + return vscode.GetValue("DisplayIcon") as string; + } + + vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1"); + if (vscode != null) { + return vscode.GetValue("DisplayIcon") as string; + } + + vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1"); + if (vscode != null) { + return vscode.GetValue("DisplayIcon") as string; + } + + return string.Empty; + } + + public void OpenBrowser(string url) { + var info = new ProcessStartInfo("cmd", $"/c start {url}"); + info.CreateNoWindow = true; + Process.Start(info); + } + + public void OpenTerminal(string workdir) { + var bash = Path.Combine(ViewModels.Preference.Instance.GitInstallDir, "bin", "bash.exe"); + if (!File.Exists(bash)) { + App.RaiseException("", $"Can NOT found bash.exe under '{ViewModels.Preference.Instance.GitInstallDir}'"); + return; + } + + var startInfo = new ProcessStartInfo(); + startInfo.UseShellExecute = true; + startInfo.FileName = bash; + if (!string.IsNullOrEmpty(workdir) && Path.Exists(workdir)) startInfo.WorkingDirectory = workdir; + Process.Start(startInfo); + } + + public void OpenInFileManager(string path, bool select) { + if (select) { + Process.Start("explorer", $"/select,\"{path}\""); + } else { + Process.Start("explorer", path); + } + } + + public void OpenWithDefaultEditor(string file) { + var info = new ProcessStartInfo("cmd", $"/c start {file}"); + info.CreateNoWindow = true; + Process.Start(info); + } + } +} diff --git a/src/Resources/Controls.xaml b/src/Resources/Controls.xaml deleted file mode 100644 index 30a338bc..00000000 --- a/src/Resources/Controls.xaml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Converters.xaml b/src/Resources/Converters.xaml deleted file mode 100644 index ef9b7eeb..00000000 --- a/src/Resources/Converters.xaml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Fonts.axaml b/src/Resources/Fonts.axaml new file mode 100644 index 00000000..29681db5 --- /dev/null +++ b/src/Resources/Fonts.axaml @@ -0,0 +1,6 @@ + + avares://SourceGit/Resources/Fonts/JetBrainsMono-Regular.ttf#JetBrains Mono + avares://SourceGit/Resources/Fonts/JetBrainsMono-Bold.ttf#JetBrains Mono + avares://SourceGit/Resources/Fonts/JetBrainsMono-Italic.ttf#JetBrains Mono + diff --git a/src/Resources/Fonts/JetBrainsMono-Bold.ttf b/src/Resources/Fonts/JetBrainsMono-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8c93043de6454ad2d5575f0751150c6551d9c588 GIT binary patch literal 277828 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

^Q2@p_nJptvCyhdNtfUDv9XvP^J$Tq^Yf}I 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 s3n!(`lCJDArN|NmBd zKdgiJ+%&Wl6FVMj0CI>8)Ow@rtyO-B%8>X_s*R?aQsT9662VZ#ZD+re0H)Kwoa8UF zT2jA8UM2AhKAMIP((f2uP=a8+F~P)s*ov!{8%kWTeL!c0Cy5A_RAR~bz&y@J0xCkxjty#l&)B;+UG|cgMU)Pk8M_GtL2-wSPxsD-@-_MhZB=tT4F089wQGV!Ow8`96KI^)2D)t zHnPEdB4S{7?WQpsqQOX`Wu*1a{7>r-Qvi+9T;PxD=RBE15i zYmqt}VnK4A1|Y;e`KhPrW3dQtLEo3?AX>{*S1F^RzN)3Vr4$b<(T|l9%oGTYva{Zv z@n%bRtxwV=>?BIZ9Hcrnd|=J8*#iM{;@~;sz8F7kVb=#XOzORGQg3fqU4FsBbz7!( zHFU06Iuc^4U=&apmj!fry;KYI54EnqHKpCCXLYDaT0t2{Y z+iAGU1g>N+nRNmfY)TI#^rXoU&p`*T-=Yf(T-#<(UI)pOe7zMUYP1) znG|yqm}E-V%KSviFo>^PZvKp_3Q;siI?)&(w=0cyL=+W2jF2CVA%s&Ul#h8lpw`Gn znzFcvbU@$Q!e9Zlv8GDmcF$2RH%&6&?5P>DBGW!$e5yWH zT12|+WZR8lH5nh2URGEvV?H<_0o}Rk2H5MuLg~PN|iUx}@?rGFcJ?cHV$i?yw za_zf=+)cq@jo-e7r%#{eVt8=z>1QoGYffg`g6RuL*QDq{{nXUdBXJ$Kaoy|UIyj1J z@j?W_ewkhi?FS{V$|DYW^04i=`z}OB_oiJ_=BnA#`sOJ$`pG1sMk-406hMaAE!eB6$>JPi6yaEwPa`cE8 zZ>$k8#Bk0@L;OB9Q0TXT=ek#No$Y`wb#LhW`!;;)xyaVY)A!u-bfjPK5 z_^uDg^GstBzl!J)DV1*)Wai~Oksuy8hd;2J2Z_o7V+aOy?m!Tw8cP#_*mPNmN!=M2 ziu@y?5H)@*Bvx9z`j{6QGH`*%Pdvi!(RnP8CNW|Y1SZxf-;`cRKp@gRDQ_av`qvZ$ z!^9!@hnpJhc(L}j#tBUmj?7k!z_z`1jg%)u#^-tO{ak&kt*57rAM4ZdrnRT1)p=D# z&Nu4xt`@O^v~;+2H)BHQHz z>(li()~YJvb%{FQj5VejQ^gWfqQgj?cv2@D-D?sO6OpX%!G)3|cgyX4vuF1e*C)!$ z6Y=u$*PMX=)JjR8n?I|kdwP0HqO>$2KM!u*`Wxv*%vt#ZA_G5YPQ<2zYRznF_%*}) zJ9338B(_J8g9?oq03~07dp%Z7p@+a=8972_6=jsc8_f^OW6_UDVOlwG`IKnw)XaKf7b5*iT-VnyJW*NH8Xh>-%iZiQQ#ru zO~b<+;)v0rXOUv*oMK|ebYlhtc~z=IV30Tg8!GEXU~zzOK(b*`#8EFYA5yQfZ_mgq zj8o3i=txz@q=}VkuP1AQk~mtNzA2m^@<|c}yNB;q2%`(G zRVra`7#q8ja^R9(P2)%JTg>zbJiRD|QcyAbcIdDFdgIPp@j8Yo3YTr%S?<2O_uh@m z!j<`jtxeE;?}FexI2itSGtEeLZksQz4n?%6Gr}DhzEivDCJlgpOr%BVp7w z#}iIy3?t`<$O>^{sgnrT;8n{{kIm~&x9nPb89hgRe|KoXlGV(VJCMc99%Qe$#GXq= z8`3m(!kfgFfE7BM>?S6q!Bo0jM^h=JDxGbtMMkaqP(|lr9YDRSar_lmbT%<;toyY5 z@Z4@QKJtgwaofhV>E~_MvYNzE+qK!`A=tkq9$aK3Qw^sYN_>!nJR>de(`o(UT?5_o z-nW(*>irA0oey8fnv@ei9k%1AqkRG5rvoibfoLdA=m<&1)*l)$d(^#bE^qoTJInY# z({DdVqOn&F_5Du6wuJglg5Q9KP0m9Qlw+_W6iQ$VNL#XQb+Pr!Pwt{I{U5k&i8C+z zUOuqrCDHp54s9yJ0Nr_^yfLLustP9b2Pd8y0FXhKBWAJ=vM{1OiNOLBh7JN0L%T9X z`y7n{oJ1m{WS{L+uUbv!^0&7Cfv-szHY8W+ReerKuD;|)qX$Xq1Yrbr-Vp8&l4r;G z+{u~6>0Vu3u2b9X)Y$T>a@;rG1oyE=hUiOLyg?)qjZvRz9n%*soGw3AsZ zy*YPs-`u%=JoW>o!gu4>`e3XqWxFTyjJ}uA3+O_JrSJ*I}lIq z=8;WiJ;-LSdE*x)Xj0aKz|Net5Is%w@ox3b&i^*+M_yN7utder8LK`gPSK9-lxW-0 z7*Pr9WT@_)|Gm>HGV86g)E7oxXD=E?E%b4@)*E9|n}AO!GU+3`O2UeXmDT&mrZ>q< zQ-gE@r|EKDEI%ftLAaAX5~x(Y0NO-tHl)`qzRcw#uZXeepcDJbvql~gGtc+4p{`?K zC~#q5U=AJ58md;1T*Z^UFpTyyJUR@Ji~H#xkrl3RxAqF3XY)?g}{bReh;sG8$-!_JvXecmLBv*8V^4vmX82A0Jl3 z`k`vqLkAyHOZGi%?O(P}>x9{7OXWL}0ME=U;tN7Ic%~l%Y(CCXU+|*52)-m0S|+a{ zwTfWRkRmBqba}}fwNrzo7@Vh0W1$OHZ<-c{z3G_q3TS(uQB$7bpLO7wXKctW{QB3w zZbkUTge8A?x16TkM`xk1fM`@`JdqFnnOA;#5@<#3Ab8BkOU0l2HmG@6R|9n~+ zfKTDYX?XD#W^B_DeNG9!WS40Bo454mQF*aj#_|}YD3&51_Y^ z&%`cTxoCeW(tSz}#GAkg%wsFMoBu{u78cK8DXN>~^JN-5*Z4)jJMto`;elguWtEm{ z70-@1YUwU3>rP;s4-MF=-PXu+pW1NW`JuiK+!fBdZO5vMZ=H1hY`vG-+UsbiEGt2FjLo&ecyaG4Wgl}Ft#iO%9F z{rE>`lbo$5AzJttaLvs;_H*?Q1`(6#hX8V8eIX}nwBPj*vPGPr0#D? z+4YR5Y~-nP)J${@>(WJDV6%A-8g93-SY5Mde-xf^fe+`NN=eAAjXic_nJe4=KCoZ;!2ZU) z5`3oNGx*pzaZ5yE#ORKXNV6A0Z%N;Q>-hazsgjz5N+POA#Iq%uAPi0l(vidAYq9eI z`qHmGwY&Xm( z4SuY5ouV4n@0>Tst*VjBLsNvwvP~%Ju2oGjhB&$=9LPt_}LCdb!oeOrC^ znOuMKGj`paAUKbkYw@cUL$YEIosPGG${h3O%$z!T()jk4bbWP2S&Sm{j!lD86o{RA zy0AUuj2Zm|=8z|JAeiaDMfLLW)uR3?-F;IgCykRw-waQ|yrqXn03QgRGiP@L(| z*hL70&Xfz4<>VJO=Y=|2FRWV8_xaZLU|!#fs^-S}nMLV%Z`*}Fb@R;nM&E^Pz47$+ z>D`&4WPX0KDAOJHrXQ*oDkmhXi<`#J2!EouIys^8g7A#-AL>1~VMV+t8Ypf~T(+X& z+}^T8b8#Tr^lWlPyt6zxKE8sa0Gl>lJe3)|QID@zv@a|=IZy*86=_2hpbg@BTER<@L}|%#9d1Yo z^i7SamWCF5Tov(H2?aat8AvFLwtjk7xq%5ZH>-FuC99bVDm(=#Y^OO{qVqPV)g$T6 z){~_1n&!wMGL87iT9V#8Waowk~T6;c^bM zVEP=^rVf19RfL9CrmND)gblr8>TtqRFqPsORnVi-;V6JD#+VkB3%T`~@vW+&YbwdB z%FnNfUn19a#zkrCjARbXVxQNv!>-1XWcVp^BRW5%B)f_i zgm-9%T~)7hbnCU<{8V#vTi^h>Shurs_3yJ)CI{0wX}RCgCTSz!p&f|?Zhmc8>ws0dqAKFth-}+&^bkCkr1V+6!2K?_5_+On}$5x~rNa#K1Pp712m}gf= zP#4ssDGTG?spU_5`B%<5jiyiMtW_K?UJH#a%A8&i#ps!?A}GzYtU|@aJ`nK~1oa6X5r&3F}$Vg;LC3OMUw$A@^f-9693YZ4v zBkCd!z14|d&4!6kALLXzhZ3_eEOetJ=-Gjp^DC- z&JY_C%e#=Tb0b>0p@=?Gk`xi^OGQIsoL4Py!*s_6b<37u!20{M)x!M7xVdG6wPUL? zOPdR;sFva84W}G2bsfUS4Ib zQ}=|c!k_ri&;O!SJ;R2ij{Z^uZyG2i;~*=klGxW&3^1ISpg#QIu6b19;_C5cz81wPWL$bNb)=o`RR zXDz^)ae0Lf2uV=rLCOtdWrYIFsn$Ez?;a_Nu^tJQ;*MYa`Xe8Y)P&96k#C1<#k|0n z{j6`FhMNu;r0RhmXDtShEx3ZbBVMU>X?x1u#b^qpqJ2_iMkN&i0%pes>nc`5%{trq zGOMv+gSu_2Ww97)&Jyb>SqxI*Sc`djE${<~k?eT>m?%9jCA+-o$wUN@&{9AS@kR$w8|&auT+;&Uv=3qyD( z(t^0;TB0d6@{h4}fm(RB^(^U(>OOOe8MK~SqUIK~lzyi{eMaN!@B_@@G3JmodNW<6 z0ir8)7Y<$52(vDp5uz@|O6;Zwa2qj~rSW(vrfQUqvSczlBZsu`uJz5Stk`?&&wlp7 zyTerl>iY$i;d?&#v!C61cX^eHh3cPoXXz3g#K)ZK4xepKrQSgWG3gZ%$sMp)-`R@^ zy)_|(zC@r-$iUr|vPGeY&s*)-=6QyA6neKprM1n zlhSzIgOg?~N~Z94u0C`Ak3PM6w!E*eB>X|>aTRm>meFKfn~B6>%OR{JNczl9GCJF| zKL&>??$uym3o}Fx2yH-#;1tYptX5@OJ=f|Cmt$SJB12^dp4gO5r(#k0C2m-f^27kr zC6UJ{p51t26F6O&9~B9T%yc?=`wcg&yr(Bx+t6ND*WOSYU2xBatFBtVskX7bx+;;V zs%lFYZd&z``5$?(zP+CRALQ-!`nG!h$F%I3Yt&f(1dKz-)~-F)VCzPkSvQ!K)E5u} z7NcBXbsqKj%)n4prd}>mgwP0yAMFx*$)bjB-ieX9!;&S__OzH9o0q>fT)*;$8&=(L z-F0hjRblJhTUTHAn>_%!XV0FYJ$nE%2hKH5P)Gs`jsquRt7JoNwo^Q|*wQ3@X^?DI z!v}o^VAg3(>?XNHNmdAgM9vk2-a7^;UA?DC9NTbs@q61CSO0O{b*pc+-c{jS*IaiU zpx&_ZHyStrGY7sRW6hTf0B97Ebq*q-1VQ8|BUnx3k;bB3Lj~t&E~9Y1w*&Jjm4jFv z)7f(_0>wBsiamNRt-tr)i*`Dwd-QQIH%iMz#yMY(0N5zvYFRH#Uz;VV!cbzZcWmLS zc_Ubpj0-CLfgH>7yUL zcm5|n`q3e^@x}{o`_P3qSa(aYvC(-K8mpgxT=9}Q^rFL0i7Sw}V>EU0GhDmKYO#wj zZ>Ow^zQAiYvA?&M19(`BsJ$HGk+x^vmr8_}sK3&azTUcYm0GW-t%=w>Z+sATX0dJg z#>ss%K)VtS1cNxhd~HjFL_L%6$)+I&dXu{5qaXdm{Chw8QL}l-y8DI;KXls#H!7)I zfJS0mV=geuS*lWFe5RfJ@?#%D2BbF_FO5r~6Inr=xB*+FN!n2zDRaTrts_sV#!x)G zYTJVkZd;uf&$C{;M&0`fU##$o?|=X5a15HTalIZ{>J=ne@~501ncCXL z+h@<-zW9;+Sl;UO-~avl{{Dp*&AfQ-f{oLCvGCPnU?V2HR)9N$%79WKe7Atk zxs3W?ifYGqQc_eR7yMwqG1c7YphnCd$ON{UATjT{{GmwHth#z=t6=7lGj`0Lz2l79 zS^@5nP$GZT*3YgN6iwf_VD7~;FIw2&f4VOox#Igg^zh*`=-ah`-7Y>h#2xBT%O;el zV)B0^su&dv`ab;b^N;!d^zXC3dguy`=fgh%&r68dy&@AWq(+9Xh!r+V=svG}&f(NV zL=@amf*K9ZCJ>K2wVsFO2s%}nG}rl^*vDgCWs|yQ-tZn-p@3$K4sT3Ivbef!tyQDm zSnK>=!@rg*hOXE?bj8kjSMQv+6GAxrd*Z>@>iHHJcrV!TA(?faF@P=#4PA~6gQE4p z0-3$8hp138Jl99%@vIXXF$rBO5gzRxCiTRgD zg6wy=okjvxDDg|Q=mR3=Q=yI4ht>XBv#cStW#fi7f0@2zh<~?OKe2wI=RSwI&tPT+ zvfAPlhuxWZjz=|7q9x95+2FqJC|g`z+Qn?U-hJLT0IBedl6VP*&pp9*t$%!)S-M4u{okcNq(u}jlXpTl&G5D5?K zsov|?tun+Y9rP2%*d$jPH|YzttL&)PX=#b_3}_uJi~yW3?nw=zS;F zacHL)zA$`Z(4y{~q&;Hof^W*tgo17dr3U88Oy*vB=K1{hJ|VLA@oW=))-dP!$}lm<4C082BBpOZPB74Ehe+nKH_GzF z3S-eYrNU%i6L{v-wu`1!jQS{OisrmermC*5N_|q5fBo5KH{VM#%rXgVQV&z>`*%P1 zft6A}cctB&CB z?p2flO$fEM{6-%38=m#2Z+**pO*M8skgB@6D)oT%##g@b^dZXMr<{npgvr@!`mZ zPRFB#4au75BVR08*N}{t}J31!ENSEPqmYO>vV!4Yb(y%B4SwXsFo{3Sp4gYRE-v&6-=m8jRP z`jR$@oBiOT!-pS?REA*gLw-f*eDMQEz){SaC5kYdK*LD$C&)Sj4y1CiS~X?Y(MwSw zwMaw3!quzS?U>yUZur6X7iln*Z%Bf40fT5kC)OBzv>9_VnUWB@q_L)~m>6QeYO02@ z`y7Yk5t4IO+G{h~`bv}YbEG|um{A(HrB6-zF0QWi;A;ISj5hv&{GKDQmxbDyhKo%0 zH$l3~4_^f@I9{J*tcp==^0@t8dhkJboO2)2>alC`M~N|%8`OaHgz!c8p65^Ip8v3- zo_iGS%8g_7HF2L~-ZgTG=N_~0B<}G|J@hT_JzqMRdwzPT!E=xJHctqf|As#y`DJlH zeDR6z8ST?zsP*RZlemY=MseU<=G=2q>*`xJx~|5e6FjRsy2xoGqu12fd&2AY=3IZ^ zgx4SNUavY%aD6H&M~nww5Zx6gzUPXu_dIvvd!8F}k6C!ad(1-5Jp$+n)>5%=baO0c zMoI=a;XTMFh3=|2bjBbj+BX^;mu76(j(g82Y&NB_{&d|qnf1w`O=ms;6sXArncD3V)?dF>4 z#w=qlu^NkvL1QWU!nwFk-cRb%#l~gEHscE81ID$+4aP0TZN`Ui z^?luaYJdNg|Eq6d1z1KsG;YV>|8LOJ&x|*WUmCwL{=@h$;}6E4jKkyQVJ&n{rX1@YRg|1U&EE&)v^lDwof_6z+Z=;&XW~-@Bi= z*STZK=k8~+W87=KpULO$b?$rjI)UY6o*}Tf_qwkxeD2>eru&(U>HfISkn7xQkM@~+ zuXk?pnG37jEANknzoXsjeTF-R;Kd!soj1GkG3P(Ee)R3@ytU5Lf4^X=Yz)W#z%~g86P)3 zWqjIr(0JJRqVade;5g@yW`1c$0rd^n(Y73 z;t_v9_I%H8>mL1LUo2PJ-?*>N71^9}?pz?7NdEn~uR=@6 zkmR%4y)dwhBNl7tpK&>5ICb=y?aU))? zoOUhb!{UIp;W=6uv(m8Sz+rtx$Aad-pe73hI=0ZI#~NbtuoKtC3C9y7BvH4Lwxh|3 zYI?-u5y_(iya~purGQ**x;9-^QRc*}9xZN?cvS+hQWCg9)JS3`(N*3!TkA73HqKMk z!TDR}gC$jyxnNds43vBoRMqn~&X{-k`+psrbwP%tx-88X;1#qhWc2EoaC)l7idar# z?u@BZ$aH8QM>&T|I*k+;1Y)L|H@yenzgRI*MGZwFEq_Wbns1F*r-v7uHhWeh#(6sT z5nhuF>3GNxLE*_@sHv}v4+1^BmxY%1b^vZ46hfquMS@I+b#`X`3XF8eh+^39I)jEVuZVMe;6ZQ!lKXsWcd zp`?%z%)6YcSIez4R`YemjIU=0E^I0vy|!r)59w(0$xv-I&NFa4WN{`jq!WyVnfb0q z7@rkpOjEa+MysZ}h~5d_FkibBPg_gfdV5#&OhLR=jXUUfAUX zkh9AtO|)k>wQu6ANwbceqa?wP4F?{}C8?K~Gsm&tpVXZD+UK>j%xl-bPjdbf-0S5o zCi9r_u6jWID_+;+3C@B)E3f|DBl7CA=h|Om^9(-9=Ay1EbvZh$@4#p#ck!#w`nHGH z$#vfT(S()h_R;qB)_2}~^G)ZbH{bjf(RVKW+V@Gn`EZ`}o~GYC&yjnR6bg2qr|*B0 z7Sx_Gq%pPFQzr7O)qH`cR~b#2hVmqVyx8tZIgo00kj_mK<0&R8@`Pd%YTNDWrSf9J z*V2u9s;^f93DXi)m>|0Jm3{jje(=F_0-^Gvp`oJkP#{zp85}IEP?OiHDb|72)`2sN zdn-OR^s$QG;*#ES-jq*fXdYu2{rC^>I*yH4^7qZ&xl2lUbg$}oQOt(i0vx0FoDv)|Pzw?v zgB?6@K#$jTK*saVi<AdRKCJee@=Nk6K$gI%!r+H`oGJ`9cvD2!s06KRl>T$2gn zpa97WRZ8rt5!B?8pcI!_qz#ycwL6+7_n9Qxkyn2HbM?EQ|J(&y8sUii+HZ>dY8u4A z;~vZg@lYs3f_Pe#3d^47s*(FNo!k7`gfFngx_F2(*LIngSl>%Z%r zOJ6jwS50;%BjX*=e9AP6GZ9vDArM`z%RbU#8IH1-p5Wi%xqBRbmc@yRHmT=0Q!MMH zy+AVx*GbX{fVOnE04wv-;I@8Aa3|x~@Op3u%$!4jZN2lQFS)3bYhBonM;pFWs4p!O z0pnjdpeDbfpEEk2F*pzDSxs*Jl7n*2$KO)D@e*+EGxEt3IxRCN9HRKMZr|xtp@3x} z@8-1c3{x?hMry@jT3`?;oA1{QfiZm10=A(P`GMUKib=^EDrv;c-PSv*XrJ}cKGInp zShw!bcg+{pjof8kvTlSreFB4e4j3fvYf7d!L?teKHKIhohooc~QF;5?Yo+B3zyJm`*fGP}*bd)%4XwEtV-e!qs?bV9$L2!HZV^>V7-jj)N{`BSuX?#A1*B)HDC~+FM`tD-sW=J zna98(d3-#qNk1~Kwyv|r?OXTSI$!;vYYyl~>HRV~Po4um^>b`WEg>bfgyc|?5=kZ} z6EQo-cupTrNhO;2-JRIGPHnZ`S-0;s-;IY}Uw1&y%ccoEACWCjqUNrW5%~d0)=TTG z&4ITMmdFolv)nZ+Va^^i^#3NJE3ah{}0yu-Kq%r1+V2J^X{J3b^JBlB@ zcD^1jZjc)^PgD=UjgSH*K*z`_L~`_++a#i}aqODgd-s`a9!LW=2a7XbfX&(CavnVZ zr=^w(eP`Gln#pM@HdcXoJ3owm91`9rMM?Fjx^CFsed=XrNLfW0QOcVsb1D#vKTIu4xHvCjA_?4lgKGAn65}9iVjeUSnbaJz-#92*W6ia_%v_7ld<&4 ziG-dzWtoAbEZChK#Db!!<-lv|0X@v2@49%q6Q6UwL4FA;WocB06){JpR;c@7gGI#+G^O(V@iA;j=XU@~Nyb z=W8MrJt$`aNs*h)`wko!S?j>6R?=(3#p4OrSWarjff>svhd$x#jXO0S?V7;u8nX(n zEPnL;VINv)8Sk|Nvc&ftFh6wY5rItB=b&EWQP@smje$*$2^RT6nV>@_S=cyh9QuP% z8m3q`_1-l#CFd+L`j8x}(SuzFUb9i=TQYLF#@e5|#`d)J;4*P%c&45mU&~dF{Gsc& zac-LAml)`AL_{L$V8N7vUM6+%khkm>&jo%*y%U<%^#C1dPA|Sla(CDS;K*^8z~G4A z)%$!UGF-kvHjTYK89@>%IH9PC4_@}Iq$(uPP&Xc}JASfq-~LTFabe7L+YHIy%22x`{l_SyV?DSCnN zAc$N8GJ=R@oN3wvt*+Q>&D^Vs4)6~;GmQg>FH?8=q+o_)-@qcIEi1>5JBmReK4DP& zLpS)8_5P91sK2(>XxfE8DdC;Rjt7+64{({UN3*o|wy*7T%jMn9u6F2?=a2546Ygta zw+`>t+uGebo-s~h|7Cfs-hMJC*6I?yRwsy86jFsmWmz=*XdA+pP8h$c?^9bn_vz<3 zH5Az`h?%y1iFNVRJR3a$Wd!*TNmeM0k-FA(K|MwVu+P#k*zrzO$dC@}n%eXm=&c|W z4&);o%7Jqd1^rwal!ycQQLnBWd1W2XgVPHg$v@!qLz6S0$5=)mD7aJA3DmGnjfU~;~M+q3j039;y& zXdeMTodN@a%a8LYcT>9?8+@!&^R1_?CF%m}lh$SOUWPxQGD^<86YX!6K`nY+0|l@nUj`_!2|o7F>E*(L}ET&-3~bH zvc?`6QKoZ1W}}bO95_ydOI!PJ<}b1V&wXz)mW@@I)oUPDq+Ss`oYU7n4`prb(?Dca zy)<4yT1Y?;oP#2*Gs%$7fLg8OLit-@^`0&qdW@IFlt{*7#}iBeq6RSVc2+iUbR5Pj zl>R?jrVc5ZON&fRdJxtYRs$tFSGLxfeWIG=kp}C^nJ^)FgqE;9c<2&I)_D$l*DDvG z1@Hvi5Ns@@gJTKr$OOVuZAsBSzrnFPu0u0=Tupb-t{+Ctu<+N5z$+DCL`@hYZ@{S` zLz`ENY^<3z3pY>r^(bBnjGoLSjTgJ;g*FdKI6tKx(LlXGXM0pgX$ozH(&O9mjDCVk z99p_R5+@?lh@>Q^3_0gmypAc;NUV=m8eT(1vQ@3FQF$^4C&IKCO$w|rtTv=!M-!Z2 z*8)9WSxI7uG?UQ8tctosS-AYbk@#RtH%0u4fwi; z4v&Iw;qZFHj3Gl;6i!2iS_^pXHCGRiRrTs94?yo&`8|4QZaWZ0PBm&zzO)6h%gYl7 zaB$j&)6?k-_bx>sdwt<0)7PE#@r$f_rt0b!qvN{r%?kN;4Hu|k-g(C*w}}lSfgjO> zOYgoQonEkUnv8qV$In_f{gQ?DxbXRmTB-LZ@X9a}Gj0+*#>*NFzdddwt|Je8Qpn$_ z2*!@9XdURb zki$b^N4!&#mvnlu9E5rm;tVYe6G|o0J}RRWTK_fl_S-|&f2l%V?{8JFtJhntW$NKp ztKO<_RS#Pvkd8h}*V9LT0YkXKI67Q6EXPHuxM2f~uM%eU$O}W}#QS}3wjL^{rg0Y5 z6W$Mm02enW4voBUf2+P9d>Qk>W2u(S-<$rh^?s}8ewK%SvZThr!{@4%zMU`_FNe=c zAIHjDh_NyvW*lT3^`!cyH68uoTh>&2ts$$GC)6JfVy7CbKb(^7NAqdfd5&E`+r0D| zInUSUx@XUc(RQ>NlIL7^wZN?~%onhwd-j(*hP%HY0wh1#7{bfEW4Ich%*r`~gyuCK zPEvnhSv35z2T%GwJJXSSe=9D4=eu% z^amRwxMY`Dk60Vjnbwympf+s%t?;^o&KYrhnfKs2kNzNXq?Y+Z#);*1EfPdUla;+B z=0nFGEBi^yqAe$_gV;h>T$>}=C)yU7-D8ZU_qS|k`<#>QZDJ7@|B-ip+tQmJMg|eB zNaI1nV~@cHC)OX(FOkdyF^C9y4B=J4aUxv)rSu13bw?2sJyu#R8)=wMQHQXH)||w4 zL72~o7U9S$dfjw%3wY9;=)^jNmckuL7A?XyQfv4eSweIOcp6GF{un84g{e`Coycrb zqn=7`x`+--IYxLxJk+A6Bh)&!aJYY$t4ul1@#qkzATNUp(IRYF+_vh<*xIg})unA6 z;*{iNclm5ALrGbY`7a?eyQ4dByE?=XaDef z_zkol&Q#$jN06cW3~=5h8oh0=cN8HsQ*lFc3$c@}WBJ^EN`tUIV4GCQJUCo=Xru<9{II@Sfe zrKFUo^m?N*}rb*gGuZP>&w0G*~ii1|MZDmgt z2UdN~f50Hj0G&>0*!@lW=+HVz#3%xL(QMz^qHkDbeo z1rpnzNPjXgkw6mLBs%!$ITe#AG!bGF9@4fc%G-@aijc}C za2d0lvGU*EeVXaJ0U^RrHB)s$F0`Lk^MBFssEBJnS)$;u<$D>H%-t@vq#Yb+gl>;5 z?_=%9X$@KgKOLFm#Jy?R&v}NRO+Vu(M&zUR#8PrPFeOH zOriudzgZ_i2<{%%Jja$b1qR1{?}K2D9*p3X)wKQI;XPWm*N+srM?WeTZ}u}}BRMv4 z*GW9eg^q3$*NUG#Er$=ubl6J5iex5W3H^6Db=V5*m?t@YkzujoYiX13k0ESM0&8Fo zj1k2dv!ol#TPoIR?(Xay+1;s!)qSF8Q|~NpG#HCB1Es|g8YW>rMQfvS@opR(fNkss zihy4Q$n4@TKMfp^NS)>%^jtM+suB%l4bhUK!f>7uS8-_?oD^fR(;*;9haAej1%sqD zc^c6FVcPOZla^1Ly(~0g>za#qOb#rY+%|Dy8$aPmEBe3KzoKi&+}o_*Tfewx;ff1; zJ3rsi)6?w1LC42iR}<6$Y2${fE@=! zu``hdASW>sj=;pRXY)BT|F{#fenJOlYRA)KtePHUHqQ__K}aSye9R6(9IIBudg()i z;XEBn`}!Yv;Q9xi{_q{w-=RvjtEa8`+pX)>E{dm8PxPbI)SPKH^9(|N{9h3gFry70F7mPg=3E)>_z9mQFU?!sc=F68nWSnewts{9m6M0Xzgq3OVyRP zRJ3?|t&VN~N*Om@cU-E|(o^*K8E>CXwYH`jT3cyU)Y{s>A7?GUeK^}G!qe%)*V;le zu$BgC6K21TgtOOiPfqrqJ701^>|Pjsr=0KC*+EZ3s&7xVdvh93GT$$Y z3E32*9cYg+$$`lW7fzO+^!V}Vh6xjno!@tpecj}R<0mxO7iV+d>^Zj?^v!5wC%1t( za+pmrb@9je{5qFecWgOI2B+jQH#emlyfe)<2R~jTkZjw_W9BfbY(LCn&+21tr%dwr zY&xb7VJ-BEPpmOrRT+=uD>+4SJwFK}QK>yYz2i;cMSTRQ)SA)DCI497jU411 z{0SI)P09Ze8cQ3qGc(!W2#h*6mP9SA5tL5O3fDxD(0={TX@w&U=$QEZzB%#Oh_(KY zw2v3R(;j1v`Y!dw#lJv3QaL0E2Be>+&vzr|keiT1DBIBk;>6UhV%@$yDr0?T=>F@~ z<6E{^w>xQ@*jmy&q}SmyRYs#h{8K)CrFC!hJTt_>LVH2#*#>onhXi!{gM)T|=~VrZ zy`{sjW^ZHSZVz+u`BJ_XmOGY?m6d(j;UC|27|NSZnx#*k=`YS-^7`5N#Yb|FLq9fO z8EM{HKHR7_SszK`T=f}gXs%cD<{~o2cRW!XAwx(C-4;^lRs>LGZU%fzoE1NeI*76GrD-^krjV+N zO?mR|uD73@(xJ}1WyOk3o6J`b8k#q$A$6OzL)|9pIu`aVPehBz3lZ2Oy5_S$B)}>O zWmxxlF_DwjA}s9n)~f(uo%0@4I~}2FzW3 zPrd!2b2`sCCkytgYLB&Dpe9O>{Ud!xd{oCRG3Y-jdDnJ!Bbp=SP1#J|44tt(mvIFIAgbhstTuH?qRs58zGH>(lfD zsWF<3yBF;*17E$8^zILA1sxb!w<1C0g``kBJB4xpesX}3G3X(gm#0%C@>bCfG7tRq zxykbOjmGYiymLjSrjoW^>4wUh%9`q`vP3Le6v-!Z8FyfXNGD1+wMbC7NO$zewqMNI z0%~hrOJ%a8cJY+)?wfDs?tQ+3k3$q$pV1 zj>tyaB90?EXzE~MUD26!><&4_@_Um`QUAnSyG&_LZ|c5^`kg6F>doEyx4HaANvYqm zZQGW)y-vIO`|p>g_4f<-hQWSX4mQQEARZbAX{Ti8ndAfpiJeFHcQlu8qq=p~wttiz zgC0U%Y0*=PB)eJrfTS3)Zg2v7)ICrLsFpBF>0vMbLR9SzNKUsiGFVWZ-~Qy`!{;16 z{K@dcpB-WyzB2S#;GYTnX|ogjPcYVIRuR809SNj(uIb-xV7v%~g1dPbwCH0S(&=8( z!YJCn_Tx~=%g@hSX5{7NuPRXadHI7>#4tK0bW9lE+1A>euB)l8Ovb5;9x>X@b`jqK zQdyhUSXxBwmex{fi2-V$ZU>PsMC|A{l<4Rt{YZ|zg?($b&6&|V?b4Owmv1Y$CO9cQ zesOElb+@*5bhh+0bq97vc36+9Cqv5@O<9l^nmJ+q&{+#BYi87>-rrPT*IZq3`qFP~ z*#hmKVN5Y2zEZQe71uZIotu+0uS-3Co55NjIa!3TfiyNC*RoObJ(o`pzyDd)pRT#y3aLM?9a6q6 z)}ETiEqawD4&hRASASzZsr3cvrFruDLQ|uyFSNHcPH37?S5uy@NS}hfaCC|JNIgMz zNAKLZy|Od-F%fe`HyHh~_8nP&!e{I`+)Ld3b9!D?Cn>Yp;>nT1iBs3)s1Vz1a14+; zRp!MbfU>5$vRN&>ZyLNW&RIleu0D)8L9j=Ps;1D{{TmAMOgX<-$ZJ5W=K*85EMZWx zDN$2a!@aaE;a_7aBKt0C(|ET&nla{yL62SvHgT(Q6EC>piVG%o{gi(f_4b~DbfNq^ z)~?-g@L;F)g-+{`nxxJ;@60pLgB-r75VCnk+mHGD*;3TG!8Q6VS_V;7#~$X-%A zB!P+8TRY@3-xm)p?er}LH=L{4F@spxWL?GJE!x(-&-+GiS@A>US+S3=LiH0PmkaePpEn zw2`08*|I=?FTeL&)90VopEI8j_R>1zjEqz>@R6BBIxM^Z9MQa-Oky)c>$uSsk|cgL zn26`+8Aeq_ye?5!T$mTjr;nb4R7u!(SInzf`8=8xHdb9EBBWNpxniufzW^p;fG)!p``Izp8hLz%_J6n~<(3Rya%LPr)}Kk)j27nY|}krm-jYct_&q z<3Rh{ky+}md(JH@JEv#l`*X&?n}@a(6%H)Nd_g!*N&p;pKNS@^V7Z%BQEM$4qZpEp z=AfEd4_c9f)KV%{zdLx)2Bqjv>Qd{ktk)3Pt+Umpg9ot?j_zflNvWuYO_taP<&Y(< zLrft{n*QNNyU55dRC&StJnt?G>s?k+&hoMbws0VM?>3Qomb}PaazOQxCd;1^#wdD_ z2-WubyM$`JF35EMa*%3Wqi(cba_MyBC+fy`+&@4q(2mZl)>x347mv|g%3bSvc<0F9mvt5fYf6VVJpAy6q0*XAVP}~+D~F#Jan@6ZFCHx_fEfr0 zkOx7r!rd|_e=chkU0PUctW+!R0;FU&YCZr=vYB9Mg6gyn?)6wfsXq`4{|H|DyH{tv zs)2gDdRjC(y~!F((t*Zq&+ol+qxK(&D& zeY1V3-Xz~ z+ld`J30drHjzb_TA&J9IQa9|BftIqBP)diEQYZvk_P?df4A6E4Izw5?zy}O1LqKH!eVUTCC=AJP0no5Qe9c91vVk%(7tj8!TG|$Sd<2!E9zD*;KHKi+i8f zlh12)pvV|;uP!r(I#=Uf37w+AxN#PX^J2FoxzNA`OaI2iU0`B+LV5U^)=!x5$ur7} zn9CnKyF2evMONt%aP?o0D5Lk>!&&=*2QbJ?-u4R>!go~fUaOTEWfbt(M>7FG5K~~U zQr8{U)po(0g-DOtl&&YBg*3RtNBy4o$toSr?Cjx0NnrtD2g1REio$ZE-{^;r9tyYx z8=nCbg9(6TQSEDl9C&_6jU!ZP0p4{_( z`qSN5Xpoy2OHx+g6Y0#b{Hfzxy6}QWhqPwArPO8_sxD-LNj4#7r)nYO7v)-$jX??F zlzr1br8>VMRh)Q8DnIc{%s+DvL{*qvgz%|{9BnE6aJeD;A#q9>FsQANpD(8vTSzaWut=30$#hoJ8t$by|)!UM*{xU8R2 zGO-bAN@j+_mLin{OV$B#lhoL-8Gih>VtxB<_Q}7S9Z=USZ>mdlF*d-PCiViO^uc^_ zz}IH8wfZMejVtr{QBO70P_?4=B)PJ{c8I)>Q(*>3po4TUM_^3}Z|sm*mUFaM8K|KqIa3BAWgfzjiTo6*7iBO( zUz2f?-cB2!dLwdW9LqXreHF0PhrfK+U0B=v0>k)TSxbGG+l8g%WH19zM&aEVw4UAj{yFm9tQa< zfKNe#HxZsfG;V+mMc4UIy@dNI!a|u{Qpb_{_!UL!0tV8WwUcYgcu%O*ezB{|i4pTz);29|+ z&q{s(mLVb0$5A4Kp9RA;wI$Ay+M4iT#NlpYTg3L-l9F0_O}t3~x%X7NemdshR5MRT zqiE)C2WE~N!fhnA-+1HG-#+y1Z~yB}yV=1c=zvHpJTUnc_VAhe4af>Xs3`OR)BWO} zdy*^+ik&CCmGV6X1Vj-1u-93Cpt#7R(=CPH8Q{@Gv}Bjkn+e$H}|k7U8>Q1A=i>je=$GIB7E#8bOgq-i_SUcqI&XdJ}}Ki z%T|(0RQVno9$3GAK>5yxGyJs}CFVe59g^(ak6c9FS~zNTs-qV1E6vph?-P|RD|O#%O+uYDCG6RAyyfxjD)>GZ?Mph zOL?^TRt{Xu2<&L`2@xKQaw^zGF&`d8iPM(^e>ti}w`kBjx-~9ReCyl%XGU!6%I~Rh zE&h{aj7skiJd?ai8_92KAj^vufB}=Z1?WYHN^%c4cR2HkTLj7lLJoI52zrs_3fi#W zuuhJNQ1^);$Za0e;^8c;R#5_u5t&Q?0Rz3rYbK%G0>82v(_(Il=W)m{a+*S{>L*%E zOZ)&qEi@rrgrFAwNkT7t*|~gQC`2S>D6y&lln+4{g`=IYm#gaDky|ts{yim$iu|y zN^p_4vau0MpNi~bpUq>@?U!A)9j{xQ{(Mhvpdl7($Xbh6jrP0w;vKAF$HfaEBrt@SbNd)1eQ(!?uYor312f|U)7i-Jmso?8PG}S zKPrL}#VWz4gM>cx=DYlPe$^%yvenRBEQmI;ypMnb?yOKa=0JNDM}1=h#Ua}kIsTj* z2;~0Bxk&l6&b%-XjYgZj)naF9>BK))dz*jC+&w+Y#tQr+eaC@)Xo1uVUhyY9SPAeL zrxphOoUAa+Bwk_o^~YFn;*Wn(j!M1C9{78|g}I^ghp=Ak5HH0w3_s9IsKe}tyB%)k zS$*K6QRP|IzUDHvdA?G@k8MQ4Qb=5c6>0c^S%+ie5&Y+lyJfc=xpu|9-&*;td->0y zw9mMQQy3H%isQ4Nw{^wyFRXmwdH!?RisxTk`Qi&_;hPt6UIew9Yf(RW>VEME?iR=X zEk_~`KJd$`=r135@RwCpD0jQ~y?1-*=X+RCkZNKAdLTNd;CYQEHp0^;y3s?H9KAa6 zDhswBOqg-a9JaIJpw$eKXe0rHRg6B8F(I|2Z3Hz>C9BM`=5^}L7-+kT{x2(S90r--#~8j@&Q3O{Jo@u;Usj2uf8t{{I6i*Qk?XF*I3kl1Vk722e)S@B%G;FyP8ap)`vtQ_ zMjPgFK{i@sBey9u2fOBbvm$n=1CGW-g}f~YlN$`iVT8#Iqgl*gG%ODk3qn_WYfHQV zk|k8!7g$nM;Bn_UbE$#2H9!WugG^lXuYlc#h*>0Q#cNVM6oP&gi9x?m{}6$VNB&uy zqeawtD-1Y4`Pn~>Eou%%x*M*$!0GikFC2Fl6u8Hk*fF3V5KH{^1p!aqP^_c7J=W4E z4~RieEZ?8!SliUuHxEtI2CGY3oW{z+@`mRf`1cB4&WR6O>g;Htob3?JhQ>%$Lwz(W z*Oq0?!LRZ1x@PdIC4JX zhDE~`t-YdW;lZJ0W1SlY2iA3WlsB2gWlLAAKYMt0C=dz-@OrH0&{}rZ+C$wP=k+Ro zUbbP~(w%*)gM~ed2e*$X4{g{w!aA$Uc9uu+qpS*Zv|*pwgE>Z^oqau#1t}y(O9F*Z zH%S%EhboH7KS?wNv?)SUz=NPo82*D%J*syIT}wd^L-H44z(<8~PI4OM`s!P>ldx3@ zUBbX{!dG6#gqo_dhH{9q;-dUKrvn@nVG*KQGPJ33ib}gs5+3UYlAP9x%%SA*c!abF z>{#!OoWE=#ywx>lHMOp(8;gbtOReUo`p0kl(iMGM?Yn;&Dp+t|Ns z>4p>gq#IAbCnpN6ljn$qcrL9%uW;)V^-fZD@j%>oAjQCN0J^ac&@z$jfjt1&zi=+- zDUIUMbm|9u>VpeR9R~yZ;A{sbg6c7B92cA8pyADT_ zwtnL(oObqQEUO4xATLR;!x?;uv;4C!FnmGR5Q$Vb4X+?QM&o`NA43$KnCr>E|SsnMKDs(%Bb66QI!+Ci2{J{cnZtm997RXjpJn51mSuHXAdVr z;Sz_P2_5aN)MThST2ay!?(%!dG=u!GFeqmvL ze!pXg+$0Kgmd6+HbRv(B4!Gi0xNfluq7o-Xl-t&FR3f3 zs|7p@==wspOQnf*PMU-P<;H~5=mM*joAm0&nsP2@kVE->k{Cpil@~WJDDoxx2(mAj~9(?c@6}@>vNJ74ialk`0pzsssg& zWN927XG{p$TuCuGs1IxC9obL#C7I0^F-D3CNo_EjG_8TGSIX%lD-W`S;)Dn|#mVtR zfOZ5y41oo7;kh}=|@2gOfUMIdGM5Kzz+r@?0x+{c3IL@9r?ol>%0uzo--pqD#f7Hq9 z|Crp6;4$0LgS(~goj|cHG3~rFtly7IK}F()nT{Sjco0Ru5u`!`5c~uO2uq-sXJb6P zjsPrI)XKp20^k%?%fPzesa;}b9RU-ZZ{&7_r@ZNiOiz8!iy)`!RP4)TJ zPacDN{sUx4lu?|s*vnBKGTcv)U);8YVvF@Swf=BV2sUWa;ru+SStw+M)EkZr$3{64 z*B&DTkSwdxCGO4^C0Cr*93&B2y>iFA@VQ%-4{ln$xCgF)3e!eQP4{_$rEzb8VdShT z&z8s^21Z5(7SG@1_iZQ)g=}4}+JM;cY*yit#a)4PS~v*T1s}0VM>yK4bV2A3fL9}A ztTQ2V3y=y#7BUkCO+}S%0+aF=rw&ob1qyK*fl9yKuk3&KUHteh(Mb~W@o>q$&*IwO zB?&Xx-YzKiNwugDE+*Uuq*fZ!sIrzGT#ZYVkUYNzV>ia&jzqv1mN{=V+Y&K$qmVl#TACs{-X` zpR?M>yc4DDUhlw)V*1sue8_fX=P21ZIoS%V7UKJM=#75R$BVran4Ll&Bsu~>go8;! z3MiR@!9@x89K;P!TcGPOG!$VJRcs|@w1!M6UYa3gD`yhd* zC8i1hwM{$~?K!W#{k)#VJKL2jt&8I=n_P{#3)elz@~yqi@x|tO7cTv9;DUMc_77g7 zOt_*Qb=BPwar~WkT+t}T4!Z~wpd13#E(!~45-T;k$bc@*2G~UyX{JGhNPvM1BH3t^ z*Wf`K!D#5zFj!nH2*u&za7ic_$S{EjGIc5wh}M716`9qz`NqJTbD1vx8}{Dky^s+> z=%7s)t1K+;dn6Z{VLojbOn}A$6`cxLNSm;^G6@MLZoZeG5Wuv*3@5Do3du74$@a~` z1I&BivB#DzdklZZ^R@}g#FgR=h%OcrUJ(v;7?N;4Q{W7t5jvi$ z{=nHNLR1Lj7-for15Is_i;cw)JgAi&LH}gg4YW=vv=VKzrm~8SWEw%MVg2u~{e0wC zE0%p>$)?(sjg2d7@e{u+`2usjiNCz59AA8JuxV9Y-Kr-19A122aPT0(-Y{nDzG9vD z9=fqr37Zld%8EoIt_QmW3c8;B;)v)Tq-+%^9ma7B5*v+If~=|lgc+|JrV<3C+3AGk zem@iZRsO07l0gZ(L&h#3XXy3De1r@KE{^szVq0#(s!(th2@Yf4fjlz=ghCRs%v}H4 zxT3mxMPp((XPLRSFxt`*Evz*!%NZ`-{7q!^FXcbk)zN|a*g$lCgUi=iRn_8iHOyBo z#iO6_MQY2oD~~eC8?GulA1jIGxromb-g9ARVp|C_nrBQj3-~HZNl8r*fe&Ph0BHpy z-(cA$n2kn`TcFewvvH6nTwfcl@R$3M4_F!oduL2K`_z-3P1Bld?%scF@2@N0w0>>> z1)icp&xLc%zpF0NXuf3s{zpA77eV1MA1C_>d_y0A@6h)aK5AhJSQBlW@W*6@M+ajt z4xKEY<==GO_3S6CK>77eH{smpu!ZcH~**wXvtwYoJ2p z9oXkn_eX#Su45HC3!{u2)BOG{M80x%$AKd(D%vpuj2-9xfuEbaKf)on^-1a;hFs6F z=n;NPIumnvMsX>uWRvDtu z$s<;2gC>mLtTe{5kTGo93^NIR?G=}fZrwIx#J$h0S<^#%AoyVNxaiZyTP-}0u#_>Q zsXPQxM~WIHGB#t=)*C5$YB0i0J2igrH~NT@lamZ}IBY3!2wb;r6!w#pP&YnPG z6E;i-FgEnuY1$8Gz@z zI!{v~3WMu*S`iLPW%v8wbj-8m&E1w;3L4}&%eq#%?%FIb%XPl7U`NNqXYJeOzv{>cB8jc-u~@rO?5@fC?QipHUWnH! zw;6i&bT8V|EwimP&}Ztg&r?jof?S1>#QNd_yM*+@e$rq}2se<&3IFpz1XpBej-+<; zhFFAQ5%dt-A5Ay1Ukd zfV^z3K5vO+S~9Q{?j81$Ca;wSHh7ualb!9!zf^g2WZ}BV3;*b?U(kS2q|YftoLJ;> zWI+h3lgd=fGG?QOI?)v^b2cF>k}C7vuu+nT6zlZ40KSo$3^{?+^j~>uDpwp@Ki_E5 zL4qXK9XhmOo)MuQy%8Dksp)+8zSS0&1^=(U|2wNJZi~fjUY(rFV*Y#_h2Fb{NlK8*7neDqdZFo%X?NvU9&N{^} zY9t>~EC;i||2BP0R^knm^QM)K6k^j>XvD}8AS|mabDdBg$zs5Z@-bGHquK+6n$=^? zN8VJD(%I7~eX0OZa{PN{HGtA5G+P9RcxUoOv7ghsMtCA&@iVir+9v{E49V|;K#&O zESM}}er9r9XE1I$!N|1@h_Jz69tO~vM+MCWK&b$;(e^vcHIfxMow?4imZs*IKZl)^ zHouuh676xK(4@saO+q6@ZwV0(7@!d{(rf^-e;hik*&vfP08;^598?<}K@&TaYV2vCcptLQ+}gMoR-^E^on?#ar-k6boERFaxKeA{68}Nm#Rq z)&A=F=RZEpC}vM@&n3#fQ-&>R?jS%lo~sGEF>=}vxmGcxJjEF%!n7%;^!wEA4u!~w zj)`dB>M(2K$ZAQ>*P1zBLd$SxnmU_H`J8CC=HtCUeOI4}mp_yDcr5yy#AXUP!LbQ` zvl^`yJQ}0PYBbR!fv*CaGu#L;&}jn$8U%hMGokOTSf{eg*QtzPG{Q6pMrVeiOKkR% zpgoigppj!XgB*T9@+<&?RC*>sXm8DmhL(}sWtKWWtu=q0b@-v`fx3njHEhFaJVLP^ z>#(CL*V%>z=<#=D*AGOi1}09N@_~xn)=BOw@U^L)95e91(AOk+JhjgN$V}G5>2E98 z*vV4Bh62Xbww1`heFFpceZuz?JjH_tl}E3;4mCs6O$84}s|&?-tN_MVMR{=1kcR+l z&_(drAZv}Pf(7prB5=s?mn8j&*IYG@2_?apP*zn|6^sNUg#|8WE+ERP#zm+k z9^6XCw(zK+aUmlrwQX064!Ki|2jB^fWa?YpdwcX34QG3-P`HLK)c5S&bVK6YPW{9& zeaDurFHKXoI(II-J+H;0+O%qN)-QcyJ3D zX6^fpjgyL@VcaoW+)Zh%5qbIBZzF|l#dp8U*1i6^^6h$N`}VhqcEGU+;zB788*LUM zC_bt&g-Vgq5_+6?uxg4vz>#n^T9|A_Q39E^W8g2SOaaxtF;eZD>}*+9lgqQ+*>0yz z&XTii03YXa-3?t%04{SxWO}>~Fgq#~z^UrSZ{otk#}2dnXP$Xx*IhLM-yvV1<}UV| zH{M`%%3of7`HeS}$FA@N0=_FsIS^Q!Tqn#EZxZjt;>)AT!`PUT0ztIG*O02RAzX+j zFCq5@OBD|=z*vA#l?d+u-y=tW?g8pXtX9@5udo=qC)(JU_w_p$-TAd8hwtdQ{mywT z%9@&@%I3=W?S~I9y7SJ(hwtdV<4*Ju{u%B+G3)(JGwu(sxpsewd=jh;!w3`FM;k*$ zWm8inyNAV;SCsB0ci!G}$KfSk!zkv(E7{#qv3^9Z-fDyqvlHy|_ zd_v_P{tf>YiEqxD_NGd4AqX6u_{OeXckJ55F5UISl3(rG^&$TaUnp0z4;62sy@uTy zC902BHnF?M$H$e|Sd@O^BYy*{;<*dr0QM5XP@?t|@&IH#3hwhD6lSg_18^ts>{Ukt z5=Vpc?r&_(qj!Dw=;4=M61R>`d~FOrRX^I$Vq4?&w%Y%fS&;lZz|9y$NyP}l-~cMO6? zA{Zu*3G>B`;4{LLc!UyQK1YPziJdtti>9B&wr4vb8*;6X5`whdW)Ve)(Io+;V*nCK zHk;*PyvgQKd!A^P&CBa*ecs%h_3MUKuUIy?baBtZ1&Pkq=6GZ6NZm+vw5+ta&|Bgw zaXE54xgKnn0XH^vmc(YkzQqhcxMWyXkAm%*R|2)BlAi5k7FH6>3l@cd;V>pHa&S4) zW4Y+T1q|%IW_NM1JHPn;_3N)}F3!g<>@xLT`H}Xg=B6b}nwr(0{pWzO~d^u zCPuym8zRH-0;Bo3h(c0GBI~EshX&jD0IKsfZ5v% z?cD9BW;^0?Hd`K61wj=uV<24jNCF%qX@QT+(y)y)!yPna4dBS>M&qVf9`#XQkVx=x zCwoa@%ce2I|Dn4ojcDr9<%884sB6PSR^4pFB)4SR`0i07bJOE6G%#{W5-kxMt>a?~ z3EhbW8DoN%1X?eOM4>X#RcS6qHC2HU6^e^cbSRGyKiaU&vkZ%eg`vjsRmt_)^dT~T z@9;o%I9xq2Oq%$Nq3&F@=*K@UjYdm<{NqKdIRBy7)u+gNdL3FxT&Pdffc+TCWc;YA!Vv@L=5alJo?08OG4O3Iq6liQ|IC2??aR>tV`rLW&Y!J?i2FhVyrKRzv zJg9OmXST?qd4WJ4355mwhwADpT(R6-+36{0*wEg-p~3HU%DJ|BS0!q}?teezVV8SC zA&+vUC-nQ;HT&kz+gF(<+j48&{=R6(NK?~DN3_rHuFbW{c@<;x=I>hrq@JYBYZrF! z5cX{3jU+QUjR+3FJK>K}=&o^1Xd?OUvf=_R-eX(RPgpW||d^JNZ7+wxy$EOPl($ zJLGwc=8qrXvCMfTPKLpuIi!!C9@nWxf70=ue4OB%zf2xyXNz0GONhsDPJ+Hb3Zlv{ zq#z>7pT;jKKG5SSf5M-#m8OYv4U>Nr&xM-d67mv`92v1aE*hbFUg#)u@4@WHF zortUOE@z9STWV@pZEd8oP+whLbLhI(yh!Z@mtU}|20v;ec`er;s;;Tl7gk1UYgrBF zf5Z?=5JRN8f+p(f2*C#>pr4SOlr%CGRf7TqP`r+!!&9|8c-|vi^(GNP#%gwA;&)o0 zkyISzM_RBEHv;A`4U;{eVzsc8;FyMtKq|S&;A)zI&DJAjh3X*y0t%we1Yjgyaru)9!2;0(cJ5wr*1 znhMuJIWWpw0Wz{!3~+=S3>Nrk;ip6mI|!|K6bP(_SUop3u%md~P7dC!WEEQA{#T7QyI1YhJrKL0kMhdg%ll#u{ElLCNa zKn%b`2Uj(`kf=pK$q9Hvb^>ZpBRip^y$U2)Ft5F*qo*+zZL4a_BtRAf(3!A(8nlBH z+6=_v=@8^d%2f-8ZAEKq1^h>+q|R8%b6Yo1)eWz08##_tT7g#*oK{W1EtBUqlU7uo z0f{Inp0PlT62)L1n+#@%5IE4*z|k#_Q1U4IM|@6~ALQWcr%qcx=O)gcMiHod;1?jfvMeaM zj-X76HUxs<+yoPff-&Y1#5~OZ{c9-A({?)4$mU+S(;+g7nOvbEo=#haNOOeUPKOq3u87s!2N!TdyfYqTZWJbf{pvFJTT4DEEtmpyxU z?UcluHE$}#XX;w(5P+*|u2FGynZ!FIF6)SP&~{ZlP4=8Amrb`ZPm4ThrrqhRx9P?d zbW}3>3ZSd_$=b`y1q{-Kt&70aFqfD#&O=M6NNXv4O%JNJVa>qO-87SZvq9gRQap)|S}%`=MH{y?^MOFNh!P9j%VX ztK)U?E#KU|pakFJ>wrwg1V%BuE^z=K zk*n`ceII$YgP^u?lH0e!7{Wy$s6G3yR*hc!^ zo@O7bkQ~a5sjH46pVDuA0RQ*VqAR|azvN2917luR-O_1a{xsL0p?c2HzM>)K|BlmK$Xkq zC%Rw-^PEQV>qzi=!Q4T^WZ>ATRK0+Dg@Vu`3=|a-R=cni1&IN#plVG5Tv;Y?YCwxw z$Elt+MQi}8SN(&OmXgZmXL;Q_+DZ}?@$UHM`42x_+0a<&{IQo%0Xh5w`%e1Vo0E@#S?DX$%P^@6KRR#CK={u+#x2cu?>^OBXz&&z$}NcQ=w>sni? z!_D|=cmMM3lpY|kiO(?4d_GT9_d}#kLhwwKsOlgL*+$sB0_KTIOK@k8)5GFPCorl} zSV*Vq(2*Q!5}L{*;gaGad#(Z0cQ=+s!i`~+Z>REZZarV)Jdcn#j~aNusg#xw%JJi} z>*&Uf6F*_kDepQX7E8p*Ufj5DL3erS*@NrW4<6`Ud09(sO}vz*zINqf-!n!&IPmJ& z)l`VjDu1uExn^ow-@4y^?vjdz)Z_lcjbx^Zbk~$=sAx^5VrTOVGKa`|D)%;HSu$2c!HctGM{aAU|QI?$P zqJ`z^V?0pbByK@zK|k^c`4gW%zZOZd=PcVe%e2#<=C|g4`~y4fKcERsyat)F88W2- zvb|eapBT;sGL-d0gk)Jv5;N-=ntG8uqmoX|OLMI`62Ju`Vm3h9kS<-FO^pn~sI$AP zyCvS((bNI6wWbmU*aJmApkAqjn$W-+Y$%a``m4{ltsAch`4xh*zF}KFO((uAUm@ z$-Ue}FWC?l`&;a@s5vRwJUbO}H>I z=0OaN^`pwI?6l`*BegUK_&${Tf>(cpm*{R@n(uJi^7J`5XtN=@Bu=H#O+#x2p)G9L zylLC$wspg6MutXKEE`xoxVpb@N$^SUTKvaY70tOThjkPtbXZcEux&osJ$LT5t2 z(<10J%0BViQ`7g!3|b%Cola}VAm$ z^xG%f6P>$v3*yAjCNJdqS7ku?UHZ6}mnKltzDuVn6X=nkrWargrz#Z4vNQ_CuT4gR zS_yW(X{8LNpP-|p{0dYXuc<1puc)V_+x$EmI1BYi%chqy&?>QM>1r933{ZLub)=|1 ziOZ=RRjU|WeBD$LgO!V^h=Eiy;a|9L;q_F)pd2L(F4_4h3K$IRKmmh-`mP=nF!<%l zjeA+!s+}uW?ci|>-NZjYzugAiI|4gyNH|CMmqc!jjHu6sWxXh4U_d7ht&Bl}ASsB^ z>&CMgmEW{l#!)&^Vn{~gwHvT6jhb@}$clwrUJ4JMg#7)fZ>Xd}>nSe*kPGytmelFF zh8(P3UfZB-E9HkZ%A+yjzTEzv!@jZCXBieI5U$|Vj zPPk3@JmEO228&m`@r(O+(c?De+LDjEKg)=)6@T6?hiKE~>TJ2=*(g1WCvKIYdUGR8 z7|qGInB4|_9x9S{E%iCQc8?yFS~9+-N59~-E~g*KF{8yoOWK9L5Zo2-qFWq+Kp##?)yzZh4_FTUA@|`;%+((cBb^g-x=jIIme=}$N*SIHx zm41HmY8%JfHWDl4<<+c+w+vA=j-pa@>-pl9ONf;mGyXWmXA{>JRy(OsM|GVzmf|yU zU+Uwj%6FWUHKU9iH{s z|CmiEW%o@lwt{XdZ_oL}{xtTBX%$zRRh*D=>*RyT>i;7=xjpJu=On$Fk{n2-hI%Ck z%@U~P;dT?U@YFw&j!H_mAPr_@yPD7j3K_VVlufzyp!k)E^H)g=l-u?zhoRTMB!p0Z zZ7)Lb@S7kwk6<8KjS`aUU?Rdm+lF6p-KoBtR8!CnGhId+ei-iFKxrfrb4Z~$wA6s> z_)7=$Stir{Uw!{qH$Nzuq+=h4#DSxF%R9=yEC2p3YUms#eNj}ti@KQqgq*$-j%SOz_820$l1hILV<7~ z8Zcu_NVKhQf(=MT$>Z@R1hnu)9?}og&P6a9;o!l*Z5@|{%28mqwXv}k^&-kkF8R`` zbMkRhJeJ4u4-1+9Xx-3IB-{Nk;%Luc2_t!wqg0#%PPjsx+6XqWol^z%;lTN zj~u~VQrIz!ose9h8_|)-$Gc18!OZL?ji*C+8X($G@)Ix?=2CvoZl0KbVE35#;@F9m zbeG9V+%D~pFF^}9N>i~XzrZKx?@jHJ}A78 zdD8VMpM4}@&a)#i8D%1Fl#!i*ix2a<-N3uYIXzcg=>46DOJP$tV;Pj=QAIlPEJ8Bgm zTP{Ui)K7spP>Z#wq;-*?0A)!)psyMInUmF?L=6Bi3}Iw5Op!B#l+PbzO({B>&1rO; zK|_|3i56j4rJ#R01<`k!XMCyT3t&vMP!G=|*(`cEK`xwRRu4(DbdqV3Qj?aPR`e`W z^3n>V20S5fWx~dtl9)0oD{Mmm5-@4qR#kEWVnHHV5CES+(Kx|M4o;gXDLqx$8E`cQ zAU~0$6@UcQJvc=2bLjXgz0U2^>HO|v6A+`oSU`x5jeS^9BG3DinGzlL48c=k4X7`>iMMje8#hp z;OpBDU*EYv3pWUD0`d^s>g&)++MVafHCY7K81>{MFbU`>z<+Ru3*tCEJDxr+qL>&E zAymkuN~rQXb*LRiJqsi7=D8bHgMhodGF?KY5m(apC((fh1Df7Yu`T7z&3oB_>T>() z4LjC#CHgbmf4K$K`NeD2k8Qdp47bWPW!2SXH(X&nZ>Y4^_?kX%-||)GnXb4>E=~6W zewvdb{);JZ?^$d1n63~XEQwj4lA}tAp$-m5sM1XF3hUX2Vl!}8Yx^GYLkf9;r9r43 zB};)FHUmH?^ZKYFH${u7vaF2i@M*{3D-l3n94X2WNz@n;h@d zkkvtB*s%5A5~{>}aVyqjK6P&aiUrJJ1DED!S+V2u%H*e6A1FyNs(u4!s=Bnoje3{z z^n34J{@!~Gbs5P~TINvRWzSXpdjGHSzXcvq0F8fS^+nt#QXyv<^N(cKiI>fe_^L_m0>?-4WTQj?r2@>r-3 zN-laQ%NB#Y6;(tv8M{=lSj?j~W;V|^Q+Yzl%Ri&jT5#H#oq-#na$&tnQ&pkyqXxCB zIGvU=)Gj-OeOQV~Nph06j>t-u5y-02HKg2~eQh?HXA?0e{!OLfi60e}mKGHjha*M5 zW`9*o_k(g{f1w|T!vzIldeK_`Ot?)nGKJHzCJ}{;7?Dt(fSAgrP+Kc-WnrqF87T{l z27=ebXS&?uAL(-0m-rphA=&3Gpa(plFra-j;@n|`^y}%A6C-wlK$T{y%oSkRY`mmP!6oC7RAqJAzL8Y9je@m6AlnIq=)k=?m& zK;UzP93Jha5yvBV+I-k>cgk*e481JqJw9~&pQI1#>rZ%fokKT2(9(AQUH|d^S08L{ z{Vqm1Z{FD0yS;tyun3FLJ9zav4A3Zk4RWh7QP24LUYqq zjQ~-OzhVT&;4fufe8MJqYfn5O?QdDxB0f1$R43U_JRiReCdA>x_;DNOyFY^O44_Lk zWfFoBRVMV1cIFJq77@FI;kW?ZOZ*Fh7YV;VpR zgGA_8Bl-69S1sWB^NS2``X95MN5yVON0w7uuX6RAjEw z#2S%1D95_+MAiydNwT1Y)3YNmMJRI+D{I}t**GK z@eJv@PPk(+>3<)8ZYX_J>dK!Q*yQAiEz~y~v;&WwKut%^YgP={!24*kiI0R3`-HE} z+C&xYVMu8OQz7?-x;els)9kTLkhbD7T!7YS$j;X&i6Vdzut0%Fa4WBH*B+Fh?i2qH zEtC)!KUfg^X?0+Y-jR|6|J8e)xJ0{`{*hyYlB}P)*Iu1juicCNFU-p-aQ`JxdlVpALleYdX)I=C~nAU$oH5mo~1##6X|Y-Ssk-8Zx_cd5{d+pw_EqbSKod2mdB6Z zm%80s%A0gMti1y%8Rb{%W|L8qlgFROG2iEM%zyl1;xVam;(n}IgU){+$-F6fru#er zJ=(zaXzDZ>6yA9pfEPfpR8=}gwh=4_Y5m=*({jwHkTgHXCWE1zuM$FM!N*fEW>D%gus-23mvMTiKc_t>?>x zuM)SLLIs+MqUq8nx}i%?oKO4=f9NC1a)W%oc$@Br$F^qb)6enMWR&;Irgx6+->>|B zN~^|mn)nUYd5O3S>)Su^H9P}@)bIycqY{Ek5hmEGW1XI6H5(vLJ$MWb?_4n zO!^*Qm+2IVEOr_3MY|}w@&WRs= zNv!zPYD2f!EDQlY#%=K7evs~C%t1j$ecCAg1|K6jvL$_t+>M+$uyYSiyfkfWv(lyV)%qS>{l7 ziajSrCY~01lwHnJi>1`TZrv?idg5x&O&{ZF>ui?Kt|ceoH`pk+;LD#82iPp1AcpuE z{Dda>2`ho+Xc2Br6xIg)9-sonmYR^AAahVgDj2%K^~WFr`cxwejhe6{No*WlD3BPY zJDWV1qZs*o1PuEg$%2JF=OH|%IOkt+0W5Li%}Sxt=_!Px+B*|{m*IsZT*6$g$Ite! zn)q270Pp#ZQOBx{JJxrt+jU?`(VF%9HeHKYROz*4RaJ01>O+4>BJi^0A9$rVk`Bl# zj5}8?$@%BGeU8%crzZEak3h#J;LsKdUrdA+wzZ;Cd?--t_kfOohd;LgS|au>2us?xd^|E4 z6<&V4fUz7BlXc*ywm|~ z26{-Mk_4bgOY^csi>1Ya-Dv)d%g`ZobPgN{C%t6yB5?VXm-~?tR30ryjSVVX>o4(Z zRW>mCnLb(%e6#^nT13lTykatcxk9L&f{r}#kR0Y3iYwp_Q&V2@hkQNQzqV-gIl-ae zjd=C1D_V1IU?_M~K#6h{sX=-9aklryq8r%_x~6AWJ+|su{8Qglb!Pw3j~;t+|9ITyIw7 zMd6=>J+lgXraGpuuqZJf|8~c7`r!0a!WCZVyVRkcl{r-L z7bzxB^Gc@={DKS@HTLy%yAhqOV2enCYB$2pH^Oj)&4_kR@b+ng3AvD#u15!HJ*y)# zQE78Tv$884>;-N#zWRmNF8Dixj%tA1tbFIS^IwC_1Kn5Cv27IHfp9r}laZJBqo5c5 zsU{<5GfnR%r-#gy;UV3F2M!!xHFQKT9-FA;{4+K-@qGll4ka{wnEMv8gk4V%JT1)$ zM;ih;;oujdOc3%nY@1&3lALg4?$BXy!p(7%>V%uAN5e`a_k^lP59wDNTU}G9zyH|7 zL)thK^Wi-fAv=;bf`u&}_x^X4V6RvVD_^pbcnqOC5$ z_;@z@vWoSUjaZj@mp&Py(op}XCHfM3Nr|x@tD_r|j{Q`b@Z)d@7$wa*GJygXEZa z1g^*@b(b8!=1HXyEcX8UeAY+zend8mf~lwYiT#D!&L{JkV9lyfh|}|#XhTsKX4-g$ zbf)9qh&AXo9slc4`l!anXq8S43^vmrK(7~7dLbT0J`?}MLn_^#Nz%=Zad)F?&9|QH z>t$$2POCWwnm~gfBVH=+%K0h!K!|7f)zq7p=RS>G9A$tk3??VB*gF>)G6spt_tM8Pw zcdFch&&lq#l+}}s7k`)Vj^usWkw(8;r6^Kl+;+deq&{5PQdZ)22P4Ros44b%?3G*k zIsmB)xKX3CrMkK$5_CK0z>;u%iQn(&HT^oBaXMn`o0Q)AuD-k}9CxbjP0}uyjT*Zv&>LZO? z85;dv4JSMzkPKvp&l?DEN|2J|&7ij=BQL5sb*6|m2xo9AKsMltcA-7qSXWa5isPTn zo~037%Hy;Kjg#*wT7$mXDvd#NS@oR#GOy56R22-@1-xFH&5OLLl5lOnjECH-R|?27Ji4XE9JM$UQpolgu|ZXtGcwheR1*^de18^l;2i$8}bLN zgSX2g3<}>$6y?LiC)yK$-J>FcaVs+z0xh zay#5M=vA%*E)TuR;SAYqgp$iM&8q2eNhaG2c}}7*e)1E5vG;i?Z8(8KmQ4+{)fHvo zl0cDn&^Ks7m^Z1R@T{i{bqUF6AR`*xY1wFLv_?l5s&dI08N{C0c`}Xa#APa}oq0R@ zyUH5SzSm~=nU;&Mf*I#4M7$+>C~6(p3ULY&r{QE{Io~&@l{2vUmu3wjJHF&>J0O=N~|L zbmFjs(3IrYk~&pyA^M;GxxVjUs9l3u@Zr}lyujdE-e!Ig*4F9*w z9W0Vy-G?Mf(5FRS$m3xzTOfHf>2o?lh=dXFJVE$TK1DnyR|qAFv*0;_rU@jE<}D{Q z)rEMjxk7Fs_k+bL_L9uhL&3+qs)_e_YFZe8HV3<(WgSxP-F+dGl8J>@(@(@-P)jZF z#vgF6AL3pG0y-=D^La@`?UqS@jc`@+mIXoqzomZKE#>sYNNM7Os)@JB`f-$+&G5`T zhyiqo@igms{;?Uu`Hu3ZDFgYn3AhiLViaCx$JkGR3_+9#CNWiy7{?CHr2%z=Lem&j zra?C(>PICYy99iRR!7vl+wHQ!vj_Y}5i2r+g(B(87nlv=Ago(x)CR+{s?Ms?GM_)N zqhd*IH9kjt1pzTf>2meFTvSz681T0*s$5t`A4`gC`-@6oOa3q9TvET%ZegYHFRDY? zQtJ_|(n1VE?Dr*-zac-SA8Zax(W{H9YjbXAGn&z4;D6xYwZ==8& zBV_{Rv{4|#+(B@RuB3FEd5J7kO+4ilh7;l59`s0A+Ox8EWumjAt*N0p8bP0#6rH+R zcg|dNqRwM#O3cX`r~=OhNINp?GFoJ9tM9HaFZ23>JEDCxRqa(34dMF5mN`1B?nb(j zx10TIAQTGV^?QF=xi9Ex>8e~*MjvBQ*GamqB9-Y;kAF0+1iKlma7RYh&Z)D*a37EPC>J1c!@UyeX3`CA5Wf%wm#!$ zaB6BFM{+0GwG%Pm%d>vZpRLu{)Mxk04;nx$#^4E zHVE&qC)v}e4Ryu;&=!r#14v3O4O8iRDy_v=awBV`EjnW*s|#5r3}4Xc8}V7#X`#wE z-PlrTr}d~?YM8$EIh^htOFL<&_0a|#sC|d;ry09v@|(6LN&0Y^`DKl#)(Q_NEUVG~ zX&CjyfGkys(=6ngb5U*qEqRRl>r-HTMLent+U}Bp&-qA!{ z)si|DS%2eaa_(>DxR!woAdPiv)FyC*S?&46qTHQ(QsIFBX4%dhL1$M5cSGSbWt`}~Kb}Tu&(oR}T zxT>`@Wdd4bnkU=}}6{<*S#?RKKSF$hC{Uej(mdpIPNY zi^w4W3f#uN#;z9s4cbGa8VZ<-n3fUVcP?Z-kFOcg4+;*G+m_vP2$zW~Kf@)dx9j94 zc1XBcG*E9>even!)shkSDCHezefViJbPzvHwt z4GLL-BHAm5aQ{+%{|LXkA2-9z=>d$AzmRlW^^THu$8-d; z@)}i{#cS&7xMp0uYBDO>PXBo_G(S-u+<*N}@v0kcz_4(QgSf`-Gr9&sJIcWwSMN(+ zLsWve#w+|9e;ZHWiUGRnA9h|kraZsvk}wpn{b0+Q8^&WhhiRx=B2+d;#&` zg5|h}s4NrLO%9hzqC9Yf06ko6)-n5{r*y}4U_IZmj49vAm z{Nm)eE$e+`ek+f?o zWo$^ib<-xydp~%u6E=6YFhI{(4b8ZTX#{v_Xw4IbiwnrPCVezVVyVcPj4F^2cI7^)PCB|^IwjRTr)4Zyz zTf=3Y)(#y3vs@hz37r2?rCyJh^22>&m$JrvW0&wHi23ZswN^`eKo4y#VfoM`l1owT z&6!+@Tv8(1`@ph04oQ1{|9j30lUeMQ$!En|P?JBMCVb8SjY4A?reVYW0x6&=!%d>@ zyqSMcX#&mL9!SzeLguaJpZmBxbWTqzdu3IqZ)xK^eqZI#WYJ_PYFk;7W9PR#natJF z6L`hil+1lU5(q{j!9WCqR8~(4lPgd|>;KgHYFV_X1+P}Bf{XPuDUchSUX4C?zR&M= zW77uxl?{`*lZnZ1o<5)INhWO=x$^vvzVa2EziYB=awU*+cA<%LNTr1*9zgs+1ff*c zRBAB*9X!1dhuUn;00A=SG3%36NpW2&-E9adyZZAB+8RR(oc_+r#cgjU+*vN~aEs(B zEeJKzm{;MR4WL|(P|fcdg4UNV{x%`OQ>4xw*;ZiRa#)T|6MpJr!Hswky_idRy=_sf zez`m5bI*ITwJMPCxuOBPXEl5R*Rx!-R^NU4bfNV-alLp8%iXvU=f5!dtI1!B1315k zkI_jBHRU8r=W{*WaT71F$G0neh2pM>Th!;`n7n!NR`B}iov%1pY*2Z5w60c%cVvWm&9jWi$z;`e5?{=oXyHvbNEa%_t zN`1E-$NeY&ZuczTouB&dfOvqt&5zrY`tE*l9eV`(i)`4N`fgnOB6}0EUN-DY`)-;Z zhj~0dS;oExo|6rirH*?D-`xP7kPVk7zf1BqOqNSQ6zFbJ8FaU&`I@UtoW9<6+B%z5 zmR#b=cfQ6B;l8EPQ@C$Y;^g|;thyRqRn-K+`*EoPy4n-q3$;J;oO*XkN0Ta2IvUR1 zeOl+z8YkT>c`kG_ocjttS0*noshYSo-qKR4XVKLeu9T^h(Ir)#OtImbx8s@yqRUBj zD_kaBucB)t^{Vu1kmR3Jry_#Tyfl3Z*GTG9rBV?3)DzIBxJ8*MOh#wdrRYgz=uw+S zE4Usd1*hpzpF)rN9p^cq9U%l@8%owH6z#n!k?GCe94C(xUjs}{^>dc#|KYh9& zPWn@PK^NRv0eY!&q)aM>{`3s=r*rxq`5&msc=~#iR648PB(-C_cbx`*Yx863fO{B_KNWlfm zRTiYZayQ1-Bb}gqB;h})&Lu(LdV1a+W73IV{DSk&WC3)xpGm#Yx7t;j+PUJh2g?jx zi({Ha?ol8$EB(7^8djQi_3M(*Io++|Ris@tC1`w8y49&<uCdeRse3DfFM;;k(;X-;GP9(0`u6 zcekf~*N*S*!gqJ5-(d+p2fgbt=u@Y*E#c539ovC@@3|e4SDeBZLZ`gm za=|>Y^`3jkL!#=FU&W^!pmrB3ORcPxpvPwnK+*N~%K1cakAf{*cq$zrlYq_U|PB zP4j~U#PP{%q_1G)HsS2PN6HYQEx~g&FH46pBXBU=;CXE)at={*Gv`h&VLL z25-ipIuQkp^N2P%*{F3P>uh>@mJ%WgaNdLNB#+Y8#w}y}FN+Rt*;o}RuiCgpGGBAe z^?e<_q9R{MAIWJ^`5zx6G^uH0y#KQOV~JCb@rT%-{lYiZ-J}GxKrmk1hX@Dy`RS?k z6KqCdJCITm4{%fUFqP(E?O5ITNe@m`%y}G&O-p)U0rb)+uCJGtBd5Zhi=Yl#vA7)e z9DlA~b(EOk%gfmObHvQ&@%5@K-~3fo&8|R2+KiiXtga=fpy#bfdpaU`^AL2)A3*<# zFj=izQ3jwFAkVUqU`UuVuXQZ~t0e1$9q5>1FpeU^-)x|{N5F42$vWAo^S~`9KaCW< zrsQcvQ&1Y^HMQH)=_0=?)pwtM(?r|ppU4}b4pSpkt<}pgs9NOnGJ*O|7Z!LUzDQ=> zZmP^WmyVdpQ@<^Wc=F(BYpE$NRX>a^-Ml_p8mU^pSt@h-oW%tNH7zc$(_iebX}J$e z0&YVr36cD^LOW_eqy4GCvn}DS>IZ941cr%Nv$S$4)+$}dO%}6`O7p5kpk%B;@&jY5 zCX3^#3(?QxEO>J-S+Yd@?1mf0RKtVcWAZ<>d!W7&N|(|-RIdk~D?M20Anutgtj0sT zlG>Jlz^+zFUl60aS`JgI^UR)&bxvI$gSmKFBhO4GR{mKflhokIw-=+^aSPrHJb5)ON0Pkdm8_G0$8~mx&_-}jbp4~P zR-#P@eF_FoT+E3E&kKFa7lp5#zJVmCb1^_%2_Os=44Ipi!NDbh~=Nm)=AC4NgJ1qG@8?m#CO_th%wxdN8&Cu(;s&dGef&+#*{c zw40nj$&4+)klF)M)3`TiwDz1GDzzHzed)8gupIsB=NI{NycYM4OuClhxgNrEbqf*U zhJ@8=hn7SYxH!(T5VOSY1+1DuPZ&$peG1etW=OrFlF1jFP@c)4Tjont@`KT;WBk-d z1JUZqX9Doog??Xt#8V3FrUln^GdC4*f`bdK3X-r@>J6!S!;=CzIq7%G>Pua|r47-t zI{tolSKlh1d%*2m)z>B6>~#7JhRS;F?drC;3;*J6Do@!Yt5Cyv+Dw>>OnFJdks&pm zb}F^#8}|myAuXq*^V8eEus124IUj3oA(A(s_50fWc08`$PDR)SG;)peyD}uWOuwx! zQJitiEF(h=%GtM$DN%mw1qaVlYPH2qi%c7wI<^lnwoF-W0|Wg3E6cwdvA7nlE3bEX zm9^Tqca)hexr-v9LVNzkjPc*j`M6LRO!QCjaRHDrQgRmrf|0irR+6r=PLd&1ky=H4W(*?)v(Xs_c_ZOML!p8U;3*&i|QLL_KRWEw#0(f;nv)&~- zM4dOFwu<5kb$zdOe(YR**Q35PrAysAnjO9(mbhX}Hr+w`^1iAIE~u)SAp#W&7!jC) z>q)m2pY!L~<)cPVjQ$yy(M~EpN1p#0m@atdh7B1bhmDLx;AE(LiFXg3dLDJ*Z4B&Z zq4}y6Y;_Tc+MI*)YJBSQ()@F9ux)lUDkq(K-X8f*C&e=ij)(HA4PkQ_;v{PiZL>zH z1Wc?!_yxY@2=U6P;XQE55bbWc=AF)TIm;=1tP-j%2&aj`mU$ABC1ZX$>78wIQWPTY zk@vi%exQT_?|-TX1dtkK;+39L!=VaEd1wART_k7XyCdZoSz#2>&B7RQY-bLPA=RBW z7haB!XSntScX(P>4c+@vWE7n}Lj5ihhk=%!kK5eU9Y;8|^&Iyz+BxbizG~vG&0@rZ zPvyjdMRl{rHch=jG~RIPvKzALi`v)=#>MEn)Wmm(sI`3jOI-|O7k)mfVS!(+sd>Dj;~r9RHp8Y-Db8cqGAeMV zlIF3EX()aTJH~MF8-_EC+2S3BGmQW&qj|E73HbW1;T$6>f531q;J-1PCx)3r8TJ^J zR-J~S_(*Fp!vg1Ce4SzJg|ePv*fa{Qe>QZs^!0VjDWB8b)z{xUFsHwxyQ`tIyRWTv z5UIYmdqG=QdD{&GEuDSkogMR9`UiU3`lhxh#@Q{s<#UY&qub~)mKwe2gXiJ0lYXNd z4LVFuIX0!Y8*QLWfZK1>gR2F6T}B^hmm_8uo^oRf=-tMA@XVq3b;bbvcF=l>b0o>_ zM>>5taQ$QWp9d+5W5Aegj6tk!Vu5o3idmW04Vo zR@Hdoc&cz*Yyz4lV2d;5>3^$T2id#@DfLr%wL;5<(4*H_09rS6I&*pUL6=@U-FV)c z|0$G%PUNBo|K-Lcd`IgOzFDXw7^2DjdjAEO?E4X z#=5nXgQn80Gp>N&g0#oNKixWg$c<{vJqTB2s_JJaeAOD~p_NUBgo*#hSPi6CAIY49 zv{lPhy@9G}1Hk%`mZB)WZqT(o-;Yc6C*7zO+MaF34Uo}-n5uVCr81B5sOni8Xj6f? z5@!Qer88H}-{|k%HZ*k}6EsD;&d=cwH-3U48C$0Ds|(CYM5gf!=2qGGg+{K(LqF~@ zzJr4?d;;sY#uj6>2pA)b3gaE)U87axivqlh{ROAU6r$<2iL*qJu@65y42qB_5v9g{ zQ6`2OKNaQTY%xrnBZi|9j}jw{lcEB@1RaSn1@||L^U*+_6*z;#IAFXbE--#B#t7VZ zE+RO$0bP`c86OjI9CA`As*G3AT}4E-s1akug~(#mI4CYMejzRvEimmA+TwxY}X8#c+DMaEx6r||=^P;`lI(Iaj!E*8DUYobr|ivh7nEEY?|QgNgBl+kE(h?~Tx#WHcT zST1hC4&epjR&ks7jJREV*60)~jrrnpVwLzjs?0)T19oRLp>m!QhS4p)Anr6SF_TGsrZ4|WqeBfQ0x{z5_`mtjhncv3tio)*uD zec~r#zxb(m*7%0_nel1kd+0RY#1MLwI3Rv54jNaBUl@Nf-WJb^UmBki&l}f>Ux^pQ zuf-wUg0xKhM!aY&H*OKX6)%b3iI>Iijhn?`@dxpW_@j7L{7Jkf{w$7&zlhhxU$Kd8 zhIm8#O}r`oZhX?X)_6l4HFk=Bh-2cP;<)&iah*6}{8{{4oD~1TFW>$v-WKnOQ{r8G zAjVe+tSDI0#t9U-f*q@0*)j*eP|lMc>BXsVei@MYvOo@zh1kPhgnLMXGKAYwN@bZG zD$C{Ba+o{^r*@6N{OnvgQl2MA$@ArCc>!i@AH#0#h>XgZjLU?qlvT1?*2uB)Lj1z* zVmS_1mDU>f8u!UMSuY!8qimAR@)9{7_iIj&m&u9paydy(mQ&;vaw_h-oF=c5SIcYU zbomK6Lw-_TE3cE+<36ETvPI68b7ZS*lXK-f+$7Z@=gS4MQ!bQUvRn4Z8)UESll^i) zE|QDo61h~~C_g1{lAo5#mfS2Kln=>o%ZKH6 z%&ac|iVL9+bb3&&glP=jE^D3-Z_Uko=8&QT|rGB!4GgmcN&W&*tU(QGoC%}dPj=B4HY^D>&p55avHXW^}*7;ly#qr{wO zUT#h@C!15uE6l0pmF6__D)Vad8gsh&33G<|N%LCsI`evSra8-OF=v}|%vQ6_oNLZA z+szJhzPZ5cG#8p(X1Cd6-eC5ceK^-~z+7Z5HkX)7%^S^6nKzlAHkX+Z~Q~x&J6TfK; zGd3De7@sxr%rBYum|r&6m|wwJc5BVAVjJVf&2{G2%=PBIcpJ$zuE%eu#~HQeedgEA z4dyq@jpjGaP3HaP1Ln8P&E|vVL*};)pZTz{&3FXs`1Qt<##6?l#$(2IfS{GIu- z`Fr!Q`3LhA^N;4M=AX>h%s-n)%)gkgn}0RmF#l%0Y5v_jYW~AKX8zMWZvM+WVgB1Z zY5vE2%lxnTw)u{E%6u0usscZwM#Drjj$@LbFGoqdDbZF zd~39Ifi=eZm=(4nR@91FaVud}T2)pxUQEYY7g`tLV$yNe$E{kc&Z@T>tVXNJYPK%1 z##@(K6RgXuiPq)TBx|xY#k#_piq*Ei8%K>Dm{@$#I2$X=UpDSBzGQsM*o5guopHBy zr8UjE%DNg$Fzc=9#yaDx#{I@x>l4-t>yy^C)^*nP)=V6knQ1(2wOF&QIaaIHX3e$c zS?yMbHQ!obby^FpF00$>v2L(>tv;*Y8n6~wi>)QrQtL+RQ`Sw^r>$ky&DL`37Hfrd zt96_88S8fIv(`%MbJi;B^VS{K7pyz2FIuauyR5sdFIo3kU$)j*U$NF&U$xd*U$fR* z_geQ^U$-_`->^1X-?TPa_gfEG-?BDa4_Xgd-?kpMzGH2%zH4o@zGrQ-9A6t8^C#)x}r>v)~XRLkJPptjcPpxOIpIHa2pIZm5 zUs%sszqFpWer3I2{n|QY{l-W}S>krl|)*r1`tv^|>S%0>USbwoz zxBhCqVg1c`+;EYhPzyZ_l)6*)8^Ldyd^|x7l;;d3L+qVb8Z0*q!!5yUXsjd+ZzRUc1lkw+HM+ z_F{X9z0|(Z{*--_{b_rdeY3sXzQtZ)-)i4xf5yJu{;a*y{+zwa{=9vM{RR6@`-}E! z`!4%#`%Csc_LuE7_E+q+_E+t7_Sfw7_PzFf_SfwV_BZT}_BZWK_Wkw)_P6ZK_Jj6A z_P6ba?eExI?C;uJ?eE#!>__ZJ?Z@ox_V?`__T%+e`-k>!`$zU3`^WZP`w9C= z`ziZr`x$$m{S$k?{Zso{`)Bq6`{(vS`xo|e_Al+{?O)k1*uS<9*}uU`V3x7cxZU`? zvC6o^xYd5q{w;odaKu<)zhvySe`mjJ|K2`q|G|F6{-gb>{U`f1`_J|f`!DwE_FwHc z?7!J>+JCo?+W)YR+5fbU+yAmp*#EXq+W)cNvj1zpZNFomvfst8UxXta6HA}A7N}V!is8jBo?F@6yafUl1 zoC@b$XQXqUGs-#N8SPx)jB!5Zgq?^Jbz)B3NjQ~Gl~e81IAfg)or|1{opH{`om!{P zsdpNjMyJVXb}n(oJC`~WoXebv&gIS|EXPi9u5hM0S31+2tDLKyYnzwPIna(Vy#hLBQaax@=XRb5PX?HrD`OX5T(^=?rIo(c=bA!|C^f~>`fV0S1>@0DX zIyX9>a&B@y?JRR{c9uK0I4hi6o!gwxIJY~Wbyhl`b5=Q@ckXb$;N0na(OK=><=pLj z$+^e*va`ndinG@Fs&S|B5?;u@VEo=VWcqW~pYwHR zgYyk%qw`H?lXJiGfb%V9v-6YB7)U&(%${V4l!<%c5;jT)~b9Pt_$ z=XiD1jMuTxa_XWPbqibO^mcb;)OFA6?rK|*QPS z_qWWM)7I7RG|p)OH@x2NmVT#+bak4Ds72Sa$_<=!11HtMDmTQlo08%<3Tszim(j%9 zG_f|6yBaswl&f&0IqqCSDdt>~Qc{TuN21Z}OOn#9@mSK!8K2^DE={v-1KXmZI^$AK z^-`Uxb7_A^XKP!|gj7r|Jyz*VB%Rqm| zWX>8{r>QPyavxSo+qI@s*-KM6QR!Ss)SN3%%X~D#3fE9l;Yf9}H4XN4rlm!%B(=iP zNS$+4TArIYZ%vi>SX_N66C1Wz($=A|?Ub}mz1buN`htdbDt3V4x9rzOo~bS*!|JnI_4OHTtVUa^$wMS%BOFZ-ADVo^nMWz+%u8vf zEf9@m&r3?T+EtTpPw_Y%Nu!1%jp3}0F`eCWI;Ty&OzY9WW@)I&=wLlMxD)80dPQkO zV^ulxQ!;hBv8vn!^LpFbx;k6BT07=Aon$>7R#WeECJlrhqQUBficXg{a92xDH@;GI z_q4Y;U1@CbO0x|Wx;tHCx1F;|8`*A+Y}!UH_D0wAT>K5qc6X|!HF7SS+*r)j$OYFF z$?G1}(z@9ey4R@B=}zl4dMOi5Z<^wXDyL6pt?x7|MI$wKXLr{;Ozl-)(VrHnl9WMT zQSS_-=a`2y-9Mtts8@i`oxj^ZHgV3Is_cPOPMg?1&1|nGH|Lz=rslkXK{*|8bIN_n z0QD&Yx=&fGGqpICsjS7xK4r1)Q>HD{);q7Q zcOh!g?9RS1b6Pt4oley+L~?YMVwg-vFn1megk-6j#Vk}x1Up2EW+|&HLNrI4o?son zGL}M62!uHElo!(s%?Uzs=27V|oYW%bOKXW~v7#h4=1sLp3UuZxFHVxtoCxuywJ9R# zd?(WK4LF%%B0b-UWWK?Y%y+_Bs039yi=!ILaX=1uFQ7bue3Nt3eG z{`AI9!OmRe)i}z)k#LS~7Yw^fN5Wd^gc1@7J4=-p$yrJ@fM8!*1tNlHsawSeRq-QD zR2C|tt7Ig?0VJ3ZRmak!)DYqfC@-2bkW3P=TcVMuZy;5FiP1BVo_tiN(xj?_5|T{b z4WQ(s+0+Dyha*tp%+wr74^cHO;zn;wN16!<3~Hbpu3IT1x~US9P3@Jy9NlOMrlN^d z<>(R`)7KXbgQPs_pW_kNr*0w~kLYc+!vJUwBEM|2~P z>p2qm^{^F>=*}x1)}2>8LfsLk(zWqBO;VJk}@d%IEZ>tY4J%3cGx+-fn)~bXeao%MWvUVNNH^a-yz&oL-pquW@~r zkM|EX#KiS`PB>oA<)YtrfHNNt25}w);(EFpj_ZjfeAY)#DZ}w*-OlJ4` z;;dJk^@+1S30FR+A7}mItQXJN;t5x8H@|K=tZ#zlCpf(Xr;}hgaaTW1FX2`l*RITm ziL9rs-kdx;t9L1PqFk` zlNeN=Xf)O1M?9YDlu;|v>r-6UC%dk*r@h6hZ|iTdFKJo0u!S-dtBGec_4IXM_GHaK zklBRbY3+clOYt^tU)Ivo(}LI0g|k~*sQ`$Ss20T;xI&?C1HQAJYe|J}RUoNLeNG4JwAWJ2v!&$yYSSBIalNYvO zHw_6kBrg}qYSW7dSH+_c!1j7Dj!)& zuFi~tT;=`ghz@2Egm@Wo*$Ym?bqrRCkZej#gOqX2!kHx`6PYDI>#ryJ;h3KI!>`Py z?33}b$zlL8Q_$uh*cpga)?o565NS-rs!ux!GumQp%YB#1QR;JN}b6{oykg_UZqa2Qm0p`)2r0! zRqFIAb$XRLy-J;4WqsC-ZN1%Nl#|)NxLXxQb!K135(2WiI=b2j%5LkJ*RCe)k(#Wz z9g7rEPXMCfYHfgUHT7!1sTYJ#y&imR_;5A#V!*ZK!`0NA0jFLSevMY8wX1t!Cf51J z5TJBYKz4sSUStW(n%h0l%aBrq_>{`T*N2tKE)8m(6;)@1Aj)pnz`|Zig8-F=0yqr? z>NGT@(jY#S2Jz`M2-Im1BNbiD}j(N&^X$Cr+Js zReIu8I&oG@jYN?eZ_4uNxGR%0RHd0YrkcJ-!yc+!tfz}|yePe>z_6N?hwxp6vLgizMe}&!;#9Ywk31WGvQ5Fhr}8^sEv4o#WG4;OKTe! zub#|A!?EVIW4p+8!s zD^IklQg?7wRl0FiRnin98q>2`_%wxyRyF8s#PsYKbed9tt}7J|oTbdv=*AWf>oquJ zH=K9F0J5gm^Lw$wRA*Vw@sYQ9pnWNpfVyUCZ;aZGT0Dr99x`%p$ZWiB!W`%~W~M+A z5gg2jC8`#vKtWy(Qi}pAwS30Yc|wC}GtBMA)DOIp)IcOQPQQjT%x^$|#YR=Rik%|YA(nHP~9Fk0-fay03wDqaI%_PRpMB2_w z6d$#gnYzuW5-v<5ydOCgJzsMVN+dLxHn=|Mu#|v+CZyLHuO=mPQqGZbXcSF^NZxV41>7|jMB$C1;pfE{u0U{4| zv6=4yC_E)9mFCcvKD5k^z6CSi15lhHNpiYD^OID)>_y#1s?N>qY3Ws+DYvcSw2)-= zC+VTnLb$5yl+(h}vYtjwD}tglvYUC55=;w7ffO_%h0i=ANL=AF27N$;A!!j(c23u0 zpbLXc-<4X*SF!LnjM~B9;uM4G#3_z?24sX@s;&7trMN(&eZWKnZcw zGy(4=r??9=-)!_;X~ZNtGes;*%PxAunJJXy2UGazL51l}h?udm7O5#oC#PkS0%_>0 z(#tc^DxM!y>17+>`aPjauhs*v%4+Gtz}eZ>!7syAO-|G0XvD3a8BJ3sUvp{WwDDI? zsnZ|7FyMw|!&DmUo0|0)9SdB>R4>;=qm{JW5sl(&v6?XAyN31?S}F$nEd2IlUhe=zGKZeJ$LjPf zBC3^z1dgHC@*qjiE|8>-rx$>tQT?Gf8pWp!7-C^kF3(D$O?vhc4QoGK)sTY<*RiOj zv#o1Ro0b*VGoEOyCZ}yl2YQ>X{*IQ;OswMK9o1D7zYV1<@uaIpujoXp^kQ2yravLV z*IAF%>FG`s-^^6Jm|nz#ujRz*^mHm(MRW3Kv`H69R4=Z=lzRFUtz^U0vq`IUM(V=a zaCKpAm^!`K7>(AuMZj|P=SA3!_2GA&Xf+oN7j%>hKB`x+qS0_ewys<$n^v-&cm^2d z8DUg^&xXouYo0kqo1$3|(Kj%=uZ>H)I+5A7u%~}12-%(8^E&Vu2Oo5@80_xN?NGDU zzBc-0k`23I%LQ4Tmr}#^ZZ+V^W{u8mtVXLAtI<}A)$k;>j;ojc9N*l;s)noBs!5-% zrq>svQN4bLid~u8+opDR=v;VQ1ak^EQ#BDi!Hw~RHm0|gprpA3^%e@?taF{q&sCXg zMpUoyp{iGB^mQ!kKugnkui=`9r7p6CTXQ&%QJuGFl=B*`=9*N)HB2whN27XK8BI&4 zAFYh#_Tcj>a*|d;RrQ&D19RH@+go&zMdD$P%axYtsNO?^L|M6TJPT`0Fb~EIt!BJF z3+k#leT#mLiRvv3h{J`b7agG(8;T1t%7q%O;VNHK=~16w(7Xpa`#X9%m$F@IvNgG< zbD%GyrMI_x@jwp+MQWjc_X zXFqlTwYJ&hX0$HQzG{JqhGs|CJe^hUx)XYHQZ%7g9npudG7Y+ON$^mRXlTktv+J5Y z(AmiyV}jdcLT?U13e{NSLU8Ns?j<@AZuJSSFA45l5?sCsF5d*t5)wRnNMN>rH?Ed> z+%yvSXhb;I>xABU7ESP)P(ptZ0IutM0^3xGzqeoeopZH5+;|iG+MnR@Bf;ZTg6nfa z?`VTOE+M@Z1zh)12_Bykm252T1#yXjPKOsa6P3EXBzSoup}(BqI0@a$C3xzQ&>JSA ziAvq`Cn`Bz{Ut>mKcVI7FH_Nk{-_yE=#PTY1Wyzaya18VUxpCQ`bIcC{jm`IY$yE@ zGK!xoYx$L~{%-nuJWlXrBN1nLRa~?67cuC=`st65kk9GyT3n(^53&hfW=ZfeOM*wT z1kVQ&`lBNT6}FrHA_n=KUX`04)>nVbh5j6_KXxMDtPihJC8C^Pp7JGPF3$OjaSP-5 zQG%z437#AzcoLD|NkoDt3kjZIC3rrR;K@ZI&W%TZ=|X;3-zd}dM{3xKah?|?cx^LL z<-`QmcaAFW|O#`VYAD82@0e(p~b`lB`M$ny0^ zUhpx_^T&k#A__W}56`<2`b#6~FYBwnXrkV_d|a=20-Vqvv7r~+U4JA8>ndlSs$4 zo6E=Y^ha~>bG|E?PcH`|UDku=qY0itC3uFE&|geZPneI_7}w(%mrqQ8p^e7aJ~3|3F}7oj^@wpjjIq69oKB4Ojp;9=kfZB&jO`iY ze8spwiE+Kdoi0?aF|KzpuFo;s5^VF&t&V`e-}GxShqg{=~T7jB$I3as7yKeTs4Wk8%GL<8~Qymvgxt z#<;%3xZjI$|BJ&Jboy?2alMFfJBx9Bk8wMXarwu%-o?0Ij&b{sas7^QdyjFy9^-lu zOXzwN? z?DWw)e-9Rkncyt!=)%5k2d^OAU9FVk2uqBpb$Ki+F2q7{YtHPp&Q6X)TygFvW7YL} zb6W6f>4px$CMPxiwoPrrDGFWbabn!(#<3ag|)pU`gdD^=0Uf#Qu(Y&_w z3>RQB)OKYX7VYqr*Ug?6?-~o6+uB+=%8+@z-2*+RbA_<&vExSArloUQ3#MExYGIgT z=o(#JW4Ct?^y$|Y>p!vR)=NA%8!Xhd(aiN<(*h&42MJMqafO|M{= z`sR-}p}P0fKTlWNV#Uq+DkZe zZR^1XNm?24^tE@)?Z4E`y_e9*18%|P_N6t1A$|QVz5SEtj>q>V>>p>P0!&RSZohha z?C*uNk}i@sieBOWiZeUtnPReq_QpuFKq|)uML{|n57M&JA(8a7N+8*H-bPKxkc|m zI4g~mE+LpkPl2SwuoRw_<3ah*>FXW#X`SMbv;rkj^o=Z(MonW*eLt(@H;hXCW)tQ& z6!<)8&|gTxQQkJqUub~S8*!L7w!p8-qK*7>x);vQqTdB5NKIo9R-dE&hB5dh3e#u> z1YtB+gHJO@_;p$ILz%SL@x1o#?gcHgyBA@0l$1#;GT|6+vyVmUbFt+IFB;v8Q%t;v zou{SY81H3|@g8=5jSll`7yN3iXtYu(s&*kkA?$j>P9a9{+XNt3W{JGa0*FJT>UBsA^vn@rSV2} zDmu3?LrO|iXPraRjS|HMbBNKOCPm(Xnf$p<23^G`^WO6fnMB0jIK zkWNQStcaCIGY4&#QDe*^R&)_tza@u=QqGaw#yW9FcNJl&UmymU2CJcaufwfIOt27B#ayJFN~XTqJc0zhhNPL{CfTv+~daEa8Kb} zFd+(Y_LmT6AYZf8%sXfis)b+0PbEZ5KGPDI0FC%!TWfKDhZZ2)9g@!7Z1= z;a13V;f|5GMG9vzRl=>7HE=JK&=99ET@1HYHp3l{>yQP`U78H{3V9{mt0eA=RHrS$ z#c4}$XW~>#AzNe%+&Qup?p!$+ZinoG+am|yE|p8+eoB4{E>2j2dyBjU?yd4xxSx}s zgZp{;dAN7tt~!A;mA(phy<87>tK16rSqaPFq?#w-K4U%u_h;tM4WUlLfcudB5ZtG1 zSQ4jB9EAH<`>$~SX8#RWapM#SL*f*Ln++2uAZ&vBi1P$ooI8LrQV03s*mFGUR^nRV zImr_hzK}fi0B0Vc>`h3)1*aIdzW#zEnGoOZx>;$Pr=p+zdxKv{?*#%q8BxO)SKfa9zpSz1<#8kMvF z+6q|$7A+0nDZ*36cDotbQPwXayM^D!nmHvWN?tE{yW}XI6Zn6-RF-CzdXUEZmx_f_ z!4bPS8)FfUF;`{d!3h*k;PfTjT3Yh#Y0uNA;i>QoN;cvDp^~j7+e>!Q|K5^)B?n5L zFL|-#aOuR7*Gk?fIaYGAv_$=voGP_SvrB!Yg}{cDj>P|%(s=3E(%RDI(ut*0>3@Fd z^wR4~TTAC7w;_~$KP|6YV%7?lzo;ca7MJe&bs4BKn7JoYW?b9%|h;;H0!>qnFxaAI>>jX!KM2 z8ld*I2q%`^V=ShY@+oR5H&ILZG_{mv)KYH7!H-7C9sJ;~!xEhHOE~mS(%tYg`FDd) zJ+x>y6M! z{2vRQ#Q9T3@L7I#!r#L1qiJ+E{Jp*i?lffaOM@O0xD~`%*CE`R5XwZ@3y}33KYQS# zJtc9M{{FrQc?QW>>8Ui_bW{4jSHD!)`^#6lNSqL<#%P1a=<8^VUQA>35*nj#r7`*t zjnS{u82wK*M&qncjM1V%jnO!F6JxZvNR81r!xCdO&V9rfjgucSMvKSP7>#ouF-D6$ zYK+DSj~Js7jxib>7^A^;T>J~?IAV+z&#N(7{8o+8;`eHd7KhasE&im&Xz`jFqs0-` zip5cNexmq?IzLhTQ;pQ(UuvWl|5hWlIH^WzaY~KUIH$1|EuO|`oY9Ce8mBa3jD|#v z(Kw+IV>C`^#2Ae;8Zk!WltzrvIH?h1G)`;87>)B9F-9X*jL|r)5o0vYX~Y$z~F-AiVjL|s35o0t?Zp0Xk83V>>oZW~qT7F!O(Xv*J(XvjB z(Xw8R(XvVPJn|AX=3-Vcq2wX-4%LKq?w4z?nHH7(qhSEZlv?AOD#GzzAV0&G8C3}^vO12NCl|+G8mq53o+mg`g4}?~w zhZb$2xNDNM^+{->3x!hhHoG*HF2_aR!y2GRVrY*GrSx<2fp}_!CY@6>1r;HG+L8)r5ri~QUlc6~aV-H&N!FoCGU|(>>H4Ce32rFY7tpi@ z#HVWsXdz`?lpC}>-q0SN8=Id$%G_?Sr^Bc z?b3>qp`o2gTDc1;8po(`X~m{I@zp6la8Swo@9me0 z@qTvZbg5oxT~HFNl}`7A>M1y=?W-6HDsDtg+yx1HlF)PSg-Ry4G!;YHPt{nLRy-*5 zba@{fqxkBilF;&ZVVdq->Rf&S

Zh7cd=34FaGWy`SDvi)66*nbC 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 RXt+-ctVzYaTEQjd{{RH8A$|Y= literal 0 HcmV?d00001 diff --git a/src/Resources/Fonts/JetBrainsMono-Italic.ttf b/src/Resources/Fonts/JetBrainsMono-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ccc9d6a5bc15ffbcce66f923bf3d7999c73ede84 GIT binary patch literal 276840 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 &?JvTsIyg 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 z

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 + M0 0M1024 1024 M960 544H64a32 32 0 1 1 0-64h896a32 32 0 1 1 0 64 + M153 154h768v768h-768v-768zm64 64v640h640v-640h-640z + M256 128l0 192L64 320l0 576 704 0 0-192 192 0L960 128 256 128zM704 832 128 832 128 384l576 0L704 832zM896 640l-128 0L768 320 320 320 320 192l576 0L896 640z + M519 459 222 162a37 37 0 10-52 52l297 297L169 809a37 37 0 1052 52l297-297 297 297a37 37 0 1052-52l-297-297 297-297a37 37 0 10-52-52L519 459z + M1024 750v110c0 50-41 91-91 91h-841A92 92 0 010 859v-110C0 699 41 658 91 658h841c50 0 91 41 91 91z + M0 4 0 20 16 20 0 4M4 0 20 0 20 16 4 0z + M851 755q0 23-16 39l-78 78q-16 16-39 16t-39-16l-168-168-168 168q-16 16-39 16t-39-16l-78-78q-16-16-16-39t16-39l168-168-168-168q-16-16-16-39t16-39l78-78q16-16 39-16t39 16l168 168 168-168q16-16 39-16t39 16l78 78q16 16 16 39t-16 39l-168 168 168 168q16 16 16 39z + M30 0 30 30 0 15z + M0 0 0 30 30 15z + M512 945c-238 0-433-195-433-433S274 79 512 79c238 0 433 195 433 433S750 945 512 945M512 0C228 0 0 228 0 512s228 512 512 512 512-228 512-512-228-512-512-512zM752 477H364l128-128a38 38 0 000-55 38 38 0 00-55 0l-185 183a55 55 0 00-16 39c0 16 6 30 16 39l185 185a39 39 0 0028 12 34 34 0 0028-14 38 38 0 000-55l-128-128h386c22 0 41-18 41-39a39 39 0 00-39-39 + M1231 0v372H120V0zM1352 484v56H0v-56zM1147 939H205V737h942v203M1231 1024V652H120V1024z + M576 832C576 867 547 896 512 896 477 896 448 867 448 832 448 797 477 768 512 768 547 768 576 797 576 832ZM512 256C477 256 448 285 448 320L448 640C448 675 477 704 512 704 547 704 576 675 576 640L576 320C576 285 547 256 512 256ZM1024 896C1024 967 967 1024 896 1024L128 1024C57 1024 0 967 0 896 0 875 5 855 14 837L14 837 398 69 398 69C420 28 462 0 512 0 562 0 604 28 626 69L1008 835C1018 853 1024 874 1024 896ZM960 896C960 885 957 875 952 865L952 864 951 863 569 98C557 77 536 64 512 64 488 64 466 77 455 99L452 105 92 825 93 825 71 867C66 876 64 886 64 896 64 931 93 960 128 960L896 960C931 960 960 931 960 896Z + M352 64h320L960 352v320L672 960h-320L64 672v-320L352 64zm161 363L344 256 260 341 429 512l-169 171L344 768 513 597 682 768l85-85L598 512l169-171L682 256 513 427z + m186 532 287 0 0 287c0 11 9 20 20 20s20-9 20-20l0-287 287 0c11 0 20-9 20-20s-9-20-20-20l-287 0 0-287c0-11-9-20-20-20s-20 9-20 20l0 287-287 0c-11 0-20 9-20 20s9 20 20 20z + M716.3 383.1c0 38.4-6.5 76-19.4 111.8l-10.7 29.7 229.6 229.5c44.5 44.6 44.5 117.1 0 161.6a113.6 113.6 0 01-80.8 33.5a113.6 113.6 0 01-80.8-33.5L529 694l-32 13a331.6 331.6 0 01-111.9 19.4A333.5 333.5 0 0150 383.1c0-39 6.8-77.2 20-113.6L285 482l194-195-214-210A331 331 0 01383.1 50A333.5 333.5 0 01716.3 383.1zM231.6 31.6l-22.9 9.9a22.2 22.2 0 00-5.9 4.2a19.5 19.5 0 000 27.5l215 215.2L288.4 417.8 77.8 207.1a26 26 0 00-17.2-7.1a22.8 22.8 0 00-21.5 15a400.5 400.5 0 00-7.5 16.6A381.6 381.6 0 000 384c0 211.7 172.2 384 384 384c44.3 0 87.6-7.5 129-22.3L743.1 975.8A163.5 163.5 0 00859.5 1024c43.9 0 85.3-17.1 116.4-48.2a164.8 164.8 0 000-233L745.5 513C760.5 471.5 768 428 768 384C768 172 596 0 384 0c-53 0-104 10.5-152.5 31.5z + M928 500a21 21 0 00-19-20L858 472a11 11 0 01-9-9c-1-6-2-13-3-19a11 11 0 015-12l46-25a21 21 0 0010-26l-8-22a21 21 0 00-24-13l-51 10a11 11 0 01-12-6c-3-6-6-11-10-17a11 11 0 011-13l34-39a21 21 0 001-28l-15-18a20 20 0 00-27-4l-45 27a11 11 0 01-13-1c-5-4-10-9-15-12a11 11 0 01-3-12l19-49a21 21 0 00-9-26l-20-12a21 21 0 00-27 6L650 193a9 9 0 01-11 3c-1-1-12-5-20-7a11 11 0 01-7-10l1-52a21 21 0 00-17-22l-23-4a21 21 0 00-24 14L532 164a11 11 0 01-11 7h-20a11 11 0 01-11-7l-17-49a21 21 0 00-24-15l-23 4a21 21 0 00-17 22l1 52a11 11 0 01-8 11c-5 2-15 6-19 7c-4 1-8 0-12-4l-33-40A21 21 0 00313 146l-20 12A21 21 0 00285 184l19 49a11 11 0 01-3 12c-5 4-10 8-15 12a11 11 0 01-13 1L228 231a21 21 0 00-27 4L186 253a21 21 0 001 28L221 320a11 11 0 011 13c-3 5-7 11-10 17a11 11 0 01-12 6l-51-10a21 21 0 00-24 13l-8 22a21 21 0 0010 26l46 25a11 11 0 015 12l0 3c-1 6-2 11-3 16a11 11 0 01-9 9l-51 8A21 21 0 0096 500v23A21 21 0 00114 544l51 8a11 11 0 019 9c1 6 2 13 3 19a11 11 0 01-5 12l-46 25a21 21 0 00-10 26l8 22a21 21 0 0024 13l51-10a11 11 0 0112 6c3 6 6 11 10 17a11 11 0 01-1 13l-34 39a21 21 0 00-1 28l15 18a20 20 0 0027 4l45-27a11 11 0 0113 1c5 4 10 9 15 12a11 11 0 013 12l-19 49a21 21 0 009 26l20 12a21 21 0 0027-6L374 832c3-3 7-5 10-4c7 3 12 5 20 7a11 11 0 018 10l-1 52a21 21 0 0017 22l23 4a21 21 0 0024-14l17-50a11 11 0 0111-7h20a11 11 0 0111 7l17 49a21 21 0 0020 15a19 19 0 004 0l23-4a21 21 0 0017-22l-1-52a11 11 0 018-10c8-3 13-5 18-7l1 0c6-2 9 0 11 3l34 41A21 21 0 00710 878l20-12a21 21 0 009-26l-18-49a11 11 0 013-12c5-4 10-8 15-12a11 11 0 0113-1l45 27a21 21 0 0027-4l15-18a21 21 0 00-1-28l-34-39a11 11 0 01-1-13c3-5 7-11 10-17a11 11 0 0112-6l51 10a21 21 0 0024-13l8-22a21 21 0 00-10-26l-46-25a11 11 0 01-5-12l0-3c1-6 2-11 3-16a11 11 0 019-9l51-8a21 21 0 0018-21v-23zm-565 188a32 32 0 01-51 5a270 270 0 011-363a32 32 0 0151 6l91 161a32 32 0 010 31zM512 782a270 270 0 01-57-6a32 32 0 01-20-47l92-160a32 32 0 0127-16h184a32 32 0 0130 41c-35 109-137 188-257 188zm15-328L436 294a32 32 0 0121-47a268 268 0 0155-6c120 0 222 79 257 188a32 32 0 01-30 41h-184a32 32 0 01-28-16z + M512 0C229 0 0 229 0 512s229 512 512 512 512-229 512-512S795 0 512 0zM512 928c-230 0-416-186-416-416S282 96 512 96s416 186 416 416S742 928 512 928zM538 343c47 0 83-38 83-78 0-32-21-61-62-61-55 0-82 45-82 77C475 320 498 343 538 343zM533 729c-8 0-11-10-3-40l43-166c16-61 11-100-22-100-39 0-131 40-211 108l16 27c25-17 68-35 78-35 8 0 7 10 0 36l-38 158c-23 89 1 110 34 110 33 0 118-30 196-110l-19-25C575 717 543 729 533 729z + M702 677 590 565a148 148 0 10-25 27L676 703zm-346-200a115 115 0 11115 115A115 115 0 01355 478z + M512 57c251 0 455 204 455 455S763 967 512 967 57 763 57 512 261 57 512 57zm181 274c-11-11-29-11-40 0L512 472 371 331c-11-11-29-11-40 0-11 11-11 29 0 40L471 512 331 653c-11 11-11 29 0 40 11 11 29 11 40 0l141-141 141 141c11 11 29 11 40 0 11-11 11-29 0-40L552 512l141-141c11-11 11-29 0-40z + M899 870l-53-306H864c14 0 26-12 26-26V346c0-14-12-26-26-26H618V138c0-14-12-26-26-26H432c-14 0-26 12-26 26v182H160c-14 0-26 12-26 26v192c0 14 12 26 26 26h18l-53 306c0 2 0 3 0 4c0 14 12 26 26 26h723c2 0 3 0 4 0c14-2 24-16 21-30zM204 390h272V182h72v208h272v104H204V390zm468 440V674c0-4-4-8-8-8h-48c-4 0-8 4-8 8v156H416V674c0-4-4-8-8-8h-48c-4 0-8 4-8 8v156H203l45-260H776l45 260H672z + M960 146v91C960 318 759 384 512 384S64 318 64 238V146C64 66 265 0 512 0s448 66 448 146zM960 352v206C960 638 759 704 512 704S64 638 64 558V352c96 66 272 97 448 97S864 418 960 352zm0 320v206C960 958 759 1024 512 1024S64 958 64 878V672c96 66 272 97 448 97S864 738 960 672z + M800 928l-512 0 0-704 224 0 0 292 113-86 111 86 0-292 128 0 0 640c0 35-29 64-64 64zM625 388l-81 64 0-260 160 0 0 260-79-64zM192 160l0 32c0 18 14 32 32 32l32 0 0 704-32 0c-35 0-64-29-64-64l0-704c0-35 29-64 64-64l576 0c24 0 44 13 55 32l-631 0c-18 0-32 14-32 32z + M64 864h896V288h-396a64 64 0 01-57-35L460 160H64v704zm-64 32V128a32 32 0 0132-32h448a32 32 0 0129 18L564 224H992a32 32 0 0132 32v640a32 32 0 01-32 32H32a32 32 0 01-32-32z + M1088 227H609L453 78a11 11 0 00-7-3H107a43 43 0 00-43 43v789a43 43 0 0043 43h981a43 43 0 0043-43V270a43 43 0 00-43-43zM757 599c0 5-5 9-10 9h-113v113c0 5-4 9-9 9h-56c-5 0-9-4-9-9V608h-113c-5 0-10-4-10-9V543c0-5 5-9 10-9h113V420c0-5 4-9 9-9h56c5 0 9 4 9 9V533h113c5 0 10 4 10 9v56z + M448 64l128 128h448v768H0V64z + M832 960l192-512H192L0 960zM128 384L0 960V128h288l128 128h416v128z + M959 320H960v640A64 64 0 01896 1024H192A64 64 0 01128 960V64A64 64 0 01192 0H640v321h320L959 320zM320 544c0 17 14 32 32 32h384A32 32 0 00768 544c0-17-14-32-32-32H352A32 32 0 00320 544zm0 128c0 17 14 32 32 32h384a32 32 0 0032-32c0-17-14-32-32-32H352a32 32 0 00-32 32zm0 128c0 17 14 32 32 32h384a32 32 0 0032-32c0-17-14-32-32-32H352a32 32 0 00-32 32z + M683 85l213 213v598a42 42 0 01-42 42H170A43 43 0 01128 896V128C128 104 147 85 170 85H683zm-213 384H341v85h128v128h85v-128h128v-85h-128V341h-85v128z + M896 320c0-19-6-32-19-45l-192-192c-13-13-26-19-45-19H192c-38 0-64 26-64 64v768c0 38 26 64 64 64h640c38 0 64-26 64-64V320zm-256 384H384c-19 0-32-13-32-32s13-32 32-32h256c19 0 32 13 32 32s-13 32-32 32zm166-384H640V128l192 192h-26z + M416 832H128V128h384v192C512 355 541 384 576 384L768 384v32c0 19 13 32 32 32S832 435 832 416v-64c0-6 0-19-6-25l-256-256c-6-6-19-6-25-6H128A64 64 0 0064 128v704C64 867 93 896 129 896h288c19 0 32-13 32-32S435 832 416 832zM576 172 722 320H576V172zM736 512C614 512 512 614 512 736S614 960 736 960s224-102 224-224S858 512 736 512zM576 736C576 646 646 576 736 576c32 0 58 6 83 26l-218 218c-19-26-26-51-26-83zm160 160c-32 0-64-13-96-32l224-224c19 26 32 58 32 96 0 90-70 160-160 160z + M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z + M1024 896v128H0V704h128v192h768V704h128v192zM576 555 811 320 896 405l-384 384-384-384L213 320 448 555V0h128v555z + M432 0h160c27 0 48 21 48 48v336h175c36 0 53 43 28 68L539 757c-15 15-40 15-55 0L180 452c-25-25-7-68 28-68H384V48c0-27 21-48 48-48zm592 752v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h293l98 98c40 40 105 40 145 0l98-98H976c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z + M592 768h-160c-27 0-48-21-48-48V384h-175c-36 0-53-43-28-68L485 11c15-15 40-15 55 0l304 304c25 25 7 68-28 68H640v336c0 27-21 48-48 48zm432-16v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h272v16c0 62 50 112 112 112h160c62 0 112-50 112-112v-16h272c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z + M961 320 512 577 63 320 512 62l449 258zM512 628 185 442 63 512 512 770 961 512l-123-70L512 628zM512 821 185 634 63 704 512 962l449-258L839 634 512 821z + M144 112h736c18 0 32 14 32 32v736c0 18-14 32-32 32H144c-18 0-32-14-32-32V144c0-18 14-32 32-32zm112 211v72a9 9 0 003 7L386 509 259 615a9 9 0 00-3 7v72a9 9 0 0015 7L493 516a9 9 0 000-14l-222-186a9 9 0 00-15 7zM522 624a10 10 0 00-10 10v60a10 10 0 0010 10h237a10 10 0 0010-10v-60a10 10 0 00-10-10H522z + M509 556l93 149h124l-80-79 49-50 165 164-165 163-49-50 79-79h-163l-96-153 41-65zm187-395 165 164-165 163-49-50L726 360H530l-136 224H140v-70h215l136-224h236l-80-79 49-50z + M683 409v204L1024 308 683 0v191c-413 0-427 526-427 526c117-229 203-307 427-307zm85 492H102V327h153s38-63 114-122H51c-28 0-51 27-51 61v697c0 34 23 61 51 61h768c28 0 51-27 51-61V614l-102 100v187z + M652 157a113 113 0 11156 161L731 395 572 236l80-80 1 1zM334 792v0H175v-159l358-358 159 159-358 358zM62 850h900v113H62v-113z + M853 256h-43v512h43c47 0 85-38 85-85v-341c0-47-38-85-85-85zM725 768V171h128V85h-341v85H640v85H171c-47 0-85 38-85 85v341c0 47 38 85 85 85h469V853h-128v85h341v-85H725v-86zm-469-171v-171h384v171H256z + M762 1024C876 818 895 504 448 514V768L64 384l384-384v248c535-14 595 472 314 776z + M854 307 611 73c-6-6-14-9-22-9H296c-4 0-8 4-8 8v56c0 4 4 8 8 8h277l219 211V824c0 4 4 8 8 8h56c4 0 8-4 8-8V330c0-9-4-17-10-23zM553 201c-6-6-14-9-23-9H192c-18 0-32 14-32 32v704c0 18 14 32 32 32h512c18 0 32-14 32-32V397c0-9-3-17-9-23L553 201zM568 753c0 4-3 7-8 7h-225c-4 0-8-3-8-7v-42c0-4 3-7 8-7h225c4 0 8 3 8 7v42zm0-220c0 4-3 7-8 7H476v85c0 4-3 7-7 7h-42c-4 0-7-3-7-7V540h-85c-4 0-8-3-8-7v-42c0-4 3-7 8-7H420v-85c0-4 3-7 7-7h42c4 0 7 3 7 7V484h85c4 0 8 3 8 7v42z + M797 829a49 49 0 1049 49 49 49 0 00-49-49zm147-114A49 49 0 10992 764a49 49 0 00-49-49zM928 861a49 49 0 1049 49A49 49 0 00928 861zm-5-586L992 205 851 64l-71 71a67 67 0 00-94 0l235 235a67 67 0 000-94zm-853 128a32 32 0 00-32 50 1291 1291 0 0075 112L288 552c20 0 25 21 8 37l-93 86a1282 1282 0 00120 114l100-32c19-6 28 15 14 34l-40 55c26 19 53 36 82 53a89 89 0 00115-20 1391 1391 0 00256-485l-188-188s-306 224-595 198z + M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688 + M529 511c115 0 212 79 239 185h224a62 62 0 017 123l-7 0-224 0a247 247 0 01-479 0H65a62 62 0 01-7-123l7-0h224a247 247 0 01239-185zm0 124a124 124 0 100 247 124 124 0 000-247zm0-618c32 0 58 24 61 55l0 7V206c89 11 165 45 225 103a74 74 0 0122 45l0 9v87a62 62 0 01-123 7l-0-7v-65l-6-4c-43-33-97-51-163-53l-17-0c-74 0-133 18-180 54l-6 4v65a62 62 0 01-55 61l-7 0a62 62 0 01-61-55l-0-7V362c0-20 8-39 23-53 60-58 135-92 224-103V79c0-34 28-62 62-62z + M824 645V307c0-56-46-102-102-102h-102V102l-154 154 154 154V307h102v338c-46 20-82 67-82 123 0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123zm-51 195c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72zM384 256c0-72-61-133-133-133-72 0-133 61-133 133 0 56 36 102 82 123v266C154 666 118 712 118 768c0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123V379C348 358 384 312 384 256zM323 768c0 41-31 72-72 72-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72zM251 328c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72z + M826 498 538 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L826 526c8-7 8-21 0-28zm-320 0L218 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L506 526c4-4 6-9 6-14 0-5-2-10-6-14z + M512 939C465 939 427 900 427 853 427 806 465 768 512 768 559 768 597 806 597 853 597 900 559 939 512 939M555 85 555 555 747 363 807 423 512 719 217 423 277 363 469 555 469 85 555 85Z + M883 567l-128-128c-17-17-43-17-60 0l-128 128c-17 17-17 43 0 60 17 17 43 17 60 0l55-55V683c0 21-21 43-43 43H418c-13-38-43-64-77-77V375c51-17 85-64 85-119 0-73-60-128-128-128-73 0-128 55-128 128 0 55 34 102 85 119v269c-51 17-85 64-85 119 0 73 55 128 128 128 55 0 102-34 119-85H640c73 0 128-55 128-128v-111l55 55c9 9 17 13 30 13 13 0 21-4 30-13 17-13 17-43 0-55zM299 213c26 0 43 17 43 43 0 21-21 43-43 43-26 0-43-21-43-43 0-26 17-43 43-43zm0 597c-26 0-43-21-43-43 0-26 17-43 43-43s43 17 43 43c0 21-17 43-43 43zM725 384c-73 0-128-60-128-128 0-73 55-128 128-128s128 55 128 128c0 68-55 128-128 128zm0-171c-26 0-43 17-43 43s17 43 43 43 43-17 43-43-17-43-43-43z + M277 85a149 149 0 00-43 292v230a32 32 0 0064 0V555h267A160 160 0 00725 395v-12a149 149 0 10-64-5v17a96 96 0 01-96 96H299V383A149 149 0 00277 85zM228 720a32 32 0 00-37-52 150 150 0 00-53 68 32 32 0 1060 23 85 85 0 0130-39zm136-52a32 32 0 00-37 52 86 86 0 0130 39 32 32 0 1060-23 149 149 0 00-53-68zM204 833a32 32 0 10-55 32 149 149 0 0063 58 32 32 0 0028-57 85 85 0 01-36-33zm202 32a32 32 0 00-55-32 85 85 0 01-36 33 32 32 0 0028 57 149 149 0 0063-58z + M0 586l404 119 498-410-386 441-2 251 155-205 279 83L1170 37z + M128 256h192a64 64 0 110 128H128a64 64 0 110-128zm576 192h192a64 64 0 010 128h-192a64 64 0 010-128zm-576 192h192a64 64 0 010 128H128a64 64 0 010-128zm576 0h192a64 64 0 010 128h-192a64 64 0 010-128zm0-384h192a64 64 0 010 128h-192a64 64 0 010-128zM128 448h192a64 64 0 110 128H128a64 64 0 110-128zm384-320a64 64 0 0164 64v640a64 64 0 01-128 0V192a64 64 0 0164-64z + M24 512A488 488 0 01512 24A488 488 0 011000 512A488 488 0 01512 1000A488 488 0 0124 512zm447-325v327L243 619l51 111 300-138V187H471z + M715 254h-405l-58 57h520zm-492 86v201h578V340zm405 143h-29v-29H425v29h-29v-57h231v57zm-405 295h578V559H223zm174-133h231v57h-29v-29H425v29h-29v-57z + M869 145a145 145 0 10-289 0c0 56 33 107 83 131c-5 96-77 128-201 175c-52 20-110 42-160 74V276A144 144 0 00242 0a145 145 0 00-145 145c0 58 35 108 84 131v461a144 144 0 00-84 131a145 145 0 10289 0a144 144 0 00-84-131c5-95 77-128 201-175c122-46 274-103 280-287a145 145 0 0085-132zM242 61a83 83 0 110 167a83 83 0 010-167zm0 891a84 84 0 110-167a84 84 0 010 167zM724 228a84 84 0 110-167a84 84 0 010 167z + M896 128h-64V64c0-35-29-64-64-64s-64 29-64 64v64h-64c-35 0-64 29-64 64s29 64 64 64h64v64c0 35 29 64 64 64s64-29 64-64V256h64c35 0 64-29 64-64s-29-64-64-64zm-204 307C673 481 628 512 576 512H448c-47 0-90 13-128 35V372C394 346 448 275 448 192c0-106-86-192-192-192S64 86 64 192c0 83 54 154 128 180v280c-74 26-128 97-128 180c0 106 86 192 192 192s192-86 192-192c0-67-34-125-84-159c22-20 52-33 84-33h128c122 0 223-85 249-199c-19 4-37 7-57 7c-26 0-51-5-76-13zM256 128c35 0 64 29 64 64s-29 64-64 64s-64-29-64-64s29-64 64-64zm0 768c-35 0-64-29-64-64s29-64 64-64s64 29 64 64s-29 64-64 64z + M902 479v-1c0-133-112-242-250-242c-106 0-196 64-232 154c-28-20-62-32-100-32c-76 0-140 49-160 116c-52 37-86 97-86 165c0 112 90 202 202 202h503c112 0 202-90 202-202c0-65-31-123-79-160z + M364 512h67v108h108v67h-108v108h-67v-108h-108v-67h108v-108zm298-64A107 107 0 01768 555C768 614 720 660 660 660h-108v-54h-108v-108h-94v108h-94c4-21 22-47 44-51l-1-12a75 75 0 0171-75a128 128 0 01239-7a106 106 0 0153-14z + M177 156c-22 5-33 17-36 37c-10 57-33 258-13 278l445 445c23 23 61 23 84 0l246-246c23-23 23-61 0-84l-445-445C437 120 231 145 177 156zM331 344c-26 26-69 26-95 0c-26-26-26-69 0-95s69-26 95 0C357 276 357 318 331 344z + M683 537h-144v-142h-142V283H239a44 44 0 00-41 41v171a56 56 0 0014 34l321 321a41 41 0 0058 0l174-174a41 41 0 000-58zm-341-109a41 41 0 110-58a41 41 0 010 58zM649 284V142h-69v142h-142v68h142v142h69v-142h142v-68h-142z + M557.7 545.3 789.9 402.7c24-15 31.3-46.5 16.4-70.5c-14.8-23.8-46-31.2-70-16.7L506.5 456.6 277.1 315.4c-24.1-14.8-55.6-7.3-70.5 16.8c-14.8 24.1-7.3 55.6 16.8 70.5l231.8 142.6V819.1c0 28.3 22.9 51.2 51.2 51.2c28.3 0 51.2-22.9 51.2-51.2V545.3h.1zM506.5 0l443.4 256v511.9L506.5 1023.9 63.1 767.9v-511.9L506.5 0z + M170 470l0 84 86 0 0-84-86 0zM86 598l0-172 852 0 0 172-852 0zM256 298l0-84-86 0 0 84 86 0zM86 170l852 0 0 172-852 0 0-172zM170 726l0 84 86 0 0-84-86 0zM86 854l0-172 852 0 0 172-852 0z + M812 864h-29V654c0-21-11-40-28-52l-133-88 134-89c18-12 28-31 28-52V164h28c18 0 32-14 32-32s-14-32-32-32H212c-18 0-32 14-32 32s14 32 32 32h30v210c0 21 11 40 28 52l133 88-134 89c-18 12-28 31-28 52V864H212c-18 0-32 14-32 32s14 32 32 32h600c18 0 32-14 32-32s-14-32-32-32zM441 566c18-12 28-31 28-52s-11-40-28-52L306 373V164h414v209l-136 90c-18 12-28 31-28 52 0 21 11 40 28 52l135 89V695c-9-7-20-13-32-19-30-15-93-41-176-41-63 0-125 14-175 38-12 6-22 12-31 18v-36l136-90z + M512 0C233 0 7 223 0 500C6 258 190 64 416 64c230 0 416 200 416 448c0 53 43 96 96 96s96-43 96-96c0-283-229-512-512-512zm0 1023c279 0 505-223 512-500c-6 242-190 436-416 436c-230 0-416-200-416-448c0-53-43-96-96-96s-96 43-96 96c0 283 229 512 512 512z + M888.8 0H135.2c-32.3 0-58.9 26.1-58.9 58.9v906.2c0 32.3 26.1 58.9 58.9 58.9h753.2c32.3 0 58.9-26.1 58.9-58.9v-906.2c.5-32.8-26.1-58.9-58.4-58.9zm-164.9 176.6c30.7 0 55.8 25.1 55.8 55.8s-25.1 55.8-55.8 55.8s-55.8-25.1-55.8-55.8s24.6-55.8 55.8-55.8zm-212 0c30.7 0 55.8 25.1 55.8 55.8S542.7 288.3 512 288.3s-55.8-25.1-55.8-55.8S481.3 176.6 512 176.6zm-212 0c30.7 0 55.8 25.1 55.8 55.8s-25.1 55.8-55.8 55.8s-55.8-25.1-55.8-55.8s25.1-55.8 55.8-55.8zm208.9 606.2H285.2c-24.6 0-44-20-44-44c0-24.6 20-44 44-44h223.7c24.6 0 44 20 44 44c0 24.1-19.5 44-44 44zm229.9-212H285.2c-24.6 0-44-20-44-44c0-24.6 20-44 44-44h453.1c24.6 0 44 20 44 44c.5 24.1-19.5 44-43.5 44z + M512 597m-1 0a1 1 0 103 0a1 1 0 10-3 0ZM810 393 732 315 448 600 293 444 214 522l156 156 78 78 362-362z + M0 33h1024v160H0zM0 432h1024v160H0zM0 831h1024v160H0z + M1024 610v-224H640v48H256V224h128V0H0v224h128v752h512v48h384V800H640v48H256V562h384v48z + M30 271l241 0 0-241-241 0 0 241zM392 271l241 0 0-241-241 0 0 241zM753 30l0 241 241 0 0-241-241 0zM30 632l241 0 0-241-241 0 0 241zM392 632l241 0 0-241-241 0 0 241zM753 632l241 0 0-241-241 0 0 241zM30 994l241 0 0-241-241 0 0 241zM392 994l241 0 0-241-241 0 0 241zM753 994l241 0 0-241-241 0 0 241z + M509 546l271-271 91 91-348 349-1-1-13 13-363-361 91-91z + M256 224l0 115L512 544l256-205 0-115-256 205L256 224zM512 685l-256-205L256 595 512 800 768 595l0-115L512 685z + M170 831l343-342L855 831l105-105-448-448L64 726 170 831z + M768 800V685L512 480 256 685V800l256-205L768 800zM512 339 768 544V429L512 224 256 429V544l256-205z + M599 425 599 657 425 832 425 425 192 192 832 192Z + M280 145l243 341 0-0 45 63-0 0 79 110a143 143 0 11-36 75l-88-123-92 126c1 4 1 9 1 13l0 5a143 143 0 11-36-95l82-113L221 188l60-43zm473 541a70 70 0 100 140 70 70 0 000-140zm-463 0a70 70 0 100 140 70 70 0 000-140zM772 145l59 43-232 319-45-63L772 145z + M896 811l-128 0c-23 0-43-19-43-43 0-23 19-43 43-43l107 0c13 0 21-9 21-21L896 107c0-13-9-21-21-21L448 85c-13 0-21 9-21 21l0 21c0 23-19 43-43 43-23 0-43-19-43-43L341 85c0-47 38-85 85-85l469 0c47 0 85 38 85 85l0 640C981 772 943 811 896 811zM683 299l0 640c0 47-38 85-85 85L128 1024c-47 0-85-38-85-85L43 299c0-47 38-85 85-85l469 0C644 213 683 252 683 299zM576 299 149 299c-13 0-21 9-21 21l0 597c0 13 9 21 21 21l427 0c13 0 21-9 21-21L597 320C597 307 589 299 576 299z + M544 85c49 0 90 37 95 85h75a96 96 0 0196 89L811 267a32 32 0 01-28 32L779 299a32 32 0 01-32-28L747 267a32 32 0 00-28-32L715 235h-91a96 96 0 01-80 42H395c-33 0-62-17-80-42L224 235a32 32 0 00-32 28L192 267v576c0 16 12 30 28 32l4 0h128a32 32 0 0132 28l0 4a32 32 0 01-32 32h-128a96 96 0 01-96-89L128 843V267a96 96 0 0189-96L224 171h75a96 96 0 0195-85h150zm256 256a96 96 0 0196 89l0 7v405a96 96 0 01-89 96L800 939h-277a96 96 0 01-96-89L427 843v-405a96 96 0 0189-96L523 341h277zm-256-192H395a32 32 0 000 64h150a32 32 0 100-64z + M293 122v244h439V146l171 175V829a73 73 0 01-73 73h-98V536H293v366H195a73 73 0 01-73-73V195a73 73 0 0173-73h98zm366 512v268H366V634h293zm-49 49h-195v73h195v-73zm49-561v171H366V122h293z + M520 168C291 168 95 311 16 512c79 201 275 344 504 344 229 0 425-143 504-344-79-201-275-344-504-344zm0 573c-126 0-229-103-229-229s103-229 229-229c126 0 229 103 229 229s-103 229-229 229zm0-367c-76 0-137 62-137 137s62 137 137 137S657 588 657 512s-62-137-137-137z + M734 128c-33-19-74-8-93 25l-41 70c-28-6-58-9-90-9-294 0-445 298-445 298s82 149 231 236l-31 54c-19 33-8 74 25 93 33 19 74 8 93-25L759 222C778 189 767 147 734 128zM305 512c0-115 93-208 207-208 14 0 27 1 40 4l-37 64c-1 0-2 0-2 0-77 0-140 63-140 140 0 26 7 51 20 71l-37 64C324 611 305 564 305 512zM771 301 700 423c13 27 20 57 20 89 0 110-84 200-192 208l-51 89c12 1 24 2 36 2 292 0 446-298 446-298S895 388 771 301z + M469 235V107h85v128h-85zm-162-94 85 85-60 60-85-85 60-60zm469 60-85 85-60-60 85-85 60 60zm-549 183A85 85 0 01302 341H722a85 85 0 0174 42l131 225A85 85 0 01939 652V832a85 85 0 01-85 85H171a85 85 0 01-85-85v-180a85 85 0 0112-43l131-225zM722 427H302l-100 171h255l10 29a59 59 0 002 5c2 4 5 9 9 14 8 9 18 17 34 17 16 0 26-7 34-17a72 72 0 0011-18l0-0 10-29h255l-100-171zM853 683H624a155 155 0 01-12 17C593 722 560 747 512 747c-48 0-81-25-99-47a155 155 0 01-12-17H171v149h683v-149z + M719 85 388 417l-209-165L87 299v427l92 47 210-164L720 939 939 850V171zM186 610V412l104 104zm526 55L514 512l198-153z + diff --git a/src/Resources/Icons.xaml b/src/Resources/Icons.xaml deleted file mode 100644 index 1c6470cc..00000000 --- a/src/Resources/Icons.xaml +++ /dev/null @@ -1,76 +0,0 @@ - - M557.7 545.3 789.9 402.7c24-15 31.3-46.5 16.4-70.5c-14.8-23.8-46-31.2-70-16.7L506.5 456.6 277.1 315.4c-24.1-14.8-55.6-7.3-70.5 16.8c-14.8 24.1-7.3 55.6 16.8 70.5l231.8 142.6V819.1c0 28.3 22.9 51.2 51.2 51.2c28.3 0 51.2-22.9 51.2-51.2V545.3h.1zM506.5 0l443.4 256v511.9L506.5 1023.9 63.1 767.9v-511.9L506.5 0z - M491 256h469c13 0 21-9 21-21v-171c0-13-9-21-21-21h-469c-13 0-21 9-21 21V128H256V64c0-13-9-21-21-21h-171c-13 0-21 9-21 21v171c0 13 9 21 21 21H128v597h341v64c0 13 9 21 21 21h469c13 0 21-9 21-21v-171c0-13-9-21-21-21h-469c-13 0-21 9-21 21V811H171v-299h299v64c0 13 9 21 21 21h469c13 0 21-9 21-21v-171c0-13-9-21-21-21h-469c-13 0-21 9-21 21V469H171V256h64c13 0 21-9 21-21V171h213v64c0 13 9 21 21 21z - M170 470l0 84 86 0 0-84-86 0zM86 598l0-172 852 0 0 172-852 0zM256 298l0-84-86 0 0 84 86 0zM86 170l852 0 0 172-852 0 0-172zM170 726l0 84 86 0 0-84-86 0zM86 854l0-172 852 0 0 172-852 0z - M853.3 960H170.7V64h469.3l213.3 213.3zM821.3 298.7H618.7V96z - M888.8 0H135.2c-32.3 0-58.9 26.1-58.9 58.9v906.2c0 32.3 26.1 58.9 58.9 58.9h753.2c32.3 0 58.9-26.1 58.9-58.9v-906.2c.5-32.8-26.1-58.9-58.4-58.9zm-164.9 176.6c30.7 0 55.8 25.1 55.8 55.8s-25.1 55.8-55.8 55.8s-55.8-25.1-55.8-55.8s24.6-55.8 55.8-55.8zm-212 0c30.7 0 55.8 25.1 55.8 55.8S542.7 288.3 512 288.3s-55.8-25.1-55.8-55.8S481.3 176.6 512 176.6zm-212 0c30.7 0 55.8 25.1 55.8 55.8s-25.1 55.8-55.8 55.8s-55.8-25.1-55.8-55.8s25.1-55.8 55.8-55.8zm208.9 606.2H285.2c-24.6 0-44-20-44-44c0-24.6 20-44 44-44h223.7c24.6 0 44 20 44 44c0 24.1-19.5 44-44 44zm229.9-212H285.2c-24.6 0-44-20-44-44c0-24.6 20-44 44-44h453.1c24.6 0 44 20 44 44c.5 24.1-19.5 44-43.5 44z - m186 532 287 0 0 287c0 11 9 20 20 20s20-9 20-20l0-287 287 0c11 0 20-9 20-20s-9-20-20-20l-287 0 0-287c0-11-9-20-20-20s-20 9-20 20l0 287-287 0c-11 0-20 9-20 20s9 20 20 20z - M682.7 42.7H85.3v682.7h85.3V128h512V42.7zM256 213.3l4.5 768H896V213.3H256zm554.7 682.7H341.3V298.7h469.3v597.3z - M204 291c45-11 77-49 77-96c0-53-43-98-98-98c-53 0-98 45-98 98c0 47 34 87 77 96v91c0 13 9 21 21 21h236c2 38 32 68 70 68h372c41 0 73-32 73-73v-38c0-41-32-73-73-73h-370c-38 0-70 30-70 68H204V291zm258 74h2c0-15 13-30 30-30h372c15 0 30 13 30 30v38c0 15-13 30-30 30h-375c-15 0-30-13-30-30v-38zM183 250c-30 0-55-26-55-55s26-55 55-55s55 26 55 55s-26 55-55 55zM679 495c-134 0-244 109-244 244s109 244 244 244c134 0 244-109 244-244s-109-244-244-244zm159 268h-134v134h-50V764H521v-50h134v-134h50v134h134V764zM244 766H185c-13 0-23-11-23-23s11-23 23-23h59c13 0 23 11 23 23s-11 23-23 23zM368 766h-42c-9 0-17-8-17-17v-13c0-9 8-17 17-17h42c9 0 17 8 17 17v13c0 9-8 17-17 17zM183 766c-12 0-21-9-21-21V320c0-12 9-21 21-21c12 0 21 9 21 21v425c0 12-10 21-21 21z - M785 898H341c-58 0-102-44-102-102v-188c0-10 7-17 17-17s17 7 17 17v188c0 38 31 68 68 68h444c38 0 68-31 68-68v-444c0-38-31-68-68-68h-222c-10 0-17-10-17-17s7-17 17-17H785c58 0 102 44 102 102v444c0 55-44 102-102 102zM181 488h-17c-10 0-17-7-17-17s7-17 17-17h17c10 0 17 7 17 17s-10 17-17 17zM461 181c-10 0-17-10-17-17v-17c0-10 7-17 17-17s17 7 17 17v17c0 7-7 17-17 17zM375 625H283c-61 0-113-65-113-119v-68-3l-14-14c-14-14-20-31-20-48 0-17 7-34 20-44 10-10 27-17 41-17v-10c0-17 7-34 20-44 14-14 38-20 58-17 0-14 7-27 17-38 20-17 48-24 72-14 3-10 10-20 17-31 27-20 68-20 92 3l68 68c44 44 72 102 72 164 0 126-106 232-239 232zm-171-160v41c0 38 38 85 79 85H375c113 0 205-89 205-201 0-51-20-102-58-140l-68-68c-14-10-31-10-44 0-7 7-10 14-10 20s3 14 10 20c7 7 7 17 0 24-7 7-17 7-24 0l-24-20c-14-10-31-10-44 0-3 7-7 14-7 20s3 14 10 20l20 24c7 7 7 17 0 24-7 7-17 7-24 0l-34-31c-14-14-31-14-44-3-3 7-7 14-7 20s3 14 7 20L297 375c7 7 7 17 0 24-7 7-17 7-24 0l-48-48c-14-10-31-10-44 0-7 7-10 14-10 20s3 14 10 20l106 99c7 7 7 17 0 24-7 7-17 7-24 0L205 464z - - M205 500h614v60H205z - M153 154h768v768h-768v-768zm64 64v640h640v-640h-640z - M256 128l0 192L64 320l0 576 704 0 0-192 192 0L960 128 256 128zM704 832 128 832 128 384l576 0L704 832zM896 640l-128 0L768 320 320 320 320 192l576 0L896 640z - M519 459 222 162a37 37 0 10-52 52l297 297L169 809a37 37 0 1052 52l297-297 297 297a37 37 0 1052-52l-297-297 297-297a37 37 0 10-52-52L519 459z - M512 597m-1 0a1 1 0 103 0a1 1 0 10-3 0ZM810 393 732 315 448 600 293 444 214 522l156 156 78 78 362-362z - M512 0C233 0 7 223 0 500C6 258 190 64 416 64c230 0 416 200 416 448c0 53 43 96 96 96s96-43 96-96c0-283-229-512-512-512zm0 1023c279 0 505-223 512-500c-6 242-190 436-416 436c-230 0-416-200-416-448c0-53-43-96-96-96s-96 43-96 96c0 283 229 512 512 512z - M702 677 590 565a148 148 0 10-25 27L676 703zm-346-200a115 115 0 11115 115A115 115 0 01355 478z - M352 64h320L960 352v320L672 960h-320L64 672v-320L352 64zm161 363L344 256 260 341 429 512l-169 171L344 768 513 597 682 768l85-85L598 512l169-171L682 256 513 427z - M899 870l-53-306H864c14 0 26-12 26-26V346c0-14-12-26-26-26H618V138c0-14-12-26-26-26H432c-14 0-26 12-26 26v182H160c-14 0-26 12-26 26v192c0 14 12 26 26 26h18l-53 306c0 2 0 3 0 4c0 14 12 26 26 26h723c2 0 3 0 4 0c14-2 24-16 21-30zM204 390h272V182h72v208h272v104H204V390zm468 440V674c0-4-4-8-8-8h-48c-4 0-8 4-8 8v156H416V674c0-4-4-8-8-8h-48c-4 0-8 4-8 8v156H203l45-260H776l45 260H672z - M512 64C265 64 64 265 64 512s201 448 448 448s448-201 448-448S759 64 512 64zm238 642-46 46L512 558 318 750l-46-46L467 512 274 318l46-46L512 467l194-194 46 46L558 512l193 194z - M797 829a49 49 0 1049 49 49 49 0 00-49-49zm147-114A49 49 0 10992 764a49 49 0 00-49-49zM928 861a49 49 0 1049 49A49 49 0 00928 861zm-5-586L992 205 851 64l-71 71a67 67 0 00-94 0l235 235a67 67 0 000-94zm-853 128a32 32 0 00-32 50 1291 1291 0 0075 112L288 552c20 0 25 21 8 37l-93 86a1282 1282 0 00120 114l100-32c19-6 28 15 14 34l-40 55c26 19 53 36 82 53a89 89 0 00115-20 1391 1391 0 00256-485l-188-188s-306 224-595 198z - - M0 33h1024v160H0zM0 432h1024v160H0zM0 831h1024v160H0z - M1024 610v-224H640v48H256V224h128V0H0v224h128v752h512v48h384V800H640v48H256V562h384v48z - M30 271l241 0 0-241-241 0 0 241zM392 271l241 0 0-241-241 0 0 241zM753 30l0 241 241 0 0-241-241 0zM30 632l241 0 0-241-241 0 0 241zM392 632l241 0 0-241-241 0 0 241zM753 632l241 0 0-241-241 0 0 241zM30 994l241 0 0-241-241 0 0 241zM392 994l241 0 0-241-241 0 0 241zM753 994l241 0 0-241-241 0 0 241z - M509 546l271-271 91 91-348 349-1-1-13 13-363-361 91-91z - M256 224l0 115L512 544l256-205 0-115-256 205L256 224zM512 685l-256-205L256 595 512 800 768 595l0-115L512 685z - M170 831l343-342L855 831l105-105-448-448L64 726 170 831z - M768 800V685L512 480 256 685V800l256-205L768 800zM512 339 768 544V429L512 224 256 429V544l256-205z - M1231 0v372H120V0zM1352 484v56H0v-56zM1147 939H205V737h942v203M1231 1024V652H120V1024z - M75 100 27 51 76 3z - M27 3 L 75 51 L 27 100z - - M520 168C291 168 95 311 16 512c79 201 275 344 504 344 229 0 425-143 504-344-79-201-275-344-504-344zm0 573c-126 0-229-103-229-229s103-229 229-229c126 0 229 103 229 229s-103 229-229 229zm0-367c-76 0-137 62-137 137s62 137 137 137S657 588 657 512s-62-137-137-137z - M734 128c-33-19-74-8-93 25l-41 70c-28-6-58-9-90-9-294 0-445 298-445 298s82 149 231 236l-31 54c-19 33-8 74 25 93 33 19 74 8 93-25L759 222C778 189 767 147 734 128zM305 512c0-115 93-208 207-208 14 0 27 1 40 4l-37 64c-1 0-2 0-2 0-77 0-140 63-140 140 0 26 7 51 20 71l-37 64C324 611 305 564 305 512zM771 301 700 423c13 27 20 57 20 89 0 110-84 200-192 208l-51 89c12 1 24 2 36 2 292 0 446-298 446-298S895 388 771 301z - - M808 195h-592v634h592V195zm-32 602h-528V227h528v570zM429 322h246v16H429zM358 355c14 0 27-11 27-27s-11-27-27-27-27 11-27 27 13 27 27 27zm0-37c6 0 11 5 11 11 0 6-5 11-11 11s-11-5-11-11c2-6 6-11 11-11zM429 443h246v16H429zM358 478c14 0 27-11 27-27s-11-27-27-27-27 11-27 27 13 27 27 27zm0-38c6 0 11 5 11 11 0 6-5 11-11 11s-11-5-11-11c2-6 6-11 11-11zM429 565h246v16H429zM358 600c14 0 27-11 27-27s-11-27-27-27-27 11-27 27 13 27 27 27zm0-38c6 0 11 5 11 11 0 6-5 11-11 11s-11-5-11-11c2-6 6-11 11-11zM429 686h246v16H429zM358 722c14 0 27-11 27-27s-11-27-27-27-27 11-27 27 13 27 27 27zm0-37c6 0 11 5 11 11 0 6-5 11-11 11s-11-5-11-11c2-6 6-11 11-11z - M716.3 383.1c0 38.4-6.5 76-19.4 111.8l-10.7 29.7 229.6 229.5c44.5 44.6 44.5 117.1 0 161.6a113.6 113.6 0 01-80.8 33.5a113.6 113.6 0 01-80.8-33.5L529 694l-32 13a331.6 331.6 0 01-111.9 19.4A333.5 333.5 0 0150 383.1c0-39 6.8-77.2 20-113.6L285 482l194-195-214-210A331 331 0 01383.1 50A333.5 333.5 0 01716.3 383.1zM231.6 31.6l-22.9 9.9a22.2 22.2 0 00-5.9 4.2a19.5 19.5 0 000 27.5l215 215.2L288.4 417.8 77.8 207.1a26 26 0 00-17.2-7.1a22.8 22.8 0 00-21.5 15a400.5 400.5 0 00-7.5 16.6A381.6 381.6 0 000 384c0 211.7 172.2 384 384 384c44.3 0 87.6-7.5 129-22.3L743.1 975.8A163.5 163.5 0 00859.5 1024c43.9 0 85.3-17.1 116.4-48.2a164.8 164.8 0 000-233L745.5 513C760.5 471.5 768 428 768 384C768 172 596 0 384 0c-53 0-104 10.5-152.5 31.5z - M928 500a21 21 0 00-19-20L858 472a11 11 0 01-9-9c-1-6-2-13-3-19a11 11 0 015-12l46-25a21 21 0 0010-26l-8-22a21 21 0 00-24-13l-51 10a11 11 0 01-12-6c-3-6-6-11-10-17a11 11 0 011-13l34-39a21 21 0 001-28l-15-18a20 20 0 00-27-4l-45 27a11 11 0 01-13-1c-5-4-10-9-15-12a11 11 0 01-3-12l19-49a21 21 0 00-9-26l-20-12a21 21 0 00-27 6L650 193a9 9 0 01-11 3c-1-1-12-5-20-7a11 11 0 01-7-10l1-52a21 21 0 00-17-22l-23-4a21 21 0 00-24 14L532 164a11 11 0 01-11 7h-20a11 11 0 01-11-7l-17-49a21 21 0 00-24-15l-23 4a21 21 0 00-17 22l1 52a11 11 0 01-8 11c-5 2-15 6-19 7c-4 1-8 0-12-4l-33-40A21 21 0 00313 146l-20 12A21 21 0 00285 184l19 49a11 11 0 01-3 12c-5 4-10 8-15 12a11 11 0 01-13 1L228 231a21 21 0 00-27 4L186 253a21 21 0 001 28L221 320a11 11 0 011 13c-3 5-7 11-10 17a11 11 0 01-12 6l-51-10a21 21 0 00-24 13l-8 22a21 21 0 0010 26l46 25a11 11 0 015 12l0 3c-1 6-2 11-3 16a11 11 0 01-9 9l-51 8A21 21 0 0096 500v23A21 21 0 00114 544l51 8a11 11 0 019 9c1 6 2 13 3 19a11 11 0 01-5 12l-46 25a21 21 0 00-10 26l8 22a21 21 0 0024 13l51-10a11 11 0 0112 6c3 6 6 11 10 17a11 11 0 01-1 13l-34 39a21 21 0 00-1 28l15 18a20 20 0 0027 4l45-27a11 11 0 0113 1c5 4 10 9 15 12a11 11 0 013 12l-19 49a21 21 0 009 26l20 12a21 21 0 0027-6L374 832c3-3 7-5 10-4c7 3 12 5 20 7a11 11 0 018 10l-1 52a21 21 0 0017 22l23 4a21 21 0 0024-14l17-50a11 11 0 0111-7h20a11 11 0 0111 7l17 49a21 21 0 0020 15a19 19 0 004 0l23-4a21 21 0 0017-22l-1-52a11 11 0 018-10c8-3 13-5 18-7l1 0c6-2 9 0 11 3l34 41A21 21 0 00710 878l20-12a21 21 0 009-26l-18-49a11 11 0 013-12c5-4 10-8 15-12a11 11 0 0113-1l45 27a21 21 0 0027-4l15-18a21 21 0 00-1-28l-34-39a11 11 0 01-1-13c3-5 7-11 10-17a11 11 0 0112-6l51 10a21 21 0 0024-13l8-22a21 21 0 00-10-26l-46-25a11 11 0 01-5-12l0-3c1-6 2-11 3-16a11 11 0 019-9l51-8a21 21 0 0018-21v-23zm-565 188a32 32 0 01-51 5a270 270 0 011-363a32 32 0 0151 6l91 161a32 32 0 010 31zM512 782a270 270 0 01-57-6a32 32 0 01-20-47l92-160a32 32 0 0127-16h184a32 32 0 0130 41c-35 109-137 188-257 188zm15-328L436 294a32 32 0 0121-47a268 268 0 0155-6c120 0 222 79 257 188a32 32 0 01-30 41h-184a32 32 0 01-28-16z - M296 912H120c-4.4 0-8-3.6-8-8V520c0-4.4 3.6-8 8-8h176c4.4 0 8 3.6 8 8v384c0 4.4-3.6 8-8 8zM600 912H424c-4.4 0-8-3.6-8-8V121c0-4.4 3.6-8 8-8h176c4.4 0 8 3.6 8 8v783c0 4.4-3.6 8-8 8zM904 912H728c-4.4 0-8-3.6-8-8V280c0-4.4 3.6-8 8-8h176c4.4 0 8 3.6 8 8v624c0 4.4-3.6 8-8 8z - M550 627h-81v-21a142 142 0 0113-64a198 198 0 0152-57a390 390 0 0047-42a56 56 0 0012-34a58 58 0 00-21-45a81 81 0 00-56-19a85 85 0 00-57 20a103 103 0 00-32 59l-82-10a136 136 0 0149-96A172 172 0 01512 276a178 178 0 01123 40a122 122 0 0145 94a103 103 0 01-17 56a366 366 0 01-71 72A136 136 0 00556 576a128 128 0 00-6 51zm-81 120v-89h89v89zM512 64a448 448 0 10448 448A448 448 0 00512 64zm0 832a384 384 0 010-768a389 389 0 01384 384a389 389 0 01-384 384z - M64 864h896V288h-396a64 64 0 01-57-35L460 160H64v704zm-64 32V128a32 32 0 0132-32h448a32 32 0 0129 18L564 224H992a32 32 0 0132 32v640a32 32 0 01-32 32H32a32 32 0 01-32-32z - M448 64l128 128h448v768H0V64z - M832 960l192-512H192L0 960zM128 384L0 960V128h288l128 128h416v128z - M959 320H960v640A64 64 0 01896 1024H192A64 64 0 01128 960V64A64 64 0 01192 0H640v321h320L959 320zM320 544c0 17 14 32 32 32h384A32 32 0 00768 544c0-17-14-32-32-32H352A32 32 0 00320 544zm0 128c0 17 14 32 32 32h384a32 32 0 0032-32c0-17-14-32-32-32H352a32 32 0 00-32 32zm0 128c0 17 14 32 32 32h384a32 32 0 0032-32c0-17-14-32-32-32H352a32 32 0 00-32 32z - M854 307 611 73c-6-6-14-9-22-9H296c-4 0-8 4-8 8v56c0 4 4 8 8 8h277l219 211V824c0 4 4 8 8 8h56c4 0 8-4 8-8V330c0-9-4-17-10-23zM553 201c-6-6-14-9-23-9H192c-18 0-32 14-32 32v704c0 18 14 32 32 32h512c18 0 32-14 32-32V397c0-9-3-17-9-23L553 201zM568 753c0 4-3 7-8 7h-225c-4 0-8-3-8-7v-42c0-4 3-7 8-7h225c4 0 8 3 8 7v42zm0-220c0 4-3 7-8 7H476v85c0 4-3 7-7 7h-42c-4 0-7-3-7-7V540h-85c-4 0-8-3-8-7v-42c0-4 3-7 8-7H420v-85c0-4 3-7 7-7h42c4 0 7 3 7 7V484h85c4 0 8 3 8 7v42z - M683 409v204L1024 308 683 0v191c-413 0-427 526-427 526c117-229 203-307 427-307zm85 492H102V327h153s38-63 114-122H51c-28 0-51 27-51 61v697c0 34 23 61 51 61h768c28 0 51-27 51-61V614l-102 100v187z - M599 425 599 657 425 832 425 425 192 192 832 192Z - M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z - - M1024 896v128H0V704h128v192h768V704h128v192zM576 555 811 320 896 405l-384 384-384-384L213 320 448 555V0h128v555z - M432 0h160c27 0 48 21 48 48v336h175c36 0 53 43 28 68L539 757c-15 15-40 15-55 0L180 452c-25-25-7-68 28-68H384V48c0-27 21-48 48-48zm592 752v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h293l98 98c40 40 105 40 145 0l98-98H976c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z - M592 768h-160c-27 0-48-21-48-48V384h-175c-36 0-53-43-28-68L485 11c15-15 40-15 55 0l304 304c25 25 7 68-28 68H640v336c0 27-21 48-48 48zm432-16v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h272v16c0 62 50 112 112 112h160c62 0 112-50 112-112v-16h272c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z - M961 320 512 577 63 320 512 62l449 258zM512 628 185 442 63 512 512 770 961 512l-123-70L512 628zM512 821 185 634 63 704 512 962l449-258L839 634 512 821z - M144 112h736c18 0 32 14 32 32v736c0 18-14 32-32 32H144c-18 0-32-14-32-32V144c0-18 14-32 32-32zm112 211v72a9 9 0 003 7L386 509 259 615a9 9 0 00-3 7v72a9 9 0 0015 7L493 516a9 9 0 000-14l-222-186a9 9 0 00-15 7zM522 624a10 10 0 00-10 10v60a10 10 0 0010 10h237a10 10 0 0010-10v-60a10 10 0 00-10-10H522z - M509 556l93 149h124l-80-79 49-50 165 164-165 163-49-50 79-79h-163l-96-153 41-65zm187-395 165 164-165 163-49-50L726 360H530l-136 224H140v-70h215l136-224h236l-80-79 49-50z - - M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688 - M0 586l404 119 498-410-386 441-2 251 155-205 279 83L1170 37z - M24 512A488 488 0 01512 24A488 488 0 011000 512A488 488 0 01512 1000A488 488 0 0124 512zm447-325v327L243 619l51 111 300-138V187H471z - M715 254h-405l-58 57h520zm-492 86v201h578V340zm405 143h-29v-29H425v29h-29v-57h231v57zm-405 295h578V559H223zm174-133h231v57h-29v-29H425v29h-29v-57z - M869 145a145 145 0 10-289 0c0 56 33 107 83 131c-5 96-77 128-201 175c-52 20-110 42-160 74V276A144 144 0 00242 0a145 145 0 00-145 145c0 58 35 108 84 131v461a144 144 0 00-84 131a145 145 0 10289 0a144 144 0 00-84-131c5-95 77-128 201-175c122-46 274-103 280-287a145 145 0 0085-132zM242 61a83 83 0 110 167a83 83 0 010-167zm0 891a84 84 0 110-167a84 84 0 010 167zM724 228a84 84 0 110-167a84 84 0 010 167z - M896 128h-64V64c0-35-29-64-64-64s-64 29-64 64v64h-64c-35 0-64 29-64 64s29 64 64 64h64v64c0 35 29 64 64 64s64-29 64-64V256h64c35 0 64-29 64-64s-29-64-64-64zm-204 307C673 481 628 512 576 512H448c-47 0-90 13-128 35V372C394 346 448 275 448 192c0-106-86-192-192-192S64 86 64 192c0 83 54 154 128 180v280c-74 26-128 97-128 180c0 106 86 192 192 192s192-86 192-192c0-67-34-125-84-159c22-20 52-33 84-33h128c122 0 223-85 249-199c-19 4-37 7-57 7c-26 0-51-5-76-13zM256 128c35 0 64 29 64 64s-29 64-64 64s-64-29-64-64s29-64 64-64zm0 768c-35 0-64-29-64-64s29-64 64-64s64 29 64 64s-29 64-64 64z - M902 479v-1c0-133-112-242-250-242c-106 0-196 64-232 154c-28-20-62-32-100-32c-76 0-140 49-160 116c-52 37-86 97-86 165c0 112 90 202 202 202h503c112 0 202-90 202-202c0-65-31-123-79-160z - M364 512h67v108h108v67h-108v108h-67v-108h-108v-67h108v-108zm298-64A107 107 0 01768 555C768 614 720 660 660 660h-108v-54h-108v-108h-94v108h-94c4-21 22-47 44-51l-1-12a75 75 0 0171-75a128 128 0 01239-7a106 106 0 0153-14z - M177 156c-22 5-33 17-36 37c-10 57-33 258-13 278l445 445c23 23 61 23 84 0l246-246c23-23 23-61 0-84l-445-445C437 120 231 145 177 156zM331 344c-26 26-69 26-95 0c-26-26-26-69 0-95s69-26 95 0C357 276 357 318 331 344z - M683 537h-144v-142h-142V283H239a44 44 0 00-41 41v171a56 56 0 0014 34l321 321a41 41 0 0058 0l174-174a41 41 0 000-58zm-341-109a41 41 0 110-58a41 41 0 010 58zM649 284V142h-69v142h-142v68h142v142h69v-142h142v-68h-142z - - M800 928l-512 0 0-704 224 0 0 292 113-86 111 86 0-292 128 0 0 640c0 35-29 64-64 64zM625 388l-81 64 0-260 160 0 0 260-79-64zM192 160l0 32c0 18 14 32 32 32l32 0 0 704-32 0c-35 0-64-29-64-64l0-704c0-35 29-64 64-64l576 0c24 0 44 13 55 32l-631 0c-18 0-32 14-32 32z - M719 85 388 417l-209-165L87 299v427l92 47 210-164L720 939 939 850V171zM186 610V412l104 104zm526 55L514 512l198-153z - M426.7 554.7v-85.3h341.3v85.3h-341.3m0 256v-85.3h170.7v85.3h-170.7m0-512V213.3h512v85.3H426.7M256 725.3h106.7L213.3 874.7 64 725.3H170.7V298.7H64L213.3 149.3 362.7 298.7H256v426.7z - M854 170c-189-189-495-189-684 0s-189 495 0 684 495 189 684 0 187-495 0-684zM213 706c-89-137-74-325 48-444 122-122 307-137 444-48L213 706zm106 105 493-493c89 137 74 325-48 444-120 122-307 137-444 48z - M469 235V107h85v128h-85zm-162-94 85 85-60 60-85-85 60-60zm469 60-85 85-60-60 85-85 60 60zm-549 183A85 85 0 01302 341H722a85 85 0 0174 42l131 225A85 85 0 01939 652V832a85 85 0 01-85 85H171a85 85 0 01-85-85v-180a85 85 0 0112-43l131-225zM722 427H302l-100 171h255l10 29a59 59 0 002 5c2 4 5 9 9 14 8 9 18 17 34 17 16 0 26-7 34-17a72 72 0 0011-18l0-0 10-29h255l-100-171zM853 683H624a155 155 0 01-12 17C593 722 560 747 512 747c-48 0-81-25-99-47a155 155 0 01-12-17H171v149h683v-149z - \ No newline at end of file diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml new file mode 100644 index 00000000..69844221 --- /dev/null +++ b/src/Resources/Locales/en_US.axaml @@ -0,0 +1,523 @@ + + START + SURE + SAVE + CLOSE + CANCEL + Reveal in File Explorer + Save As ... + Save File to ... + Copy Path + Bytes + FILTER + Optional. + SELECT FOLDER + NOTICE + Open With ... + Running. Please wait ... + Warnning + + Copy + Paste + Cut + + Refresh + + Name : + URL : + Git Repository URL + + SSH Private Key : + Private SSH key store path + + About + Copyright © 2023-2024 sourcegit-scm + • Build with + • TextEditor from + • Monospace fonts come from + + Patch + Apply Patch + Patch File : + Select .patch file to apply + Whitespace : + Ignore whitespace changes + No Warn + Turns off the trailing whitespace warning + Warn + Outputs warnings for a few such errors, but applies + Error + Raise errors and refuses to apply the patch + Error All + Similar to 'error', but shows more + + Archive ... + Archive + Revision : + Save Archive To : + Select archive file path + + Blame + + SUBMODULES + Add Submodule + Relative Path : + Relative foler to store this module. + Fetch nested submodules + Open Submodule Repository + Copy Relative Path + Delete Submodule + + Checkout Branch + Target : + + Cherry-Pick This Commit + Cherry Pick + Commit : + Commit all changes + + Clone Remote Repository + Repository URL : + Parent Folder : + Local Name : + Repository name. Optional. + Extra Parameters : + Additional arguments to clone repository. Optional. + + INFORMATION + AUTHOR + COMMITTER + SHA + PARENTS + REFS + MESSAGE + CHANGED + CHANGES + Search Files ... + FILES + LFS File + Submodule + Tag + Tree + + Configure + User Name + User name for this repository + Email Address + Email address + HTTP Proxy + HTTP proxy used by this repository + + Create Branch + Create Local Branch + Based On : + New Branch Name : + Enter branch name. + Local Changes : + Stash & Reapply + Discard + Check out after created + + Create Tag + New Tag At : + Tag Name : + Recommanded format :v1.0.0-alpha + Tag Message : + Optional. + + Open In File Browser + Open In Visual Studio Code + Open In Git Bash + Refresh + Search Commit (Ctrl+F) + Search Author/Committer/Message/SHA + Statistics + Cleanup(GC & Prune) + Run `gc` command and do `lfs prune` if LFS is installed. + Configure this repository + WORKSPACE + LOCAL BRANCHES + NEW BRANCH + REMOTES + ADD REMOTE + TAGS + NEW TAG + SUBMODULES + ADD SUBMODULE + UPDATE SUBMODULE + SUBTREES + ADD/LINK SUBTREE + RESOLVE + CONTINUE + ABORT + + GIT FLOW + Initialize Git-Flow + Production Branch : + Development Branch : + Feature : + Release : + Hotfix : + Feature Prefix : + Release Prefix : + Hotfix Prefix : + Version Tag Prefix : + Start Feature ... + Start Release ... + Start Hotfix ... + FLOW - Start Feature + FLOW - Start Release + FLOW - Start Hotfix + Enter name + FLOW - Finish Feature + FLOW - Finish Release + FLOW - Finish Hotfix + Target : + Keep branch + + Bookmark + Open + Explore in File Manager + + Push '{0}' + Discard all changes + Fast-Forward to '{0}' + Pull '{0}' + Pull '{0}' into '{1}' + Checkout '{0}' + Merge '{0}' into '{1}' + Rebase '{0}' on '{1}' + Git Flow - Finish '{0}' + Rename '{0}' + Delete '{0}' + Tracking ... + Copy Branch Name + Unset Upstream + + Fetch ... + Prune + Target : + Edit ... + Delete ... + Copy URL + + Reset '{0}' to Here + Rebase '{0}' to Here + Cherry-Pick This Commit + Reword + Squash Into Parent + Revert Commit + Save as Patch ... + Copy SHA + + Push '{0}' + Delete '{0}' + Copy Tag Name + + Apply + Pop + Drop + + Unstage + Stage... + Discard... + Stash... + Unstage {0} files + Stage {0} files... + Discard {0} files... + Stash {0} files... + Save As Patch... + Assume unchaged + + Delete Branch + Branch : + + Delete Remote + Remote : + + Delete Tag + Tag : + Delete from remote repositories + + Delete Submodule + Submodule Path : + + Next Difference + Previous Difference + Toggle One-Side/Two-Sides + Open With Merge Tool + SELECT FILE TO VIEW CHANGES + NO CHANGES OR ONLY EOL CHANGES + BINARY DIFF + OLD + NEW + LFS OBJECT CHANGE + Copy + + Discard Changes + Changes : + You can't undo this action!!! + All local changes in working copy. + Total {0} changes will be discard + + Fetch + Fetch Remote Changes + Remote : + Fetch all remotes + Prune remote dead branches + + Fast-Forward (without checkout) + + File History + USE THIS VERSION + + CHANGE DISPLAY MODE + Show as Grid + Show as List + Show as Tree + + SELECT FOLDER + SELECTED : + + Histories + SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT + CLEAR + Switch Curve/Polyline Graph Mode + Switch Horizontal/Vertical Layout + SELECTED {0} COMMITS + + Initialize Repository + Path : + Invalid repository detected. Run `git init` under this path? + + Source Git + Open Main Menu + ERROR + NOTICE + + Create New Page (Ctrl+T) + Repositories + Close Tab + Close Other Tabs + Close Tabs to the Right + Bookmark + Copy Repository Path + + Merge Branch + Source Branch : + Into : + Merge Option : + + Open Repository + Open In Git Bash + Clone Repository + Edit + Create Group + Create Sub-Group + Delete + Search Repositories ... + Sort + DRAG & DROP FOLDER SUPPORTED + + Edit Selected Group + Edit Selected Repository + Target : + New Name : + Bookmark : + + Confirm Deleting Group + Confirm Deleting Repository + Target : + + Pull + Pull (Fetch & Merge) + Remote : + Branch : + Into : + Use rebase instead of merge + Stash & reapply local changes + + Push + Push Changes To Remote + Local Branch : + Remote : + Remote Branch : + Push all tags + Force push + + Push Tag To Remote + Tag : + Remote : + + Rebase Current Branch + Rebase : + On : + Stash & reapply local changes + + Add Remote + Edit Remote + Name : + Remote name + Repository URL : + Remote git repository URL + + Rename Branch + Branch : + New Name : + Unique name for this branch + + Reset Current Branch To Revision + Current Branch : + Move To : + Reset Mode : + + Revert Commit + Commit : + Commit revert changes + + Preference + GENERAL + Language + Theme + History Commits + Restore windows + Use macOS style titlebar + GIT + Install Path + Git version + Default Clone Dir + User Name + Global git user name + User Email + Global git user email + Enable Auto CRLF + Fetch remotes automatically (need restart) + GPG SIGNING + Commit GPG signing + Install Path + Input path for installed gpg program + User Signing Key + User's gpg signing key + MERGE + Merger + Install Path + Input path for merge tool + Merge Command + Diff Command + + Stash + Stash Local Changes + Message : + Optional. Name of this stash + Include untracked files + + Stashes + STASHES + CHANGES + + Drop Stash + Drop : + + COMMIT : {0} -> {1} + + Changes + UNSTAGED + VIEW ASSUME UNCHANGED + STAGE + STAGE ALL + STAGED + UNSTAGE + UNSTAGE ALL + CONFLICTS DETECTED + USE THEIRS + USE MINE + OPEN MERGE + Enter commit message + MESSAGE HISTORIES + Amend + COMMIT + CTRL + Enter + COMMIT & PUSH + NO RECENT INPUT MESSAGES + RECENT INPUT MESSAGES + INCLUDE UNTRACKED FILES + + Conflict detected! Press 'Abort' to restore original HEAD + + Clear Stashes + You are trying to clear all stashes. Are you sure to continue? + + Reword Commit Message + On : + Message : + + Squash HEAD Into Parent + HEAD : + To : + Reword : + + Statistics + WEEK + MONTH + YEAR + Total Committers: {0} + Total Commits:{0} + COMMITTER + COMMITS + + FILES ASSUME UNCHANGED + REMOVE + NO FILES ASSUMED AS UNCHANGED + + SUN + MON + TUE + WED + THU + FRI + SAT + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + By Name + By Recently Opened + By Bookmark Color + + Git has NOT been configured. Please to go [Preference] and configure it first. + Path[{0}] not exists! + Can NOT locate bash.exe. Make sure bash.exe exists under the same folder with git.exe + BINARY FILE NOT SUPPORTED!!! + BLAME ON THIS FILE IS NOT SUPPORTED!!! + GIT_DIR for this repository NOT FOUND! + Initialize Git-flow failed! + Bad git-flow branch type! + EXISTS and FULL ACCESS CONTROL needed + Remote git URL not supported + Bad local repository name + Remote name can NOT be null + Bad name for remote. Regex: ^[\\w\\-\\.]+$ + Duplicated remote name! + Branch name can NOT be null + Bad name for branch. Regex: ^[\\w\\-/\\.]+$ + Duplicated branch name! + Commit message can NOT be empty + Invalid path for patch file + Invalid relative path + Invalid path for archive file + This field is required + You are removing repository '{0}'. Are you sure to continue? + Patch has been saved successfully! + diff --git a/src/Resources/Locales/en_US.xaml b/src/Resources/Locales/en_US.xaml deleted file mode 100644 index 1575c486..00000000 --- a/src/Resources/Locales/en_US.xaml +++ /dev/null @@ -1,549 +0,0 @@ - - START - SURE - SAVE - CLOSE - CANCEL - CLICK TO GO - Reveal in File Explorer - Save As ... - Save File to ... - Copy Path - {0} Bytes - FILTER - Optional. - SELECT FOLDER - NOTICE - Open With ... - - URL : - Git Repository URL - Parent Folder : - Relative foler to store this module. Optional. - - SSH Private Key : - Private SSH key store path - - About - SourceGit - OPEN SOURCE GIT CLIENT - - Patch - Apply Patch - Patch File : - Select .patch file to apply - Whitespace : - Ignore whitespace changes - No Warn - Turns off the trailing whitespace warning - Warn - Outputs warnings for a few such errors, but applies - Error - Raise errors and refuses to apply the patch - Error All - Similar to 'error', but shows more - - Archive ... - Archive - Revision : - Save Archive To : - Select archive file path - - Blame - - SUBMODULES - Add Submodule - Fetch nested submodules - Open Submodule Repository - Copy Relative Path - Delete Submodule - - Cherry-Pick This Commit - Cherry Pick - Commit : - Commit all changes - - Clone Remote Repository - Repository URL : - Git Repository URL - Parent Folder : - Folder to contain this repository - Local Name : - Repository name. Optional. - Remote Name : - Remote name. Optional. - Extra Parameters : - Additional arguments to clone repository. Optional. - - INFORMATION - AUTHOR - COMMITTER - SHA - PARENTS - REFS - MESSAGE - CHANGED - CHANGES - Search Files ... - FILES - - Configure - User Name - User name for this repository - Email Address - Email address - HTTP Proxy - HTTP proxy used by this repository - - Create Branch - Create Local Branch - Based On : - New Branch Name : - Enter branch name. - Local Changes : - Stash & Reapply - Discard - Check out after created - Git do not hold any branch until you do first commit. - - Create Tag - New Tag At : - Tag Name : - Recommanded format :v1.0.0-alpha - Tag Message : - Optional. - - Open In File Browser - Open In Visual Studio Code - Open In Git Bash - Refresh - Search Commit - Statistics - Cleanup(GC & Prune) - Configure this repository - WORKSPACE - LOCAL BRANCHES - NEW BRANCH - REMOTES - ADD REMOTE - TAGS - NEW TAG - SUBMODULES - ADD SUBMODULE - UPDATE SUBMODULE - SUBTREES - ADD/LINK SUBTREE - RESOLVE - CONTINUE - ABORT - - GIT FLOW - Initialize Git-Flow - Production Branch : - Development Branch : - Feature : - Release : - Hotfix : - Feature Prefix : - Release Prefix : - Hotfix Prefix : - Version Tag Prefix : - Start Feature ... - Start Release ... - Start Hotfix ... - GIT FLOW - Start Feature - GIT FLOW - Start Release - GIT FLOW - Start Hotfix - Enter name - GIT FLOW - Finish Feature - GIT FLOW - Finish Release - GIT FLOW - Finish Hotfix - {0} branch name is required. - {0} branch name contains invalid characters. - {0} prefix is required. - {0} contains invalid characters. - Development branch is same with production! - Keep branch - - Bookmark - Open - Open Container Folder - - Push '{0}' - Discard all changes - Fast-Forward to '{0}' - Pull '{0}' - Pull '{0}' into '{1}' - Checkout '{0}' - Merge '{0}' into '{1}' - Rebase '{0}' on '{1}' - Git Flow - Finish '{0}' - Rename '{0}' - Delete '{0}' - Tracking ... - Copy Branch Name - Unset Upstream - - Fetch ... - Prune - Edit ... - Delete ... - Copy URL - - Reset '{0}' to Here - Rebase '{0}' to Here - Cherry-Pick This Commit - Reword - Squash Into Parent - Revert Commit - Save as Patch ... - Copy Commit SHA - Copy Commit Info - - Push '{0}' - Delete '{0}' - Copy Tag Name - - Apply - Pop - Drop - - Unstage - Stage... - Discard... - Stash... - Unstage {0} files - Stage {0} files... - Discard {0} files... - Stash {0} files... - Save As Patch... - Assume unchaged - - Confirm To Delete Branch - Branch : - - Confirm To Delete Remote - Remote : - - Confirm To Delete Tag - Tag : - Delete from remote repositories - - Confirm To Delete Submodule - Submodule Path : - - Next Difference - Previous Difference - Toggle One-Side/Two-Sides - Open With Merge Tool - SELECT FILE TO VIEW CHANGES - NO CHANGES OR ONLY EOL CHANGES - BINARY DIFF - OLD : - NEW : - LFS OBJECT CHANGE - Copy - - Confirm To Discard Changes - Changes : - You can't undo this action!!! - All local changes in working copy. - Total {0} changes will be discard - - Fetch - Fetch Remote Changes - Remote : - Fetch all remotes - Prune remote dead branches - - File History - USE THIS VERSION - - CHANGE DISPLAY MODE - Show as Grid - Show as List - Show as Tree - - SELECT FOLDER - SELECTED : - - Histories - SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT - CLEAR - Switch Curve/Polyline Graph Mode - Switch Horizontal/Vertical Layout - SELECTED {0} COMMITS - - Initialize Repository - Path : - Invalid repository detected. Run `git init` under this path? - - Source Git - Open Main Menu - ERROR - - NEW PAGE - Repositories - WELCOME PAGE - Close Tab - Close Other Tabs - Close Tabs to the Right - Bookmark - Copy Path - - Merge Branch - Source Branch : - Into : - Merge Option : - - Open Repository - Open Terminal - Clone Repository - REPOSITORIES - Delete - Search Repositories ... - Sort - DRAG & DROP FOLDER HERE - Edit Display Name - Display Name : - - Pull - Pull (Fetch & Merge) - Remote : - Branch : - Into : - Use rebase instead of merge - Stash & reapply local changes - - Push - Push Changes To Remote - Local Branch : - Remote : - Remote Branch : - Push all tags - Force push - - Push Tag To Remote - Tag : - Remote : - - Rebase Current Branch - Rebase : - On : - Stash & reapply local changes - - Add Remote - Edit Remote - Name : - Remote name - Repository URL : - Remote git repository URL - - Rename Branch - Branch : - New Name : - Unique name for this branch - - Reset Current Branch To Revision - Current Branch : - Move To : - Reset Mode : - - Confirm To Revert Commit - Commit : - Commit revert changes - - Preference - GENERAL - Display Language - Window Font - Content Font - Use dark theme - Restore windows - Max History Commits - GIT - Install Path - Input path for git.exe - Git version - Default Clone Dir - Default path to clone repo into - User Name - Global git user name - User Email - Global git user email - Enable Auto CRLF - Fetch remotes automatically (need restart) - MERGE - Merger - Install Path - Input path for merge tool - Select Git Executable File - Select {0} Install Path - - Stash - Stash Local Changes - Message : - Optional. Name of this stash - Include untracked files - - Stashes - STASHES - CHANGES - - Confirm To Drop Stash - Drop : - - COMMIT : {0} -> {1} - - Changes - UNSTAGED - VIEW ASSUME UNCHANGED - STAGE - STAGE ALL - STAGED - UNSTAGE - UNSTAGE ALL - CONFLICTS DETECTED - USE THEIRS - USE MINE - OPEN MERGE - Enter commit message - MESSAGE HISTORIES - Amend - COMMIT - CTRL + Enter - COMMIT & PUSH - NO RECENT INPUT MESSAGES - RECENT INPUT MESSAGES - INCLUDE UNTRACKED FILES - - Cherry-Pick merge request detected! Press 'Abort' to restore original HEAD - Rebase merge request detected! Press 'Abort' to restore original HEAD - Revert merge request detected! Press 'Abort' to restore original HEAD - Merge request detected! Press 'Abort' to restore original HEAD - - NOTICE - Restart required to apply changes in preference. Restart now? - - Add/Link SubTree - Source URL : - Branch/Commit : - Local Relative Path : - Squash commits? - - Edit SubTree - Source URL : - Local Relative Path : - - Unlink SubTree - Local Relative Path : - This will only remove links. - - Pull Changes Of SubTree - Push Changes Of SubTree - Local Relative Path : - Remote : - Branch : - Squash commits? - - Edit ... - Unlink ... - Pull ... - Push ... - - HOTKEYS - KEY - DESCRIPTION - Create a new page - Close current active page - Switch to next page - Switch to page at given index - Toggle search bar if possible - Reload current repository if possible - Stage or unstage selected files - Close current popup panel - - Reword Commit Message - On : - Message : - - Squash HEAD Into Parent - HEAD : - To : - Reword : - - Statistics - WEEK - MONTH - YEAR - Total Committers: {0} - Total Commits:{0} - COMMITTER - COMMITS - - FILES ASSUME UNCHANGED - REMOVE - NO FILES ASSUMED AS UNCHANGED - - SUN - MON - TUE - WED - THU - FRI - SAT - - Jan - Feb - Mar - Apr - May - Jun - Jul - Aug - Sep - Oct - Nov - Dec - - By Name - By Recently Opened - By Bookmark Color - - GPG SIGNING - Commit GPG signing - Install Path - Input path for installed gpg program - User Signing Key - User's gpg signing key - - Git has NOT been configured. Please to go [Preference] and configure it first. - Path[{0}] not exists! - Can NOT locate bash.exe. Make sure bash.exe exists under the same folder with git.exe - BINARY FILE NOT SUPPORTED!!! - BLAME ON THIS FILE IS NOT SUPPORTED!!! - GIT_DIR for this repository NOT FOUND! - Initialize Git-flow failed! - Bad git-flow branch type! - EXISTS and FULL ACCESS CONTROL needed - Remote git URL not supported - Bad local repository name - Remote name can NOT be null - Bad name for remote. Regex: ^[\\w\\-\\.]+$ - Duplicated remote name! - Branch name can NOT be null - Bad name for branch. Regex: ^[\\w\\-/\\.]+$ - Duplicated branch name! - Tag name can NOT be null - Bad name for tag. Regex: ^[\\w\\-\\.]+$ - Duplicated tag name! - Commit message can NOT be empty - Invalid path for patch file - Invalid relative path - Invalid path for archive file - This field is required - You are removing repository '{0}'. Are you sure to continue? - You are trying to clear all stashes. Are you sure to continue? - Patch has been saved successfully! - \ No newline at end of file diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml new file mode 100644 index 00000000..b733a23d --- /dev/null +++ b/src/Resources/Locales/zh_CN.axaml @@ -0,0 +1,522 @@ + + 开 始 + 确 定 + 保 存 + 关闭 + 取 消 + 在文件浏览器中查看 + 另存为... + 复制路径 + 字节 + 过滤 + 选填 + 选择文件夹 + 系统提示 + 打开文件... + 执行操作中,请耐心等待... + 警告 + + 复制 + 粘贴 + 剪切 + + 重新加载 + + 名称 : + 仓库地址 : + 远程仓库地址 + + SSH密钥 : + SSH密钥文件 + + 关于软件 + Copyright © 2023-2024 sourcegit-scm + • 使用的框架为 + • 文本编辑器使用 + • 等宽字体来自于 + + 补丁 + 应用补丁 + 补丁文件 : + 选择补丁文件 + 空白符号处理 : + 忽略空白符号 + 忽略 + 关闭所有警告 + 警告 + 应用补丁,输出关于空白符的警告 + 错误 + 输出错误,并终止应用补丁 + 更多错误 + 与【错误】级别相似,但输出内容更多 + + 存档 ... + 存档 + 指定的提交: + 存档文件路径: + 选择存档文件的存放路径 + + 逐行追溯 + + 子模块 + 添加子模块 + 相对仓库路径 : + 本地存放的相对路径 + 拉取子孙模块 + 打开仓库 + 复制路径 + 删除子模块 + + 检出分支 + 目标分支 : + + 挑选此提交 + 挑选提交 + 提交ID : + 提交变化 + + 克隆远程仓库 + 远程仓库 : + 父级目录 : + 本地仓库名 : + 本地仓库目录的名字,选填 + 额外参数 : + 其他克隆参数,选填 + + 基本信息 + 修改者 + 提交者 + 提交指纹 + 父提交 + 相关引用 + 提交信息 + 变更列表 + 变更对比 + 查找文件... + 文件列表 + LFS文件 + 子模块 + 标签文件 + 子树 + + 仓库配置 + 用户名 + 应用于本仓库的用户名 + 电子邮箱 + 邮箱地址 + HTTP代理 + HTTP网络代理 + + 新建分支 + 创建本地分支 + 新分支基于 : + 新分支名 : + 填写分支名称 + 未提交更改 : + 贮藏并自动恢复 + 忽略 + 完成后切换到新分支 + + 新建标签 + 标签位于 : + 标签名 : + 推荐格式 :v1.0.0-alpha + 标签描述 : + 选填 + + 在文件浏览器中打开 + 在Visual Studio Code中打开 + 在GIT终端中打开 + 重新加载 + 查找提交(Ctrl+F) + 支持搜索作者/提交者/主题/指纹 + 提交统计 + 清理本仓库(GC) + 本操作将执行`gc`,对于启用LFS的仓库也会执行`lfs prune` + 配置本仓库 + 工作区 + 本地分支 + 新建分支 + 远程列表 + 添加远程 + 标签列表 + 新建标签 + 子模块列表 + 添加子模块 + 更新子模块 + 子树列表 + 添加子树 + 解决冲突 + 下一步 + 终止冲突解决 + + GIT工作流 + 初始化GIT工作流 + 发布分支 : + 开发分支 : + 特性分支 : + 版本分支 : + 修复分支 : + 特性分支名前缀 : + 版本分支名前缀 : + 修复分支名前缀 : + 版本标签前缀 : + 开始特性分支... + 开始版本分支... + 开始修复分支... + 开始特性分支 + 开始版本分支 + 开始修复分支 + 输入分支名 + 结束特性分支 + 结束版本分支 + 结束修复分支 + 目标分支 : + 保留分支 + + 书签 + 打开 + 在浏览器中查看 + + 推送 '{0}' + 放弃所有更改 + 快进到 '{0}' + 拉回 '{0}' + 拉回 '{0}' 内容至 '{1}' + 检出 '{0}' + 合并 '{0}' 到 '{1}' + 变基 '{0}' 分支至 '{1}' + GIT工作流 - 完成 '{0}' + 重命名 '{0}' + 删除 '{0}' + 切换上游分支... + 复制分支名 + 取消追踪 + + 拉取更新 ... + 清理远程已删除分支 + 目标 : + 编辑 ... + 删除 ... + 复制远程地址 + + 重置 '{0}' 到此处 + 变基 '{0}' 到此处 + 挑选此提交 + 编辑提交信息 + 合并此提交到上一个提交 + 回滚此提交 + 另存为补丁 ... + 复制提交指纹 + + 推送 '{0}' + 删除 '{0}' + 复制标签名 + + 应用 + 应用并删除 + 删除 + + 从暂存中移除 + 暂存... + 放弃更改... + 贮藏... + 从暂存中移除 {0} 个文件 + 暂存 {0} 个文件... + 放弃 {0} 个文件的更改... + 贮藏选中的 {0} 个文件... + 另存为补丁... + 不跟踪此文件的更改 + + 确定要删除此分支吗? + 分支名 : + + 确定要移除该远程吗? + 远程名 : + + 确定要移除该标签吗? + 标签名 : + 同时删除远程仓库中的此标签 + + 确定要移除该子模块吗? + 子模块路径 : + + 下一个差异 + 上一个差异 + 切换显示模式 + 使用外部合并工具查看 + 请选择需要对比的文件 + 没有变更或仅有换行符差异 + 二进制文件 + 原始大小 + 当前大小 + LFS对象变更 + 复制 + + 放弃更改确认 + 需要放弃的变更 : + 本操作不支持回退,请确认后继续!!! + 所有本地址未提交的修改 + 总计{0}项选中更改 + + 拉取 + 拉取远程仓库内容 + 远程仓库 : + 拉取所有的远程仓库 + 自动清理远程已删除分支 + + 快进(无需Checkout) + + 文件历史 + 使用该版本 + + 切换变更显示模式 + 网格模式 + 列表模式 + 树形模式 + + 选择目录... + 当前选择 : + + 历史记录 + 查询提交指纹、信息、作者。回车键开始,ESC键取消 + 清空 + 切换曲线/折线显示 + 切换横向/纵向显示 + 已选中 {0} 项提交 + + 初始化新仓库 + 路径 : + 点击【确定】将在此目录执行`git init`操作 + + Source Git + 主菜单 + 出错了 + 系统提示 + + 新建空白页 (Ctrl+T) + 新标签页 + 关闭标签页 (Ctrl+W) + 关闭其他标签页 + 关闭右侧标签页 + 设置书签 + 复制仓库路径 + + 合并分支 + 合并分支 : + 目标分支 : + 合并方式 : + + 打开本地仓库 + 打开GIT终端 + 克隆远程仓库 + 编辑 + 新建分组 + 新建子分组 + 删除 + 快速查找仓库 + 排序 + 支持拖放目录添加 + + 编辑分组 + 编辑仓库 + 目标 : + 名称 : + 书签 : + + 删除分组确认 + 删除仓库确认 + 目标 : + + 拉回 + 拉回(拉取并合并) + 远程 : + 拉取分支 : + 本地分支 : + 使用变基方式合并分支 + 自动贮藏并恢复本地变更 + + 推送 + 推送到远程仓库 + 本地分支 : + 远程仓库 : + 远程分支 : + 同时推送标签 + 启用强制推送 + + 推送标签到远程仓库 + 标签 : + 远程仓库 : + + 变基操作 + 分支 : + 目标提交 : + 自动贮藏并恢复本地变更 + + 添加远程仓库 + 编辑远程仓库 + 远程名 : + 唯一远程名 + 仓库地址 : + 远程仓库的地址 + + 分支重命名 + 分支 : + 新的名称 : + 新的分支名不能与现有分支名相同 + + 重置当前分支到指定版本 + 当前分支 : + 提交 : + 重置模式 : + + 确定要回滚吗? + 目标提交 : + 回滚后提交更改 + + 偏好设置 + 通用配置 + 显示语言 + 主题 + 最大历史提交数 + 启动时恢复上次打开的仓库 + 使用macOS风格的标题栏 + GIT配置 + 安装路径 + Git 版本 + 默认克隆路径 + 用户名 + 默认GIT用户名 + 邮箱 + 默认GIT用户邮箱 + 自动换行转换 + 启用定时自动拉取远程更新(重启生效) + GPG签名 + 启用提交签名 + 可执行文件位置 + gpg.exe所在路径 + 用户签名KEY + 输入签名提交所使用的KEY + 外部合并工具 + 工具 + 安装路径 + 填写工具可执行文件所在位置 + 合并模式启动参数 + 对比模式启动参数 + + 贮藏 + 贮藏本地变更 + 信息 : + 选填,用于命名此贮藏 + 包含未跟踪的文件 + + 贮藏列表 + 贮藏列表 + 查看变更 + + 丢弃贮藏确认 + 丢弃贮藏 : + + 对比提交 : {0} -> {1} + + 本地更改 + 未暂存 + 查看忽略变更文件 + 暂存选中 + 暂存所有 + 已暂存 + 从暂存区移除选中 + 从暂存区移除所有 + 检测到冲突 + 使用THEIRS + 使用MINE + 打开合并工具 + 填写提交信息 + 历史提交信息 + 修补 + 提交 + CTRL + Enter + 提交并推送 + 没有提交信息记录 + 最近输入的提交信息 + 显示未跟踪文件 + + 检测到本地冲突! + + 丢弃贮藏确认 + 您正在丢弃所有的贮藏,一经操作,无法回退,是否继续? + + 编辑提交信息 + 提交: + 提交信息: + + 合并HEAD到上一个提交 + 当前提交 : + 合并到 : + 修改提交信息: + + 提交统计 + 本周 + 本月 + 本年 + 提交者人数:{0} + 总计提交次数:{0} + 提交者 + 提交次数 + + 不跟踪更改的文件 + 移除 + 没有不跟踪更改的文件 + + 星期日 + 星期一 + 星期二 + 星期三 + 星期四 + 星期五 + 星期六 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 按名称升序 + 按最近访问 + 按书签颜色 + + GIT尚未配置。请打开【偏好设置】配置GIT路径。 + 路径({0})不存在或不可读取! + 无法找到bash.exe,请确保其在git.exe同目录中! + 二进制文件不支持该操作!!! + 选中文件不支持该操作!!! + 获取仓库GIT_DIR失败! + 初始化GIT FLOW失败! + 不支持的GIT FLOW分支! + 目录不存在或不可写!!! + 非法的远程仓库地址! + 非法的本地仓库地址! + 远程仓库地址不可为空 + 远程仓库地址包含非法字符!仅支持字母、数字、下划线、横线或英文点号! + 远程仓库名已存在! + 分支名不可为空 + 分支名包含非法字符!仅支持字母、数字、下划线、横线或英文点号! + 分支名已存在! + 提交信息未填写! + 补丁文件不存在或不可访问! + 非法的子路径! + 非法的存档文件路径! + 内容未填写! + 正在将 '{0}' 从列表中移除,是否要继续? + 补丁已成功保存! + diff --git a/src/Resources/Locales/zh_CN.xaml b/src/Resources/Locales/zh_CN.xaml deleted file mode 100644 index 123f9cc0..00000000 --- a/src/Resources/Locales/zh_CN.xaml +++ /dev/null @@ -1,548 +0,0 @@ - - 开 始 - 确 定 - 保 存 - 关闭 - 取 消 - 点击前往 - 在文件浏览器中查看 - 另存为... - 复制路径 - {0} 字节 - 过滤 - 选填 - 选择文件夹 - 系统提示 - 打开文件... - - 仓库地址 : - 远程仓库地址 - 本地目录 : - 本地存放的父级目录,选填. - - SSH密钥 : - SSH密钥文件 - - 关于软件 - SourceGit - 开源Git图形客户端 - - 补丁 - 应用补丁 - 补丁文件 : - 选择补丁文件 - 空白符号处理 : - 忽略空白符号 - 忽略 - 关闭所有警告 - 警告 - 应用补丁,输出关于空白符的警告 - 错误 - 输出错误,并终止应用补丁 - 更多错误 - 与【错误】级别相似,但输出内容更多 - - 存档 ... - 存档 - 指定的提交: - 存档文件路径: - 选择存档文件的存放路径 - - 逐行追溯 - - 子模块 - 添加子模块 - 拉取子孙模块 - 打开仓库 - 复制路径 - 删除子模块 - - 挑选此提交 - 挑选提交 - 提交ID : - 提交变化 - - 克隆远程仓库 - 远程仓库 : - 远程仓库地址 - 父级目录 : - 选择存放本仓库的父级文件夹路径 - 本地仓库名 : - 本地仓库目录的名字,选填 - 远程名 : - 远程的名字,选填 - 额外参数 : - 其他克隆参数,选填 - - 基本信息 - 修改者 - 提交者 - 提交指纹 - 父提交 - 相关引用 - 提交信息 - 变更列表 - 变更对比 - 查找文件... - 文件列表 - - 仓库配置 - 用户名 - 应用于本仓库的用户名 - 电子邮箱 - 邮箱地址 - HTTP代理 - HTTP网络代理 - - 新建分支 - 创建本地分支 - 新分支基于 : - 新分支名 : - 填写分支名称 - 未提交更改 : - 贮藏并自动恢复 - 忽略 - 完成后切换到新分支 - 对于空仓库,只有提交一次有效数据,Git 才会创建第一个分支 - - 新建标签 - 标签位于 : - 标签名 : - 推荐格式 :v1.0.0-alpha - 标签描述 : - 选填 - - 在文件浏览器中打开 - 在Visual Studio Code中打开 - 在GIT终端中打开 - 重新加载 - 查找提交 - 提交统计 - 清理本仓库(GC) - 配置本仓库 - 工作区 - 本地分支 - 新建分支 - 远程列表 - 添加远程 - 标签列表 - 新建标签 - 子模块列表 - 添加子模块 - 更新子模块 - 子树列表 - 添加子树 - 解决冲突 - 下一步 - 终止冲突解决 - - GIT工作流 - 初始化GIT工作流 - 发布分支 : - 开发分支 : - 特性分支 : - 版本分支 : - 修复分支 : - 特性分支名前缀 : - 版本分支名前缀 : - 修复分支名前缀 : - 版本标签前缀 : - 开始特性分支... - 开始版本分支... - 开始修复分支... - 开始特性分支 - 开始版本分支 - 开始修复分支 - 输入分支名 - 结束特性分支 - 结束版本分支 - 结束修复分支 - {0}分支名未填写! - {0}分支名包含非法字符! - {0}前缀未填写! - {0}前缀包含非法字符! - 开发分支与发布分支不可相同! - 保留分支 - - 书签 - 打开 - 在浏览器中查看 - - 推送 '{0}' - 放弃所有更改 - 快进到 '{0}' - 拉回 '{0}' - 拉回 '{0}' 内容至 '{1}' - 检出 '{0}' - 合并 '{0}' 到 '{1}' - 变基 '{0}' 分支至 '{1}' - GIT工作流 - 完成 '{0}' - 重命名 '{0}' - 删除 '{0}' - 切换上游分支... - 复制分支名 - 取消追踪 - - 拉取更新 ... - 清理远程已删除分支 - 编辑 ... - 删除 ... - 复制远程地址 - - 重置 '{0}' 到此处 - 变基 '{0}' 到此处 - 挑选此提交 - 编辑提交信息 - 合并此提交到上一个提交 - 回滚此提交 - 另存为补丁 ... - 复制提交指纹 - 复制提交信息 - - 推送 '{0}' - 删除 '{0}' - 复制标签名 - - 应用 - 应用并删除 - 删除 - - 从暂存中移除 - 暂存... - 放弃更改... - 贮藏... - 从暂存中移除 {0} 个文件 - 暂存 {0} 个文件... - 放弃 {0} 个文件的更改... - 贮藏选中的 {0} 个文件... - 另存为补丁... - 不跟踪此文件的更改 - - 确定要删除此分支吗? - 分支名 : - - 确定要移除该远程吗? - 远程名 : - - 确定要移除该标签吗? - 标签名 : - 同时删除远程仓库中的此标签 - - 确定要移除该子模块吗? - 子模块路径 : - - 下一个差异 - 上一个差异 - 切换显示模式 - 使用外部合并工具查看 - 请选择需要对比的文件 - 没有变更或仅有换行符差异 - 二进制文件 - 原始大小 : - 当前大小 : - LFS对象变更 - 复制 - - 放弃更改确认 - 需要放弃的变更 : - 本操作不支持回退,请确认后继续!!! - 所有本地址未提交的修改 - 总计{0}项选中更改 - - 拉取 - 拉取远程仓库内容 - 远程仓库 : - 拉取所有的远程仓库 - 自动清理远程已删除分支 - - 文件历史 - 使用该版本 - - 切换变更显示模式 - 网格模式 - 列表模式 - 树形模式 - - 选择目录... - 当前选择 : - - 历史记录 - 查询提交指纹、信息、作者。回车键开始,ESC键取消 - 清空 - 切换曲线/折线显示 - 切换横向/纵向显示 - 已选中 {0} 项提交 - - 初始化新仓库 - 路径 : - 点击【确定】将在此目录执行`git init`操作 - - Source Git - 主菜单 - 出错了 - - 新建空白页 - 新标签页 - 起始页 - 关闭标签页 - 关闭其他标签页 - 关闭右侧标签页 - 设置书签 - 复制仓库路径 - - 合并分支 - 合并分支 : - 目标分支 : - 合并方式 : - - 打开本地仓库 - 打开GIT终端 - 克隆远程仓库 - 仓库列表 - 删除 - 快速查找仓库 - 排序 - 支持拖放目录添加 - 修改显示名称 - 显示名称 : - - 拉回 - 拉回(拉取并合并) - 远程 : - 拉取分支 : - 本地分支 : - 使用变基方式合并分支 - 自动贮藏并恢复本地变更 - - 推送 - 推送到远程仓库 - 本地分支 : - 远程仓库 : - 远程分支 : - 同时推送标签 - 启用强制推送 - - 推送标签到远程仓库 - 标签 : - 远程仓库 : - - 变基操作 - 分支 : - 目标提交 : - 自动贮藏并恢复本地变更 - - 添加远程仓库 - 编辑远程仓库 - 远程名 : - 唯一远程名 - 仓库地址 : - 远程仓库的地址 - - 分支重命名 - 分支 : - 新的名称 : - 新的分支名不能与现有分支名相同 - - 重置当前分支到指定版本 - 当前分支 : - 提交 : - 重置模式 : - - 确定要回滚吗? - 目标提交 : - 回滚后提交更改 - - 偏好设置 - 通用配置 - 显示语言 - 系统字体 - 文本字体 - 启用暗色主题 - 启动时恢复上次打开的仓库 - 最大历史提交数 - GIT配置 - 安装路径 - 填写git.exe所在位置 - Git 版本 - 默认克隆路径 - 默认的仓库本地存放位置 - 用户名 - 默认GIT用户名 - 邮箱 - 默认GIT用户邮箱 - 自动换行转换 - 启用定时自动拉取远程更新(重启生效) - 外部合并工具 - 工具 - 安装路径 - 填写工具可执行文件所在位置 - 选择git.exe所在位置 - 选择{0}所在位置 - - 贮藏 - 贮藏本地变更 - 信息 : - 选填,用于命名此贮藏 - 包含未跟踪的文件 - - 贮藏列表 - 贮藏列表 - 查看变更 - - 丢弃贮藏确认 - 丢弃贮藏 : - - 对比提交 : {0} -> {1} - - 本地更改 - 未暂存 - 查看忽略变更文件 - 暂存选中 - 暂存所有 - 已暂存 - 从暂存区移除选中 - 从暂存区移除所有 - 检测到冲突 - 使用THEIRS - 使用MINE - 打开合并工具 - 填写提交信息 - 历史提交信息 - 修补 - 提交 - CTRL + Enter - 提交并推送 - 没有提交信息记录 - 最近输入的提交信息 - 显示未跟踪文件 - - 检测到挑选提交冲突! - 检测到变基冲突! - 检测到回滚提交冲突! - 检测到分支合并冲突! - - 系统提示 - 本次配置变更需要在重启后生效,是否立即重启? - - 添加子树 - 远程地址: - 分支或提交ID: - 本地相对路径: - 合并提交为单一提交 - - 编辑子树信息 - 远程地址: - 本地相对路径: - - 删除子树 - 本地相对路径: - 本操作仅将子树信息删除,相关文件及提交不会更改 - - 拉取子树更新 - 推送子树更新到远程 - 本地相对路径: - 远程地址: - 远程分支: - 合并提交为单一提交 - - 编辑子树 ... - 删除子树 ... - 拉取子树更新 - 推送子树变更 - - 快捷键 - 快捷键 - 功能说明 - 新建标签页 - 关闭当前浏览标签页 - 切换到下一个标签页 - 切换到指定位置的标签页 - 打开/隐藏搜索框(仅在仓库页起效) - 重新加载当前仓库信息(仅在仓库页起效) - 暂存或从暂存中移除当前选中 - 关闭当前弹出面板 - - 编辑提交信息 - 提交: - 提交信息: - - 合并HEAD到上一个提交 - 当前提交 : - 合并到 : - 修改提交信息: - - 提交统计 - 本周 - 本月 - 本年 - 提交者人数:{0} - 总计提交次数:{0} - 提交者 - 提交次数 - - 不跟踪更改的文件 - 移除 - 没有不跟踪更改的文件 - - 星期日 - 星期一 - 星期二 - 星期三 - 星期四 - 星期五 - 星期六 - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 - - 按名称升序 - 按最近访问 - 按书签颜色 - - GPG签名 - 启用提交签名 - 可执行文件位置 - gpg.exe所在路径 - 用户签名KEY - 输入签名提交所使用的KEY - - GIT尚未配置。请打开【偏好设置】配置GIT路径。 - 路径({0})不存在或不可读取! - 无法找到bash.exe,请确保其在git.exe同目录中! - 二进制文件不支持该操作!!! - 选中文件不支持该操作!!! - 获取仓库GIT_DIR失败! - 初始化GIT FLOW失败! - 不支持的GIT FLOW分支! - 目录不存在或不可写!!! - 非法的远程仓库地址! - 非法的本地仓库地址! - 远程仓库地址不可为空 - 远程仓库地址包含非法字符!仅支持字母、数字、下划线、横线或英文点号! - 远程仓库名已存在! - 分支名不可为空 - 分支名包含非法字符!仅支持字母、数字、下划线、横线或英文点号! - 分支名已存在! - 标签名不可为空! - 标签名包含非法字符!仅支持字母、数字、下划线、横线或英文点号! - 标签名已存在! - 提交信息未填写! - 补丁文件不存在或不可访问! - 非法的子路径! - 非法的存档文件路径! - 内容未填写! - 正在将 '{0}' 从列表中移除,是否要继续? - 您正在丢弃所有的贮藏,一经操作,无法回退,是否继续? - 补丁已成功保存! - \ No newline at end of file diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml new file mode 100644 index 00000000..75d5a5d9 --- /dev/null +++ b/src/Resources/Styles.axaml @@ -0,0 +1,897 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/Styles/Button.xaml b/src/Resources/Styles/Button.xaml deleted file mode 100644 index 76aabb6e..00000000 --- a/src/Resources/Styles/Button.xaml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/ComboBox.xaml b/src/Resources/Styles/ComboBox.xaml deleted file mode 100644 index b2f36a3f..00000000 --- a/src/Resources/Styles/ComboBox.xaml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/ContextMenu.xaml b/src/Resources/Styles/ContextMenu.xaml deleted file mode 100644 index 2c5be7b9..00000000 --- a/src/Resources/Styles/ContextMenu.xaml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/DataGrid.xaml b/src/Resources/Styles/DataGrid.xaml deleted file mode 100644 index 1ca58ae3..00000000 --- a/src/Resources/Styles/DataGrid.xaml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/HyperLink.xaml b/src/Resources/Styles/HyperLink.xaml deleted file mode 100644 index fcba9d56..00000000 --- a/src/Resources/Styles/HyperLink.xaml +++ /dev/null @@ -1,12 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/IconButton.xaml b/src/Resources/Styles/IconButton.xaml deleted file mode 100644 index f530df8f..00000000 --- a/src/Resources/Styles/IconButton.xaml +++ /dev/null @@ -1,46 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/ListBox.xaml b/src/Resources/Styles/ListBox.xaml deleted file mode 100644 index 2fda7c79..00000000 --- a/src/Resources/Styles/ListBox.xaml +++ /dev/null @@ -1,26 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/ListView.xaml b/src/Resources/Styles/ListView.xaml deleted file mode 100644 index 3c81d5af..00000000 --- a/src/Resources/Styles/ListView.xaml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/Path.xaml b/src/Resources/Styles/Path.xaml deleted file mode 100644 index a91dee33..00000000 --- a/src/Resources/Styles/Path.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/RadioButton.xaml b/src/Resources/Styles/RadioButton.xaml deleted file mode 100644 index f935b69f..00000000 --- a/src/Resources/Styles/RadioButton.xaml +++ /dev/null @@ -1,52 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/ScrollBar.xaml b/src/Resources/Styles/ScrollBar.xaml deleted file mode 100644 index 464ac563..00000000 --- a/src/Resources/Styles/ScrollBar.xaml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/ScrollViewer.xaml b/src/Resources/Styles/ScrollViewer.xaml deleted file mode 100644 index b1d1aecb..00000000 --- a/src/Resources/Styles/ScrollViewer.xaml +++ /dev/null @@ -1,52 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/Slider.xaml b/src/Resources/Styles/Slider.xaml deleted file mode 100644 index c54077e5..00000000 --- a/src/Resources/Styles/Slider.xaml +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/TabControl.xaml b/src/Resources/Styles/TabControl.xaml deleted file mode 100644 index 69cc26a8..00000000 --- a/src/Resources/Styles/TabControl.xaml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/TextBlock.xaml b/src/Resources/Styles/TextBlock.xaml deleted file mode 100644 index b2540c91..00000000 --- a/src/Resources/Styles/TextBlock.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/TextBox.xaml b/src/Resources/Styles/TextBox.xaml deleted file mode 100644 index 3d95136d..00000000 --- a/src/Resources/Styles/TextBox.xaml +++ /dev/null @@ -1,107 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/ToggleButton.xaml b/src/Resources/Styles/ToggleButton.xaml deleted file mode 100644 index f284536a..00000000 --- a/src/Resources/Styles/ToggleButton.xaml +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/Tooltip.xaml b/src/Resources/Styles/Tooltip.xaml deleted file mode 100644 index 4ce7cf0a..00000000 --- a/src/Resources/Styles/Tooltip.xaml +++ /dev/null @@ -1,22 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Resources/Styles/Tree.xaml b/src/Resources/Styles/Tree.xaml deleted file mode 100644 index d0980c64..00000000 --- a/src/Resources/Styles/Tree.xaml +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Styles/Window.xaml b/src/Resources/Styles/Window.xaml deleted file mode 100644 index 710e1f58..00000000 --- a/src/Resources/Styles/Window.xaml +++ /dev/null @@ -1,40 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml new file mode 100644 index 00000000..88f38d8c --- /dev/null +++ b/src/Resources/Themes.axaml @@ -0,0 +1,95 @@ + + + + #FFFF6059 + #FFFFBE2F + #FF29c941 + #FFF0F5F9 + #FFCFDEEA + #FFF0F5F9 + #FFF8F8F8 + #FFFAFAFA + #FFB0CEE8 + #FF6F6F6F + #FFF8F8F8 + #FF836C2E + #FFDFDFDF + #FFCFCFCF + #FFCFCFCF + #FF898989 + #FFCFCFCF + #FFEFEFEF + #FFF8F8F8 + White + #FF4295FF + #FF529DFB + #FF1F1F1F + #FF6F6F6F + #FFFFFFFF + #FF4295FF + #FF529DFB + #4C007ACC + + + + #FFFF5E56 + #FFFCBB2D + #FF25C53C + #FF252525 + #FF1F1F1F + #FF2C2C2C + #FF2B2B2B + #FF181818 + #FF8F8F8F + #FF505050 + #FFF8F8F8 + #FFFAFAD2 + #FF323232 + #FF3F3F3F + #FF181818 + #FF7C7C7C + #FF404040 + #FF252525 + #FF303030 + #FF333333 + #FF3A3A3A + #FF404040 + #FFF1F1F1 + #40F1F1F1 + #FF252525 + #FF007ACC + #FF006BBE + #8C007ACC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/Themes/Dark.xaml b/src/Resources/Themes/Dark.xaml deleted file mode 100644 index 657a3845..00000000 --- a/src/Resources/Themes/Dark.xaml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Resources/Themes/Light.xaml b/src/Resources/Themes/Light.xaml deleted file mode 100644 index eb80a516..00000000 --- a/src/Resources/Themes/Light.xaml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index a5e7e0bd..e32977e5 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -1,30 +1,34 @@ - - - net48 - WinExe - true - true - App.ico - sourcegit - OpenSource GIT client for Windows - Copyright © sourcegit 2023. All rights reserved. - App.manifest - 7.0 - MIT - SourceGit.App - https://github.com/sourcegit-scm/sourcegit.git - https://github.com/sourcegit-scm/sourcegit.git - Public - true - none - false - false - - - - - - - - - \ No newline at end of file + + + WinExe + net8.0 + true + App.manifest + App.ico + 8.0 + false + true + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/SourceGit.sln b/src/SourceGit.sln new file mode 100644 index 00000000..13ea93f0 --- /dev/null +++ b/src/SourceGit.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34408.163 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGit", "SourceGit.csproj", "{89AD3F88-E72C-4399-AD61-6A87FC424E7B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {89AD3F88-E72C-4399-AD61-6A87FC424E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89AD3F88-E72C-4399-AD61-6A87FC424E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89AD3F88-E72C-4399-AD61-6A87FC424E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89AD3F88-E72C-4399-AD61-6A87FC424E7B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DA70C1D9-A8D2-4C89-98F3-B263CCBC5F28} + EndGlobalSection +EndGlobal diff --git a/src/ViewModels/AddRemote.cs b/src/ViewModels/AddRemote.cs new file mode 100644 index 00000000..8bb2652a --- /dev/null +++ b/src/ViewModels/AddRemote.cs @@ -0,0 +1,82 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class AddRemote : Popup { + [Required(ErrorMessage = "Remote name is required!!!")] + [RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad remote name format!!!")] + [CustomValidation(typeof(AddRemote), nameof(ValidateRemoteName))] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + [Required(ErrorMessage = "Remote URL is required!!!")] + [CustomValidation(typeof(AddRemote), nameof(ValidateRemoteURL))] + public string Url { + get => _url; + set { + if (SetProperty(ref _url, value, true)) UseSSH = Models.Remote.IsSSH(value); + } + } + + public bool UseSSH { + get => _useSSH; + set => SetProperty(ref _useSSH, value); + } + + public string SSHKey { + get; + set; + } + + public AddRemote(Repository repo) { + _repo = repo; + View = new Views.AddRemote() { DataContext = this }; + } + + public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx) { + if (ctx.ObjectInstance is AddRemote add) { + var exists = add._repo.Remotes.Find(x => x.Name == name); + if (exists != null) return new ValidationResult("A remote with given name already exists!!!"); + } + + return ValidationResult.Success; + } + + public static ValidationResult ValidateRemoteURL(string url, ValidationContext ctx) { + if (ctx.ObjectInstance is AddRemote add) { + if (!Models.Remote.IsValidURL(url)) return new ValidationResult("Bad remote URL format!!!"); + + var exists = add._repo.Remotes.Find(x => x.URL == url); + if (exists != null) return new ValidationResult("A remote with the same url already exists!!!"); + } + + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription("Adding remote ..."); + var succ = new Commands.Remote(_repo.FullPath).Add(_name, _url); + if (succ) { + SetProgressDescription("Fetching from added remote ..."); + new Commands.Fetch(_repo.FullPath, _name, true, SetProgressDescription).Exec(); + + if (_useSSH) { + SetProgressDescription("Post processing ..."); + new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", SSHKey); + } + } + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _name = string.Empty; + private string _url = string.Empty; + private bool _useSSH = false; + } +} diff --git a/src/ViewModels/AddSubmodule.cs b/src/ViewModels/AddSubmodule.cs new file mode 100644 index 00000000..1e2e1c02 --- /dev/null +++ b/src/ViewModels/AddSubmodule.cs @@ -0,0 +1,61 @@ +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class AddSubmodule : Popup { + [Required(ErrorMessage = "Url is required!!!")] + [CustomValidation(typeof(AddSubmodule), nameof(ValidateURL))] + public string Url { + get => _url; + set => SetProperty(ref _url, value, true); + } + + [Required(ErrorMessage = "Reletive path is required!!!")] + [CustomValidation(typeof(AddSubmodule), nameof(ValidateRelativePath))] + public string RelativePath { + get => _relativePath; + set => SetProperty(ref _relativePath, value, true); + } + + public bool Recursive { + get; + set; + } + + public AddSubmodule(Repository repo) { + _repo = repo; + View = new Views.AddSubmodule() { DataContext = this }; + } + + public static ValidationResult ValidateURL(string url, ValidationContext ctx) { + if (!Models.Remote.IsValidURL(url)) return new ValidationResult("Invalid repository URL format"); + return ValidationResult.Success; + } + + public static ValidationResult ValidateRelativePath(string path, ValidationContext ctx) { + if (Path.Exists(path)) { + return new ValidationResult("Give path is exists already!"); + } + + if (Path.IsPathRooted(path)) { + return new ValidationResult("Path must be relative to this repository!"); + } + + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.Submodule(_repo.FullPath).Add(_url, _relativePath, Recursive, SetProgressDescription); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _url = string.Empty; + private string _relativePath = string.Empty; + } +} diff --git a/src/ViewModels/Apply.cs b/src/ViewModels/Apply.cs new file mode 100644 index 00000000..c3685426 --- /dev/null +++ b/src/ViewModels/Apply.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Apply : Popup { + [Required(ErrorMessage = "Patch file is required!!!")] + [CustomValidation(typeof(Apply), nameof(ValidatePatchFile))] + public string PatchFile { + get => _patchFile; + set => SetProperty(ref _patchFile, value, true); + } + + public bool IgnoreWhiteSpace { + get => _ignoreWhiteSpace; + set => SetProperty(ref _ignoreWhiteSpace, value); + } + + public List WhiteSpaceModes { + get; + private set; + } + + public Models.ApplyWhiteSpaceMode SelectedWhiteSpaceMode { + get; + set; + } + + public Apply(Repository repo) { + _repo = repo; + + WhiteSpaceModes = new List { + new Models.ApplyWhiteSpaceMode("Apply.NoWarn", "Apply.NoWarn.Desc", "nowarn"), + new Models.ApplyWhiteSpaceMode("Apply.Warn", "Apply.Warn.Desc", "warn"), + new Models.ApplyWhiteSpaceMode("Apply.Error", "Apply.Error.Desc", "error"), + new Models.ApplyWhiteSpaceMode("Apply.ErrorAll", "Apply.ErrorAll.Desc", "error-all") + }; + SelectedWhiteSpaceMode = WhiteSpaceModes[0]; + + View = new Views.Apply() { DataContext = this }; + } + + public static ValidationResult ValidatePatchFile(string file, ValidationContext _) { + if (File.Exists(file)) { + return ValidationResult.Success; + } + + return new ValidationResult($"File '{file}' can NOT be found!!!"); + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.Apply(_repo.FullPath, _patchFile, _ignoreWhiteSpace, SelectedWhiteSpaceMode.Arg).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _patchFile = string.Empty; + private bool _ignoreWhiteSpace = true; + } +} diff --git a/src/ViewModels/Archive.cs b/src/ViewModels/Archive.cs new file mode 100644 index 00000000..3661a978 --- /dev/null +++ b/src/ViewModels/Archive.cs @@ -0,0 +1,56 @@ +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Archive : Popup { + + [Required(ErrorMessage = "Output file name is required")] + public string SaveFile { + get => _saveFile; + set => SetProperty(ref _saveFile, value, true); + } + + public object BasedOn { + get; + private set; + } + + public Archive(Repository repo, Models.Branch branch) { + _repo = repo; + _revision = branch.Head; + _saveFile = $"archive-{Path.GetFileNameWithoutExtension(branch.Name)}.zip"; + BasedOn = branch; + View = new Views.Archive() { DataContext = this }; + } + + public Archive(Repository repo, Models.Commit commit) { + _repo = repo; + _revision = commit.SHA; + _saveFile = $"archive-{commit.SHA.Substring(0,10)}.zip"; + BasedOn = commit; + View = new Views.Archive() { DataContext = this }; + } + + public Archive(Repository repo, Models.Tag tag) { + _repo = repo; + _revision = tag.SHA; + _saveFile = $"archive-{tag.Name}.zip"; + BasedOn = tag; + View = new Views.Archive() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.Archive(_repo.FullPath, _revision, _saveFile, SetProgressDescription).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _saveFile = string.Empty; + private string _revision = string.Empty; + } +} diff --git a/src/ViewModels/AssumeUnchangedManager.cs b/src/ViewModels/AssumeUnchangedManager.cs new file mode 100644 index 00000000..f633de75 --- /dev/null +++ b/src/ViewModels/AssumeUnchangedManager.cs @@ -0,0 +1,30 @@ +using Avalonia.Collections; +using Avalonia.Threading; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class AssumeUnchangedManager { + public AvaloniaList Files { get; private set; } + + public AssumeUnchangedManager(string repo) { + _repo = repo; + Files = new AvaloniaList(); + + Task.Run(() => { + var collect = new Commands.AssumeUnchanged(_repo).View(); + Dispatcher.UIThread.Invoke(() => { + Files.AddRange(collect); + }); + }); + } + + public void Remove(object param) { + if (param is string file) { + new Commands.AssumeUnchanged(_repo).Remove(file); + Files.Remove(file); + } + } + + private string _repo; + } +} diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs new file mode 100644 index 00000000..3aa1e66e --- /dev/null +++ b/src/ViewModels/Blame.cs @@ -0,0 +1,48 @@ +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Blame : ObservableObject { + public string Title { + get; + private set; + } + + public string SelectedSHA { + get => _selectedSHA; + private set => SetProperty(ref _selectedSHA, value); + } + + public bool IsBinary { + get => _data != null && _data.IsBinary; + } + + public Models.BlameData Data { + get => _data; + private set => SetProperty(ref _data, value); + } + + public Blame(string repo, string file, string revision) { + _repo = repo; + + Title = $"{file}@{revision.Substring(0, 10)}"; + Task.Run(() => { + var result = new Commands.Blame(repo, file, revision).Result(); + Dispatcher.UIThread.Invoke(() => { + Data = result; + OnPropertyChanged(nameof(IsBinary)); + }); + }); + } + + public void NavigateToCommit(string commitSHA) { + var repo = Preference.FindRepository(_repo); + if (repo != null) repo.NavigateToCommit(commitSHA); + } + + private string _repo = string.Empty; + private string _selectedSHA = string.Empty; + private Models.BlameData _data = null; + } +} diff --git a/src/ViewModels/Checkout.cs b/src/ViewModels/Checkout.cs new file mode 100644 index 00000000..33c70d6b --- /dev/null +++ b/src/ViewModels/Checkout.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Checkout : Popup { + public string Branch { + get; + private set; + } + + public Checkout(Repository repo, string branch) { + _repo = repo; + Branch = branch; + View = new Views.Checkout() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Checkout '{Branch}' ..."); + var succ = new Commands.Checkout(_repo.FullPath).Branch(Branch, SetProgressDescription); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo; + } +} diff --git a/src/ViewModels/CherryPick.cs b/src/ViewModels/CherryPick.cs new file mode 100644 index 00000000..658c1e0e --- /dev/null +++ b/src/ViewModels/CherryPick.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class CherryPick : Popup { + public Models.Commit Target { + get; + private set; + } + + public bool AutoCommit { + get; + set; + } + + public CherryPick(Repository repo, Models.Commit target) { + _repo = repo; + Target = target; + AutoCommit = true; + View = new Views.CherryPick() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Cherry-Pick commit '{Target.SHA}' ..."); + var succ = new Commands.CherryPick(_repo.FullPath, Target.SHA, !AutoCommit).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Cleanup.cs b/src/ViewModels/Cleanup.cs new file mode 100644 index 00000000..07561d5e --- /dev/null +++ b/src/ViewModels/Cleanup.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Cleanup : Popup { + public Cleanup(Repository repo) { + _repo = repo; + View = new Views.Cleanup() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + ProgressDescription = "Cleanup (GC & prune) ..."; + return Task.Run(() => { + SetProgressDescription("Run GC ..."); + new Commands.GC(_repo.FullPath, SetProgressDescription).Exec(); + + var lfs = new Commands.LFS(_repo.FullPath); + if (lfs.IsEnabled()) { + SetProgressDescription("Run LFS prune ..."); + lfs.Prune(SetProgressDescription); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/ClearStashes.cs b/src/ViewModels/ClearStashes.cs new file mode 100644 index 00000000..ba10adbf --- /dev/null +++ b/src/ViewModels/ClearStashes.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class ClearStashes : Popup { + public ClearStashes(Repository repo) { + _repo = repo; + View = new Views.ClearStashes() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + new Commands.Stash(_repo.FullPath).Clear(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Clone.cs b/src/ViewModels/Clone.cs new file mode 100644 index 00000000..3a7de344 --- /dev/null +++ b/src/ViewModels/Clone.cs @@ -0,0 +1,111 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Clone : Popup { + [Required(ErrorMessage = "Remote URL is required")] + [CustomValidation(typeof(Clone), nameof(ValidateRemote))] + public string Remote { + get => _remote; + set { + if (SetProperty(ref _remote, value, true)) UseSSH = Models.Remote.IsSSH(value); + } + } + + public bool UseSSH { + get => _useSSH; + set => SetProperty(ref _useSSH, value); + } + + public string SSHKey { + get => _sshKey; + set => SetProperty(ref _sshKey, value); + } + + [Required(ErrorMessage = "Parent folder is required")] + [CustomValidation(typeof(Clone), nameof(ValidateParentFolder))] + public string ParentFolder { + get => _parentFolder; + set => SetProperty(ref _parentFolder, value, true); + } + + public string Local { + get => _local; + set => SetProperty(ref _local, value); + } + + public string ExtraArgs { + get => _extraArgs; + set => SetProperty(ref _extraArgs, value); + } + + public Clone(LauncherPage page) { + View = new Views.Clone() { DataContext = this }; + _page = page; + } + + public static ValidationResult ValidateRemote(string remote, ValidationContext _) { + if (!Models.Remote.IsValidURL(remote)) return new ValidationResult("Invalid remote repository URL format"); + return ValidationResult.Success; + } + + public static ValidationResult ValidateParentFolder(string folder, ValidationContext _) { + if (!Directory.Exists(folder)) return new ValidationResult("Given path can NOT be found"); + return ValidationResult.Success; + } + + public override Task Sure() { + return Task.Run(() => { + var cmd = new Commands.Clone(HostPageId, _parentFolder, _remote, _local, _useSSH ? _sshKey : "", _extraArgs, SetProgressDescription); + if (!cmd.Exec()) return false; + + var path = _parentFolder; + if (!string.IsNullOrEmpty(_local)) { + path = Path.GetFullPath(Path.Combine(path, _local)); + } else { + var name = Path.GetFileName(_remote); + if (name.EndsWith(".git")) name = name.Substring(0, name.Length - 4); + path = Path.GetFullPath(Path.Combine(path, name)); + } + + if (!Directory.Exists(path)) { + CallUIThread(() => { + App.RaiseException(HostPageId, $"Folder '{path}' can NOT be found"); + }); + return false; + } + + if (_useSSH && !string.IsNullOrEmpty(_sshKey)) { + var config = new Commands.Config(path); + config.Set("remote.origin.sshkey", _sshKey); + } + + CallUIThread(() => { + var repo = Preference.AddRepository(path, Path.Combine(path, ".git")); + var node = new RepositoryNode() { + Id = path, + Name = Path.GetFileName(path), + Bookmark = 0, + IsRepository = true, + }; + Preference.AddNode(node); + + _page.View = new Views.Repository() { DataContext = repo }; + _page.Node = node; + }); + + return true; + }); + } + + private LauncherPage _page = null; + private string _remote = string.Empty; + private bool _useSSH = false; + private string _sshKey = string.Empty; + private string _parentFolder = Preference.Instance.GitDefaultCloneDir; + private string _local = string.Empty; + private string _extraArgs = string.Empty; + } +} diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs new file mode 100644 index 00000000..602f1db3 --- /dev/null +++ b/src/ViewModels/CommitDetail.cs @@ -0,0 +1,408 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Platform.Storage; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class CommitDetail : ObservableObject { + public DiffContext DiffContext { + get => _diffContext; + private set => SetProperty(ref _diffContext, value); + } + + public int ActivePageIndex { + get => _activePageIndex; + set => SetProperty(ref _activePageIndex, value); + } + + public Models.Commit Commit { + get => _commit; + set { + if (SetProperty(ref _commit, value)) Refresh(); + } + } + + public List Changes { + get => _changes; + set => SetProperty(ref _changes, value); + } + + public List VisibleChanges { + get => _visibleChanges; + set => SetProperty(ref _visibleChanges, value); + } + + public List ChangeTree { + get => _changeTree; + set => SetProperty(ref _changeTree, value); + } + + public Models.Change SelectedChange { + get => _selectedChange; + set { + if (SetProperty(ref _selectedChange, value)) { + if (value == null) { + SelectedChangeNode = null; + DiffContext = null; + } else { + SelectedChangeNode = FileTreeNode.SelectByPath(_changeTree, value.Path); + DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value)); + } + } + } + } + + public FileTreeNode SelectedChangeNode { + get => _selectedChangeNode; + set { + if (SetProperty(ref _selectedChangeNode, value)) { + if (value == null) { + SelectedChange = null; + } else { + SelectedChange = value.Backend as Models.Change; + } + } + } + } + + public string SearchChangeFilter { + get => _searchChangeFilter; + set { + if (SetProperty(ref _searchChangeFilter, value)) { + RefreshVisibleChanges(); + } + } + } + + public List RevisionFilesTree { + get => _revisionFilesTree; + set => SetProperty(ref _revisionFilesTree, value); + } + + public FileTreeNode SelectedRevisionFileNode { + get => _selectedRevisionFileNode; + set { + if (SetProperty(ref _selectedRevisionFileNode, value) && value != null && !value.IsFolder) { + RefreshViewRevisionFile(value.Backend as Models.Object); + } else { + ViewRevisionFileContent = null; + } + } + } + + public string SearchFileFilter { + get => _searchFileFilter; + set { + if (SetProperty(ref _searchFileFilter, value)) { + RefreshVisibleFiles(); + } + } + } + + public object ViewRevisionFileContent { + get => _viewRevisionFileContent; + set => SetProperty(ref _viewRevisionFileContent, value); + } + + public CommitDetail(string repo) { + _repo = repo; + } + + public void NavigateTo(string commitSHA) { + var repo = Preference.FindRepository(_repo); + if (repo != null) repo.NavigateToCommit(commitSHA); + } + + public void ClearSearchChangeFilter() { + SearchChangeFilter = string.Empty; + } + + public void ClearSearchFileFilter() { + SearchFileFilter = string.Empty; + } + + public ContextMenu CreateChangeContextMenu(Models.Change change) { + var menu = new ContextMenu(); + + if (change.Index != Models.ChangeState.Deleted) { + var history = new MenuItem(); + history.Header = App.Text("FileHistory"); + history.Icon = CreateMenuIcon("Icons.Histories"); + history.Click += (_, ev) => { + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) }; + window.Show(); + ev.Handled = true; + }; + + var blame = new MenuItem(); + blame.Header = App.Text("Blame"); + blame.Icon = CreateMenuIcon("Icons.Blame"); + blame.Click += (o, ev) => { + var window = new Views.Blame() { DataContext = new Blame(_repo, change.Path, _commit.SHA) }; + window.Show(); + ev.Handled = true; + }; + + var full = Path.GetFullPath(Path.Combine(_repo, change.Path)); + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = CreateMenuIcon("Icons.Folder.Open"); + explore.IsEnabled = File.Exists(full); + explore.Click += (_, ev) => { + Native.OS.OpenInFileManager(full, true); + ev.Handled = true; + }; + + menu.Items.Add(history); + menu.Items.Add(blame); + menu.Items.Add(explore); + } + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = CreateMenuIcon("Icons.Copy"); + copyPath.Click += (_, ev) => { + App.CopyText(change.Path); + ev.Handled = true; + }; + + menu.Items.Add(copyPath); + return menu; + } + + public ContextMenu CreateRevisionFileContextMenu(Models.Object file) { + var history = new MenuItem(); + history.Header = App.Text("FileHistory"); + history.Icon = CreateMenuIcon("Icons.Histories"); + history.Click += (_, ev) => { + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, file.Path) }; + window.Show(); + ev.Handled = true; + }; + + var blame = new MenuItem(); + blame.Header = App.Text("Blame"); + blame.Icon = CreateMenuIcon("Icons.Blame"); + blame.Click += (o, ev) => { + var window = new Views.Blame() { DataContext = new Blame(_repo, file.Path, _commit.SHA) }; + window.Show(); + ev.Handled = true; + }; + + var full = Path.GetFullPath(Path.Combine(_repo, file.Path)); + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = CreateMenuIcon("Icons.Folder.Open"); + explore.Click += (_, ev) => { + Native.OS.OpenInFileManager(full, file.Type == Models.ObjectType.Blob); + ev.Handled = true; + }; + + var saveAs = new MenuItem(); + saveAs.Header = App.Text("SaveAs"); + saveAs.Icon = CreateMenuIcon("Icons.Save"); + saveAs.IsEnabled = file.Type == Models.ObjectType.Blob; + saveAs.Click += async (_, ev) => { + var topLevel = App.GetTopLevel(); + if (topLevel == null) return; + + var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) { + var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path)); + Commands.SaveRevisionFile.Run(_repo, _commit.SHA, file.Path, saveTo); + } + + ev.Handled = true; + }; + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = CreateMenuIcon("Icons.Copy"); + copyPath.Click += (_, ev) => { + App.CopyText(file.Path); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(history); + menu.Items.Add(blame); + menu.Items.Add(explore); + menu.Items.Add(saveAs); + menu.Items.Add(copyPath); + return menu; + } + + private void Refresh() { + _changes = null; + VisibleChanges = null; + SelectedChange = null; + RevisionFilesTree = null; + SelectedRevisionFileNode = null; + if (_commit == null) return; + if (_cancelToken != null) _cancelToken.Requested = true; + + _cancelToken = new Commands.Command.CancelToken(); + var cmdChanges = new Commands.QueryCommitChanges(_repo, _commit.SHA) { Cancel = _cancelToken }; + var cmdRevisionFiles = new Commands.QueryRevisionObjects(_repo, _commit.SHA) { Cancel = _cancelToken }; + + Task.Run(() => { + var changes = cmdChanges.Result(); + if (cmdChanges.Cancel.Requested) return; + + var visible = changes; + if (!string.IsNullOrWhiteSpace(_searchChangeFilter)) { + visible = new List(); + foreach (var c in changes) { + if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(c); + } + } + } + + var tree = FileTreeNode.Build(visible); + Dispatcher.UIThread.Invoke(() => { + Changes = changes; + VisibleChanges = visible; + ChangeTree = tree; + }); + }); + + Task.Run(() => { + var files = cmdRevisionFiles.Result(); + if (cmdRevisionFiles.Cancel.Requested) return; + + var visible = files; + if (!string.IsNullOrWhiteSpace(_searchFileFilter)) { + visible = new List(); + foreach (var f in files) { + if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(f); + } + } + } + + var tree = FileTreeNode.Build(visible); + Dispatcher.UIThread.Invoke(() => { + _revisionFiles = files; + RevisionFilesTree = tree; + }); + }); + } + + private void RefreshVisibleChanges() { + if (_changes == null) return; + + if (string.IsNullOrEmpty(_searchChangeFilter)) { + VisibleChanges = _changes; + } else { + var visible = new List(); + foreach (var c in _changes) { + if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(c); + } + } + + VisibleChanges = visible; + } + + ChangeTree = FileTreeNode.Build(_visibleChanges); + } + + private void RefreshVisibleFiles() { + if (_revisionFiles == null) return; + + var visible = _revisionFiles; + if (!string.IsNullOrWhiteSpace(_searchFileFilter)) { + visible = new List(); + foreach (var f in _revisionFiles) { + if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(f); + } + } + } + + RevisionFilesTree = FileTreeNode.Build(visible); + } + + private void RefreshViewRevisionFile(Models.Object file) { + switch (file.Type) { + case Models.ObjectType.Blob: + Task.Run(() => { + var isBinary = new Commands.IsBinary(_repo, _commit.SHA, file.Path).Result(); + if (isBinary) { + Dispatcher.UIThread.Invoke(() => { + ViewRevisionFileContent = new Models.RevisionBinaryFile(); + }); + return; + } + + var content = new Commands.QueryFileContent(_repo, _commit.SHA, file.Path).Result(); + if (content.StartsWith("version https://git-lfs.github.com/spec/", StringComparison.OrdinalIgnoreCase)) { + var obj = new Models.RevisionLFSObject() { Object = new Models.LFSObject() }; + var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries); + if (lines.Length == 3) { + foreach (var line in lines) { + if (line.StartsWith("oid sha256:")) { + obj.Object.Oid = line.Substring(11); + } else if (line.StartsWith("size ")) { + obj.Object.Size = long.Parse(line.Substring(5)); + } + } + Dispatcher.UIThread.Invoke(() => { + ViewRevisionFileContent = obj; + }); + return; + } + } + + Dispatcher.UIThread.Invoke(() => { + ViewRevisionFileContent = new Models.RevisionTextFile() { + FileName = file.Path, + Content = content + }; + }); + }); + break; + case Models.ObjectType.Commit: + ViewRevisionFileContent = new Models.RevisionSubmodule() { SHA = file.SHA }; + break; + default: + ViewRevisionFileContent = null; + break; + } + } + + private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { + var icon = new Avalonia.Controls.Shapes.Path(); + icon.Width = 12; + icon.Height = 12; + icon.Stretch = Stretch.Uniform; + icon.Data = App.Current?.FindResource(key) as StreamGeometry; + return icon; + } + + private string _repo = string.Empty; + private int _activePageIndex = 0; + private Models.Commit _commit = null; + private List _changes = null; + private List _visibleChanges = null; + private List _changeTree = null; + private Models.Change _selectedChange = null; + private FileTreeNode _selectedChangeNode = null; + private string _searchChangeFilter = string.Empty; + private DiffContext _diffContext = null; + private List _revisionFiles = null; + private List _revisionFilesTree = null; + private FileTreeNode _selectedRevisionFileNode = null; + private string _searchFileFilter = string.Empty; + private object _viewRevisionFileContent = null; + private Commands.Command.CancelToken _cancelToken = null; + } +} diff --git a/src/ViewModels/CreateBranch.cs b/src/ViewModels/CreateBranch.cs new file mode 100644 index 00000000..73d19ca4 --- /dev/null +++ b/src/ViewModels/CreateBranch.cs @@ -0,0 +1,111 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class CreateBranch : Popup { + [Required(ErrorMessage = "Branch name is required!")] + [RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")] + [CustomValidation(typeof(CreateBranch), nameof(ValidateBranchName))] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + public object BasedOn { + get; + private set; + } + + public bool CheckoutAfterCreated { + get; + set; + } = true; + + public bool AutoStash { + get; + set; + } = true; + + public CreateBranch(Repository repo, Models.Branch branch) { + _repo = repo; + _baseOnRevision = branch.Head; + + BasedOn = branch; + View = new Views.CreateBranch() { DataContext = this }; + } + + public CreateBranch(Repository repo, Models.Commit commit) { + _repo = repo; + _baseOnRevision = commit.SHA; + + BasedOn = commit; + View = new Views.CreateBranch() { DataContext = this }; + } + + public CreateBranch(Repository repo, Models.Tag tag) { + _repo = repo; + _baseOnRevision = tag.SHA; + + BasedOn = tag; + View = new Views.CreateBranch() { DataContext = this }; + } + + public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) { + var creator = ctx.ObjectInstance as CreateBranch; + if (creator == null) return new ValidationResult("Missing runtime context to create branch!"); + + foreach (var b in creator._repo.Branches) { + var test = b.IsLocal ? b.Name : $"{b.Remote}/{b.Name}"; + if (test == name) return new ValidationResult("A branch with same name already exists!"); + } + + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + if (CheckoutAfterCreated) { + bool needPopStash = false; + if (_repo.WorkingCopyChangesCount > 0) { + if (AutoStash) { + SetProgressDescription("Adding untracked changes..."); + var succ = new Commands.Add(_repo.FullPath).Exec(); + if (succ) { + SetProgressDescription("Stash local changes"); + succ = new Commands.Stash(_repo.FullPath).Push("CREATE_BRANCH_AUTO_STASH"); + } + + if (!succ) { + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return false; + } + + needPopStash = true; + } else { + SetProgressDescription("Discard local changes..."); + Commands.Discard.All(_repo.FullPath); + } + } + + SetProgressDescription($"Create new branch '{_name}'"); + new Commands.Checkout(_repo.FullPath).Branch(_name, _baseOnRevision, SetProgressDescription); + + if (needPopStash) { + SetProgressDescription("Re-apply local changes..."); + new Commands.Stash(_repo.FullPath).Pop("stash@{0}"); + } + } else { + Commands.Branch.Create(_repo.FullPath, _name, _baseOnRevision); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + private string _name = null; + private string _baseOnRevision = null; + } +} diff --git a/src/ViewModels/CreateGroup.cs b/src/ViewModels/CreateGroup.cs new file mode 100644 index 00000000..a398f610 --- /dev/null +++ b/src/ViewModels/CreateGroup.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class CreateGroup : Popup { + [Required(ErrorMessage = "Group name is required!")] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + public CreateGroup(RepositoryNode parent) { + _parent = parent; + View = new Views.CreateGroup() { DataContext = this }; + } + + public override Task Sure() { + Preference.AddNode(new RepositoryNode() { + Id = Guid.NewGuid().ToString(), + Name = _name, + IsRepository = false, + IsExpanded = false, + }, _parent); + + return null; + } + + private RepositoryNode _parent = null; + private string _name = string.Empty; + } +} diff --git a/src/ViewModels/CreateTag.cs b/src/ViewModels/CreateTag.cs new file mode 100644 index 00000000..ef33d0b3 --- /dev/null +++ b/src/ViewModels/CreateTag.cs @@ -0,0 +1,64 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class CreateTag : Popup { + [Required(ErrorMessage = "Tag name is required!")] + [RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad tag name format!")] + [CustomValidation(typeof(CreateTag), nameof(ValidateTagName))] + public string TagName { + get => _tagName; + set => SetProperty(ref _tagName, value, true); + } + + public string Message { + get; + set; + } + + public object BasedOn { + get; + private set; + } + + public CreateTag(Repository repo, Models.Branch branch) { + _repo = repo; + _basedOn = branch.Head; + + BasedOn = branch; + View = new Views.CreateTag() { DataContext = this }; + } + + public CreateTag(Repository repo, Models.Commit commit) { + _repo = repo; + _basedOn = commit.SHA; + + BasedOn = commit; + View = new Views.CreateTag() { DataContext = this }; + } + + public static ValidationResult ValidateTagName(string name, ValidationContext ctx) { + var creator = ctx.ObjectInstance as CreateTag; + if (creator != null) { + var found = creator._repo.Tags.Find(x => x.Name == name); + if (found != null) return new ValidationResult("A tag with same name already exists!"); + } + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + Commands.Tag.Add(_repo.FullPath, TagName, _basedOn, Message); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + private string _tagName = string.Empty; + private string _basedOn = string.Empty; + } +} diff --git a/src/ViewModels/DeleteBranch.cs b/src/ViewModels/DeleteBranch.cs new file mode 100644 index 00000000..0421cf04 --- /dev/null +++ b/src/ViewModels/DeleteBranch.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DeleteBranch : Popup { + public Models.Branch Target { + get; + private set; + } + + public DeleteBranch(Repository repo, Models.Branch branch) { + _repo = repo; + Target = branch; + View = new Views.DeleteBranch() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + if (Target.IsLocal) { + Commands.Branch.Delete(_repo.FullPath, Target.Name); + } else { + new Commands.Push(_repo.FullPath, Target.Remote, Target.Name).Exec(); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/DeleteRemote.cs b/src/ViewModels/DeleteRemote.cs new file mode 100644 index 00000000..2ede9853 --- /dev/null +++ b/src/ViewModels/DeleteRemote.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DeleteRemote : Popup { + public Models.Remote Remote { + get; + private set; + } + + public DeleteRemote(Repository repo, Models.Remote remote) { + _repo = repo; + Remote = remote; + View = new Views.DeleteRemote() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription("Deleting remote ..."); + var succ = new Commands.Remote(_repo.FullPath).Delete(Remote.Name); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/DeleteRepositoryNode.cs b/src/ViewModels/DeleteRepositoryNode.cs new file mode 100644 index 00000000..03e4e9ef --- /dev/null +++ b/src/ViewModels/DeleteRepositoryNode.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DeleteRepositoryNode : Popup { + public RepositoryNode Node { + get => _node; + set => SetProperty(ref _node, value); + } + + public DeleteRepositoryNode(RepositoryNode node) { + _node = node; + View = new Views.DeleteRepositoryNode() { DataContext = this }; + } + + public override Task Sure() { + Preference.RemoveNode(_node); + return null; + } + + private RepositoryNode _node = null; + } +} diff --git a/src/ViewModels/DeleteSubmodule.cs b/src/ViewModels/DeleteSubmodule.cs new file mode 100644 index 00000000..2d7f1d5c --- /dev/null +++ b/src/ViewModels/DeleteSubmodule.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DeleteSubmodule : Popup { + + public string Submodule { + get; + private set; + } + + public DeleteSubmodule(Repository repo, string submodule) { + _repo = repo; + Submodule = submodule; + View = new Views.DeleteSubmodule() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.Submodule(_repo.FullPath).Delete(Submodule); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/DeleteTag.cs b/src/ViewModels/DeleteTag.cs new file mode 100644 index 00000000..3b33e000 --- /dev/null +++ b/src/ViewModels/DeleteTag.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DeleteTag : Popup { + public Models.Tag Target { + get; + private set; + } + + public bool ShouldPushToRemote { + get; + set; + } + + public DeleteTag(Repository repo, Models.Tag tag) { + _repo = repo; + Target = tag; + ShouldPushToRemote = true; + View = new Views.DeleteTag() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Deleting tag '{Target.Name}' ..."); + var remotes = ShouldPushToRemote ? _repo.Remotes : null; + var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, remotes); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs new file mode 100644 index 00000000..da8fa957 --- /dev/null +++ b/src/ViewModels/DiffContext.cs @@ -0,0 +1,105 @@ +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DiffContext : ObservableObject { + public string FilePath { + get => _option.Path; + } + + public bool IsOrgFilePathVisible { + get => !string.IsNullOrWhiteSpace(_option.OrgPath) && _option.OrgPath != "/dev/null"; + } + + public string OrgFilePath { + get => _option.OrgPath; + } + + public bool IsLoading { + get => _isLoading; + private set => SetProperty(ref _isLoading, value); + } + + public bool IsNoChange { + get => _isNoChange; + private set => SetProperty(ref _isNoChange, value); + } + + public bool IsTextDiff { + get => _isTextDiff; + private set => SetProperty(ref _isTextDiff, value); + } + + public object Content { + get => _content; + private set => SetProperty(ref _content, value); + } + + public DiffContext(string repo, Models.DiffOption option) { + _repo = repo; + _option = option; + + OnPropertyChanged(nameof(FilePath)); + OnPropertyChanged(nameof(IsOrgFilePathVisible)); + OnPropertyChanged(nameof(OrgFilePath)); + + Task.Run(() => { + var latest = new Commands.Diff(repo, option).Result(); + var binaryDiff = null as Models.BinaryDiff; + + if (latest.IsBinary) { + binaryDiff = new Models.BinaryDiff(); + + var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath; + if (option.Revisions.Count == 2) { + binaryDiff.OldSize = new Commands.QueryFileSize(repo, oldPath, option.Revisions[0]).Result(); + binaryDiff.NewSize = new Commands.QueryFileSize(repo, _option.Path, option.Revisions[1]).Result(); + } else { + binaryDiff.OldSize = new Commands.QueryFileSize(repo, oldPath, "HEAD").Result(); + binaryDiff.NewSize = new FileInfo(Path.Combine(repo, _option.Path)).Length; + } + } + + Dispatcher.UIThread.InvokeAsync(() => { + if (latest.IsBinary) { + Content = binaryDiff; + } else if (latest.IsLFS) { + Content = latest.LFSDiff; + } else if (latest.TextDiff != null) { + latest.TextDiff.File = _option.Path; + Content = latest.TextDiff; + IsTextDiff = true; + } else { + IsTextDiff = false; + IsNoChange = true; + } + + IsLoading = false; + }); + }); + } + + public async void OpenExternalMergeTool() { + var type = Preference.Instance.ExternalMergeToolType; + var exec = Preference.Instance.ExternalMergeToolPath; + + var tool = Models.ExternalMergeTools.Supported.Find(x => x.Type == type); + if (tool == null || !File.Exists(exec)) { + App.RaiseException(_repo, "Invalid merge tool in preference setting!"); + return; + } + + var args = tool.Type != 0 ? tool.DiffCmd : Preference.Instance.ExternalMergeToolDiffCmd; + await Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, exec, args, _option)); + } + + private string _repo = string.Empty; + private Models.DiffOption _option = null; + private bool _isLoading = true; + private bool _isNoChange = false; + private bool _isTextDiff = false; + private object _content = null; + } +} diff --git a/src/ViewModels/Discard.cs b/src/ViewModels/Discard.cs new file mode 100644 index 00000000..4f091448 --- /dev/null +++ b/src/ViewModels/Discard.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DiscardModeAll { } + public class DiscardModeSingle { public string File { get; set; } } + public class DiscardModeMulti { public int Count { get; set; } } + + public class Discard : Popup { + + public object Mode { + get; + private set; + } + + public Discard(Repository repo, List changes = null) { + _repo = repo; + _changes = changes; + + if (_changes == null) { + Mode = new DiscardModeAll(); + } else if (_changes.Count == 1) { + Mode = new DiscardModeSingle() { File = _changes[0].Path }; + } else { + Mode = new DiscardModeMulti() { Count = _changes.Count }; + } + + View = new Views.Discard() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + if (_changes == null) { + SetProgressDescription("Discard all local changes ..."); + Commands.Discard.All(_repo.FullPath); + } else { + SetProgressDescription($"Discard total {_changes.Count} changes ..."); + Commands.Discard.Changes(_repo.FullPath, _changes); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + private List _changes = null; + } +} diff --git a/src/ViewModels/DropStash.cs b/src/ViewModels/DropStash.cs new file mode 100644 index 00000000..767bdd9f --- /dev/null +++ b/src/ViewModels/DropStash.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class DropStash : Popup { + public Models.Stash Stash { get; private set; } + + public DropStash(string repo, Models.Stash stash) { + _repo = repo; + Stash = stash; + View = new Views.DropStash() { DataContext = this }; + } + + public override Task Sure() { + return Task.Run(() => { + new Commands.Stash(_repo).Drop(Stash.Name); + return true; + }); + } + + private string _repo; + } +} diff --git a/src/ViewModels/EditRemote.cs b/src/ViewModels/EditRemote.cs new file mode 100644 index 00000000..48082cc2 --- /dev/null +++ b/src/ViewModels/EditRemote.cs @@ -0,0 +1,100 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class EditRemote : Popup { + [Required(ErrorMessage = "Remote name is required!!!")] + [RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad remote name format!!!")] + [CustomValidation(typeof(EditRemote), nameof(ValidateRemoteName))] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + [Required(ErrorMessage = "Remote URL is required!!!")] + [CustomValidation(typeof(EditRemote), nameof(ValidateRemoteURL))] + public string Url { + get => _url; + set { + if (SetProperty(ref _url, value, true)) UseSSH = Models.Remote.IsSSH(value); + } + } + + public bool UseSSH { + get => _useSSH; + set => SetProperty(ref _useSSH, value); + } + + public string SSHKey { + get; + set; + } + + public EditRemote(Repository repo, Models.Remote remote) { + _repo = repo; + _remote = remote; + _name = remote.Name; + _url = remote.URL; + _useSSH = Models.Remote.IsSSH(remote.URL); + + if (_useSSH) { + SSHKey = new Commands.Config(repo.FullPath).Get($"remote.{remote.Name}.sshkey"); + } + + View = new Views.EditRemote() { DataContext = this }; + } + + public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx) { + if (ctx.ObjectInstance is EditRemote edit) { + foreach (var remote in edit._repo.Remotes) { + if (remote != edit._remote && name == remote.Name) new ValidationResult("A remote with given name already exists!!!"); + } + } + + return ValidationResult.Success; + } + + public static ValidationResult ValidateRemoteURL(string url, ValidationContext ctx) { + if (ctx.ObjectInstance is EditRemote edit) { + if (!Models.Remote.IsValidURL(url)) return new ValidationResult("Bad remote URL format!!!"); + + foreach (var remote in edit._repo.Remotes) { + if (remote != edit._remote && url == remote.URL) new ValidationResult("A remote with the same url already exists!!!"); + } + } + + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Editing remote '{_remote.Name}' ..."); + + if (_remote.Name != _name) { + var succ = new Commands.Remote(_repo.FullPath).Rename(_remote.Name, _name); + if (succ) _remote.Name = _name; + } + + if (_remote.URL != _url) { + var succ = new Commands.Remote(_repo.FullPath).SetURL(_name, _url); + if (succ) _remote.URL = _url; + } + + if (_useSSH) { + SetProgressDescription("Post processing ..."); + new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", SSHKey); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + private Models.Remote _remote = null; + private string _name = string.Empty; + private string _url = string.Empty; + private bool _useSSH = false; + } +} diff --git a/src/ViewModels/EditRepositoryNode.cs b/src/ViewModels/EditRepositoryNode.cs new file mode 100644 index 00000000..6111b72e --- /dev/null +++ b/src/ViewModels/EditRepositoryNode.cs @@ -0,0 +1,55 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class EditRepositoryNode : Popup { + public RepositoryNode Node { + get => _node; + set => SetProperty(ref _node, value); + } + + public string Id { + get => _id; + set => SetProperty(ref _id, value); + } + + [Required(ErrorMessage = "Name is required!")] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + public int Bookmark { + get => _bookmark; + set => SetProperty(ref _bookmark, value); + } + + public bool IsRepository { + get => _isRepository; + set => SetProperty(ref _isRepository, value); + } + + public EditRepositoryNode(RepositoryNode node) { + _node = node; + _id = node.Id; + _name = node.Name; + _isRepository = node.IsRepository; + _bookmark = node.Bookmark; + + View = new Views.EditRepositoryNode() { DataContext = this }; + } + + public override Task Sure() { + _node.Name = _name; + _node.Bookmark = _bookmark; + return null; + } + + private RepositoryNode _node = null; + private string _id = string.Empty; + private string _name = string.Empty; + private bool _isRepository = false; + private int _bookmark = 0; + } +} diff --git a/src/ViewModels/FastForwardWithoutCheckout.cs b/src/ViewModels/FastForwardWithoutCheckout.cs new file mode 100644 index 00000000..d9eb5462 --- /dev/null +++ b/src/ViewModels/FastForwardWithoutCheckout.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class FastForwardWithoutCheckout : Popup { + public Models.Branch Local { + get; + private set; + } + + public Models.Branch To { + get; + private set; + } + + public FastForwardWithoutCheckout(Repository repo, Models.Branch local, Models.Branch upstream) { + _repo = repo; + Local = local; + To = upstream; + View = new Views.FastForwardWithoutCheckout() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription("Fast-Forward ..."); + new Commands.Fetch(_repo.FullPath, To.Remote, Local.Name, To.Name, SetProgressDescription).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Fetch.cs b/src/ViewModels/Fetch.cs new file mode 100644 index 00000000..e5d94c77 --- /dev/null +++ b/src/ViewModels/Fetch.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Fetch : Popup { + public List Remotes { + get => _repo.Remotes; + } + + public bool FetchAllRemotes { + get => _fetchAllRemotes; + set => SetProperty(ref _fetchAllRemotes, value); + } + + public Models.Remote SelectedRemote { + get; + set; + } + + public bool Prune { + get; + set; + } + + public Fetch(Repository repo, Models.Remote preferedRemote = null) { + _repo = repo; + _fetchAllRemotes = true; + SelectedRemote = preferedRemote != null ? preferedRemote : _repo.Remotes[0]; + Prune = true; + View = new Views.Fetch() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + if (FetchAllRemotes) { + foreach (var remote in _repo.Remotes) { + SetProgressDescription($"Fetching remote: {remote.Name}"); + new Commands.Fetch(_repo.FullPath, remote.Name, Prune, SetProgressDescription).Exec(); + } + } else { + SetProgressDescription($"Fetching remote: {SelectedRemote.Name}"); + new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, Prune, SetProgressDescription).Exec(); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + private bool _fetchAllRemotes = true; + } +} diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs new file mode 100644 index 00000000..90356484 --- /dev/null +++ b/src/ViewModels/FileHistories.cs @@ -0,0 +1,66 @@ +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class FileHistories : ObservableObject { + public string File { + get => _file; + } + + public bool IsLoading { + get => _isLoading; + private set => SetProperty(ref _isLoading, value); + } + + public List Commits { + get => _commits; + set => SetProperty(ref _commits, value); + } + + public Models.Commit SelectedCommit { + get => _selectedCommit; + set { + if (SetProperty(ref _selectedCommit, value)) { + if (value == null) { + DiffContext = null; + } else { + DiffContext = new DiffContext(_repo, new Models.DiffOption(value, _file)); + } + } + } + } + + public DiffContext DiffContext { + get => _diffContext; + set => SetProperty(ref _diffContext, value); + } + + public FileHistories(string repo, string file) { + _repo = repo; + _file = file; + + Task.Run(() => { + var commits = new Commands.QueryCommits(_repo, $"-n 10000 -- \"{file}\"").Result(); + Dispatcher.UIThread.Invoke(() => { + IsLoading = false; + Commits = commits; + if (commits.Count > 0) SelectedCommit = commits[0]; + }); + }); + } + + public void NavigateToCommit(string commitSHA) { + var repo = Preference.FindRepository(_repo); + if (repo != null) repo.NavigateToCommit(commitSHA); + } + + private string _repo = string.Empty; + private string _file = string.Empty; + private bool _isLoading = true; + private List _commits = null; + private Models.Commit _selectedCommit = null; + private DiffContext _diffContext = null; + } +} diff --git a/src/ViewModels/FileTreeNode.cs b/src/ViewModels/FileTreeNode.cs new file mode 100644 index 00000000..94f30561 --- /dev/null +++ b/src/ViewModels/FileTreeNode.cs @@ -0,0 +1,170 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.Generic; + +namespace SourceGit.ViewModels { + public class FileTreeNode : ObservableObject { + public string FullPath { get; set; } = string.Empty; + public bool IsFolder { get; set; } = false; + public object Backend { get; set; } = null; + public List Children { get; set; } = new List(); + + public bool IsExpanded { + get => _isExpanded; + set => SetProperty(ref _isExpanded, value); + } + + public static List Build(List changes) { + var nodes = new List(); + var folders = new Dictionary(); + var expanded = changes.Count <= 50; + + foreach (var c in changes) { + var sepIdx = c.Path.IndexOf('/'); + if (sepIdx == -1) { + nodes.Add(new FileTreeNode() { + FullPath = c.Path, + Backend = c, + IsFolder = false, + IsExpanded = false + }); + } else { + FileTreeNode lastFolder = null; + var start = 0; + + while (sepIdx != -1) { + var folder = c.Path.Substring(0, sepIdx); + if (folders.ContainsKey(folder)) { + lastFolder = folders[folder]; + } else if (lastFolder == null) { + lastFolder = new FileTreeNode() { + FullPath = folder, + Backend = null, + IsFolder = true, + IsExpanded = expanded + }; + nodes.Add(lastFolder); + folders.Add(folder, lastFolder); + } else { + var cur = new FileTreeNode() { + FullPath = folder, + Backend = null, + IsFolder = true, + IsExpanded = expanded + }; + folders.Add(folder, cur); + lastFolder.Children.Add(cur); + lastFolder = cur; + } + + start = sepIdx + 1; + sepIdx = c.Path.IndexOf('/', start); + } + + lastFolder.Children.Add(new FileTreeNode() { + FullPath = c.Path, + Backend = c, + IsFolder = false, + IsExpanded = false + }); + } + } + + folders.Clear(); + Sort(nodes); + return nodes; + } + + public static List Build(List files) { + var nodes = new List(); + var folders = new Dictionary(); + var expanded = files.Count <= 50; + + foreach (var f in files) { + var sepIdx = f.Path.IndexOf('/'); + if (sepIdx == -1) { + nodes.Add(new FileTreeNode() { + FullPath = f.Path, + Backend = f, + IsFolder = false, + IsExpanded = false + }); + } else { + FileTreeNode lastFolder = null; + var start = 0; + + while (sepIdx != -1) { + var folder = f.Path.Substring(0, sepIdx); + if (folders.ContainsKey(folder)) { + lastFolder = folders[folder]; + } else if (lastFolder == null) { + lastFolder = new FileTreeNode() { + FullPath = folder, + Backend = null, + IsFolder = true, + IsExpanded = expanded + }; + nodes.Add(lastFolder); + folders.Add(folder, lastFolder); + } else { + var cur = new FileTreeNode() { + FullPath = folder, + Backend = null, + IsFolder = true, + IsExpanded = expanded + }; + folders.Add(folder, cur); + lastFolder.Children.Add(cur); + lastFolder = cur; + } + + start = sepIdx + 1; + sepIdx = f.Path.IndexOf('/', start); + } + + lastFolder.Children.Add(new FileTreeNode() { + FullPath = f.Path, + Backend = f, + IsFolder = false, + IsExpanded = false + }); + } + } + + folders.Clear(); + Sort(nodes); + return nodes; + } + + public static FileTreeNode SelectByPath(List nodes, string path) { + foreach (var node in nodes) { + if (node.FullPath == path) return node; + + if (node.IsFolder && path.StartsWith(node.FullPath + "/")) { + var foundInChildren = SelectByPath(node.Children, path); + if (foundInChildren != null) { + node.IsExpanded = true; + } + return foundInChildren; + } + } + + return null; + } + + private static void Sort(List nodes) { + nodes.Sort((l, r) => { + if (l.IsFolder == r.IsFolder) { + return l.FullPath.CompareTo(r.FullPath); + } else { + return l.IsFolder ? -1 : 1; + } + }); + + foreach (var node in nodes) { + if (node.Children.Count > 1) Sort(node.Children); + } + } + + private bool _isExpanded = true; + } +} diff --git a/src/ViewModels/GitFlowFinish.cs b/src/ViewModels/GitFlowFinish.cs new file mode 100644 index 00000000..a50251ce --- /dev/null +++ b/src/ViewModels/GitFlowFinish.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class GitFlowFinish : Popup { + public Models.Branch Branch => _branch; + public bool IsFeature => _type == Models.GitFlowBranchType.Feature; + public bool IsRelease => _type == Models.GitFlowBranchType.Release; + public bool IsHotfix => _type == Models.GitFlowBranchType.Hotfix; + + public bool KeepBranch { + get; + set; + } = false; + + public GitFlowFinish(Repository repo, Models.Branch branch, Models.GitFlowBranchType type) { + _repo = repo; + _branch = branch; + _type = type; + View = new Views.GitFlowFinish() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var branch = _branch.Name; + switch (_type) { + case Models.GitFlowBranchType.Feature: + branch = branch.Substring(_repo.GitFlow.Feature.Length); + break; + case Models.GitFlowBranchType.Release: + branch = branch.Substring(_repo.GitFlow.Release.Length); + break; + default: + branch = branch.Substring(_repo.GitFlow.Hotfix.Length); + break; + } + + var succ = new Commands.GitFlow(_repo.FullPath).Finish(_type, branch, KeepBranch); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private Models.Branch _branch = null; + private Models.GitFlowBranchType _type = Models.GitFlowBranchType.None; + } +} diff --git a/src/ViewModels/GitFlowStart.cs b/src/ViewModels/GitFlowStart.cs new file mode 100644 index 00000000..d1e4cf5c --- /dev/null +++ b/src/ViewModels/GitFlowStart.cs @@ -0,0 +1,67 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class GitFlowStart : Popup { + [Required(ErrorMessage = "Name is required!!!")] + [RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")] + [CustomValidation(typeof(GitFlowStart), nameof(ValidateBranchName))] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + public string Prefix { + get => _prefix; + } + + public bool IsFeature => _type == Models.GitFlowBranchType.Feature; + public bool IsRelease => _type == Models.GitFlowBranchType.Release; + public bool IsHotfix => _type == Models.GitFlowBranchType.Hotfix; + + public GitFlowStart(Repository repo, Models.GitFlowBranchType type) { + _repo = repo; + _type = type; + + switch (type) { + case Models.GitFlowBranchType.Feature: + _prefix = repo.GitFlow.Feature; + break; + case Models.GitFlowBranchType.Release: + _prefix = repo.GitFlow.Release; + break; + default: + _prefix = repo.GitFlow.Hotfix; + break; + } + + View = new Views.GitFlowStart() { DataContext = this }; + } + + public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) { + if (ctx.ObjectInstance is GitFlowStart starter) { + var check = $"{starter._prefix}{name}"; + foreach (var b in starter._repo.Branches) { + var test = b.IsLocal ? b.Name : $"{b.Remote}/{b.Name}"; + if (test == check) return new ValidationResult("A branch with same name already exists!"); + } + } + + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.GitFlow(_repo.FullPath).Start(_type, _name); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private Models.GitFlowBranchType _type = Models.GitFlowBranchType.Feature; + private string _prefix = string.Empty; + private string _name = null; + } +} diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs new file mode 100644 index 00000000..3a9222e9 --- /dev/null +++ b/src/ViewModels/Histories.cs @@ -0,0 +1,473 @@ +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Platform.Storage; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class CountSelectedCommits { + public int Count { get; set; } + } + + public class Histories : ObservableObject { + public bool IsLoading { + get => _isLoading; + set => SetProperty(ref _isLoading, value); + } + + public double DataGridRowHeight { + get => _dataGridRowHeight; + } + + public List Commits { + get => _commits; + set { + if (SetProperty(ref _commits, value)) { + Graph = null; + Task.Run(() => { + var graph = Models.CommitGraph.Parse(value, DataGridRowHeight, 8); + Dispatcher.UIThread.Invoke(() => { + Graph = graph; + }); + }); + } + } + } + + public Models.CommitGraph Graph { + get => _graph; + set => SetProperty(ref _graph, value); + } + + public Models.Commit AutoSelectedCommit { + get => _autoSelectedCommit; + private set => SetProperty(ref _autoSelectedCommit, value); + } + + public object DetailContext { + get => _detailContext; + private set => SetProperty(ref _detailContext, value); + } + + public Histories(Repository repo) { + _repo = repo; + } + + public void NavigateTo(string commitSHA) { + var commit = _commits.Find(x => x.SHA.StartsWith(commitSHA)); + if (commit != null) { + AutoSelectedCommit = commit; + + if (_detailContext is CommitDetail detail) { + detail.Commit = commit; + } else { + var commitDetail = new CommitDetail(_repo.FullPath); + commitDetail.Commit = commit; + DetailContext = commitDetail; + } + } + } + + public void Select(IList commits) { + if (commits.Count == 0) { + DetailContext = null; + } else if (commits.Count == 1) { + var commit = commits[0] as Models.Commit; + AutoSelectedCommit = commit; + + if (_detailContext is CommitDetail detail) { + detail.Commit = commit; + } else { + var commitDetail = new CommitDetail(_repo.FullPath); + commitDetail.Commit = commit; + DetailContext = commitDetail; + } + } else if (commits.Count == 2) { + var end = commits[0] as Models.Commit; + var start = commits[1] as Models.Commit; + DetailContext = new RevisionCompare(_repo.FullPath, start, end); + } else { + DetailContext = new CountSelectedCommits() { Count = commits.Count }; + } + } + + public ContextMenu MakeContextMenu() { + var detail = _detailContext as CommitDetail; + if (detail == null) return null; + + var current = _repo.Branches.Find(x => x.IsCurrent); + if (current == null) return null; + + var commit = detail.Commit; + var menu = new ContextMenu(); + var tags = new List(); + + if (commit.HasDecorators) { + foreach (var d in commit.Decorators) { + if (d.Type == Models.DecoratorType.CurrentBranchHead) { + FillCurrentBranchMenu(menu, current); + } else if (d.Type == Models.DecoratorType.LocalBranchHead) { + var b = _repo.Branches.Find(x => x.IsLocal && d.Name == x.Name); + FillOtherLocalBranchMenu(menu, b, current, commit.IsMerged); + } else if (d.Type == Models.DecoratorType.RemoteBranchHead) { + var b = _repo.Branches.Find(x => !x.IsLocal && d.Name == $"{x.Remote}/{x.Name}"); + FillRemoteBranchMenu(menu, b, current, commit.IsMerged); + } else if (d.Type == Models.DecoratorType.Tag) { + var t = _repo.Tags.Find(x => x.Name == d.Name); + if (t != null) tags.Add(t); + } + } + + if (menu.Items.Count > 0) menu.Items.Add(new MenuItem() { Header = "-" }); + } + + if (tags.Count > 0) { + foreach (var tag in tags) FillTagMenu(menu, tag); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + + if (current.Head != commit.SHA) { + var reset = new MenuItem(); + reset.Header = App.Text("CommitCM.Reset", current.Name); + reset.Icon = CreateMenuIcon("Icons.Reset"); + reset.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reset(_repo, current, commit)); + e.Handled = true; + }; + menu.Items.Add(reset); + } else { + var reword = new MenuItem(); + reword.Header = App.Text("CommitCM.Reword"); + reword.Icon = CreateMenuIcon("Icons.Edit"); + reword.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reword(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(reword); + + var squash = new MenuItem(); + squash.Header = App.Text("CommitCM.Squash"); + squash.Icon = CreateMenuIcon("Icons.SquashIntoParent"); + squash.IsEnabled = commit.Parents.Count == 1; + squash.Click += (o, e) => { + if (commit.Parents.Count == 1) { + var parent = _commits.Find(x => x.SHA == commit.Parents[0]); + if (parent != null && PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Squash(_repo, commit, parent)); + } + + e.Handled = true; + }; + menu.Items.Add(squash); + } + + if (!commit.IsMerged) { + var rebase = new MenuItem(); + rebase.Header = App.Text("CommitCM.Rebase", current.Name); + rebase.Icon = CreateMenuIcon("Icons.Rebase"); + rebase.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(_repo, current, commit)); + e.Handled = true; + }; + menu.Items.Add(rebase); + + var cherryPick = new MenuItem(); + cherryPick.Header = App.Text("CommitCM.CherryPick"); + cherryPick.Icon = CreateMenuIcon("Icons.CherryPick"); + cherryPick.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CherryPick(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(cherryPick); + } else { + var revert = new MenuItem(); + revert.Header = App.Text("CommitCM.Revert"); + revert.Icon = CreateMenuIcon("Icons.Undo"); + revert.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Revert(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(revert); + } + + menu.Items.Add(new MenuItem() { Header = "-" }); + + var createBranch = new MenuItem(); + createBranch.Icon = CreateMenuIcon("Icons.Branch.Add"); + createBranch.Header = App.Text("CreateBranch"); + createBranch.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(createBranch); + + var createTag = new MenuItem(); + createTag.Icon = CreateMenuIcon("Icons.Tag.Add"); + createTag.Header = App.Text("CreateTag"); + createTag.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(createTag); + menu.Items.Add(new MenuItem() { Header = "-" }); + + var saveToPatch = new MenuItem(); + saveToPatch.Icon = CreateMenuIcon("Icons.Diff"); + saveToPatch.Header = App.Text("CommitCM.SaveAsPatch"); + saveToPatch.Click += async (_, e) => { + var topLevel = App.GetTopLevel(); + if (topLevel == null) return; + + var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) { + var succ = new Commands.FormatPatch(_repo.FullPath, commit.SHA, selected[0].Path.LocalPath).Exec(); + if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); + } + + e.Handled = true; + }; + menu.Items.Add(saveToPatch); + + var archive = new MenuItem(); + archive.Icon = CreateMenuIcon("Icons.Archive"); + archive.Header = App.Text("Archive"); + archive.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(archive); + menu.Items.Add(new MenuItem() { Header = "-" }); + + var copySHA = new MenuItem(); + copySHA.Header = App.Text("CommitCM.CopySHA"); + copySHA.Icon = CreateMenuIcon("Icons.Copy"); + copySHA.Click += (o, e) => { + App.CopyText(commit.SHA); + e.Handled = true; + }; + menu.Items.Add(copySHA); + return menu; + } + + private void FillCurrentBranchMenu(ContextMenu menu, Models.Branch current) { + var submenu = new MenuItem(); + submenu.Icon = CreateMenuIcon("Icons.Branch"); + submenu.Header = current.Name; + + var dirty = !string.IsNullOrEmpty(current.UpstreamTrackStatus); + if (!string.IsNullOrEmpty(current.Upstream)) { + var upstream = current.Upstream.Substring(13); + + var fastForward = new MenuItem(); + fastForward.Header = App.Text("BranchCM.FastForward", upstream); + fastForward.Icon = CreateMenuIcon("Icons.FastForward"); + fastForward.IsEnabled = dirty; + fastForward.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(_repo, upstream, current.Name)); + e.Handled = true; + }; + submenu.Items.Add(fastForward); + + var pull = new MenuItem(); + pull.Header = App.Text("BranchCM.Pull", upstream); + pull.Icon = CreateMenuIcon("Icons.Pull"); + pull.IsEnabled = dirty; + pull.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(_repo, null)); + e.Handled = true; + }; + submenu.Items.Add(pull); + } + + var push = new MenuItem(); + push.Header = App.Text("BranchCM.Push", current.Name); + push.Icon = CreateMenuIcon("Icons.Push"); + push.IsEnabled = _repo.Remotes.Count > 0 && dirty; + push.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Push(_repo, current)); + e.Handled = true; + }; + submenu.Items.Add(push); + submenu.Items.Add(new MenuItem() { Header = "-" }); + + var type = _repo.GitFlow.GetBranchType(current.Name); + if (type != Models.GitFlowBranchType.None) { + var finish = new MenuItem(); + finish.Header = App.Text("BranchCM.Finish", current.Name); + finish.Icon = CreateMenuIcon("Icons.Flow"); + finish.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, current, type)); + e.Handled = true; + }; + submenu.Items.Add(finish); + submenu.Items.Add(new MenuItem() { Header = "-" }); + } + + var rename = new MenuItem(); + rename.Header = App.Text("BranchCM.Rename", current.Name); + rename.Icon = CreateMenuIcon("Icons.Rename"); + rename.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, current)); + e.Handled = true; + }; + submenu.Items.Add(rename); + + menu.Items.Add(submenu); + } + + private void FillOtherLocalBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged) { + var submenu = new MenuItem(); + submenu.Icon = CreateMenuIcon("Icons.Branch"); + submenu.Header = branch.Name; + + var checkout = new MenuItem(); + checkout.Header = App.Text("BranchCM.Checkout", branch.Name); + checkout.Icon = CreateMenuIcon("Icons.Check"); + checkout.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(_repo, branch.Name)); + e.Handled = true; + }; + submenu.Items.Add(checkout); + + var merge = new MenuItem(); + merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name); + merge.Icon = CreateMenuIcon("Icons.Merge"); + merge.IsEnabled = !merged; + merge.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, branch.Name, current.Name)); + e.Handled = true; + }; + submenu.Items.Add(merge); + submenu.Items.Add(new MenuItem() { Header = "-" }); + + var type = _repo.GitFlow.GetBranchType(branch.Name); + if (type != Models.GitFlowBranchType.None) { + var finish = new MenuItem(); + finish.Header = App.Text("BranchCM.Finish", branch.Name); + finish.Icon = CreateMenuIcon("Icons.Flow"); + finish.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, branch, type)); + e.Handled = true; + }; + submenu.Items.Add(finish); + submenu.Items.Add(new MenuItem() { Header = "-" }); + } + + var rename = new MenuItem(); + rename.Header = App.Text("BranchCM.Rename", branch.Name); + rename.Icon = CreateMenuIcon("Icons.Rename"); + rename.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, branch)); + e.Handled = true; + }; + submenu.Items.Add(rename); + + var delete = new MenuItem(); + delete.Header = App.Text("BranchCM.Delete", branch.Name); + delete.Icon = CreateMenuIcon("Icons.Clear"); + delete.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch)); + e.Handled = true; + }; + submenu.Items.Add(delete); + + menu.Items.Add(submenu); + } + + private void FillRemoteBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged) { + var name = $"{branch.Remote}/{branch.Name}"; + + var submenu = new MenuItem(); + submenu.Icon = CreateMenuIcon("Icons.Branch"); + submenu.Header = name; + + var checkout = new MenuItem(); + checkout.Header = App.Text("BranchCM.Checkout", name); + checkout.Icon = CreateMenuIcon("Icons.Check"); + checkout.Click += (o, e) => { + foreach (var b in _repo.Branches) { + if (b.IsLocal && b.Upstream == branch.FullName) { + if (b.IsCurrent) return; + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(_repo, b.Name)); + return; + } + } + + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(_repo, branch)); + e.Handled = true; + }; + submenu.Items.Add(checkout); + + var merge = new MenuItem(); + merge.Header = App.Text("BranchCM.Merge", name, current.Name); + merge.Icon = CreateMenuIcon("Icons.Merge"); + merge.IsEnabled = !merged; + merge.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, name, current.Name)); + e.Handled = true; + }; + + submenu.Items.Add(merge); + submenu.Items.Add(new MenuItem() { Header = "-" }); + + var delete = new MenuItem(); + delete.Header = App.Text("BranchCM.Delete", name); + delete.Icon = CreateMenuIcon("Icons.Clear"); + delete.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch)); + e.Handled = true; + }; + submenu.Items.Add(delete); + + menu.Items.Add(submenu); + } + + private void FillTagMenu(ContextMenu menu, Models.Tag tag) { + var submenu = new MenuItem(); + submenu.Header = tag.Name; + submenu.Icon = CreateMenuIcon("Icons.Tag"); + submenu.MinWidth = 200; + + var push = new MenuItem(); + push.Header = App.Text("TagCM.Push", tag.Name); + push.Icon = CreateMenuIcon("Icons.Push"); + push.IsEnabled = _repo.Remotes.Count > 0; + push.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(_repo, tag)); + e.Handled = true; + }; + submenu.Items.Add(push); + + var delete = new MenuItem(); + delete.Header = App.Text("TagCM.Delete", tag.Name); + delete.Icon = CreateMenuIcon("Icons.Clear"); + delete.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(_repo, tag)); + e.Handled = true; + }; + submenu.Items.Add(delete); + + menu.Items.Add(submenu); + } + + private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { + var icon = new Avalonia.Controls.Shapes.Path(); + icon.Width = 12; + icon.Height = 12; + icon.Stretch = Stretch.Uniform; + icon.Data = App.Current?.FindResource(key) as StreamGeometry; + return icon; + } + + private Repository _repo = null; + private double _dataGridRowHeight = 28; + private bool _isLoading = true; + private List _commits = new List(); + private Models.CommitGraph _graph = null; + private Models.Commit _autoSelectedCommit = null; + private object _detailContext = null; + } +} diff --git a/src/ViewModels/Init.cs b/src/ViewModels/Init.cs new file mode 100644 index 00000000..f95ed80d --- /dev/null +++ b/src/ViewModels/Init.cs @@ -0,0 +1,41 @@ +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Init : Popup { + public string TargetPath { + get => _targetPath; + set => SetProperty(ref _targetPath, value); + } + + public Init(string path) { + TargetPath = path; + View = new Views.Init() { DataContext = this }; + } + + public override Task Sure() { + return Task.Run(() => { + SetProgressDescription($"Initialize git repository at: '{_targetPath}'"); + var succ = new Commands.Init(HostPageId, _targetPath).Exec(); + if (!succ) return false; + + var gitDir = Path.GetFullPath(Path.Combine(_targetPath, ".git")); + + CallUIThread(() => { + var repo = Preference.AddRepository(_targetPath, gitDir); + var node = new RepositoryNode() { + Id = _targetPath, + Name = Path.GetFileName(_targetPath), + Bookmark = 0, + IsRepository = true, + }; + Preference.AddNode(node); + }); + + return true; + }); + } + + private string _targetPath; + } +} diff --git a/src/ViewModels/InitGitFlow.cs b/src/ViewModels/InitGitFlow.cs new file mode 100644 index 00000000..724310ee --- /dev/null +++ b/src/ViewModels/InitGitFlow.cs @@ -0,0 +1,96 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class InitGitFlow : Popup { + private static readonly Regex TAG_PREFIX = new Regex(@"^[\w\-/\.]+$"); + + [Required(ErrorMessage = "Master branch name is required!!!")] + [RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")] + [CustomValidation(typeof(InitGitFlow), nameof(ValidateBaseBranch))] + public string Master { + get => _master; + set => SetProperty(ref _master, value, true); + } + + [Required(ErrorMessage = "Develop branch name is required!!!")] + [RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")] + [CustomValidation(typeof(InitGitFlow), nameof(ValidateBaseBranch))] + public string Develop { + get => _develop; + set => SetProperty(ref _develop, value, true); + } + + [Required(ErrorMessage = "Feature prefix is required!!!")] + [RegularExpression(@"^[\w\-\.]+/$", ErrorMessage = "Bad feature prefix format!")] + public string FeturePrefix { + get => _featurePrefix; + set => SetProperty(ref _featurePrefix, value, true); + } + + [Required(ErrorMessage = "Release prefix is required!!!")] + [RegularExpression(@"^[\w\-\.]+/$", ErrorMessage = "Bad release prefix format!")] + public string ReleasePrefix { + get => _releasePrefix; + set => SetProperty(ref _releasePrefix, value, true); + } + + [Required(ErrorMessage = "Hotfix prefix is required!!!")] + [RegularExpression(@"^[\w\-\.]+/$", ErrorMessage = "Bad hotfix prefix format!")] + public string HotfixPrefix { + get => _hotfixPrefix; + set => SetProperty(ref _hotfixPrefix, value, true); + } + + [CustomValidation(typeof(InitGitFlow), nameof(ValidateTagPrefix))] + public string TagPrefix { + get => _tagPrefix; + set => SetProperty(ref _tagPrefix, value, true); + } + + public InitGitFlow(Repository repo) { + _repo = repo; + View = new Views.InitGitFlow() { DataContext = this }; + } + + public static ValidationResult ValidateBaseBranch(string _, ValidationContext ctx) { + if (ctx.ObjectInstance is InitGitFlow initializer) { + if (initializer._master == initializer._develop) return new ValidationResult("Develop branch has the same name with master branch!"); + } + + return ValidationResult.Success; + } + + public static ValidationResult ValidateTagPrefix(string tagPrefix, ValidationContext ctx) { + if (!string.IsNullOrWhiteSpace(tagPrefix) && !TAG_PREFIX.IsMatch(tagPrefix)) { + return new ValidationResult("Bad tag prefix format!"); + } + + return ValidationResult.Success; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.GitFlow(_repo.FullPath).Init(_repo.Branches, _master, _develop, _featurePrefix, _releasePrefix, _hotfixPrefix, _tagPrefix); + if (succ) { + _repo.GitFlow.Feature = _featurePrefix; + _repo.GitFlow.Release = _releasePrefix; + _repo.GitFlow.Hotfix = _hotfixPrefix; + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _master = "master"; + private string _develop = "develop"; + private string _featurePrefix = "feature/"; + private string _releasePrefix = "release/"; + private string _hotfixPrefix = "hotfix/"; + private string _tagPrefix = string.Empty; + } +} diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs new file mode 100644 index 00000000..d8754f73 --- /dev/null +++ b/src/ViewModels/Launcher.cs @@ -0,0 +1,154 @@ +using Avalonia.Collections; +using CommunityToolkit.Mvvm.ComponentModel; +using System.IO; + +namespace SourceGit.ViewModels { + public class Launcher : ObservableObject { + public AvaloniaList Pages { + get; + private set; + } + + public LauncherPage ActivePage { + get => _activePage; + set { + if (SetProperty(ref _activePage, value)) { + PopupHost.Active = value; + } + } + } + + public Launcher() { + Pages = new AvaloniaList(); + AddNewTab(); + } + + public void AddNewTab() { + var page = new LauncherPage(); + Pages.Add(page); + ActivePage = page; + } + + public void MoveTab(LauncherPage from, LauncherPage to) { + var fromIdx = Pages.IndexOf(from); + var toIdx = Pages.IndexOf(to); + Pages.Move(fromIdx, toIdx); + ActivePage = from; + } + + public void GotoNextTab() { + if (Pages.Count == 1) return; + + var activeIdx = Pages.IndexOf(_activePage); + var nextIdx = (activeIdx + 1) % Pages.Count; + ActivePage = Pages[nextIdx]; + } + + public void CloseTab(object param) { + if (Pages.Count == 1) { + App.Quit(); + return; + } + + LauncherPage page = param as LauncherPage; + if (page == null) page = _activePage; + + CloseRepositoryInTab(page); + + var removeIdx = Pages.IndexOf(page); + var activeIdx = Pages.IndexOf(_activePage); + if (removeIdx == activeIdx) { + if (removeIdx == Pages.Count - 1) { + ActivePage = Pages[removeIdx - 1]; + } else { + ActivePage = Pages[removeIdx + 1]; + } + + Pages.RemoveAt(removeIdx); + OnPropertyChanged(nameof(Pages)); + } else if (removeIdx + 1 == activeIdx) { + Pages.RemoveAt(removeIdx); + OnPropertyChanged(nameof(Pages)); + } else { + Pages.RemoveAt(removeIdx); + } + } + + public void CloseOtherTabs(object param) { + if (Pages.Count == 1) return; + + LauncherPage page = param as LauncherPage; + if (page == null) page = _activePage; + + foreach (var one in Pages) { + if (one.Node.Id != page.Node.Id) { + CloseRepositoryInTab(one); + } + } + + ActivePage = page; + Pages = new AvaloniaList { page }; + OnPropertyChanged(nameof(Pages)); + } + + public void CloseRightTabs(object param) { + LauncherPage page = param as LauncherPage; + if (page == null) page = _activePage; + + var endIdx = Pages.IndexOf(page); + var activeIdx = Pages.IndexOf(_activePage); + if (endIdx < activeIdx) { + ActivePage = page; + } + + for (var i = Pages.Count - 1; i > endIdx; i--) { + CloseRepositoryInTab(Pages[i]); + Pages.Remove(Pages[i]); + } + } + + public void OpenRepositoryInTab(RepositoryNode node, LauncherPage page) { + foreach (var one in Pages) { + if (one.Node.Id == node.Id) { + ActivePage = one; + return; + } + } + + var repo = Preference.FindRepository(node.Id); + if (repo == null || !Path.Exists(repo.FullPath)) { + var ctx = page == null ? ActivePage.Node.Id : page.Node.Id; + App.RaiseException(ctx, "Repository does NOT exists any more. Please remove it."); + return; + } + + repo.Open(); + + if (page == null) { + if (ActivePage == null || ActivePage.Node.IsRepository) { + page = new LauncherPage(node, repo); + Pages.Add(page); + } else { + page.Node = node; + page.View = new Views.Repository() { DataContext = repo }; + } + } else { + page.Node = node; + page.View = new Views.Repository() { DataContext = repo }; + } + + ActivePage = page; + } + + private void CloseRepositoryInTab(LauncherPage page) { + if (!page.Node.IsRepository) return; + + var repo = Preference.FindRepository(page.Node.Id); + if (repo == null) return; + + repo.Close(); + } + + private LauncherPage _activePage = null; + } +} diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs new file mode 100644 index 00000000..c147c270 --- /dev/null +++ b/src/ViewModels/LauncherPage.cs @@ -0,0 +1,53 @@ +using Avalonia.Collections; +using System; + +namespace SourceGit.ViewModels { + public class LauncherPage : PopupHost { + public RepositoryNode Node { + get => _node; + set => SetProperty(ref _node, value); + } + + public object View { + get => _view; + set => SetProperty(ref _view, value); + } + + public AvaloniaList Notifications { + get; + set; + } = new AvaloniaList(); + + public LauncherPage() { + _node = new RepositoryNode() { + Id = Guid.NewGuid().ToString(), + Name = "WelcomePage", + Bookmark = 0, + IsRepository = false, + }; + _view = new Views.Welcome() { DataContext = new Welcome() }; + } + + public LauncherPage(RepositoryNode node, Repository repo) { + _node = node; + _view = new Views.Repository() { DataContext = repo }; + } + + public override string GetId() { + return _node.Id; + } + + public void CopyPath() { + if (_node.IsRepository) App.CopyText(_node.Id); + } + + public void DismissNotification(object param) { + if (param is Models.Notification notice) { + Notifications.Remove(notice); + } + } + + private RepositoryNode _node = null; + private object _view = null; + } +} diff --git a/src/ViewModels/Merge.cs b/src/ViewModels/Merge.cs new file mode 100644 index 00000000..b7628739 --- /dev/null +++ b/src/ViewModels/Merge.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class MergeMode { + public string Name { get; set; } + public string Desc { get; set; } + public string Arg { get; set; } + + public MergeMode(string n, string d, string a) { + Name = n; + Desc = d; + Arg = a; + } + } + + public class Merge : Popup { + public string Source { + get; + private set; + } + + public string Into { + get; + private set; + } + + public List Modes { + get; + private set; + } + + public MergeMode SelectedMode { + get; + set; + } + + public Merge(Repository repo, string source, string into) { + _repo = repo; + Source = source; + Into = into; + Modes = new List() { + new MergeMode("Default", "Fast-forward if possible", ""), + new MergeMode("No Fast-forward", "Always create a merge commit", "--no-ff"), + new MergeMode("Squash", "Use '--squash'", "--squash"), + new MergeMode("Don't commit", "Merge without commit", "--no-commit"), + }; + SelectedMode = Modes[0]; + View = new Views.Merge() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Merging '{Source}' into '{Into}' ..."); + var succ = new Commands.Merge(_repo.FullPath, Source, SelectedMode.Arg, SetProgressDescription).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Popup.cs b/src/ViewModels/Popup.cs new file mode 100644 index 00000000..52e99c84 --- /dev/null +++ b/src/ViewModels/Popup.cs @@ -0,0 +1,51 @@ +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Popup : ObservableValidator { + public string HostPageId { + get; + set; + } + + public object View { + get; + set; + } + + public bool InProgress { + get => _inProgress; + set => SetProperty(ref _inProgress, value); + } + + public string ProgressDescription { + get => _progressDescription; + set => SetProperty(ref _progressDescription, value); + } + + [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode")] + public bool Check() { + if (HasErrors) return false; + ValidateAllProperties(); + return !HasErrors; + } + + public virtual Task Sure() { + return null; + } + + protected void CallUIThread(Action action) { + Dispatcher.UIThread.Invoke(action); + } + + protected void SetProgressDescription(string description) { + CallUIThread(() => ProgressDescription = description); + } + + private bool _inProgress = false; + private string _progressDescription = string.Empty; + } +} diff --git a/src/ViewModels/PopupHost.cs b/src/ViewModels/PopupHost.cs new file mode 100644 index 00000000..1d1cec02 --- /dev/null +++ b/src/ViewModels/PopupHost.cs @@ -0,0 +1,75 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels { + public class PopupHost : ObservableObject { + public static PopupHost Active { + get; + set; + } = null; + + public Popup Popup { + get => _popup; + set => SetProperty(ref _popup, value); + } + + public static bool CanCreatePopup() { + return Active != null && (Active._popup == null || !Active._popup.InProgress); + } + + public static void ShowPopup(Popup popup) { + popup.HostPageId = Active.GetId(); + Active.Popup = popup; + } + + public static async void ShowAndStartPopup(Popup popup) { + popup.HostPageId = Active.GetId(); + Active.Popup = popup; + + if (!popup.Check()) return; + + popup.InProgress = true; + var task = popup.Sure(); + if (task != null) { + var finished = await task; + if (finished) { + Active.Popup = null; + } else { + popup.InProgress = false; + } + } else { + Active.Popup = null; + } + } + + public virtual string GetId() { + return string.Empty; + } + + public async void ProcessPopup() { + if (_popup != null) { + if (!_popup.Check()) return; + + _popup.InProgress = true; + var task = _popup.Sure(); + if (task != null) { + var finished = await task; + if (finished) { + Popup = null; + } else { + _popup.InProgress = false; + } + } else { + Popup = null; + } + } + } + + public void CancelPopup() { + if (_popup == null) return; + if (_popup.InProgress) return; + Popup = null; + } + + private Popup _popup = null; + } +} diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs new file mode 100644 index 00000000..03951664 --- /dev/null +++ b/src/ViewModels/Preference.cs @@ -0,0 +1,258 @@ +using Avalonia.Collections; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SourceGit.ViewModels { + public class Preference : ObservableObject { + [JsonIgnore] + public static Preference Instance { + get { + if (_instance == null) { + if (!File.Exists(_savePath)) { + _instance = new Preference(); + } else { + try { + _instance = JsonSerializer.Deserialize(File.ReadAllText(_savePath), JsonSerializationCodeGen.Default.Preference); + } catch { + _instance = new Preference(); + } + } + } + + _instance.Repositories.RemoveAll(x => !Directory.Exists(x.FullPath)); + + if (!_instance.IsGitConfigured) { + _instance.GitInstallDir = Native.OS.FindGitInstallDir(); + } + + return _instance; + } + } + + public string Locale { + get => _locale; + set { + if (SetProperty(ref _locale, value)) { + App.SetLocale(value); + } + } + } + + public string Theme { + get => _theme; + set { + if (SetProperty(ref _theme, value)) { + App.SetTheme(value); + } + } + } + + public int MaxHistoryCommits { + get => _maxHistoryCommits; + set => SetProperty(ref _maxHistoryCommits, value); + } + + public bool RestoreTabs { + get => _restoreTabs; + set => SetProperty(ref _restoreTabs, value); + } + + public bool UseMacOSStyle { + get => _useMacOSStyle; + set => SetProperty(ref _useMacOSStyle, value); + } + + public bool UseTwoColumnsLayoutInHistories { + get => _useTwoColumnsLayoutInHistories; + set => SetProperty(ref _useTwoColumnsLayoutInHistories, value); + } + + public bool UseCombinedTextDiff { + get => _useCombinedTextDiff; + set => SetProperty(ref _useCombinedTextDiff, value); + } + + public Models.ChangeViewMode UnstagedChangeViewMode { + get => _unstagedChangeViewMode; + set => SetProperty(ref _unstagedChangeViewMode, value); + } + + public Models.ChangeViewMode StagedChangeViewMode { + get => _stagedChangeViewMode; + set => SetProperty(ref _stagedChangeViewMode, value); + } + + public Models.ChangeViewMode CommitChangeViewMode { + get => _commitChangeViewMode; + set => SetProperty(ref _commitChangeViewMode, value); + } + + [JsonIgnore] + public bool IsGitConfigured { + get => !string.IsNullOrEmpty(GitInstallDir) && Directory.Exists(GitInstallDir); + } + + public string GitInstallDir { + get => Native.OS.GitInstallDir; + set { + if (Native.OS.GitInstallDir != value) { + Native.OS.GitInstallDir = value; + OnPropertyChanged(nameof(GitInstallDir)); + } + } + } + + public string GitDefaultCloneDir { + get => _gitDefaultCloneDir; + set => SetProperty(ref _gitDefaultCloneDir, value); + } + + public bool GitAutoFetch { + get => _gitAutoFetch; + set => SetProperty(ref _gitAutoFetch, value); + } + + public int ExternalMergeToolType { + get => _externalMergeToolType; + set => SetProperty(ref _externalMergeToolType, value); + } + + public string ExternalMergeToolPath { + get => _externalMergeToolPath; + set => SetProperty(ref _externalMergeToolPath, value); + } + + public string ExternalMergeToolCmd { + get => _externalMergeToolCmd; + set => SetProperty(ref _externalMergeToolCmd, value); + } + + public string ExternalMergeToolDiffCmd { + get => _externalMergeToolDiffCmd; + set => SetProperty(ref _externalMergeToolDiffCmd, value); + } + + public List Repositories { + get; + set; + } = new List(); + + public AvaloniaList RepositoryNodes { + get => _repositoryNodes; + set => SetProperty(ref _repositoryNodes, value); + } + + public static void AddNode(RepositoryNode node, RepositoryNode to = null) { + var collection = to == null ? _instance._repositoryNodes : to.SubNodes; + var list = new List(); + list.AddRange(collection); + list.Add(node); + list.Sort((l, r) => { + if (l.IsRepository != r.IsRepository) { + return l.IsRepository ? 1 : -1; + } else { + return l.Name.CompareTo(r.Name); + } + }); + + collection.Clear(); + foreach (var one in list) { + collection.Add(one); + } + } + + public static void MoveNode(RepositoryNode node, RepositoryNode to = null) { + if (to == null && _instance._repositoryNodes.Contains(node)) return; + if (to != null && to.SubNodes.Contains(node)) return; + + RemoveNode(node); + AddNode(node, to); + } + + public static void RemoveNode(RepositoryNode node) { + RemoveNodeRecursive(node, _instance._repositoryNodes); + } + + public static Repository FindRepository(string path) { + var dir = new DirectoryInfo(path); + foreach (var repo in _instance.Repositories) { + if (repo.FullPath == dir.FullName) return repo; + } + return null; + } + + public static Repository AddRepository(string rootDir, string gitDir) { + var repo = FindRepository(rootDir); + if (repo != null) { + repo.GitDir = gitDir; + return repo; + } + + var dir = new DirectoryInfo(rootDir); + repo = new Repository() { + FullPath = dir.FullName, + GitDir = gitDir + }; + + _instance.Repositories.Add(repo); + return repo; + } + + public static void Save() { + var dir = Path.GetDirectoryName(_savePath); + if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); + + var data = JsonSerializer.Serialize(_instance, JsonSerializationCodeGen.Default.Preference); + File.WriteAllText(_savePath, data); + } + + private static bool RemoveNodeRecursive(RepositoryNode node, AvaloniaList collection) { + if (collection.Contains(node)) { + collection.Remove(node); + return true; + } + + foreach (RepositoryNode one in collection) { + if (RemoveNodeRecursive(node, one.SubNodes)) return true; + } + + return false; + } + + private static Preference _instance = null; + private static readonly string _savePath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "SourceGit", + "preference.json"); + + private string _locale = "en_US"; + private string _theme = "Default"; + private int _maxHistoryCommits = 20000; + private bool _restoreTabs = false; + private bool _useMacOSStyle = OperatingSystem.IsMacOS(); + private bool _useTwoColumnsLayoutInHistories = false; + private bool _useCombinedTextDiff = true; + + private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List; + private Models.ChangeViewMode _stagedChangeViewMode = Models.ChangeViewMode.List; + private Models.ChangeViewMode _commitChangeViewMode = Models.ChangeViewMode.List; + + private string _gitDefaultCloneDir = string.Empty; + private bool _gitAutoFetch = false; + + private int _externalMergeToolType = 0; + private string _externalMergeToolPath = string.Empty; + private string _externalMergeToolCmd = string.Empty; + private string _externalMergeToolDiffCmd = string.Empty; + + private AvaloniaList _repositoryNodes = new AvaloniaList(); + } + + [JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)] + [JsonSerializable(typeof(Preference))] + internal partial class JsonSerializationCodeGen : JsonSerializerContext { } +} \ No newline at end of file diff --git a/src/ViewModels/PruneRemote.cs b/src/ViewModels/PruneRemote.cs new file mode 100644 index 00000000..9d93fd18 --- /dev/null +++ b/src/ViewModels/PruneRemote.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class PruneRemote : Popup { + public Models.Remote Remote { + get; + private set; + } + + public PruneRemote(Repository repo, Models.Remote remote) { + _repo = repo; + Remote = remote; + View = new Views.PruneRemote() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription("Run `prune` on remote ..."); + var succ = new Commands.Remote(_repo.FullPath).Prune(Remote.Name); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Pull.cs b/src/ViewModels/Pull.cs new file mode 100644 index 00000000..fab1afb3 --- /dev/null +++ b/src/ViewModels/Pull.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Pull : Popup { + public List Remotes => _repo.Remotes; + public Models.Branch Current => _current; + + public bool HasSpecifiedRemoteBranch { + get; + private set; + } + + public Models.Remote SelectedRemote { + get => _selectedRemote; + set { + if (SetProperty(ref _selectedRemote, value)) { + var branches = new List(); + foreach (var branch in _repo.Branches) { + if (branch.Remote == value.Name) branches.Add(branch); + } + RemoteBranches = branches; + SelectedBranch = branches.Count > 0 ? branches[0] : null; + } + } + } + + public List RemoteBranches { + get => _remoteBranches; + private set => SetProperty(ref _remoteBranches, value); + } + + [Required(ErrorMessage = "Remote branch to pull is required!!!")] + public Models.Branch SelectedBranch { + get => _selectedBranch; + set => SetProperty(ref _selectedBranch, value); + } + + public bool UseRebase { + get; + set; + } + + public bool AutoStash { + get; + set; + } + + public Pull(Repository repo, Models.Branch specifiedRemoteBranch) { + _repo = repo; + _current = repo.Branches.Find(x => x.IsCurrent); + + if (specifiedRemoteBranch != null) { + _selectedRemote = repo.Remotes.Find(x => x.Name == specifiedRemoteBranch.Remote); + _selectedBranch = specifiedRemoteBranch; + HasSpecifiedRemoteBranch = true; + } else { + if (!string.IsNullOrEmpty(_current.Upstream)) { + foreach (var branch in repo.Branches) { + if (!branch.IsLocal && _current.Upstream == branch.FullName) { + _selectedRemote = repo.Remotes.Find(x => x.Name == branch.Remote); + _selectedBranch = branch; + break; + } + } + } + + HasSpecifiedRemoteBranch = false; + } + + // Make sure remote is exists. + if (_selectedRemote == null) { + _selectedRemote = repo.Remotes[0]; + _selectedBranch = null; + HasSpecifiedRemoteBranch = false; + } + + _remoteBranches = new List(); + foreach (var branch in _repo.Branches) { + if (branch.Remote == _selectedRemote.Name) _remoteBranches.Add(branch); + } + + if (_selectedBranch == null && _remoteBranches.Count > 0) { + _selectedBranch = _remoteBranches[0]; + } + + View = new Views.Pull() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var needPopStash = false; + if (AutoStash && _repo.WorkingCopyChangesCount > 0) { + SetProgressDescription("Adding untracked changes..."); + var succ = new Commands.Add(_repo.FullPath).Exec(); + if (succ) { + SetProgressDescription("Stash local changes..."); + succ = new Commands.Stash(_repo.FullPath).Push("PULL_AUTO_STASH"); + } + + if (!succ) { + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return false; + } + + needPopStash = true; + } + + SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}..."); + var rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, SetProgressDescription).Exec(); + if (rs && needPopStash) { + SetProgressDescription("Re-apply local changes..."); + rs = new Commands.Stash(_repo.FullPath).Pop("stash@{0}"); + } + + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return rs; + }); + } + + private Repository _repo = null; + private Models.Branch _current = null; + private Models.Remote _selectedRemote = null; + private List _remoteBranches = null; + private Models.Branch _selectedBranch = null; + } +} diff --git a/src/ViewModels/Push.cs b/src/ViewModels/Push.cs new file mode 100644 index 00000000..1a71ccc8 --- /dev/null +++ b/src/ViewModels/Push.cs @@ -0,0 +1,174 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Push : Popup { + public bool HasSpecifiedLocalBranch { + get; + private set; + } + + [Required(ErrorMessage = "Local branch is required!!!")] + public Models.Branch SelectedLocalBranch { + get => _selectedLocalBranch; + set { + if (SetProperty(ref _selectedLocalBranch, value)) { + // If selected local branch has upstream branch. Try to find it's remote. + if (!string.IsNullOrEmpty(value.Upstream)) { + var branch = _repo.Branches.Find(x => x.FullName == value.Upstream); + if (branch != null) { + var remote = _repo.Remotes.Find(x => x.Name == branch.Remote); + if (remote != null && remote != _selectedRemote) { + SelectedRemote = remote; + return; + } + } + } + + // Re-generate remote branches and auto-select remote branches. + AutoSelectBranchByRemote(); + } + } + } + + public List LocalBranches { + get; + private set; + } + + public List Remotes { + get => _repo.Remotes; + } + + [Required(ErrorMessage = "Remote is required!!!")] + public Models.Remote SelectedRemote { + get => _selectedRemote; + set { + if (SetProperty(ref _selectedRemote, value)) AutoSelectBranchByRemote(); + } + } + + public List RemoteBranches { + get => _remoteBranches; + private set => SetProperty(ref _remoteBranches, value); + } + + [Required(ErrorMessage = "Remote branch is required!!!")] + public Models.Branch SelectedRemoteBranch { + get => _selectedRemoteBranch; + set => SetProperty(ref _selectedRemoteBranch, value); + } + + public bool PushAllTags { + get; + set; + } + + public bool ForcePush { + get; + set; + } + + public Push(Repository repo, Models.Branch localBranch) { + _repo = repo; + + // Gather all local branches and find current branch. + LocalBranches = new List(); + var current = null as Models.Branch; + foreach (var branch in _repo.Branches) { + if (branch.IsLocal) LocalBranches.Add(branch); + if (branch.IsCurrent) current = branch; + } + + // Set default selected local branch. + if (localBranch != null) { + _selectedLocalBranch = localBranch; + HasSpecifiedLocalBranch = true; + } else { + _selectedLocalBranch = current; + HasSpecifiedLocalBranch = false; + } + + // Find preferred remote if selected local branch has upstream. + if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream)) { + foreach (var branch in repo.Branches) { + if (!branch.IsLocal && _selectedLocalBranch.Upstream == branch.FullName) { + _selectedRemote = repo.Remotes.Find(x => x.Name == branch.Remote); + break; + } + } + } + + // Set default remote to the first if haven't been set. + if (_selectedRemote == null) _selectedRemote = repo.Remotes[0]; + + // Auto select preferred remote branch. + AutoSelectBranchByRemote(); + + View = new Views.Push() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var remoteBranchName = _selectedRemoteBranch.Name.Replace(" (new)", ""); + SetProgressDescription($"Push {_selectedLocalBranch.Name} -> {_selectedRemote.Name}/{remoteBranchName} ..."); + var succ = new Commands.Push( + _repo.FullPath, + _selectedLocalBranch.Name, + _selectedRemote.Name, + remoteBranchName, + PushAllTags, + ForcePush, + string.IsNullOrEmpty(_selectedLocalBranch.Upstream), + SetProgressDescription).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private void AutoSelectBranchByRemote() { + // Gather branches. + var branches = new List(); + foreach (var branch in _repo.Branches) { + if (branch.Remote == _selectedRemote.Name) branches.Add(branch); + } + + // If selected local branch has upstream branch. Try to find it in current remote branches. + if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream)) { + foreach (var branch in branches) { + if (_selectedLocalBranch.Upstream == branch.FullName) { + RemoteBranches = branches; + SelectedRemoteBranch = branch; + return; + } + } + } + + // Find best remote branch by name. + foreach (var branch in branches) { + if (_selectedLocalBranch.Name == branch.Name) { + RemoteBranches = branches; + SelectedRemoteBranch = branch; + return; + } + } + + // Add a fake new branch. + var fake = new Models.Branch() { + Name = $"{_selectedLocalBranch.Name} (new)", + Remote = _selectedRemote.Name, + }; + branches.Add(fake); + RemoteBranches = branches; + SelectedRemoteBranch = fake; + } + + private Repository _repo = null; + private Models.Branch _selectedLocalBranch = null; + private Models.Remote _selectedRemote = null; + private List _remoteBranches = new List(); + private Models.Branch _selectedRemoteBranch = null; + } +} diff --git a/src/ViewModels/PushTag.cs b/src/ViewModels/PushTag.cs new file mode 100644 index 00000000..77b70998 --- /dev/null +++ b/src/ViewModels/PushTag.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class PushTag : Popup { + public Models.Tag Target { + get; + private set; + } + + public List Remotes { + get => _repo.Remotes; + } + + public Models.Remote SelectedRemote { + get; + set; + } + + public PushTag(Repository repo, Models.Tag target) { + _repo = repo; + Target = target; + SelectedRemote = _repo.Remotes[0]; + View = new Views.PushTag() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Pushing tag '{Target.Name}' to remote '{SelectedRemote.Name}' ..."); + var succ = new Commands.Push(_repo.FullPath, SelectedRemote.Name, Target.Name, false).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Rebase.cs b/src/ViewModels/Rebase.cs new file mode 100644 index 00000000..6789c9cc --- /dev/null +++ b/src/ViewModels/Rebase.cs @@ -0,0 +1,50 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Rebase : Popup { + public Models.Branch Current { + get; + private set; + } + + public object On { + get; + private set; + } + + public bool AutoStash { + get; + set; + } + + public Rebase(Repository repo, Models.Branch current, Models.Branch on) { + _repo = repo; + _revision = on.Head; + Current = current; + On = on; + AutoStash = true; + View = new Views.Rebase() { DataContext = this }; + } + + public Rebase(Repository repo, Models.Branch current, Models.Commit on) { + _repo = repo; + _revision = on.SHA; + Current = current; + On = on; + AutoStash = true; + View = new Views.Rebase() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.Rebase(_repo.FullPath, _revision, AutoStash).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _revision = string.Empty; + } +} diff --git a/src/ViewModels/RenameBranch.cs b/src/ViewModels/RenameBranch.cs new file mode 100644 index 00000000..0db0b2e9 --- /dev/null +++ b/src/ViewModels/RenameBranch.cs @@ -0,0 +1,53 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class RenameBranch : Popup { + public Models.Branch Target { + get; + private set; + } + + [Required(ErrorMessage = "Branch name is required!!!")] + [RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")] + [CustomValidation(typeof(RenameBranch), nameof(ValidateBranchName))] + public string Name { + get => _name; + set => SetProperty(ref _name, value, true); + } + + public RenameBranch(Repository repo, Models.Branch target) { + _repo = repo; + _name = target.Name; + Target = target; + View = new Views.RenameBranch() { DataContext = this }; + } + + public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) { + if (ctx.ObjectInstance is RenameBranch rename) { + foreach (var b in rename._repo.Branches) { + if (b != rename.Target && b.Name == name) { + return new ValidationResult("A branch with same name already exists!!!"); + } + } + } + + return ValidationResult.Success; + } + + public override Task Sure() { + if (_name == Target.Name) return null; + + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Rename '{Target.Name}'"); + var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, _name); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _name = string.Empty; + } +} diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs new file mode 100644 index 00000000..5cc94874 --- /dev/null +++ b/src/ViewModels/Repository.cs @@ -0,0 +1,1082 @@ +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Repository : ObservableObject, Models.IRepository { + public string FullPath { + get => _fullpath; + set => SetProperty(ref _fullpath, value); + } + + public string GitDir { + get => _gitDir; + set => SetProperty(ref _gitDir, value); + } + + public AvaloniaList Filters { + get; + private set; + } = new AvaloniaList(); + + public AvaloniaList CommitMessages { + get; + private set; + } = new AvaloniaList(); + + [JsonIgnore] + public bool IsVSCodeFound { + get => !string.IsNullOrEmpty(Native.OS.VSCodeExecutableFile); + } + + [JsonIgnore] + public Models.GitFlow GitFlow { + get => _gitflow; + set => SetProperty(ref _gitflow, value); + } + + [JsonIgnore] + public int SelectedViewIndex { + get => _selectedViewIndex; + set { + if (SetProperty(ref _selectedViewIndex, value)) { + switch (value) { + case 1: + SelectedView = _workingCopy; + break; + case 2: + SelectedView = _stashesPage; + break; + default: + SelectedView = _histories; + break; + } + } + } + } + + [JsonIgnore] + public object SelectedView { + get => _selectedView; + set => SetProperty(ref _selectedView, value); + } + + [JsonIgnore] + public List Remotes { + get => _remotes; + private set => SetProperty(ref _remotes, value); + } + + [JsonIgnore] + public List Branches { + get => _branches; + private set => SetProperty(ref _branches, value); + } + + [JsonIgnore] + public List LocalBranchTrees { + get => _localBranchTrees; + private set => SetProperty(ref _localBranchTrees, value); + } + + [JsonIgnore] + public List RemoteBranchTrees { + get => _remoteBranchTrees; + private set => SetProperty(ref _remoteBranchTrees, value); + } + + [JsonIgnore] + public List Tags { + get => _tags; + private set => SetProperty(ref _tags, value); + } + + [JsonIgnore] + public List Submodules { + get => _submodules; + private set => SetProperty(ref _submodules, value); + } + + [JsonIgnore] + public int WorkingCopyChangesCount { + get => _workingCopy.Count; + } + + [JsonIgnore] + public int StashesCount { + get => _stashesPage.Count; + } + + [JsonIgnore] + public bool IsConflictBarVisible { + get => _isConflictBarVisible; + private set => SetProperty(ref _isConflictBarVisible, value); + } + + [JsonIgnore] + public bool HasUnsolvedConflict { + get => _hasUnsolvedConflict; + private set => SetProperty(ref _hasUnsolvedConflict, value); + } + + [JsonIgnore] + public bool CanCommitWithPush { + get => _canCommitWithPush; + private set => SetProperty(ref _canCommitWithPush, value); + } + + [JsonIgnore] + public bool IncludeUntracked { + get => _includeUntracked; + set { + if (SetProperty(ref _includeUntracked, value)) { + Task.Run(RefreshWorkingCopyChanges); + } + } + } + + [JsonIgnore] + public bool IsSearching { + get => _isSearching; + set { + if (SetProperty(ref _isSearching, value)) { + SearchedCommits = new List(); + SearchCommitFilter = string.Empty; + if (value) SelectedViewIndex = 0; + } + } + } + + [JsonIgnore] + public string SearchCommitFilter { + get => _searchCommitFilter; + set => SetProperty(ref _searchCommitFilter, value); + } + + [JsonIgnore] + public List SearchedCommits { + get => _searchedCommits; + set => SetProperty(ref _searchedCommits, value); + } + + public void Open() { + _watcher = new Models.Watcher(this); + _histories = new Histories(this); + _workingCopy = new WorkingCopy(this); + _stashesPage = new StashesPage(this); + _selectedView = _histories; + _selectedViewIndex = 0; + _isConflictBarVisible = false; + _hasUnsolvedConflict = false; + + Task.Run(() => { + RefreshBranches(); + RefreshTags(); + RefreshCommits(); + }); + + Task.Run(RefreshSubmodules); + Task.Run(RefreshWorkingCopyChanges); + Task.Run(RefreshStashes); + Task.Run(RefreshGitFlow); + } + + public void Close() { + _watcher.Dispose(); + _watcher = null; + _histories = null; + _workingCopy = null; + _stashesPage = null; + _selectedView = null; + _isSearching = false; + _searchCommitFilter = string.Empty; + + _remotes.Clear(); + _branches.Clear(); + _localBranchTrees.Clear(); + _remoteBranchTrees.Clear(); + _tags.Clear(); + _submodules.Clear(); + _searchedCommits.Clear(); + + GC.Collect(); + } + + public void OpenInFileManager() { + Native.OS.OpenInFileManager(_fullpath); + } + + public void OpenInVSCode() { + Native.OS.OpenInVSCode(_fullpath); + } + + public void OpenInTerminal() { + Native.OS.OpenTerminal(_fullpath); + } + + public void Fetch() { + if (!PopupHost.CanCreatePopup()) return; + + if (Remotes.Count == 0) { + App.RaiseException(_fullpath, "No remotes added to this repository!!!"); + return; + } + + PopupHost.ShowPopup(new Fetch(this)); + } + + public void Pull() { + if (!PopupHost.CanCreatePopup()) return; + + if (Remotes.Count == 0) { + App.RaiseException(_fullpath, "No remotes added to this repository!!!"); + return; + } + + PopupHost.ShowPopup(new Pull(this, null)); + } + + public void Push() { + if (!PopupHost.CanCreatePopup()) return; + + if (Remotes.Count == 0) { + App.RaiseException(_fullpath, "No remotes added to this repository!!!"); + return; + } + + if (Branches.Find(x => x.IsCurrent) == null) App.RaiseException(_fullpath, "Can NOT found current branch!!!"); + PopupHost.ShowPopup(new Push(this, null)); + } + + public void ApplyPatch() { + if (!PopupHost.CanCreatePopup()) return; + PopupHost.ShowPopup(new Apply(this)); + } + + public void Cleanup() { + if (!PopupHost.CanCreatePopup()) return; + PopupHost.ShowAndStartPopup(new Cleanup(this)); + } + + public void OpenConfigure() { + if (!PopupHost.CanCreatePopup()) return; + PopupHost.ShowPopup(new RepositoryConfigure(this)); + } + + public void ClearSearchCommitFilter() { + SearchCommitFilter = string.Empty; + } + + public void StartSearchCommits() { + if (_histories == null) return; + + var visible = new List(); + foreach (var c in _histories.Commits) { + if (c.SHA.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) + || c.Subject.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) + || c.Message.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) + || c.Author.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase) + || c.Committer.Name.Contains(_searchCommitFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(c); + } + } + + SearchedCommits = visible; + } + + public void ExitSearchMode() { + IsSearching = false; + } + + public void SetWatcherEnabled(bool enabled) { + if (_watcher != null) _watcher.SetEnabled(enabled); + } + + public void NavigateToCommit(string sha) { + if (_histories != null) { + SelectedViewIndex = 0; + _histories.NavigateTo(sha); + } + } + + public void UpdateFilter(string filter, bool toggle) { + var changed = false; + if (toggle) { + if (!Filters.Contains(filter)) { + Filters.Add(filter); + changed = true; + } + } else { + changed = Filters.Remove(filter); + } + + if (changed) Task.Run(RefreshCommits); + } + + public void StashAll() { + if (PopupHost.CanCreatePopup()) { + var changes = new List(); + changes.AddRange(_workingCopy.Unstaged); + changes.AddRange(_workingCopy.Staged); + PopupHost.ShowPopup(new StashChanges(this, changes, true)); + } + } + + public void GotoResolve() { + if (_workingCopy != null) SelectedViewIndex = 1; + } + + public async void ContinueMerge() { + var cherryPickMerge = Path.Combine(_gitDir, "CHERRY_PICK_HEAD"); + var rebaseMerge = Path.Combine(_gitDir, "REBASE_HEAD"); + var rebaseMergeFolder = Path.Combine(_gitDir, "rebase-merge"); + var revertMerge = Path.Combine(_gitDir, "REVERT_HEAD"); + var otherMerge = Path.Combine(_gitDir, "MERGE_HEAD"); + + var mode = ""; + if (File.Exists(cherryPickMerge)) { + mode = "cherry-pick"; + } else if (File.Exists(rebaseMerge) && Directory.Exists(rebaseMergeFolder)) { + mode = "rebase"; + } else if (File.Exists(revertMerge)) { + mode = "revert"; + } else if (File.Exists(otherMerge)) { + mode = "merge"; + } else { + await Task.Run(RefreshWorkingCopyChanges); + return; + } + + var cmd = new Commands.Command(); + cmd.WorkingDirectory = _fullpath; + cmd.Context = _fullpath; + cmd.Args = $"-c core.editor=true {mode} --continue"; + + SetWatcherEnabled(false); + var succ = await Task.Run(cmd.Exec); + SetWatcherEnabled(true); + + if (succ) { + if (_workingCopy != null) _workingCopy.CommitMessage = string.Empty; + + if (mode == "rebase") { + if (File.Exists(rebaseMerge)) File.Delete(rebaseMerge); + if (Directory.Exists(rebaseMergeFolder)) Directory.Delete(rebaseMergeFolder); + } + } + } + + public async void AbortMerge() { + var cmd = new Commands.Command(); + cmd.WorkingDirectory = _fullpath; + cmd.Context = _fullpath; + + if (File.Exists(Path.Combine(_gitDir, "CHERRY_PICK_HEAD"))) { + cmd.Args = "cherry-pick --abort"; + } else if (File.Exists(Path.Combine(_gitDir, "REBASE_HEAD"))) { + cmd.Args = "rebase --abort"; + } else if (File.Exists(Path.Combine(_gitDir, "REVERT_HEAD"))) { + cmd.Args = "revert --abort"; + } else if (File.Exists(Path.Combine(_gitDir, "MERGE_HEAD"))) { + cmd.Args = "merge --abort"; + } else { + await Task.Run(RefreshWorkingCopyChanges); + return; + } + + SetWatcherEnabled(false); + await Task.Run(cmd.Exec); + SetWatcherEnabled(true); + } + + public void RefreshBranches() { + var branches = new Commands.QueryBranches(FullPath).Result(); + var remotes = new Commands.QueryRemotes(FullPath).Result(); + + var builder = new Models.BranchTreeNode.Builder(); + builder.CollectExpandedNodes(_localBranchTrees, true); + builder.CollectExpandedNodes(_remoteBranchTrees, false); + builder.Run(branches, remotes); + + Dispatcher.UIThread.Invoke(() => { + Remotes = remotes; + Branches = branches; + LocalBranchTrees = builder.Locals; + RemoteBranchTrees = builder.Remotes; + + var cur = Branches.Find(x => x.IsCurrent); + CanCommitWithPush = cur != null && !string.IsNullOrEmpty(cur.Upstream); + }); + } + + public void RefreshTags() { + var tags = new Commands.QueryTags(FullPath).Result(); + Dispatcher.UIThread.Invoke(() => { + Tags = tags; + }); + } + + public void RefreshCommits() { + Dispatcher.UIThread.Invoke(() => _histories.IsLoading = true); + + var limits = $"-{Preference.Instance.MaxHistoryCommits} "; + var validFilters = new List(); + foreach (var filter in Filters) { + if (filter.StartsWith("refs/")) { + if (_branches.FindIndex(x => x.FullName == filter) >= 0) validFilters.Add(filter); + } else { + if (_tags.FindIndex(t => t.Name == filter) >= 0) validFilters.Add(filter); + } + } + if (validFilters.Count > 0) { + limits += string.Join(" ", validFilters); + } else { + limits += "--branches --remotes --tags"; + } + + var commits = new Commands.QueryCommits(FullPath, limits).Result(); + Dispatcher.UIThread.Invoke(() => { + _histories.IsLoading = false; + _histories.Commits = commits; + }); + } + + public void RefreshSubmodules() { + var submodules = new Commands.QuerySubmodules(FullPath).Result(); + Dispatcher.UIThread.Invoke(() => { + Submodules = submodules; + }); + } + + public void RefreshWorkingCopyChanges() { + _watcher.MarkWorkingCopyRefreshed(); + + var changes = new Commands.QueryLocalChanges(FullPath, _includeUntracked).Result(); + var hasUnsolvedConflict = _workingCopy.SetData(changes); + + var cherryPickMerge = Path.Combine(_gitDir, "CHERRY_PICK_HEAD"); + var rebaseMerge = Path.Combine(_gitDir, "REBASE_HEAD"); + var rebaseMergeFolder = Path.Combine(_gitDir, "rebase-merge"); + var revertMerge = Path.Combine(_gitDir, "REVERT_HEAD"); + var otherMerge = Path.Combine(_gitDir, "MERGE_HEAD"); + var runningMerge = (File.Exists(cherryPickMerge) || + (File.Exists(rebaseMerge) && Directory.Exists(rebaseMergeFolder)) || + File.Exists(revertMerge) || + File.Exists(otherMerge)); + + if (!runningMerge) { + if (Directory.Exists(rebaseMergeFolder)) Directory.Delete(rebaseMergeFolder, true); + var applyFolder = Path.Combine(_gitDir, "rebase-apply"); + if (Directory.Exists(applyFolder)) Directory.Delete(applyFolder, true); + } + + Dispatcher.UIThread.Invoke(() => { + IsConflictBarVisible = runningMerge; + HasUnsolvedConflict = hasUnsolvedConflict; + OnPropertyChanged(nameof(WorkingCopyChangesCount)); + }); + } + + public void RefreshStashes() { + var stashes = new Commands.QueryStashes(FullPath).Result(); + Dispatcher.UIThread.Invoke(() => { + _stashesPage.Stashes = stashes; + OnPropertyChanged(nameof(StashesCount)); + }); + } + + public void RefreshGitFlow() { + var config = new Commands.Config(_fullpath).ListAll(); + var gitFlow = new Models.GitFlow(); + if (config.ContainsKey("gitflow.prefix.feature")) gitFlow.Feature = config["gitflow.prefix.feature"]; + if (config.ContainsKey("gitflow.prefix.release")) gitFlow.Release = config["gitflow.prefix.release"]; + if (config.ContainsKey("gitflow.prefix.hotfix")) gitFlow.Hotfix = config["gitflow.prefix.hotfix"]; + Dispatcher.UIThread.Invoke(() => { + GitFlow = gitFlow; + }); + } + + public void CreateNewBranch() { + var current = Branches.Find(x => x.IsCurrent); + if (current == null) { + App.RaiseException(_fullpath, "Git do not hold any branch until you do first commit."); + return; + } + + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, current)); + } + + public void CreateNewTag() { + var current = Branches.Find(x => x.IsCurrent); + if (current == null) { + App.RaiseException(_fullpath, "Git do not hold any branch until you do first commit."); + return; + } + + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, current)); + } + + public void AddRemote() { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new AddRemote(this)); + } + + public void AddSubmodule() { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new AddSubmodule(this)); + } + + public ContextMenu CreateContextMenuForGitFlow() { + var menu = new ContextMenu(); + menu.Placement = PlacementMode.BottomEdgeAlignedLeft; + + if (GitFlow.IsEnabled) { + var startFeature = new MenuItem(); + startFeature.Header = App.Text("GitFlow.StartFeature"); + startFeature.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowStart(this, Models.GitFlowBranchType.Feature)); + e.Handled = true; + }; + + var startRelease = new MenuItem(); + startRelease.Header = App.Text("GitFlow.StartRelease"); + startRelease.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowStart(this, Models.GitFlowBranchType.Release)); + e.Handled = true; + }; + + var startHotfix = new MenuItem(); + startHotfix.Header = App.Text("GitFlow.StartHotfix"); + startHotfix.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowStart(this, Models.GitFlowBranchType.Hotfix)); + e.Handled = true; + }; + + menu.Items.Add(startFeature); + menu.Items.Add(startRelease); + menu.Items.Add(startHotfix); + } else { + var init = new MenuItem(); + init.Header = App.Text("GitFlow.Init"); + init.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new InitGitFlow(this)); + e.Handled = true; + }; + menu.Items.Add(init); + } + return menu; + } + + public ContextMenu CreateContextMenuForLocalBranch(Models.Branch branch) { + var menu = new ContextMenu(); + + var push = new MenuItem(); + push.Header = App.Text("BranchCM.Push", branch.Name); + push.Icon = CreateMenuIcon("Icons.Push"); + push.IsEnabled = Remotes.Count > 0; + push.Click += (_, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Push(this, branch)); + e.Handled = true; + }; + + if (branch.IsCurrent) { + var discard = new MenuItem(); + discard.Header = App.Text("BranchCM.DiscardAll"); + discard.Icon = CreateMenuIcon("Icons.Undo"); + discard.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Discard(this)); + e.Handled = true; + }; + + menu.Items.Add(discard); + menu.Items.Add(new MenuItem() { Header = "-" }); + + if (!string.IsNullOrEmpty(branch.Upstream)) { + var upstream = branch.Upstream.Substring(13); + var fastForward = new MenuItem(); + fastForward.Header = App.Text("BranchCM.FastForward", upstream); + fastForward.Icon = CreateMenuIcon("Icons.FastForward"); + fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0; + fastForward.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(this, upstream, branch.Name)); + e.Handled = true; + }; + + var pull = new MenuItem(); + pull.Header = App.Text("BranchCM.Pull", upstream); + pull.Icon = CreateMenuIcon("Icons.Pull"); + pull.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus); + pull.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, null)); + e.Handled = true; + }; + + menu.Items.Add(fastForward); + menu.Items.Add(pull); + } + + menu.Items.Add(push); + } else { + var current = Branches.Find(x => x.IsCurrent); + + var checkout = new MenuItem(); + checkout.Header = App.Text("BranchCM.Checkout", branch.Name); + checkout.Icon = CreateMenuIcon("Icons.Check"); + checkout.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(this, branch.Name)); + e.Handled = true; + }; + menu.Items.Add(checkout); + + var upstream = Branches.Find(x => x.FullName == branch.Upstream); + if (upstream != null) { + var fastForward = new MenuItem(); + fastForward.Header = App.Text("BranchCM.FastForward", $"{upstream.Remote}/{upstream.Name}"); + fastForward.Icon = CreateMenuIcon("Icons.FastForward"); + fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0; + fastForward.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new FastForwardWithoutCheckout(this, branch, upstream)); + e.Handled = true; + }; + + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(fastForward); + } + + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(push); + + var merge = new MenuItem(); + merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name); + merge.Icon = CreateMenuIcon("Icons.Merge"); + merge.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, branch.Name, current.Name)); + e.Handled = true; + }; + + var rebase = new MenuItem(); + rebase.Header = App.Text("BranchCM.Rebase", current.Name, branch.Name); + rebase.Icon = CreateMenuIcon("Icons.Rebase"); + rebase.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch)); + e.Handled = true; + }; + + menu.Items.Add(merge); + menu.Items.Add(rebase); + } + + var type = GitFlow.GetBranchType(branch.Name); + if (type != Models.GitFlowBranchType.None) { + var finish = new MenuItem(); + finish.Header = App.Text("BranchCM.Finish", branch.Name); + finish.Icon = CreateMenuIcon("Icons.Flow"); + finish.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(this, branch, type)); + e.Handled = true; + }; + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(finish); + } + + var rename = new MenuItem(); + rename.Header = App.Text("BranchCM.Rename", branch.Name); + rename.Icon = CreateMenuIcon("Icons.Rename"); + rename.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(this, branch)); + e.Handled = true; + }; + + var delete = new MenuItem(); + delete.Header = App.Text("BranchCM.Delete", branch.Name); + delete.Icon = CreateMenuIcon("Icons.Clear"); + delete.IsEnabled = !branch.IsCurrent; + delete.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch)); + e.Handled = true; + }; + + var createBranch = new MenuItem(); + createBranch.Icon = CreateMenuIcon("Icons.Branch.Add"); + createBranch.Header = App.Text("CreateBranch"); + createBranch.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch)); + e.Handled = true; + }; + + var createTag = new MenuItem(); + createTag.Icon = CreateMenuIcon("Icons.Tag.Add"); + createTag.Header = App.Text("CreateTag"); + createTag.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, branch)); + e.Handled = true; + }; + + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(rename); + menu.Items.Add(delete); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(createBranch); + menu.Items.Add(createTag); + menu.Items.Add(new MenuItem() { Header = "-" }); + + var remoteBranches = new List(); + foreach (var b in Branches) { + if (!b.IsLocal) remoteBranches.Add(b); + } + + if (remoteBranches.Count > 0) { + var tracking = new MenuItem(); + tracking.Header = App.Text("BranchCM.Tracking"); + tracking.Icon = CreateMenuIcon("Icons.Branch"); + + foreach (var b in remoteBranches) { + var upstream = b.FullName.Replace("refs/remotes/", ""); + var target = new MenuItem(); + target.Header = upstream; + if (branch.Upstream == b.FullName) target.Icon = CreateMenuIcon("Icons.Check"); + + target.Click += (o, e) => { + if (Commands.Branch.SetUpstream(_fullpath, branch.Name, upstream)) { + Task.Run(RefreshBranches); + } + e.Handled = true; + }; + + tracking.Items.Add(target); + } + + var unsetUpstream = new MenuItem(); + unsetUpstream.Header = App.Text("BranchCM.UnsetUpstream"); + unsetUpstream.Click += (_, e) => { + if (Commands.Branch.SetUpstream(_fullpath, branch.Name, string.Empty)) { + Task.Run(RefreshBranches); + } + e.Handled = true; + }; + tracking.Items.Add(new MenuItem() { Header = "-" }); + tracking.Items.Add(unsetUpstream); + + menu.Items.Add(tracking); + } + + var archive = new MenuItem(); + archive.Icon = CreateMenuIcon("Icons.Archive"); + archive.Header = App.Text("Archive"); + archive.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, branch)); + e.Handled = true; + }; + menu.Items.Add(archive); + menu.Items.Add(new MenuItem() { Header = "-" }); + + var copy = new MenuItem(); + copy.Header = App.Text("BranchCM.CopyName"); + copy.Icon = CreateMenuIcon("Icons.Copy"); + copy.Click += (o, e) => { + App.CopyText(branch.Name); + e.Handled = true; + }; + menu.Items.Add(copy); + + return menu; + } + + public ContextMenu CreateContextMenuForRemote(Models.Remote remote) { + var menu = new ContextMenu(); + + var fetch = new MenuItem(); + fetch.Header = App.Text("RemoteCM.Fetch"); + fetch.Icon = CreateMenuIcon("Icons.Fetch"); + fetch.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Fetch(this, remote)); + e.Handled = true; + }; + + var prune = new MenuItem(); + prune.Header = App.Text("RemoteCM.Prune"); + prune.Icon = CreateMenuIcon("Icons.Clear2"); + prune.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new PruneRemote(this, remote)); + e.Handled = true; + }; + + var edit = new MenuItem(); + edit.Header = App.Text("RemoteCM.Edit"); + edit.Icon = CreateMenuIcon("Icons.Edit"); + edit.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new EditRemote(this, remote)); + e.Handled = true; + }; + + var delete = new MenuItem(); + delete.Header = App.Text("RemoteCM.Delete"); + delete.Icon = CreateMenuIcon("Icons.Clear"); + delete.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteRemote(this, remote)); + e.Handled = true; + }; + + var copy = new MenuItem(); + copy.Header = App.Text("RemoteCM.CopyURL"); + copy.Icon = CreateMenuIcon("Icons.Copy"); + copy.Click += (o, e) => { + App.CopyText(remote.URL); + e.Handled = true; + }; + + menu.Items.Add(fetch); + menu.Items.Add(prune); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(edit); + menu.Items.Add(delete); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copy); + return menu; + } + + public ContextMenu CreateContextMenuForRemoteBranch(Models.Branch branch) { + var menu = new ContextMenu(); + var current = Branches.Find(x => x.IsCurrent); + + var checkout = new MenuItem(); + checkout.Header = App.Text("BranchCM.Checkout", $"{branch.Remote}/{branch.Name}"); + checkout.Icon = CreateMenuIcon("Icons.Check"); + checkout.Click += (o, e) => { + foreach (var b in Branches) { + if (b.IsLocal && b.Upstream == branch.FullName) { + if (b.IsCurrent) return; + if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(this, b.Name)); + return; + } + } + + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch)); + e.Handled = true; + }; + menu.Items.Add(checkout); + menu.Items.Add(new MenuItem() { Header = "-" }); + + if (current != null) { + var pull = new MenuItem(); + pull.Header = App.Text("BranchCM.PullInto", $"{branch.Remote}/{branch.Name}", current.Name); + pull.Icon = CreateMenuIcon("Icons.Pull"); + pull.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, branch)); + e.Handled = true; + }; + + var merge = new MenuItem(); + merge.Header = App.Text("BranchCM.Merge", $"{branch.Remote}/{branch.Name}", current.Name); + merge.Icon = CreateMenuIcon("Icons.Merge"); + merge.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, $"{branch.Remote}/{branch.Name}", current.Name)); + e.Handled = true; + }; + + var rebase = new MenuItem(); + rebase.Header = App.Text("BranchCM.Rebase", current.Name, $"{branch.Remote}/{branch.Name}"); + rebase.Icon = CreateMenuIcon("Icons.Rebase"); + rebase.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch)); + e.Handled = true; + }; + + menu.Items.Add(pull); + menu.Items.Add(merge); + menu.Items.Add(rebase); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + + var delete = new MenuItem(); + delete.Header = App.Text("BranchCM.Delete", $"{branch.Remote}/{branch.Name}"); + delete.Icon = CreateMenuIcon("Icons.Clear"); + delete.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch)); + e.Handled = true; + }; + + var createBranch = new MenuItem(); + createBranch.Icon = CreateMenuIcon("Icons.Branch.Add"); + createBranch.Header = App.Text("CreateBranch"); + createBranch.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch)); + e.Handled = true; + }; + + var createTag = new MenuItem(); + createTag.Icon = CreateMenuIcon("Icons.Tag.Add"); + createTag.Header = App.Text("CreateTag"); + createTag.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, branch)); + e.Handled = true; + }; + + var archive = new MenuItem(); + archive.Icon = CreateMenuIcon("Icons.Archive"); + archive.Header = App.Text("Archive"); + archive.Click += (o, e) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, branch)); + e.Handled = true; + }; + + var copy = new MenuItem(); + copy.Header = App.Text("BranchCM.CopyName"); + copy.Icon = CreateMenuIcon("Icons.Copy"); + copy.Click += (o, e) => { + App.CopyText(branch.Remote + "/" + branch.Name); + e.Handled = true; + }; + + menu.Items.Add(delete); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(createBranch); + menu.Items.Add(createTag); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(archive); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copy); + return menu; + } + + public ContextMenu CreateContextMenuForTag(Models.Tag tag) { + var createBranch = new MenuItem(); + createBranch.Icon = CreateMenuIcon("Icons.Branch.Add"); + createBranch.Header = App.Text("CreateBranch"); + createBranch.Click += (o, ev) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, tag)); + ev.Handled = true; + }; + + var pushTag = new MenuItem(); + pushTag.Header = App.Text("TagCM.Push", tag.Name); + pushTag.Icon = CreateMenuIcon("Icons.Push"); + pushTag.IsEnabled = Remotes.Count > 0; + pushTag.Click += (o, ev) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(this, tag)); + ev.Handled = true; + }; + + var deleteTag = new MenuItem(); + deleteTag.Header = App.Text("TagCM.Delete", tag.Name); + deleteTag.Icon = CreateMenuIcon("Icons.Clear"); + deleteTag.Click += (o, ev) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(this, tag)); + ev.Handled = true; + }; + + var archive = new MenuItem(); + archive.Icon = CreateMenuIcon("Icons.Archive"); + archive.Header = App.Text("Archive"); + archive.Click += (o, ev) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, tag)); + ev.Handled = true; + }; + + var copy = new MenuItem(); + copy.Header = App.Text("TagCM.Copy"); + copy.Icon = CreateMenuIcon("Icons.Copy"); + copy.Click += (o, ev) => { + App.CopyText(tag.Name); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(createBranch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(pushTag); + menu.Items.Add(deleteTag); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(archive); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copy); + return menu; + } + + public ContextMenu CreateContextMenuForSubmodule(string submodule) { + var open = new MenuItem(); + open.Header = App.Text("Submodule.Open"); + open.Icon = CreateMenuIcon("Icons.Folder.Open"); + open.Click += (o, ev) => { + var root = Path.GetFullPath(Path.Combine(_fullpath, submodule)); + var gitDir = new Commands.QueryGitDir(root).Result(); + var repo = Preference.AddRepository(root, gitDir); + var node = new RepositoryNode() { + Id = root, + Name = Path.GetFileName(root), + Bookmark = 0, + IsRepository = true, + }; + + var launcher = App.GetTopLevel().DataContext as Launcher; + if (launcher != null) { + launcher.OpenRepositoryInTab(node, null); + } + + ev.Handled = true; + }; + + var copy = new MenuItem(); + copy.Header = App.Text("Submodule.CopyPath"); + copy.Icon = CreateMenuIcon("Icons.Copy"); + copy.Click += (o, ev) => { + App.CopyText(submodule); + ev.Handled = true; + }; + + var rm = new MenuItem(); + rm.Header = App.Text("Submodule.Remove"); + rm.Icon = CreateMenuIcon("Icons.Clear"); + rm.Click += (o, ev) => { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteSubmodule(this, submodule)); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(open); + menu.Items.Add(copy); + menu.Items.Add(rm); + return menu; + } + + private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { + var icon = new Avalonia.Controls.Shapes.Path(); + icon.Width = 12; + icon.Height = 12; + icon.Stretch = Stretch.Uniform; + icon.Data = App.Current?.FindResource(key) as StreamGeometry; + return icon; + } + + private string _fullpath = string.Empty; + private string _gitDir = string.Empty; + private Models.GitFlow _gitflow = new Models.GitFlow(); + + private Models.Watcher _watcher = null; + private Histories _histories = null; + private WorkingCopy _workingCopy = null; + private StashesPage _stashesPage = null; + private int _selectedViewIndex = 0; + private object _selectedView = null; + + private bool _isSearching = false; + private string _searchCommitFilter = string.Empty; + private List _searchedCommits = new List(); + + private List _remotes = new List(); + private List _branches = new List(); + private List _localBranchTrees = new List(); + private List _remoteBranchTrees = new List(); + private List _tags = new List(); + private List _submodules = new List(); + private bool _isConflictBarVisible = false; + private bool _hasUnsolvedConflict = false; + private bool _canCommitWithPush = false; + private bool _includeUntracked = true; + } +} diff --git a/src/ViewModels/RepositoryConfigure.cs b/src/ViewModels/RepositoryConfigure.cs new file mode 100644 index 00000000..928d58fd --- /dev/null +++ b/src/ViewModels/RepositoryConfigure.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class RepositoryConfigure : Popup { + public string UserName { + get; + set; + } + + public string UserEmail { + get; + set; + } + + public bool GPGSigningEnabled { + get; + set; + } + + public string GPGUserSigningKey { + get; + set; + } + + public string HttpProxy { + get; + set; + } + + public RepositoryConfigure(Repository repo) { + _repo = repo; + + _cached = new Commands.Config(repo.FullPath).ListAll(); + if (_cached.ContainsKey("user.name")) UserName = _cached["user.name"]; + if (_cached.ContainsKey("user.email")) UserEmail = _cached["user.email"]; + if (_cached.ContainsKey("commit.gpgsign")) GPGSigningEnabled = _cached["commit.gpgsign"] == "true"; + if (_cached.ContainsKey("user.signingkey")) GPGUserSigningKey = _cached["user.signingkey"]; + if (_cached.ContainsKey("http.proxy")) HttpProxy = _cached["user.signingkey"]; + + View = new Views.RepositoryConfigure() { DataContext = this }; + } + + public override Task Sure() { + SetIfChanged("user.name", UserName); + SetIfChanged("user.email", UserEmail); + SetIfChanged("commit.gpgsign", GPGSigningEnabled ? "true" : "false"); + SetIfChanged("user.signingkey", GPGUserSigningKey); + SetIfChanged("http.proxy", HttpProxy); + return null; + } + + private void SetIfChanged(string key, string value) { + bool changed = false; + if (_cached.ContainsKey(key)) { + changed = value != _cached[key]; + } else if (!string.IsNullOrEmpty(value)) { + changed = true; + } + + if (changed) { + new Commands.Config(_repo.FullPath).Set(key, value); + } + } + + private Repository _repo = null; + private Dictionary _cached = null; + } +} diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs new file mode 100644 index 00000000..6d3979ae --- /dev/null +++ b/src/ViewModels/RepositoryNode.cs @@ -0,0 +1,73 @@ +using Avalonia.Collections; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Text.Json.Serialization; + +namespace SourceGit.ViewModels { + public class RepositoryNode : ObservableObject { + public string Id { + get => _id; + set => SetProperty(ref _id, value); + } + + public string Name { + get => _name; + set => SetProperty(ref _name, value); + } + + public int Bookmark { + get => _bookmark; + set => SetProperty(ref _bookmark, value); + } + + public bool IsRepository { + get => _isRepository; + set => SetProperty(ref _isRepository, value); + } + + public bool IsExpanded { + get => _isExpanded; + set => SetProperty(ref _isExpanded, value); + } + + [JsonIgnore] + public bool IsVisible { + get => _isVisible; + set => SetProperty(ref _isVisible, value); + } + + public AvaloniaList SubNodes { + get => _subNodes; + set => SetProperty(ref _subNodes, value); + } + + public void Edit() { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new EditRepositoryNode(this)); + } + + public void AddSubFolder() { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateGroup(this)); + } + + public void OpenInFileManager() { + if (!IsRepository) return; + Native.OS.OpenInFileManager(_id); + } + + public void OpenTerminal() { + if (!IsRepository) return; + Native.OS.OpenTerminal(_id); + } + + public void Delete() { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteRepositoryNode(this)); + } + + private string _id = string.Empty; + private string _name = string.Empty; + private bool _isRepository = false; + private int _bookmark = 0; + private bool _isExpanded = false; + private bool _isVisible = true; + private AvaloniaList _subNodes = new AvaloniaList(); + } +} diff --git a/src/ViewModels/Reset.cs b/src/ViewModels/Reset.cs new file mode 100644 index 00000000..c415e08a --- /dev/null +++ b/src/ViewModels/Reset.cs @@ -0,0 +1,66 @@ +using Avalonia.Media; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class ResetMode { + public string Name { get; set; } + public string Desc { get; set; } + public string Arg { get; set; } + public IBrush Color { get; set; } + + public ResetMode(string n, string d, string a, IBrush b) { + Name = n; + Desc = d; + Arg = a; + Color = b; + } + } + + public class Reset : Popup { + public Models.Branch Current { + get; + private set; + } + + public Models.Commit To { + get; + private set; + } + + public List Modes { + get; + private set; + } + + public ResetMode SelectedMode { + get; + set; + } + + public Reset(Repository repo, Models.Branch current, Models.Commit to) { + _repo = repo; + Current = current; + To = to; + Modes = new List() { + new ResetMode("Soft", "Keep all changes. Stage differences", "--soft", Brushes.Green), + new ResetMode("Mixed", "Keep all changes. Unstage differences", "--mixed", Brushes.Orange), + new ResetMode("Hard", "Discard all changes", "--hard", Brushes.Red), + }; + SelectedMode = Modes[0]; + View = new Views.Reset() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Reset current branch to {To.SHA} ..."); + var succ = new Commands.Reset(_repo.FullPath, To.SHA, SelectedMode.Arg).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/Revert.cs b/src/ViewModels/Revert.cs new file mode 100644 index 00000000..49a379ab --- /dev/null +++ b/src/ViewModels/Revert.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Revert : Popup { + public Models.Commit Target { + get; + private set; + } + + public bool AutoCommit { + get; + set; + } + + public Revert(Repository repo, Models.Commit target) { + _repo = repo; + Target = target; + AutoCommit = true; + View = new Views.Revert() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Revert commit '{Target.SHA}' ..."); + var succ = new Commands.Revert(_repo.FullPath, Target.SHA, AutoCommit).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + } +} diff --git a/src/ViewModels/RevisionCompare.cs b/src/ViewModels/RevisionCompare.cs new file mode 100644 index 00000000..c5534931 --- /dev/null +++ b/src/ViewModels/RevisionCompare.cs @@ -0,0 +1,172 @@ +using Avalonia.Controls; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class RevisionCompare : ObservableObject { + public Models.Commit StartPoint { + get; + private set; + } + + public Models.Commit EndPoint { + get; + private set; + } + + public List VisibleChanges { + get => _visibleChanges; + private set => SetProperty(ref _visibleChanges, value); + } + + public List ChangeTree { + get => _changeTree; + private set => SetProperty(ref _changeTree, value); + } + + public Models.Change SelectedChange { + get => _selectedChange; + set { + if (SetProperty(ref _selectedChange, value)) { + if (value == null) { + SelectedNode = null; + DiffContext = null; + } else { + SelectedNode = FileTreeNode.SelectByPath(_changeTree, value.Path); + DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, EndPoint.SHA, value)); + } + } + } + } + + public FileTreeNode SelectedNode { + get => _selectedNode; + set { + if (SetProperty(ref _selectedNode, value)) { + if (value == null) { + SelectedChange = null; + } else { + SelectedChange = value.Backend as Models.Change; + } + } + } + } + + public string SearchFilter { + get => _searchFilter; + set { + if (SetProperty(ref _searchFilter, value)) { + RefreshVisible(); + } + } + } + + public DiffContext DiffContext { + get => _diffContext; + private set => SetProperty(ref _diffContext, value); + } + + public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint) { + _repo = repo; + StartPoint = startPoint; + EndPoint = endPoint; + + Task.Run(() => { + _changes = new Commands.CompareRevisions(_repo, startPoint.SHA, endPoint.SHA).Result(); + + var visible = _changes; + if (!string.IsNullOrWhiteSpace(_searchFilter)) { + visible = new List(); + foreach (var c in _changes) { + if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(c); + } + } + } + + var tree = FileTreeNode.Build(visible); + Dispatcher.UIThread.Invoke(() => { + VisibleChanges = visible; + ChangeTree = tree; + }); + }); + } + + public void NavigateTo(string commitSHA) { + var repo = Preference.FindRepository(_repo); + if (repo != null) repo.NavigateToCommit(commitSHA); + } + + public void ClearSearchFilter() { + SearchFilter = string.Empty; + } + + public ContextMenu CreateChangeContextMenu(Models.Change change) { + var menu = new ContextMenu(); + + if (change.Index != Models.ChangeState.Deleted) { + var history = new MenuItem(); + history.Header = App.Text("FileHistory"); + history.Click += (_, ev) => { + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) }; + window.Show(); + ev.Handled = true; + }; + + var full = Path.GetFullPath(Path.Combine(_repo, change.Path)); + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.IsEnabled = File.Exists(full); + explore.Click += (_, ev) => { + Native.OS.OpenInFileManager(full, true); + ev.Handled = true; + }; + + menu.Items.Add(history); + menu.Items.Add(explore); + } + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Click += (_, ev) => { + App.CopyText(change.Path); + ev.Handled = true; + }; + + menu.Items.Add(copyPath); + return menu; + } + + private void RefreshVisible() { + if (_changes == null) return; + + if (string.IsNullOrEmpty(_searchFilter)) { + VisibleChanges = _changes; + } else { + var visible = new List(); + foreach (var c in _changes) { + if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) { + visible.Add(c); + } + } + + VisibleChanges = visible; + } + + ChangeTree = FileTreeNode.Build(_visibleChanges); + } + + private string _repo = string.Empty; + private List _changes = null; + private List _visibleChanges = null; + private List _changeTree = null; + private Models.Change _selectedChange = null; + private FileTreeNode _selectedNode = null; + private string _searchFilter = string.Empty; + private DiffContext _diffContext = null; + } +} diff --git a/src/ViewModels/Reword.cs b/src/ViewModels/Reword.cs new file mode 100644 index 00000000..f864fa6d --- /dev/null +++ b/src/ViewModels/Reword.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Reword : Popup { + public Models.Commit Head { + get; + private set; + } + + [Required(ErrorMessage = "Commit message is required!!!")] + public string Message { + get => _message; + set => SetProperty(ref _message, value, true); + } + + public Reword(Repository repo, Models.Commit head) { + _repo = repo; + Head = head; + Message = head.FullMessage; + View = new Views.Reword() { DataContext = this }; + } + + public override Task Sure() { + if (_message == Head.FullMessage) return null; + + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + SetProgressDescription($"Editing head commit message ..."); + var succ = new Commands.Commit(_repo.FullPath, _message, true, true).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _message = string.Empty; + } +} diff --git a/src/ViewModels/Squash.cs b/src/ViewModels/Squash.cs new file mode 100644 index 00000000..5964b51b --- /dev/null +++ b/src/ViewModels/Squash.cs @@ -0,0 +1,43 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class Squash : Popup { + public Models.Commit Head { + get; + private set; + } + + public Models.Commit Parent { + get; + private set; + } + + [Required(ErrorMessage = "Commit message is required!!!")] + public string Message { + get => _message; + set => SetProperty(ref _message, value, true); + } + + public Squash(Repository repo, Models.Commit head, Models.Commit parent) { + _repo = repo; + _message = parent.FullMessage; + Head = head; + Parent = parent; + View = new Views.Squash() { DataContext = this }; + } + + public override Task Sure() { + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + var succ = new Commands.Reset(_repo.FullPath, Parent.SHA, "--soft").Exec(); + if (succ) succ = new Commands.Commit(_repo.FullPath, _message, true).Exec(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return succ; + }); + } + + private Repository _repo = null; + private string _message = string.Empty; + } +} diff --git a/src/ViewModels/StashChanges.cs b/src/ViewModels/StashChanges.cs new file mode 100644 index 00000000..7bdecd7c --- /dev/null +++ b/src/ViewModels/StashChanges.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class StashChanges : Popup { + + public string Message { + get; + set; + } + + public bool CanIgnoreUntracked { + get; + private set; + } + + public bool IncludeUntracked { + get; + set; + } + + public StashChanges(Repository repo, List changes, bool canIgnoreUntracked) { + _repo = repo; + _changes = changes; + + CanIgnoreUntracked = canIgnoreUntracked; + IncludeUntracked = true; + View = new Views.StashChanges() { DataContext = this }; + } + + public override Task Sure() { + var jobs = _changes; + if (CanIgnoreUntracked && !IncludeUntracked) { + jobs = new List(); + foreach (var job in _changes) { + if (job.WorkTree != Models.ChangeState.Untracked && job.WorkTree != Models.ChangeState.Added) { + jobs.Add(job); + } + } + } + + if (jobs.Count == 0) return null; + + _repo.SetWatcherEnabled(false); + return Task.Run(() => { + new Commands.Stash(_repo.FullPath).Push(jobs, Message); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return true; + }); + } + + private Repository _repo = null; + private List _changes = null; + } +} diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs new file mode 100644 index 00000000..12f8383b --- /dev/null +++ b/src/ViewModels/StashesPage.cs @@ -0,0 +1,105 @@ +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class StashesPage : ObservableObject { + public int Count { + get => _stashes == null ? 0 : _stashes.Count; + } + + public List Stashes { + get => _stashes; + set { + if (SetProperty(ref _stashes, value)) { + SelectedStash = null; + } + } + } + + public Models.Stash SelectedStash { + get => _selectedStash; + set { + if (SetProperty(ref _selectedStash, value)) { + if (value == null) { + Changes = null; + } else { + Task.Run(() => { + var changes = new Commands.QueryStashChanges(_repo.FullPath, value.SHA).Result(); + Dispatcher.UIThread.Invoke(() => { + Changes = changes; + }); + }); + } + } + } + } + + public List Changes { + get => _changes; + private set { + if (SetProperty(ref _changes, value)) { + SelectedChange = null; + } + } + } + + public Models.Change SelectedChange { + get => _selectedChange; + set { + if (SetProperty(ref _selectedChange, value)) { + if (value == null) { + DiffContext = null; + } else { + DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption($"{_selectedStash.SHA}^", _selectedStash.SHA, value)); + } + } + } + } + + public DiffContext DiffContext { + get => _diffContext; + private set => SetProperty(ref _diffContext, value); + } + + public StashesPage(Repository repo) { + _repo = repo; + } + + public void Apply(object param) { + if (param is Models.Stash stash) { + Task.Run(() => { + new Commands.Stash(_repo.FullPath).Apply(stash.Name); + }); + } + } + + public void Pop(object param) { + if (param is Models.Stash stash) { + Task.Run(() => { + new Commands.Stash(_repo.FullPath).Pop(stash.Name); + }); + } + } + + public void Drop(object param) { + if (param is Models.Stash stash && PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new DropStash(_repo.FullPath, stash)); + } + } + + public void Clear() { + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new ClearStashes(_repo)); + } + } + + private Repository _repo = null; + private List _stashes = null; + private Models.Stash _selectedStash = null; + private List _changes = null; + private Models.Change _selectedChange = null; + private DiffContext _diffContext = null; + } +} diff --git a/src/ViewModels/TwoSideTextDiff.cs b/src/ViewModels/TwoSideTextDiff.cs new file mode 100644 index 00000000..07f4cf95 --- /dev/null +++ b/src/ViewModels/TwoSideTextDiff.cs @@ -0,0 +1,52 @@ +using Avalonia; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.Generic; + +namespace SourceGit.ViewModels { + public class TwoSideTextDiff : ObservableObject { + public Vector SyncScrollOffset { + get => _syncScrollOffset; + set => SetProperty(ref _syncScrollOffset, value); + } + + public string File { get; set; } = string.Empty; + public List Old { get; set; } = new List(); + public List New { get; set; } = new List(); + public int MaxLineNumber = 0; + + public TwoSideTextDiff(Models.TextDiff diff) { + File = diff.File; + MaxLineNumber = diff.MaxLineNumber; + + foreach (var line in diff.Lines) { + switch (line.Type) { + case Models.TextDiffLineType.Added: + New.Add(line); + break; + case Models.TextDiffLineType.Deleted: + Old.Add(line); + break; + default: + FillEmptyLines(); + Old.Add(line); + New.Add(line); + break; + } + } + + FillEmptyLines(); + } + + private void FillEmptyLines() { + if (Old.Count < New.Count) { + int diff = New.Count - Old.Count; + for (int i = 0; i < diff; i++) Old.Add(new Models.TextDiffLine()); + } else if (Old.Count > New.Count) { + int diff = Old.Count - New.Count; + for (int i = 0; i < diff; i++) New.Add(new Models.TextDiffLine()); + } + } + + private Vector _syncScrollOffset; + } +} diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs new file mode 100644 index 00000000..b75ed664 --- /dev/null +++ b/src/ViewModels/Welcome.cs @@ -0,0 +1,102 @@ +using Avalonia.Collections; +using CommunityToolkit.Mvvm.ComponentModel; +using System; + +namespace SourceGit.ViewModels { + public class Welcome : ObservableObject { + public bool IsClearSearchVisible { + get => !string.IsNullOrEmpty(_searchFilter); + } + + public AvaloniaList RepositoryNodes { + get => Preference.Instance.RepositoryNodes; + } + + public string SearchFilter { + get => _searchFilter; + set { + if (SetProperty(ref _searchFilter, value)) { + Referesh(); + OnPropertyChanged(nameof(IsClearSearchVisible)); + } + } + } + + public void InitRepository(string path) { + if (!Preference.Instance.IsGitConfigured) { + App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured")); + return; + } + + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new Init(path)); + } + } + + public void Clone(object param) { + var page = param as LauncherPage; + + if (!Preference.Instance.IsGitConfigured) { + App.RaiseException(page.GetId(), App.Text("NotConfigured")); + return; + } + + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new Clone(page)); + } + } + + public void OpenTerminal() { + if (!Preference.Instance.IsGitConfigured) { + App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured")); + } else { + Native.OS.OpenTerminal(null); + } + } + + public void ClearSearchFilter() { + SearchFilter = string.Empty; + } + + public void AddFolder() { + if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateGroup(null)); + } + + public void MoveNode(RepositoryNode from, RepositoryNode to) { + Preference.MoveNode(from, to); + } + + private void Referesh() { + if (string.IsNullOrWhiteSpace(_searchFilter)) { + foreach (var node in RepositoryNodes) ResetVisibility(node); + } else { + foreach (var node in RepositoryNodes) SetVisibilityBySearch(node); + } + } + + private void ResetVisibility(RepositoryNode node) { + node.IsVisible = true; + foreach (var subNode in node.SubNodes) ResetVisibility(subNode); + } + + private void SetVisibilityBySearch(RepositoryNode node) { + if (!node.IsRepository) { + if (node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) { + node.IsVisible = true; + foreach (var subNode in node.SubNodes) ResetVisibility(subNode); + } else { + bool hasVisibleSubNode = false; + foreach (var subNode in node.SubNodes) { + SetVisibilityBySearch(subNode); + hasVisibleSubNode |= subNode.IsVisible; + } + node.IsVisible = hasVisibleSubNode; + } + } else { + node.IsVisible = node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase); + } + } + + private string _searchFilter = string.Empty; + } +} diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs new file mode 100644 index 00000000..a6ef0bb4 --- /dev/null +++ b/src/ViewModels/WorkingCopy.cs @@ -0,0 +1,600 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Platform.Storage; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels { + public class ConflictContext { + public Models.Change Change { get; set; } + } + + public class ViewChangeDetailContext { + public string FilePath { get; set; } = string.Empty; + public bool IsUnstaged { get; set; } = false; + } + + public class WorkingCopy : ObservableObject { + public bool IsStaging { + get => _isStaging; + private set => SetProperty(ref _isStaging, value); + } + + public bool IsUnstaging { + get => _isUnstaging; + private set => SetProperty(ref _isUnstaging, value); + } + + public bool IsCommitting { + get => _isCommitting; + private set => SetProperty(ref _isCommitting, value); + } + + public bool UseAmend { + get => _useAmend; + set => SetProperty(ref _useAmend, value); + } + + public List Unstaged { + get => _unstaged; + private set => SetProperty(ref _unstaged, value); + } + + public List Staged { + get => _staged; + private set => SetProperty(ref _staged, value); + } + + public int Count { + get => _count; + } + + public List UnstagedTree { + get => _unstagedTree; + private set => SetProperty(ref _unstagedTree, value); + } + + public List StagedTree { + get => _stagedTree; + private set => SetProperty(ref _stagedTree, value); + } + + public object DetailContext { + get => _detailContext; + private set => SetProperty(ref _detailContext, value); + } + + public string CommitMessage { + get => _commitMessage; + set => SetProperty(ref _commitMessage, value); + } + + public WorkingCopy(Repository repo) { + _repo = repo; + } + + public bool SetData(List changes) { + var unstaged = new List(); + var staged = new List(); + + var viewFile = _lastViewChange == null ? string.Empty : _lastViewChange.FilePath; + var viewChange = null as Models.Change; + var hasConflict = false; + foreach (var c in changes) { + if (c.Path == viewFile) { + viewChange = c; + } + + if (c.Index == Models.ChangeState.Modified + || c.Index == Models.ChangeState.Added + || c.Index == Models.ChangeState.Deleted + || c.Index == Models.ChangeState.Renamed) { + staged.Add(c); + } + + if (c.WorkTree != Models.ChangeState.None) { + unstaged.Add(c); + hasConflict |= c.IsConflit; + } + } + + _count = changes.Count; + + var unstagedTree = FileTreeNode.Build(unstaged); + var stagedTree = FileTreeNode.Build(staged); + Dispatcher.UIThread.Invoke(() => { + _isLoadingData = true; + Unstaged = unstaged; + Staged = staged; + UnstagedTree = unstagedTree; + StagedTree = stagedTree; + _isLoadingData = false; + SetDetail(viewChange, _lastViewChange == null || _lastViewChange.IsUnstaged); + }); + + return hasConflict; + } + + public void SetDetail(Models.Change change, bool isUnstaged) { + if (_isLoadingData) return; + + if (change == null) { + _lastViewChange = null; + DetailContext = null; + } else if (change.IsConflit) { + _lastViewChange = new ViewChangeDetailContext() { FilePath = change.Path, IsUnstaged = isUnstaged }; + DetailContext = new ConflictContext() { Change = change }; + } else { + _lastViewChange = new ViewChangeDetailContext() { FilePath = change.Path, IsUnstaged = isUnstaged }; + DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged)); + } + } + + public async void StageChanges(List changes) { + if (_unstaged.Count == 0 || changes.Count == 0) return; + + IsStaging = true; + _repo.SetWatcherEnabled(false); + if (changes.Count == _unstaged.Count) { + await Task.Run(() => new Commands.Add(_repo.FullPath).Exec()); + } else { + for (int i = 0; i < changes.Count; i += 10) { + var count = Math.Min(10, changes.Count - i); + var step = changes.GetRange(i, count); + await Task.Run(() => new Commands.Add(_repo.FullPath, step).Exec()); + } + } + _repo.RefreshWorkingCopyChanges(); + _repo.SetWatcherEnabled(true); + IsStaging = false; + } + + public async void UnstageChanges(List changes) { + if (_staged.Count == 0 || changes.Count == 0) return; + + IsUnstaging = true; + _repo.SetWatcherEnabled(false); + if (changes.Count == _staged.Count) { + await Task.Run(() => new Commands.Reset(_repo.FullPath).Exec()); + } else { + for (int i = 0; i < changes.Count; i += 10) { + var count = Math.Min(10, changes.Count - i); + var step = changes.GetRange(i, count); + await Task.Run(() => new Commands.Reset(_repo.FullPath, step).Exec()); + } + } + _repo.RefreshWorkingCopyChanges(); + _repo.SetWatcherEnabled(true); + IsUnstaging = false; + } + + public void Discard(List changes) { + if (PopupHost.CanCreatePopup()) { + if (changes.Count == _count) { + PopupHost.ShowPopup(new Discard(_repo)); + } else { + PopupHost.ShowPopup(new Discard(_repo, changes)); + } + } + } + + public async void UseTheirs() { + if (_detailContext is ConflictContext ctx) { + _repo.SetWatcherEnabled(false); + var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).File(ctx.Change.Path, true)); + if (succ) { + await Task.Run(() => new Commands.Add(_repo.FullPath, [ctx.Change]).Exec()); + } + _repo.RefreshWorkingCopyChanges(); + _repo.SetWatcherEnabled(true); + } + } + + public async void UseMine() { + if (_detailContext is ConflictContext ctx) { + _repo.SetWatcherEnabled(false); + var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).File(ctx.Change.Path, false)); + if (succ) { + await Task.Run(() => new Commands.Add(_repo.FullPath, [ctx.Change]).Exec()); + } + _repo.RefreshWorkingCopyChanges(); + _repo.SetWatcherEnabled(true); + } + } + + public async void UseExternalMergeTool() { + if (_detailContext is ConflictContext ctx) { + var type = Preference.Instance.ExternalMergeToolType; + var exec = Preference.Instance.ExternalMergeToolPath; + + var tool = Models.ExternalMergeTools.Supported.Find(x => x.Type == type); + if (tool == null) { + App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!"); + return; + } + + var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd; + + _repo.SetWatcherEnabled(false); + await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, ctx.Change.Path)); + _repo.SetWatcherEnabled(true); + } + } + + public async void DoCommit(bool autoPush) { + if (!PopupHost.CanCreatePopup()) { + App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!"); + return; + } + + if (_staged.Count == 0) { + App.RaiseException(_repo.FullPath, "No files added to commit!"); + return; + } + + if (string.IsNullOrWhiteSpace(_commitMessage)) { + App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!"); + return; + } + + PushCommitMessage(); + + IsCommitting = true; + _repo.SetWatcherEnabled(false); + var succ = await Task.Run(() => new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec()); + if (succ) { + CommitMessage = string.Empty; + UseAmend = false; + + if (autoPush) { + PopupHost.ShowAndStartPopup(new Push(_repo, null)); + } + } + _repo.RefreshWorkingCopyChanges(); + _repo.SetWatcherEnabled(true); + IsCommitting = false; + } + + public ContextMenu CreateContextMenuForUnstagedChanges(List changes) { + if (changes.Count == 0) return null; + + var menu = new ContextMenu(); + if (changes.Count == 1) { + var change = changes[0]; + var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path)); + + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = CreateMenuIcon("Icons.Folder.Open"); + explore.IsEnabled = File.Exists(path) || Directory.Exists(path); + explore.Click += (_, e) => { + Native.OS.OpenInFileManager(path, true); + e.Handled = true; + }; + + var openWith = new MenuItem(); + openWith.Header = App.Text("OpenWith"); + openWith.Icon = CreateMenuIcon("Icons.OpenWith"); + openWith.IsEnabled = File.Exists(path); + openWith.Click += (_, e) => { + Native.OS.OpenWithDefaultEditor(path); + e.Handled = true; + }; + + var stage = new MenuItem(); + stage.Header = App.Text("FileCM.Stage"); + stage.Icon = CreateMenuIcon("Icons.File.Add"); + stage.Click += (_, e) => { + StageChanges(changes); + e.Handled = true; + }; + + var discard = new MenuItem(); + discard.Header = App.Text("FileCM.Discard"); + discard.Icon = CreateMenuIcon("Icons.Undo"); + discard.Click += (_, e) => { + Discard(changes); + e.Handled = true; + }; + + var stash = new MenuItem(); + stash.Header = App.Text("FileCM.Stash"); + stash.Icon = CreateMenuIcon("Icons.Stashes"); + stash.Click += (_, e) => { + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new StashChanges(_repo, changes, false)); + } + e.Handled = true; + }; + + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = CreateMenuIcon("Icons.Diff"); + patch.Click += async (_, e) => { + var topLevel = App.GetTopLevel(); + if (topLevel == null) return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + if (storageFile != null) { + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath)); + if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); + } + + e.Handled = true; + }; + + var history = new MenuItem(); + history.Header = App.Text("FileHistory"); + history.Icon = CreateMenuIcon("Icons.Histories"); + history.Click += (_, e) => { + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo.FullPath, change.Path) }; + window.Show(); + e.Handled = true; + }; + + var assumeUnchanged = new MenuItem(); + assumeUnchanged.Header = App.Text("FileCM.AssumeUnchanged"); + assumeUnchanged.Icon = CreateMenuIcon("Icons.File.Ignore"); + assumeUnchanged.IsEnabled = change.WorkTree != Models.ChangeState.Untracked; + assumeUnchanged.Click += (_, e) => { + new Commands.AssumeUnchanged(_repo.FullPath).Add(change.Path); + e.Handled = true; + }; + + var copy = new MenuItem(); + copy.Header = App.Text("CopyPath"); + copy.Icon = CreateMenuIcon("Icons.Copy"); + copy.Click += (_, e) => { + App.CopyText(change.Path); + e.Handled = true; + }; + + menu.Items.Add(explore); + menu.Items.Add(openWith); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(stage); + menu.Items.Add(discard); + menu.Items.Add(stash); + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(history); + menu.Items.Add(assumeUnchanged); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copy); + } else { + var stage = new MenuItem(); + stage.Header = App.Text("FileCM.StageMulti", changes.Count); + stage.Icon = CreateMenuIcon("Icons.File.Add"); + stage.Click += (_, e) => { + StageChanges(changes); + e.Handled = true; + }; + + var discard = new MenuItem(); + discard.Header = App.Text("FileCM.DiscardMulti", changes.Count); + discard.Icon = CreateMenuIcon("Icons.Undo"); + discard.Click += (_, e) => { + Discard(changes); + e.Handled = true; + }; + + var stash = new MenuItem(); + stash.Header = App.Text("FileCM.StashMulti", changes.Count); + stash.Icon = CreateMenuIcon("Icons.Stashes"); + stash.Click += (_, e) => { + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new StashChanges(_repo, changes, false)); + } + e.Handled = true; + }; + + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = CreateMenuIcon("Icons.Diff"); + patch.Click += async (o, e) => { + var topLevel = App.GetTopLevel(); + if (topLevel == null) return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + if (storageFile != null) { + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath)); + if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); + } + + e.Handled = true; + }; + + menu.Items.Add(stage); + menu.Items.Add(discard); + menu.Items.Add(stash); + menu.Items.Add(patch); + } + + return menu; + } + + public ContextMenu CreateContextMenuForStagedChanges(List changes) { + if (changes.Count == 0) return null; + + var menu = new ContextMenu(); + if (changes.Count == 1) { + var change = changes[0]; + var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path)); + + var explore = new MenuItem(); + explore.IsEnabled = File.Exists(path) || Directory.Exists(path); + explore.Header = App.Text("RevealFile"); + explore.Icon = CreateMenuIcon("Icons.Folder.Open"); + explore.Click += (o, e) => { + Native.OS.OpenInFileManager(path, true); + e.Handled = true; + }; + + var openWith = new MenuItem(); + openWith.Header = App.Text("OpenWith"); + openWith.Icon = CreateMenuIcon("Icons.OpenWith"); + openWith.IsEnabled = File.Exists(path); + openWith.Click += (_, e) => { + Native.OS.OpenWithDefaultEditor(path); + e.Handled = true; + }; + + var unstage = new MenuItem(); + unstage.Header = App.Text("FileCM.Unstage"); + unstage.Icon = CreateMenuIcon("Icons.File.Remove"); + unstage.Click += (o, e) => { + UnstageChanges(changes); + e.Handled = true; + }; + + var stash = new MenuItem(); + stash.Header = App.Text("FileCM.Stash"); + stash.Icon = CreateMenuIcon("Icons.Stashes"); + stash.Click += (_, e) => { + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new StashChanges(_repo, changes, false)); + } + e.Handled = true; + }; + + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = CreateMenuIcon("Icons.Diff"); + patch.Click += async (o, e) => { + var topLevel = App.GetTopLevel(); + if (topLevel == null) return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + if (storageFile != null) { + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath)); + if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); + } + + e.Handled = true; + }; + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = CreateMenuIcon("Icons.Copy"); + copyPath.Click += (o, e) => { + App.CopyText(change.Path); + e.Handled = true; + }; + + menu.Items.Add(explore); + menu.Items.Add(openWith); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(unstage); + menu.Items.Add(stash); + menu.Items.Add(patch); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(copyPath); + } else { + var unstage = new MenuItem(); + unstage.Header = App.Text("FileCM.UnstageMulti", changes.Count); + unstage.Icon = CreateMenuIcon("Icons.File.Remove"); + unstage.Click += (o, e) => { + UnstageChanges(changes); + e.Handled = true; + }; + + var stash = new MenuItem(); + stash.Header = App.Text("FileCM.StashMulti", changes.Count); + stash.Icon = CreateMenuIcon("Icons.Stashes"); + stash.Click += (_, e) => { + if (PopupHost.CanCreatePopup()) { + PopupHost.ShowPopup(new StashChanges(_repo, changes, false)); + } + e.Handled = true; + }; + + var patch = new MenuItem(); + patch.Header = App.Text("FileCM.SaveAsPatch"); + patch.Icon = CreateMenuIcon("Icons.Diff"); + patch.Click += async (_, e) => { + var topLevel = App.GetTopLevel(); + if (topLevel == null) return; + + var options = new FilePickerSaveOptions(); + options.Title = App.Text("FileCM.SaveAsPatch"); + options.DefaultExtension = ".patch"; + options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; + + var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + if (storageFile != null) { + var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath)); + if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess")); + } + + e.Handled = true; + }; + + menu.Items.Add(unstage); + menu.Items.Add(stash); + menu.Items.Add(patch); + } + + return menu; + } + + private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { + var icon = new Avalonia.Controls.Shapes.Path(); + icon.Width = 12; + icon.Height = 12; + icon.Stretch = Stretch.Uniform; + icon.Data = App.Current?.FindResource(key) as StreamGeometry; + return icon; + } + + private void PushCommitMessage() { + var existIdx = _repo.CommitMessages.IndexOf(CommitMessage); + if (existIdx == 0) { + return; + } else if (existIdx > 0) { + _repo.CommitMessages.Move(existIdx, 0); + return; + } + + if (_repo.CommitMessages.Count > 9) { + _repo.CommitMessages.RemoveRange(9, _repo.CommitMessages.Count - 9); + } + + _repo.CommitMessages.Insert(0, CommitMessage); + } + + private Repository _repo = null; + private bool _isLoadingData = false; + private bool _isStaging = false; + private bool _isUnstaging = false; + private bool _isCommitting = false; + private bool _useAmend = false; + private List _unstaged = null; + private List _staged = null; + private int _count = 0; + private List _unstagedTree = null; + private List _stagedTree = null; + private ViewChangeDetailContext _lastViewChange = null; + private object _detailContext = null; + private string _commitMessage = string.Empty; + } +} diff --git a/src/Views/About.axaml b/src/Views/About.axaml new file mode 100644 index 00000000..9c9f0a9f --- /dev/null +++ b/src/Views/About.axaml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/About.axaml.cs b/src/Views/About.axaml.cs new file mode 100644 index 00000000..90d047ef --- /dev/null +++ b/src/Views/About.axaml.cs @@ -0,0 +1,39 @@ +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using System.Reflection; + +namespace SourceGit.Views { + public partial class About : Window { + public string Version { + get; + private set; + } + + public About() { + var ver = Assembly.GetExecutingAssembly().GetName().Version; + Version = $"{ver.Major}.{ver.Minor}"; + DataContext = this; + InitializeComponent(); + } + + private void CloseWindow(object sender, RoutedEventArgs e) { + Close(); + } + + private void OnVisitAvaloniaUI(object sender, PointerPressedEventArgs e) { + Native.OS.OpenBrowser("https://www.avaloniaui.net/"); + e.Handled = true; + } + + private void OnVisitAvaloniaEdit(object sender, PointerPressedEventArgs e) { + Native.OS.OpenBrowser("https://www.nuget.org/packages/OneWare.AvaloniaEdit"); + e.Handled = true; + } + + private void OnVisitJetBrainsMonoFont(object sender, PointerPressedEventArgs e) { + Native.OS.OpenBrowser("https://www.jetbrains.com/lp/mono/"); + e.Handled = true; + } + } +} diff --git a/src/Views/About.xaml b/src/Views/About.xaml deleted file mode 100644 index 1b754a30..00000000 --- a/src/Views/About.xaml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/About.xaml.cs b/src/Views/About.xaml.cs deleted file mode 100644 index cce5e618..00000000 --- a/src/Views/About.xaml.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using System.Windows; - -namespace SourceGit.Views { - - ///

- /// 关于对话框 - /// - public partial class About : Controls.Window { - - public class Keymap { - public string Key { get; set; } - public string Desc { get; set; } - public Keymap(string k, string d) { Key = k; Desc = App.Text($"Hotkeys.{d}"); } - } - - public About() { - InitializeComponent(); - - var asm = Assembly.GetExecutingAssembly().GetName(); - version.Text = $"VERSION : v{asm.Version.Major}.{asm.Version.Minor}"; - - hotkeys.ItemsSource = new List() { - new Keymap("CTRL + T", "NewTab"), - new Keymap("CTRL + W", "CloseTab"), - new Keymap("CTRL + TAB", "NextTab"), - new Keymap("CTRL + [1-9]", "SwitchTo"), - new Keymap("CTRL + F", "Search"), - new Keymap("F5", "Refresh"), - new Keymap("SPACE", "ToggleStage"), - new Keymap("ESC", "CancelPopup"), - }; - } - - private void OnRequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) { - var info = new ProcessStartInfo("cmd", $"/c start {e.Uri.AbsoluteUri}"); - info.CreateNoWindow = true; - Process.Start(info); - } - - private void Quit(object sender, RoutedEventArgs e) { - Close(); - } - } -} diff --git a/src/Views/AddRemote.axaml b/src/Views/AddRemote.axaml new file mode 100644 index 00000000..4b460d79 --- /dev/null +++ b/src/Views/AddRemote.axaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/AddRemote.axaml.cs b/src/Views/AddRemote.axaml.cs new file mode 100644 index 00000000..f3640e30 --- /dev/null +++ b/src/Views/AddRemote.axaml.cs @@ -0,0 +1,22 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; + +namespace SourceGit.Views { + public partial class AddRemote : UserControl { + public AddRemote() { + InitializeComponent(); + } + + private async void SelectSSHKey(object sender, RoutedEventArgs e) { + var options = new FilePickerOpenOptions() { AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("SSHKey") { Patterns = ["*.*"] }] }; + var toplevel = TopLevel.GetTopLevel(this); + var selected = await toplevel.StorageProvider.OpenFilePickerAsync(options); + if (selected.Count == 1) { + txtSSHKey.Text = selected[0].Path.LocalPath; + } + + e.Handled = true; + } + } +} diff --git a/src/Views/AddSubmodule.axaml b/src/Views/AddSubmodule.axaml new file mode 100644 index 00000000..f7c70bfd --- /dev/null +++ b/src/Views/AddSubmodule.axaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + diff --git a/src/Views/AddSubmodule.axaml.cs b/src/Views/AddSubmodule.axaml.cs new file mode 100644 index 00000000..358faaa7 --- /dev/null +++ b/src/Views/AddSubmodule.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class AddSubmodule : UserControl { + public AddSubmodule() { + InitializeComponent(); + } + } +} diff --git a/src/Views/Apply.axaml b/src/Views/Apply.axaml new file mode 100644 index 00000000..979741c8 --- /dev/null +++ b/src/Views/Apply.axaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Apply.axaml.cs b/src/Views/Apply.axaml.cs new file mode 100644 index 00000000..3f61a4dd --- /dev/null +++ b/src/Views/Apply.axaml.cs @@ -0,0 +1,24 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; + +namespace SourceGit.Views { + public partial class Apply : UserControl { + public Apply() { + InitializeComponent(); + } + + private async void SelectPatchFile(object sender, RoutedEventArgs e) { + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel == null) return; + + var options = new FilePickerOpenOptions() { AllowMultiple = false, FileTypeFilter = [ new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }] }; + var selected = await topLevel.StorageProvider.OpenFilePickerAsync(options); + if (selected.Count == 1) { + txtPatchFile.Text = selected[0].Path.LocalPath; + } + + e.Handled = true; + } + } +} diff --git a/src/Views/Archive.axaml b/src/Views/Archive.axaml new file mode 100644 index 00000000..be2d0ddc --- /dev/null +++ b/src/Views/Archive.axaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Archive.axaml.cs b/src/Views/Archive.axaml.cs new file mode 100644 index 00000000..63e73448 --- /dev/null +++ b/src/Views/Archive.axaml.cs @@ -0,0 +1,22 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; + +namespace SourceGit.Views { + public partial class Archive : UserControl { + public Archive() { + InitializeComponent(); + } + + private async void SelectOutputFile(object sender, RoutedEventArgs e) { + var options = new FilePickerSaveOptions() { DefaultExtension = ".zip", FileTypeChoices = [ new FilePickerFileType("ZIP") { Patterns = [ "*.zip" ]}] }; + var toplevel = TopLevel.GetTopLevel(this); + var selected = await toplevel.StorageProvider.SaveFilePickerAsync(options); + if (selected != null) { + txtSaveFile.Text = selected.Path.LocalPath; + } + + e.Handled = true; + } + } +} diff --git a/src/Views/AssumeUnchanged.xaml b/src/Views/AssumeUnchanged.xaml deleted file mode 100644 index 01d5413f..00000000 --- a/src/Views/AssumeUnchanged.xaml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/AssumeUnchanged.xaml.cs b/src/Views/AssumeUnchanged.xaml.cs deleted file mode 100644 index 1336f0bb..00000000 --- a/src/Views/AssumeUnchanged.xaml.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Collections.ObjectModel; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; - -namespace SourceGit.Views { - /// - /// 管理不跟踪变更的文件 - /// - public partial class AssumeUnchanged : Controls.Window { - private string repo = null; - - public ObservableCollection Files { get; set; } - - public AssumeUnchanged(string repo) { - this.repo = repo; - this.Files = new ObservableCollection(); - - InitializeComponent(); - - Task.Run(() => { - var unchanged = new Commands.AssumeUnchanged(repo).View(); - Dispatcher.Invoke(() => { - if (unchanged.Count > 0) { - foreach (var file in unchanged) Files.Add(file); - - mask.Visibility = Visibility.Collapsed; - list.Visibility = Visibility.Visible; - list.ItemsSource = Files; - } else { - list.Visibility = Visibility.Collapsed; - mask.Visibility = Visibility.Visible; - } - }); - }); - } - - private void OnQuit(object sender, RoutedEventArgs e) { - Close(); - } - - private void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) { - e.Handled = true; - } - - private void Remove(object sender, RoutedEventArgs e) { - var btn = sender as Button; - if (btn == null) return; - - var file = btn.DataContext as string; - if (file == null) return; - - new Commands.AssumeUnchanged(repo).Remove(file); - Files.Remove(file); - - if (Files.Count == 0) { - list.Visibility = Visibility.Collapsed; - mask.Visibility = Visibility.Visible; - } - - e.Handled = true; - } - } -} diff --git a/src/Views/AssumeUnchangedManager.axaml b/src/Views/AssumeUnchangedManager.axaml new file mode 100644 index 00000000..b7c59691 --- /dev/null +++ b/src/Views/AssumeUnchangedManager.axaml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/AssumeUnchangedManager.axaml.cs b/src/Views/AssumeUnchangedManager.axaml.cs new file mode 100644 index 00000000..616db87d --- /dev/null +++ b/src/Views/AssumeUnchangedManager.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace SourceGit.Views { + public partial class AssumeUnchangedManager : Window { + public AssumeUnchangedManager() { + InitializeComponent(); + } + + private void CloseWindow(object sender, RoutedEventArgs e) { + Close(); + } + } +} diff --git a/src/Views/Avatar.cs b/src/Views/Avatar.cs new file mode 100644 index 00000000..3feb04bb --- /dev/null +++ b/src/Views/Avatar.cs @@ -0,0 +1,122 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using System; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace SourceGit.Views { + public class Avatar : Control, Models.IAvatarHost { + private static readonly GradientStops[] FALLBACK_GRADIENTS = [ + new GradientStops() { new GradientStop(Colors.Orange, 0), new GradientStop(Color.FromRgb(255, 213, 134), 1) }, + new GradientStops() { new GradientStop(Colors.DodgerBlue, 0), new GradientStop(Colors.LightSkyBlue, 1) }, + new GradientStops() { new GradientStop(Colors.LimeGreen, 0), new GradientStop(Color.FromRgb(124, 241, 124), 1) }, + new GradientStops() { new GradientStop(Colors.Orchid, 0), new GradientStop(Color.FromRgb(248, 161, 245), 1) }, + new GradientStops() { new GradientStop(Colors.Tomato, 0), new GradientStop(Color.FromRgb(252, 165, 150), 1) }, + ]; + + public static readonly StyledProperty FallbackFontFamilyProperty = + AvaloniaProperty.Register(nameof(FallbackFontFamily)); + + public FontFamily FallbackFontFamily { + get => GetValue(FallbackFontFamilyProperty); + set => SetValue(FallbackFontFamilyProperty, value); + } + + public static readonly StyledProperty UserProperty = + AvaloniaProperty.Register(nameof(User)); + + public Models.User User { + get => GetValue(UserProperty); + set => SetValue(UserProperty, value); + } + + static Avatar() { + AffectsRender(FallbackFontFamilyProperty); + UserProperty.Changed.AddClassHandler(OnUserPropertyChanged); + } + + public Avatar() { + var refetch = new MenuItem() { Header = App.Text("RefetchAvatar") }; + refetch.Click += (o, e) => { + if (User != null) { + _image = Models.AvatarManager.Request(_emailMD5, true); + InvalidateVisual(); + } + }; + + ContextMenu = new ContextMenu(); + ContextMenu.Items.Add(refetch); + + Models.AvatarManager.Subscribe(this); + } + + public override void Render(DrawingContext context) { + if (User == null) return; + + float corner = (float)Math.Max(2, Bounds.Width / 16); + if (_image != null) { + var rect = new Rect(0, 0, Bounds.Width, Bounds.Height); + context.PushClip(new RoundedRect(rect, corner)); + context.DrawImage(_image, rect); + } else { + Point textOrigin = new Point((Bounds.Width - _fallbackLabel.Width) * 0.5, (Bounds.Height - _fallbackLabel.Height) * 0.5); + context.DrawRectangle(_fallbackBrush, null, new Rect(0, 0, Bounds.Width, Bounds.Height), corner, corner); + context.DrawText(_fallbackLabel, textOrigin); + } + } + + public void OnAvatarResourceReady(string md5, Bitmap bitmap) { + if (_emailMD5 == md5) { + _image = bitmap; + InvalidateVisual(); + } + } + + private static void OnUserPropertyChanged(Avatar avatar, AvaloniaPropertyChangedEventArgs e) { + if (avatar.User == null) { + avatar._emailMD5 = null; + return; + } + + var placeholder = string.IsNullOrWhiteSpace(avatar.User.Name) ? "?" : avatar.User.Name.Substring(0, 1); + var chars = placeholder.ToCharArray(); + var sum = 0; + foreach (var c in chars) sum += Math.Abs(c); + + var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(avatar.User.Email.ToLower().Trim())); + var builder = new StringBuilder(); + foreach (var c in hash) builder.Append(c.ToString("x2")); + var md5 = builder.ToString(); + if (avatar._emailMD5 != md5) { + avatar._emailMD5 = md5; + avatar._image = Models.AvatarManager.Request(md5, false); + } + + avatar._fallbackBrush = new LinearGradientBrush { + GradientStops = FALLBACK_GRADIENTS[sum % FALLBACK_GRADIENTS.Length], + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }; + + var typeface = avatar.FallbackFontFamily == null ? Typeface.Default : new Typeface(avatar.FallbackFontFamily); + + avatar._fallbackLabel = new FormattedText( + placeholder, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + avatar.Width * 0.65, + Brushes.White); + + avatar.InvalidateVisual(); + } + + private FormattedText _fallbackLabel = null; + private LinearGradientBrush _fallbackBrush = null; + private string _emailMD5 = null; + private Bitmap _image = null; + } +} diff --git a/src/Views/Blame.axaml b/src/Views/Blame.axaml new file mode 100644 index 00000000..d3d054da --- /dev/null +++ b/src/Views/Blame.axaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs new file mode 100644 index 00000000..5492cba7 --- /dev/null +++ b/src/Views/Blame.axaml.cs @@ -0,0 +1,261 @@ +using Avalonia; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Styling; +using AvaloniaEdit; +using AvaloniaEdit.Document; +using AvaloniaEdit.Editing; +using AvaloniaEdit.Rendering; +using AvaloniaEdit.TextMate; +using AvaloniaEdit.Utils; +using System; +using System.Globalization; +using System.IO; +using TextMateSharp.Grammars; + +namespace SourceGit.Views { + public class BlameTextEditor : TextEditor { + public class CommitInfoMargin : AbstractMargin { + public CommitInfoMargin(BlameTextEditor editor) { + _editor = editor; + ClipToBounds = true; + } + + public override void Render(DrawingContext context) { + if (_editor.BlameData == null) return; + + var view = TextView; + if (view != null && view.VisualLinesValid) { + var typeface = view.CreateTypeface(); + var underlinePen = new Pen(Brushes.DarkOrange, 1); + + foreach (var line in view.VisualLines) { + var lineNumber = line.FirstDocumentLine.LineNumber; + if (lineNumber > _editor.BlameData.LineInfos.Count) break; + + var info = _editor.BlameData.LineInfos[lineNumber - 1]; + if (!info.IsFirstInGroup) continue; + + var x = 0.0; + var y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextTop) - view.VerticalOffset; + + var shaLink = new FormattedText( + info.CommitSHA, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + _editor.FontSize, + Brushes.DarkOrange); + context.DrawText(shaLink, new Point(x, y)); + context.DrawLine(underlinePen, new Point(x, y + shaLink.Baseline + 2), new Point(x + shaLink.Width, y + shaLink.Baseline + 2)); + x += shaLink.Width + 8; + + var time = new FormattedText( + info.Time, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + _editor.FontSize, + _editor.Foreground); + context.DrawText(time, new Point(x, y)); + x += time.Width + 8; + + var author = new FormattedText( + info.Author, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + _editor.FontSize, + _editor.Foreground); + context.DrawText(author, new Point(x, y)); + } + } + } + + protected override Size MeasureOverride(Size availableSize) { + return new Size(250, 0); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) { + base.OnPointerPressed(e); + + var view = TextView; + if (!e.Handled && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && view != null && view.VisualLinesValid) { + var pos = e.GetPosition(this); + var typeface = view.CreateTypeface(); + + foreach (var line in view.VisualLines) { + var lineNumber = line.FirstDocumentLine.LineNumber; + if (lineNumber >= _editor.BlameData.LineInfos.Count) break; + + var info = _editor.BlameData.LineInfos[lineNumber - 1]; + if (!info.IsFirstInGroup) continue; + + var y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextTop) - view.VerticalOffset; + var shaLink = new FormattedText( + info.CommitSHA, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + _editor.FontSize, + Brushes.DarkOrange); + + var rect = new Rect(0, y, shaLink.Width, shaLink.Height); + if (rect.Contains(pos)) { + _editor.OnCommitSHAClicked(info.CommitSHA); + e.Handled = true; + break; + } + } + } + } + + private BlameTextEditor _editor = null; + } + + public class VerticalSeperatorMargin : AbstractMargin { + public VerticalSeperatorMargin(BlameTextEditor editor) { + _editor = editor; + } + + public override void Render(DrawingContext context) { + var pen = new Pen(_editor.BorderBrush, 1); + context.DrawLine(pen, new Point(0, 0), new Point(0, Bounds.Height)); + } + + protected override Size MeasureOverride(Size availableSize) { + return new Size(1, 0); + } + + private BlameTextEditor _editor = null; + } + + public static readonly StyledProperty BlameDataProperty = + AvaloniaProperty.Register(nameof(BlameData)); + + public Models.BlameData BlameData { + get => GetValue(BlameDataProperty); + set => SetValue(BlameDataProperty, value); + } + + protected override Type StyleKeyOverride => typeof(TextEditor); + + public BlameTextEditor() : base(new TextArea(), new TextDocument()) { + IsReadOnly = true; + ShowLineNumbers = false; + WordWrap = false; + } + + public void OnCommitSHAClicked(string sha) { + if (DataContext is ViewModels.Blame blame) { + blame.NavigateToCommit(sha); + } + } + + protected override void OnLoaded(RoutedEventArgs e) { + base.OnLoaded(e); + + TextArea.LeftMargins.Add(new LineNumberMargin() { Margin = new Thickness(8, 0) }); + TextArea.LeftMargins.Add(new VerticalSeperatorMargin(this)); + TextArea.LeftMargins.Add(new CommitInfoMargin(this) { Margin = new Thickness(8, 0) }); + TextArea.LeftMargins.Add(new VerticalSeperatorMargin(this)); + TextArea.TextView.ContextRequested += OnTextViewContextRequested; + TextArea.TextView.Margin = new Thickness(4, 0); + + if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) { + _registryOptions = new RegistryOptions(ThemeName.DarkPlus); + } else { + _registryOptions = new RegistryOptions(ThemeName.LightPlus); + } + + _textMate = this.InstallTextMate(_registryOptions); + + if (BlameData != null) { + _textMate.SetGrammar(_registryOptions.GetScopeByExtension(Path.GetExtension(BlameData.File))); + } + } + + protected override void OnUnloaded(RoutedEventArgs e) { + base.OnUnloaded(e); + + TextArea.LeftMargins.Clear(); + TextArea.TextView.ContextRequested -= OnTextViewContextRequested; + + _registryOptions = null; + _textMate.Dispose(); + _textMate = null; + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { + base.OnPropertyChanged(change); + + if (change.Property == BlameDataProperty) { + if (BlameData != null) { + Text = BlameData.Content; + if (_textMate != null) _textMate.SetGrammar(_registryOptions.GetScopeByExtension(Path.GetExtension(BlameData.File))); + } else { + Text = string.Empty; + } + } else if (change.Property.Name == "ActualThemeVariant" && change.NewValue != null && _textMate != null) { + if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) { + _textMate.SetTheme(_registryOptions.LoadTheme(ThemeName.DarkPlus)); + } else { + _textMate.SetTheme(_registryOptions.LoadTheme(ThemeName.LightPlus)); + } + } + } + + private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e) { + var selected = SelectedText; + if (string.IsNullOrEmpty(selected)) return; + + var icon = new Avalonia.Controls.Shapes.Path(); + icon.Width = 10; + icon.Height = 10; + icon.Stretch = Stretch.Uniform; + icon.Data = App.Current?.FindResource("Icons.Copy") as StreamGeometry; + + var copy = new MenuItem(); + copy.Header = App.Text("Copy"); + copy.Icon = icon; + copy.Click += (o, ev) => { + App.CopyText(selected); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(copy); + menu.Open(TextArea.TextView); + e.Handled = true; + } + + private RegistryOptions _registryOptions = null; + private TextMate.Installation _textMate = null; + } + + public partial class Blame : Window { + public Blame() { + if (App.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + Owner = desktop.MainWindow; + } + + InitializeComponent(); + } + + protected override void OnClosed(EventArgs e) { + base.OnClosed(e); + GC.Collect(); + } + + private void OnCommitSHAPointerPressed(object sender, PointerPressedEventArgs e) { + if (DataContext is ViewModels.Blame blame) { + var txt = sender as TextBlock; + blame.NavigateToCommit(txt.Text); + } + e.Handled = true; + } + } +} diff --git a/src/Views/Blame.xaml b/src/Views/Blame.xaml deleted file mode 100644 index f2c46655..00000000 --- a/src/Views/Blame.xaml +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/Blame.xaml.cs b/src/Views/Blame.xaml.cs deleted file mode 100644 index 2a8c1598..00000000 --- a/src/Views/Blame.xaml.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Navigation; - -namespace SourceGit.Views { - /// - /// 逐行追溯 - /// - public partial class Blame : Controls.Window { - /// - /// DataGrid数据源结构 - /// - public class Record : INotifyPropertyChanged { - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// 原始Blame行数据 - /// - public Models.BlameLine Line { get; set; } - - /// - /// 是否是第一行 - /// - public bool IsFirstLine { get; set; } = false; - - /// - /// 前一行与本行的提交不同 - /// - public bool IsFirstLineInGroup { get; set; } = false; - - /// - /// 是否当前选中,会影响背景色 - /// - private bool isSelected = false; - public bool IsSelected { - get { return isSelected; } - set { - if (isSelected != value) { - isSelected = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsSelected")); - } - } - } - } - - /// - /// Blame数据 - /// - public ObservableCollection Records { get; set; } - - public Blame(string repo, string file, string revision) { - InitializeComponent(); - - this.repo = repo; - Records = new ObservableCollection(); - txtFile.Text = $"{file}@{revision.Substring(0, 8)}"; - - Task.Run(() => { - var lfs = new Commands.LFS(repo).IsFiltered(file); - if (lfs) { - Dispatcher.Invoke(() => { - loading.IsAnimating = false; - loading.Visibility = Visibility.Collapsed; - notSupport.Visibility = Visibility.Visible; - }); - return; - } - - var rs = new Commands.Blame(repo, file, revision).Result(); - if (rs.IsBinary) { - Dispatcher.Invoke(() => { - loading.IsAnimating = false; - loading.Visibility = Visibility.Collapsed; - notSupport.Visibility = Visibility.Visible; - }); - } else { - string lastSHA = null; - foreach (var line in rs.Lines) { - var r = new Record(); - r.Line = line; - r.IsSelected = false; - - if (line.CommitSHA != lastSHA) { - lastSHA = line.CommitSHA; - r.IsFirstLineInGroup = true; - } else { - r.IsFirstLineInGroup = false; - } - - Records.Add(r); - } - - if (Records.Count > 0) Records[0].IsFirstLine = true; - - Dispatcher.Invoke(() => { - loading.IsAnimating = false; - loading.Visibility = Visibility.Collapsed; - blame.ItemsSource = Records; - }); - } - }); - } - - #region WINDOW_COMMANDS - private void Minimize(object sender, RoutedEventArgs e) { - SystemCommands.MinimizeWindow(this); - } - - private void Quit(object sender, RoutedEventArgs e) { - Close(); - } - #endregion - - #region EVENTS - private T GetVisualChild(DependencyObject parent) where T : Visual { - T child = null; - - int count = VisualTreeHelper.GetChildrenCount(parent); - for (int i = 0; i < count; i++) { - Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); - child = v as T; - - if (child == null) { - child = GetVisualChild(v); - } - - if (child != null) { - break; - } - } - - return child; - } - - private void OnViewerSizeChanged(object sender, SizeChangedEventArgs e) { - var total = blame.ActualWidth; - var offset = blame.NonFrozenColumnsViewportHorizontalOffset; - var minWidth = total - offset; - - var scroller = GetVisualChild(blame); - if (scroller != null && scroller.ComputedVerticalScrollBarVisibility == Visibility.Visible) minWidth -= 8; - - blame.Columns[2].MinWidth = minWidth; - blame.Columns[2].Width = DataGridLength.SizeToCells; - blame.UpdateLayout(); - } - - private void OnViewerRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) { - e.Handled = true; - } - - private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { - var r = blame.SelectedItem as Record; - if (r == null) return; - - foreach (var one in Records) { - one.IsSelected = one.Line.CommitSHA == r.Line.CommitSHA; - } - } - - private void GotoCommit(object sender, RequestNavigateEventArgs e) { - Models.Watcher.Get(repo).NavigateTo(e.Uri.OriginalString); - e.Handled = true; - } - #endregion - - private string repo = null; - } -} diff --git a/src/Views/CaptionButtons.axaml b/src/Views/CaptionButtons.axaml new file mode 100644 index 00000000..581f7be1 --- /dev/null +++ b/src/Views/CaptionButtons.axaml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/src/Views/CaptionButtons.axaml.cs b/src/Views/CaptionButtons.axaml.cs new file mode 100644 index 00000000..09f70dad --- /dev/null +++ b/src/Views/CaptionButtons.axaml.cs @@ -0,0 +1,33 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace SourceGit.Views { + public partial class CaptionButtons : UserControl { + public CaptionButtons() { + InitializeComponent(); + } + + private void MinimizeWindow(object sender, RoutedEventArgs e) { + var window = this.FindAncestorOfType(); + if (window != null) { + window.WindowState = WindowState.Minimized; + } + } + + private void MaximizeOrRestoreWindow(object sender, RoutedEventArgs e) { + var window = this.FindAncestorOfType(); + if (window != null) { + window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + } + } + + private void CloseWindow(object sender, RoutedEventArgs e) { + var window = this.FindAncestorOfType(); + if (window != null) { + window.Close(); + } + } + } +} + diff --git a/src/Views/CaptionButtonsMacOS.axaml b/src/Views/CaptionButtonsMacOS.axaml new file mode 100644 index 00000000..fa3f1f34 --- /dev/null +++ b/src/Views/CaptionButtonsMacOS.axaml @@ -0,0 +1,32 @@ + + + + + + + + + diff --git a/src/Views/CaptionButtonsMacOS.axaml.cs b/src/Views/CaptionButtonsMacOS.axaml.cs new file mode 100644 index 00000000..64b93d21 --- /dev/null +++ b/src/Views/CaptionButtonsMacOS.axaml.cs @@ -0,0 +1,33 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace SourceGit.Views { + public partial class CaptionButtonsMacOS : UserControl { + public CaptionButtonsMacOS() { + InitializeComponent(); + } + + private void MinimizeWindow(object sender, RoutedEventArgs e) { + var window = this.FindAncestorOfType(); + if (window != null) { + window.WindowState = WindowState.Minimized; + } + } + + private void MaximizeOrRestoreWindow(object sender, RoutedEventArgs e) { + var window = this.FindAncestorOfType(); + if (window != null) { + window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + } + } + + private void CloseWindow(object sender, RoutedEventArgs e) { + var window = this.FindAncestorOfType(); + if (window != null) { + window.Close(); + } + } + } +} + diff --git a/src/Views/ChangeStatusIcon.cs b/src/Views/ChangeStatusIcon.cs new file mode 100644 index 00000000..8d0001e6 --- /dev/null +++ b/src/Views/ChangeStatusIcon.cs @@ -0,0 +1,112 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using System; +using System.Globalization; + +namespace SourceGit.Views { + public class ChangeStatusIcon : Control { + private static readonly IBrush[] BACKGROUNDS = [ + Brushes.Transparent, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Colors.Tomato, 0), new GradientStop(Color.FromRgb(252, 165, 150), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Colors.Orchid, 0), new GradientStop(Color.FromRgb(248, 161, 245), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + new LinearGradientBrush { + GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) }, + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), + }, + ]; + + private static readonly string[] INDICATOR = ["?", "±", "+", "−", "➜", "❏", "U", "★"]; + + public static readonly StyledProperty IsWorkingCopyChangeProperty = + AvaloniaProperty.Register(nameof(IsWorkingCopyChange)); + + public bool IsWorkingCopyChange { + get => GetValue(IsWorkingCopyChangeProperty); + set => SetValue(IsWorkingCopyChangeProperty, value); + } + + public static readonly StyledProperty ChangeProperty = + AvaloniaProperty.Register(nameof(Change)); + + public Models.Change Change { + get => GetValue(ChangeProperty); + set => SetValue(ChangeProperty, value); + } + + public static readonly StyledProperty IconFontFamilyProperty = + AvaloniaProperty.Register(nameof(IconFontFamily)); + + public FontFamily IconFontFamily { + get => GetValue(IconFontFamilyProperty); + set => SetValue(IconFontFamilyProperty, value); + } + + static ChangeStatusIcon() { + AffectsRender(IsWorkingCopyChangeProperty, ChangeProperty, IconFontFamilyProperty); + } + + public override void Render(DrawingContext context) { + if (Change == null || Bounds.Width <= 0) return; + + var typeface = IconFontFamily == null ? Typeface.Default : new Typeface(IconFontFamily); + + IBrush background = null; + string indicator; + if (IsWorkingCopyChange) { + if (Change.IsConflit) { + background = Brushes.OrangeRed; + indicator = "!"; + } else { + background = BACKGROUNDS[(int)Change.WorkTree]; + indicator = INDICATOR[(int)Change.WorkTree]; + } + } else { + background = BACKGROUNDS[(int)Change.Index]; + indicator = INDICATOR[(int)Change.Index]; + } + + var txt = new FormattedText( + indicator, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + Bounds.Width * 0.8, + Brushes.White); + + float corner = (float)Math.Max(2, Bounds.Width / 16); + Point textOrigin = new Point((Bounds.Width - txt.Width) * 0.5, (Bounds.Height - txt.Height) * 0.5); + context.DrawRectangle(background, null, new Rect(0, 0, Bounds.Width, Bounds.Height), corner, corner); + context.DrawText(txt, textOrigin); + } + } +} diff --git a/src/Views/ChangeViewModeSwitcher.axaml b/src/Views/ChangeViewModeSwitcher.axaml new file mode 100644 index 00000000..32d4f1e9 --- /dev/null +++ b/src/Views/ChangeViewModeSwitcher.axaml @@ -0,0 +1,35 @@ + + + diff --git a/src/Views/ChangeViewModeSwitcher.axaml.cs b/src/Views/ChangeViewModeSwitcher.axaml.cs new file mode 100644 index 00000000..59a041e9 --- /dev/null +++ b/src/Views/ChangeViewModeSwitcher.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class ChangeViewModeSwitcher : UserControl { + public static readonly StyledProperty ViewModeProperty = + AvaloniaProperty.Register(nameof(ViewMode)); + + public Models.ChangeViewMode ViewMode { + get => GetValue(ViewModeProperty); + set => SetValue(ViewModeProperty, value); + } + + public ChangeViewModeSwitcher() { + DataContext = this; + InitializeComponent(); + } + + public void SwitchMode(object param) { + ViewMode = (Models.ChangeViewMode)param; + } + } +} diff --git a/src/Views/Checkout.axaml b/src/Views/Checkout.axaml new file mode 100644 index 00000000..f921326b --- /dev/null +++ b/src/Views/Checkout.axaml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/src/Views/Checkout.axaml.cs b/src/Views/Checkout.axaml.cs new file mode 100644 index 00000000..d2d9edca --- /dev/null +++ b/src/Views/Checkout.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class Checkout : UserControl { + public Checkout() { + InitializeComponent(); + } + } +} diff --git a/src/Views/CherryPick.axaml b/src/Views/CherryPick.axaml new file mode 100644 index 00000000..a4b654fd --- /dev/null +++ b/src/Views/CherryPick.axaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/src/Views/CherryPick.axaml.cs b/src/Views/CherryPick.axaml.cs new file mode 100644 index 00000000..306d4702 --- /dev/null +++ b/src/Views/CherryPick.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class CherryPick : UserControl { + public CherryPick() { + InitializeComponent(); + } + } +} diff --git a/src/Views/Cleanup.axaml b/src/Views/Cleanup.axaml new file mode 100644 index 00000000..7e5d8ff9 --- /dev/null +++ b/src/Views/Cleanup.axaml @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/Views/Cleanup.axaml.cs b/src/Views/Cleanup.axaml.cs new file mode 100644 index 00000000..a098d9a4 --- /dev/null +++ b/src/Views/Cleanup.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class Cleanup : UserControl { + public Cleanup() { + InitializeComponent(); + } + } +} diff --git a/src/Views/ClearStashes.axaml b/src/Views/ClearStashes.axaml new file mode 100644 index 00000000..666bf273 --- /dev/null +++ b/src/Views/ClearStashes.axaml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/src/Views/ClearStashes.axaml.cs b/src/Views/ClearStashes.axaml.cs new file mode 100644 index 00000000..d95e18c1 --- /dev/null +++ b/src/Views/ClearStashes.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class ClearStashes : UserControl { + public ClearStashes() { + InitializeComponent(); + } + } +} diff --git a/src/Views/Clone.axaml b/src/Views/Clone.axaml new file mode 100644 index 00000000..ca3a4d3f --- /dev/null +++ b/src/Views/Clone.axaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Clone.axaml.cs b/src/Views/Clone.axaml.cs new file mode 100644 index 00000000..cd52e0bf --- /dev/null +++ b/src/Views/Clone.axaml.cs @@ -0,0 +1,33 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; + +namespace SourceGit.Views { + public partial class Clone : UserControl { + public Clone() { + InitializeComponent(); + } + + private async void SelectParentFolder(object sender, RoutedEventArgs e) { + var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + var toplevel = TopLevel.GetTopLevel(this); + var selected = await toplevel.StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) { + txtParentFolder.Text = selected[0].Path.LocalPath; + } + + e.Handled = true; + } + + private async void SelectSSHKey(object sender, RoutedEventArgs e) { + var options = new FilePickerOpenOptions() { AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("SSHKey") { Patterns = ["*.*"] }] }; + var toplevel = TopLevel.GetTopLevel(this); + var selected = await toplevel.StorageProvider.OpenFilePickerAsync(options); + if (selected.Count == 1) { + txtSSHKey.Text = selected[0].Path.LocalPath; + } + + e.Handled = true; + } + } +} diff --git a/src/Views/Clone.xaml b/src/Views/Clone.xaml deleted file mode 100644 index 07536e82..00000000 --- a/src/Views/Clone.xaml +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CommitChanges.axaml.cs b/src/Views/CommitChanges.axaml.cs new file mode 100644 index 00000000..db65922d --- /dev/null +++ b/src/Views/CommitChanges.axaml.cs @@ -0,0 +1,39 @@ +using Avalonia.Controls; + +namespace SourceGit.Views { + public partial class CommitChanges : UserControl { + public CommitChanges() { + InitializeComponent(); + } + + private void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e) { + if (sender is DataGrid datagrid && datagrid.IsVisible && datagrid.SelectedItem != null) { + datagrid.ScrollIntoView(datagrid.SelectedItem, null); + } + e.Handled = true; + } + + private void OnDataGridContextRequested(object sender, ContextRequestedEventArgs e) { + if (sender is DataGrid datagrid && datagrid.SelectedItem != null) { + var detail = DataContext as ViewModels.CommitDetail; + var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change); + menu.Open(datagrid); + } + + e.Handled = true; + } + + private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e) { + if (sender is TreeView view && view.SelectedItem != null) { + var detail = DataContext as ViewModels.CommitDetail; + var node = view.SelectedItem as ViewModels.FileTreeNode; + if (node != null && !node.IsFolder) { + var menu = detail.CreateChangeContextMenu(node.Backend as Models.Change); + menu.Open(view); + } + } + + e.Handled = true; + } + } +} diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml new file mode 100644 index 00000000..47d9fde7 --- /dev/null +++ b/src/Views/CommitDetail.axaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CommitDetail.axaml.cs b/src/Views/CommitDetail.axaml.cs new file mode 100644 index 00000000..326c612d --- /dev/null +++ b/src/Views/CommitDetail.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace SourceGit.Views { + public partial class CommitDetail : UserControl { + public CommitDetail() { + InitializeComponent(); + } + + private void OnChangeListDoubleTapped(object sender, TappedEventArgs e) { + if (DataContext is ViewModels.CommitDetail detail) { + var datagrid = sender as DataGrid; + detail.ActivePageIndex = 1; + detail.SelectedChange = datagrid.SelectedItem as Models.Change; + } + e.Handled = true; + } + + private void OnChangeListContextRequested(object sender, ContextRequestedEventArgs e) { + if (DataContext is ViewModels.CommitDetail detail) { + var datagrid = sender as DataGrid; + var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change); + menu.Open(datagrid); + } + e.Handled = true; + } + } +} diff --git a/src/Views/ConfirmDialog.xaml b/src/Views/ConfirmDialog.xaml deleted file mode 100644 index 867fedb1..00000000 --- a/src/Views/ConfirmDialog.xaml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -