From 38227b1d57081cd1e0103100063f9111ee9dce67 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 3 Jul 2020 15:24:31 +0800 Subject: [PATCH] v1.0 --- .gitignore | 3 + App.config | 6 + App.ico | Bin 0 -> 21662 bytes App.xaml | 16 + App.xaml.cs | 134 +++ Converters/BoolToCollapsed.cs | 37 + Converters/FileStatusToColor.cs | 59 ++ Converters/FileStatusToIcon.cs | 45 + Converters/IndentToMargin.cs | 37 + Converters/InverseBool.cs | 19 + Converters/InverseBoolToCollapsed.cs | 37 + Converters/PercentToDouble.cs | 41 + Converters/TreeViewItemDepthToMargin.cs | 69 ++ Git/Blame.cs | 35 + Git/Branch.cs | 190 ++++ Git/Change.cs | 147 ++++ Git/Commit.cs | 262 ++++++ Git/Decorator.cs | 21 + Git/MergeTool.cs | 202 +++++ Git/Preference.cs | 296 +++++++ Git/Remote.cs | 93 ++ Git/Repository.cs | 1059 +++++++++++++++++++++++ Git/Stash.cs | 101 +++ Git/Tag.cs | 118 +++ Git/User.cs | 42 + Helpers/CommitGraph.cs | 274 ++++++ Helpers/TextBoxHelper.cs | 143 +++ Helpers/TreeViewHelper.cs | 329 +++++++ Helpers/Validations.cs | 124 +++ LICENSE | 20 + Preview_Dark.png | Bin 0 -> 97317 bytes Preview_Light.png | Bin 0 -> 86032 bytes Properties/AssemblyInfo.cs | 56 ++ Properties/Resources.Designer.cs | 62 ++ Properties/Resources.resx | 117 +++ Properties/Settings.Designer.cs | 26 + Properties/Settings.settings | 7 + README.md | 12 + Resources/Controls.xaml | 22 + Resources/Icons.xaml | 59 ++ Resources/Styles/Border.xaml | 12 + Resources/Styles/Button.xaml | 55 ++ Resources/Styles/CheckBox.xaml | 38 + Resources/Styles/ComboBox.xaml | 83 ++ Resources/Styles/ContextMenu.xaml | 91 ++ Resources/Styles/DataGrid.xaml | 44 + Resources/Styles/HyperLink.xaml | 11 + Resources/Styles/Label.xaml | 15 + Resources/Styles/ListView.xaml | 66 ++ Resources/Styles/Path.xaml | 21 + Resources/Styles/RadioButton.xaml | 52 ++ Resources/Styles/ScrollBar.xaml | 98 +++ Resources/Styles/ScrollViewer.xaml | 54 ++ Resources/Styles/TabControl.xaml | 62 ++ Resources/Styles/TextBox.xaml | 114 +++ Resources/Styles/ToggleButton.xaml | 111 +++ Resources/Styles/Tooltip.xaml | 21 + Resources/Styles/TreeView.xaml | 201 +++++ Resources/Themes/Dark.xaml | 16 + Resources/Themes/Light.xaml | 15 + SourceGit.csproj | 515 +++++++++++ SourceGit.sln | 25 + UI/About.xaml | 77 ++ UI/About.xaml.cs | 46 + UI/Apply.xaml | 85 ++ UI/Apply.xaml.cs | 112 +++ UI/Blame.xaml | 201 +++++ UI/Blame.xaml.cs | 211 +++++ UI/CherryPick.xaml | 45 + UI/CherryPick.xaml.cs | 54 ++ UI/Clone.xaml | 98 +++ UI/Clone.xaml.cs | 115 +++ UI/CommitViewer.xaml | 408 +++++++++ UI/CommitViewer.xaml.cs | 468 ++++++++++ UI/CreateBranch.xaml | 92 ++ UI/CreateBranch.xaml.cs | 143 +++ UI/CreateTag.xaml | 68 ++ UI/CreateTag.xaml.cs | 92 ++ UI/Dashboard.xaml | 450 ++++++++++ UI/Dashboard.xaml.cs | 984 +++++++++++++++++++++ UI/DeleteBranch.xaml | 51 ++ UI/DeleteBranch.xaml.cs | 66 ++ UI/DeleteRemote.xaml | 50 ++ UI/DeleteRemote.xaml.cs | 67 ++ UI/DeleteTag.xaml | 53 ++ UI/DeleteTag.xaml.cs | 69 ++ UI/DiffViewer.xaml | 139 +++ UI/DiffViewer.xaml.cs | 294 +++++++ UI/Discard.xaml | 55 ++ UI/Discard.xaml.cs | 67 ++ UI/Fetch.xaml | 78 ++ UI/Fetch.xaml.cs | 85 ++ UI/FileHistories.xaml | 169 ++++ UI/FileHistories.xaml.cs | 122 +++ UI/GitFlowFinishBranch.xaml | 50 ++ UI/GitFlowFinishBranch.xaml.cs | 87 ++ UI/GitFlowSetup.xaml | 68 ++ UI/GitFlowSetup.xaml.cs | 134 +++ UI/GitFlowStartBranch.xaml | 57 ++ UI/GitFlowStartBranch.xaml.cs | 103 +++ UI/Histories.xaml | 187 ++++ UI/Histories.xaml.cs | 649 ++++++++++++++ UI/Init.xaml | 53 ++ UI/Init.xaml.cs | 73 ++ UI/InteractiveRebase.xaml | 256 ++++++ UI/InteractiveRebase.xaml.cs | 299 +++++++ UI/Launcher.xaml | 149 ++++ UI/Launcher.xaml.cs | 140 +++ UI/Manager.xaml | 231 +++++ UI/Manager.xaml.cs | 495 +++++++++++ UI/Merge.xaml | 71 ++ UI/Merge.xaml.cs | 120 +++ UI/PopupManager.xaml | 21 + UI/PopupManager.xaml.cs | 95 ++ UI/Preference.xaml | 120 +++ UI/Preference.xaml.cs | 114 +++ UI/Pull.xaml | 100 +++ UI/Pull.xaml.cs | 131 +++ UI/Push.xaml | 108 +++ UI/Push.xaml.cs | 202 +++++ UI/PushTag.xaml | 65 ++ UI/PushTag.xaml.cs | 73 ++ UI/Rebase.xaml | 63 ++ UI/Rebase.xaml.cs | 87 ++ UI/Remote.xaml | 77 ++ UI/Remote.xaml.cs | 94 ++ UI/RenameBranch.xaml | 67 ++ UI/RenameBranch.xaml.cs | 79 ++ UI/Reset.xaml | 72 ++ UI/Reset.xaml.cs | 99 +++ UI/Revert.xaml | 53 ++ UI/Revert.xaml.cs | 79 ++ UI/Stash.xaml | 46 + UI/Stash.xaml.cs | 56 ++ UI/Stashes.xaml | 134 +++ UI/Stashes.xaml.cs | 118 +++ UI/WorkingCopy.xaml | 379 ++++++++ UI/WorkingCopy.xaml.cs | 1027 ++++++++++++++++++++++ 138 files changed, 17935 insertions(+) create mode 100644 .gitignore create mode 100644 App.config create mode 100644 App.ico create mode 100644 App.xaml create mode 100644 App.xaml.cs create mode 100644 Converters/BoolToCollapsed.cs create mode 100644 Converters/FileStatusToColor.cs create mode 100644 Converters/FileStatusToIcon.cs create mode 100644 Converters/IndentToMargin.cs create mode 100644 Converters/InverseBool.cs create mode 100644 Converters/InverseBoolToCollapsed.cs create mode 100644 Converters/PercentToDouble.cs create mode 100644 Converters/TreeViewItemDepthToMargin.cs create mode 100644 Git/Blame.cs create mode 100644 Git/Branch.cs create mode 100644 Git/Change.cs create mode 100644 Git/Commit.cs create mode 100644 Git/Decorator.cs create mode 100644 Git/MergeTool.cs create mode 100644 Git/Preference.cs create mode 100644 Git/Remote.cs create mode 100644 Git/Repository.cs create mode 100644 Git/Stash.cs create mode 100644 Git/Tag.cs create mode 100644 Git/User.cs create mode 100644 Helpers/CommitGraph.cs create mode 100644 Helpers/TextBoxHelper.cs create mode 100644 Helpers/TreeViewHelper.cs create mode 100644 Helpers/Validations.cs create mode 100644 LICENSE create mode 100644 Preview_Dark.png create mode 100644 Preview_Light.png create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.resx create mode 100644 Properties/Settings.Designer.cs create mode 100644 Properties/Settings.settings create mode 100644 README.md create mode 100644 Resources/Controls.xaml create mode 100644 Resources/Icons.xaml create mode 100644 Resources/Styles/Border.xaml create mode 100644 Resources/Styles/Button.xaml create mode 100644 Resources/Styles/CheckBox.xaml create mode 100644 Resources/Styles/ComboBox.xaml create mode 100644 Resources/Styles/ContextMenu.xaml create mode 100644 Resources/Styles/DataGrid.xaml create mode 100644 Resources/Styles/HyperLink.xaml create mode 100644 Resources/Styles/Label.xaml create mode 100644 Resources/Styles/ListView.xaml create mode 100644 Resources/Styles/Path.xaml create mode 100644 Resources/Styles/RadioButton.xaml create mode 100644 Resources/Styles/ScrollBar.xaml create mode 100644 Resources/Styles/ScrollViewer.xaml create mode 100644 Resources/Styles/TabControl.xaml create mode 100644 Resources/Styles/TextBox.xaml create mode 100644 Resources/Styles/ToggleButton.xaml create mode 100644 Resources/Styles/Tooltip.xaml create mode 100644 Resources/Styles/TreeView.xaml create mode 100644 Resources/Themes/Dark.xaml create mode 100644 Resources/Themes/Light.xaml create mode 100644 SourceGit.csproj create mode 100644 SourceGit.sln create mode 100644 UI/About.xaml create mode 100644 UI/About.xaml.cs create mode 100644 UI/Apply.xaml create mode 100644 UI/Apply.xaml.cs create mode 100644 UI/Blame.xaml create mode 100644 UI/Blame.xaml.cs create mode 100644 UI/CherryPick.xaml create mode 100644 UI/CherryPick.xaml.cs create mode 100644 UI/Clone.xaml create mode 100644 UI/Clone.xaml.cs create mode 100644 UI/CommitViewer.xaml create mode 100644 UI/CommitViewer.xaml.cs create mode 100644 UI/CreateBranch.xaml create mode 100644 UI/CreateBranch.xaml.cs create mode 100644 UI/CreateTag.xaml create mode 100644 UI/CreateTag.xaml.cs create mode 100644 UI/Dashboard.xaml create mode 100644 UI/Dashboard.xaml.cs create mode 100644 UI/DeleteBranch.xaml create mode 100644 UI/DeleteBranch.xaml.cs create mode 100644 UI/DeleteRemote.xaml create mode 100644 UI/DeleteRemote.xaml.cs create mode 100644 UI/DeleteTag.xaml create mode 100644 UI/DeleteTag.xaml.cs create mode 100644 UI/DiffViewer.xaml create mode 100644 UI/DiffViewer.xaml.cs create mode 100644 UI/Discard.xaml create mode 100644 UI/Discard.xaml.cs create mode 100644 UI/Fetch.xaml create mode 100644 UI/Fetch.xaml.cs create mode 100644 UI/FileHistories.xaml create mode 100644 UI/FileHistories.xaml.cs create mode 100644 UI/GitFlowFinishBranch.xaml create mode 100644 UI/GitFlowFinishBranch.xaml.cs create mode 100644 UI/GitFlowSetup.xaml create mode 100644 UI/GitFlowSetup.xaml.cs create mode 100644 UI/GitFlowStartBranch.xaml create mode 100644 UI/GitFlowStartBranch.xaml.cs create mode 100644 UI/Histories.xaml create mode 100644 UI/Histories.xaml.cs create mode 100644 UI/Init.xaml create mode 100644 UI/Init.xaml.cs create mode 100644 UI/InteractiveRebase.xaml create mode 100644 UI/InteractiveRebase.xaml.cs create mode 100644 UI/Launcher.xaml create mode 100644 UI/Launcher.xaml.cs create mode 100644 UI/Manager.xaml create mode 100644 UI/Manager.xaml.cs create mode 100644 UI/Merge.xaml create mode 100644 UI/Merge.xaml.cs create mode 100644 UI/PopupManager.xaml create mode 100644 UI/PopupManager.xaml.cs create mode 100644 UI/Preference.xaml create mode 100644 UI/Preference.xaml.cs create mode 100644 UI/Pull.xaml create mode 100644 UI/Pull.xaml.cs create mode 100644 UI/Push.xaml create mode 100644 UI/Push.xaml.cs create mode 100644 UI/PushTag.xaml create mode 100644 UI/PushTag.xaml.cs create mode 100644 UI/Rebase.xaml create mode 100644 UI/Rebase.xaml.cs create mode 100644 UI/Remote.xaml create mode 100644 UI/Remote.xaml.cs create mode 100644 UI/RenameBranch.xaml create mode 100644 UI/RenameBranch.xaml.cs create mode 100644 UI/Reset.xaml create mode 100644 UI/Reset.xaml.cs create mode 100644 UI/Revert.xaml create mode 100644 UI/Revert.xaml.cs create mode 100644 UI/Stash.xaml create mode 100644 UI/Stash.xaml.cs create mode 100644 UI/Stashes.xaml create mode 100644 UI/Stashes.xaml.cs create mode 100644 UI/WorkingCopy.xaml create mode 100644 UI/WorkingCopy.xaml.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4b82ccd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vs/ +bin/ +obj/ diff --git a/App.config b/App.config new file mode 100644 index 00000000..3699f9dd --- /dev/null +++ b/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/App.ico b/App.ico new file mode 100644 index 0000000000000000000000000000000000000000..9063ffee9085113e066ac1b14fd762bddf4ce04c GIT binary patch literal 21662 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 + + + + + + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 00000000..a5f6b39c --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,134 @@ +using Microsoft.Win32; +using System; +using System.IO; +using System.Windows; + +namespace SourceGit { + + /// + /// Application. + /// + public partial class App : Application { + + /// + /// Getter/Setter for Git preference. + /// + public static Git.Preference Preference { + get { return Git.Preference.Instance; } + set { Git.Preference.Instance = value; } + } + + /// + /// Check if GIT has been configured. + /// + public static bool IsGitConfigured { + get { + return !string.IsNullOrEmpty(Preference.GitExecutable) + && File.Exists(Preference.GitExecutable); + } + } + + /// + /// Interactive rebase sequence file. + /// + public static string InteractiveRebaseScript { + get; + private set; + } + + /// + /// TODO file for interactive rebase. + /// + public static string InteractiveRebaseTodo { + get; + private set; + } + + /// + /// Error handler. + /// + public static Action OnError { + get; + set; + } + + /// + /// Raise error message. + /// + /// + public static void RaiseError(string message) { + OnError?.Invoke(message); + } + + /// + /// Startup event. + /// + /// + /// + private void OnAppStartup(object sender, StartupEventArgs e) { + // Try auto configure git via registry. + if (!IsGitConfigured) { + var root = RegistryKey.OpenBaseKey( + RegistryHive.LocalMachine, + Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); + + var git = root.OpenSubKey("SOFTWARE\\GitForWindows"); + if (git != null) { + Preference.GitExecutable = Path.Combine( + git.GetValue("InstallPath") as string, + "bin", + "git.exe"); + } + } + + // Files for interactive rebase. + InteractiveRebaseScript = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "SourceGit", + "rebase.bat"); + InteractiveRebaseTodo = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "SourceGit", + "REBASE_TODO"); + if (!File.Exists(InteractiveRebaseScript)) { + var folder = Path.GetDirectoryName(InteractiveRebaseScript); + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + + File.WriteAllText(InteractiveRebaseScript, $"@echo off\ntype \"{InteractiveRebaseTodo}\" > .git\\rebase-merge\\git-rebase-todo"); + File.WriteAllText(InteractiveRebaseTodo, ""); + } + + // Apply themes + if (Preference.UIUseLightTheme) { + foreach (var rs in Current.Resources.MergedDictionaries) { + if (rs.Source != null && rs.Source.OriginalString.StartsWith("pack://application:,,,/Resources/Themes/")) { + rs.Source = new Uri("pack://application:,,,/Resources/Themes/Light.xaml", UriKind.Absolute); + break; + } + } + } + + // Show main window + var launcher = new UI.Launcher(); + launcher.Show(); + } + + /// + /// Deactivated event. + /// + /// + /// + private void OnAppDeactivated(object sender, EventArgs e) { + GC.Collect(); + } + + /// + /// Quit event. + /// + /// + /// + private void OnAppExit(object sender, ExitEventArgs e) { + Git.Preference.Save(); + } + } +} diff --git a/Converters/BoolToCollapsed.cs b/Converters/BoolToCollapsed.cs new file mode 100644 index 00000000..47ce3c73 --- /dev/null +++ b/Converters/BoolToCollapsed.cs @@ -0,0 +1,37 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace SourceGit.Converters { + + /// + /// Same as BoolToVisibilityConverter. + /// + public class BoolToCollapsed : IValueConverter { + + /// + /// Implement IValueConverter.Convert + /// + /// + /// + /// + /// + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + return (bool)value ? Visibility.Visible : Visibility.Collapsed; + } + + /// + /// Implement IValueConverter.ConvertBack + /// + /// + /// + /// + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/FileStatusToColor.cs b/Converters/FileStatusToColor.cs new file mode 100644 index 00000000..aba855f9 --- /dev/null +++ b/Converters/FileStatusToColor.cs @@ -0,0 +1,59 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; + +namespace SourceGit.Converters { + + /// + /// Convert file status to brush + /// + public class FileStatusToColor : IValueConverter { + + /// + /// Is only test local changes. + /// + public bool OnlyWorkTree { get; set; } = false; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + var change = value as Git.Change; + if (change == null) return Brushes.Transparent; + + var status = Git.Change.Status.None; + if (OnlyWorkTree) { + if (change.IsConflit) return Brushes.Yellow; + status = change.WorkTree; + } else { + status = change.Index; + } + + if (App.Preference.UIUseLightTheme) { + switch (status) { + case Git.Change.Status.Modified: return Brushes.Goldenrod; + case Git.Change.Status.Added: return Brushes.Green; + case Git.Change.Status.Deleted: return Brushes.Red; + case Git.Change.Status.Renamed: return Brushes.Magenta; + case Git.Change.Status.Copied: return Brushes.Goldenrod; + case Git.Change.Status.Unmerged: return Brushes.Goldenrod; + case Git.Change.Status.Untracked: return Brushes.Green; + default: return Brushes.Transparent; + } + } else { + switch (status) { + case Git.Change.Status.Modified: return Brushes.DarkGoldenrod; + case Git.Change.Status.Added: return Brushes.DarkGreen; + case Git.Change.Status.Deleted: return Brushes.DarkRed; + case Git.Change.Status.Renamed: return Brushes.DarkMagenta; + case Git.Change.Status.Copied: return Brushes.DarkGoldenrod; + case Git.Change.Status.Unmerged: return Brushes.DarkGoldenrod; + case Git.Change.Status.Untracked: return Brushes.DarkGreen; + default: return Brushes.Transparent; + } + } + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/FileStatusToIcon.cs b/Converters/FileStatusToIcon.cs new file mode 100644 index 00000000..85447770 --- /dev/null +++ b/Converters/FileStatusToIcon.cs @@ -0,0 +1,45 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace SourceGit.Converters { + + /// + /// Convert file status to icon. + /// + public class FileStatusToIcon : IValueConverter { + + /// + /// Is only test local changes. + /// + public bool OnlyWorkTree { get; set; } = false; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + var change = value as Git.Change; + if (change == null) return ""; + + var status = Git.Change.Status.None; + if (OnlyWorkTree) { + if (change.IsConflit) return "X"; + status = change.WorkTree; + } else { + status = change.Index; + } + + switch (status) { + case Git.Change.Status.Modified: return "M"; + case Git.Change.Status.Added: return "A"; + case Git.Change.Status.Deleted: return "D"; + case Git.Change.Status.Renamed: return "R"; + case Git.Change.Status.Copied: return "C"; + case Git.Change.Status.Unmerged: return "U"; + case Git.Change.Status.Untracked: return "?"; + default: return "?"; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/IndentToMargin.cs b/Converters/IndentToMargin.cs new file mode 100644 index 00000000..5214396b --- /dev/null +++ b/Converters/IndentToMargin.cs @@ -0,0 +1,37 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace SourceGit.Converters { + + /// + /// Convert indent(horizontal offset) to Margin property + /// + public class IndentToMargin : IValueConverter { + + /// + /// Implement IValueConverter.Convert + /// + /// + /// + /// + /// + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + return new Thickness((double)value, 0, 0, 0); + } + + /// + /// Implement IValueConverter.ConvertBack + /// + /// + /// + /// + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + return ((Thickness)value).Left; + } + } +} diff --git a/Converters/InverseBool.cs b/Converters/InverseBool.cs new file mode 100644 index 00000000..932ae4ef --- /dev/null +++ b/Converters/InverseBool.cs @@ -0,0 +1,19 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace SourceGit.Converters { + + /// + /// Inverse bool converter. + /// + public class InverseBool : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + return !((bool)value); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/InverseBoolToCollapsed.cs b/Converters/InverseBoolToCollapsed.cs new file mode 100644 index 00000000..862bf913 --- /dev/null +++ b/Converters/InverseBoolToCollapsed.cs @@ -0,0 +1,37 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace SourceGit.Converters { + + /// + /// Inverse BoolToCollapsed. + /// + public class InverseBoolToCollapsed : IValueConverter { + + /// + /// Implement IValueConverter.Convert + /// + /// + /// + /// + /// + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + return (bool)value ? Visibility.Collapsed : Visibility.Visible; + } + + /// + /// Implement IValueConverter.ConvertBack + /// + /// + /// + /// + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/PercentToDouble.cs b/Converters/PercentToDouble.cs new file mode 100644 index 00000000..a76c9b92 --- /dev/null +++ b/Converters/PercentToDouble.cs @@ -0,0 +1,41 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace SourceGit.Converters { + + /// + /// Convert percent to double. + /// + public class PercentToDouble : IValueConverter { + + /// + /// Percentage. + /// + public double Percent { get; set; } + + /// + /// Implement IValueConverter.Convert + /// + /// + /// + /// + /// + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + return (double)value * Percent; + } + + /// + /// Implement IValueConverter.ConvertBack + /// + /// + /// + /// + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/Converters/TreeViewItemDepthToMargin.cs b/Converters/TreeViewItemDepthToMargin.cs new file mode 100644 index 00000000..8c7eb856 --- /dev/null +++ b/Converters/TreeViewItemDepthToMargin.cs @@ -0,0 +1,69 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; + +namespace SourceGit.Converters { + + /// + /// Convert depth of a TreeViewItem to Margin property. + /// + public class TreeViewItemDepthToMargin : IValueConverter { + + /// + /// Indent length + /// + public double Indent { get; set; } = 19; + + /// + /// Implement IValueConverter.Convert + /// + /// + /// + /// + /// + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + TreeViewItem item = value as TreeViewItem; + if (item == null) return new Thickness(0); + + TreeViewItem iterator = GetParent(item); + int depth = 0; + while (iterator != null) { + depth++; + iterator = GetParent(iterator); + } + + return new Thickness(Indent * depth, 0, 0, 0); + } + + /// + /// Implement IValueConvert.ConvertBack + /// + /// + /// + /// + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + + /// + /// Get parent item. + /// + /// + /// + private TreeViewItem GetParent(TreeViewItem item) { + var parent = VisualTreeHelper.GetParent(item); + + while (parent != null && !(parent is TreeView) && !(parent is TreeViewItem)) { + parent = VisualTreeHelper.GetParent(parent); + } + + return parent as TreeViewItem; + } + } +} diff --git a/Git/Blame.cs b/Git/Blame.cs new file mode 100644 index 00000000..bf597274 --- /dev/null +++ b/Git/Blame.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace SourceGit.Git { + + /// + /// Blame + /// + public class Blame { + + /// + /// Block content. + /// + public class Block { + public string CommitSHA { get; set; } + public string Author { get; set; } + public string Time { get; set; } + public string Content { get; set; } + } + + /// + /// Blocks + /// + public List Blocks { get; set; } = new List(); + + /// + /// Is binary file? + /// + public bool IsBinary { get; set; } = false; + + /// + /// Line count. + /// + public int LineCount { get; set; } = 0; + } +} diff --git a/Git/Branch.cs b/Git/Branch.cs new file mode 100644 index 00000000..05375b95 --- /dev/null +++ b/Git/Branch.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Git { + + /// + /// Git branch + /// + public class Branch { + private static readonly string PRETTY_FORMAT = @"$%(refname)$%(objectname)$%(HEAD)$%(upstream)$%(upstream:track)$%(contents:subject)"; + private static readonly Regex PARSE = new Regex(@"\$(.*)\$(.*)\$([\* ])\$(.*)\$(.*?)\$(.*)"); + private static readonly Regex AHEAD = new Regex(@"ahead (\d+)"); + private static readonly Regex BEHIND = new Regex(@"behind (\d+)"); + + /// + /// Branch type. + /// + public enum Type { + Normal, + Feature, + Release, + Hotfix, + } + + /// + /// Branch name + /// + public string Name { get; set; } + + /// + /// Full name. + /// + public string FullName { get; set; } + + /// + /// Head ref + /// + public string Head { get; set; } + + /// + /// Subject for head ref. + /// + public string HeadSubject { get; set; } + + /// + /// Is local branch + /// + public bool IsLocal { get; set; } + + /// + /// Branch type. + /// + public Type Kind { get; set; } = Type.Normal; + + /// + /// Remote name. Only used for remote branch + /// + public string Remote { get; set; } + + /// + /// Upstream. Only used for local branches. + /// + public string Upstream { get; set; } + + /// + /// Track information for upstream. Only used for local branches. + /// + public string UpstreamTrack { get; set; } + + /// + /// Is current branch. Only used for local branches. + /// + public bool IsCurrent { get; set; } + + /// + /// Is this branch's HEAD same with upstream? + /// + public bool IsSameWithUpstream => string.IsNullOrEmpty(UpstreamTrack); + + /// + /// Enable filter in log histories. + /// + public bool IsFiltered { get; set; } + + /// + /// Load branches. + /// + /// + public static List Load(Repository repo) { + var localPrefix = "refs/heads/"; + var remotePrefix = "refs/remotes/"; + var branches = new List(); + var remoteBranches = new List(); + + repo.RunCommand("branch -l --all -v --format=\"" + PRETTY_FORMAT + "\"", line => { + var match = PARSE.Match(line); + if (!match.Success) return; + + var branch = new Branch(); + var refname = match.Groups[1].Value; + if (refname.EndsWith("/HEAD")) return; + + if (refname.StartsWith(localPrefix, StringComparison.Ordinal)) { + branch.Name = refname.Substring(localPrefix.Length); + branch.IsLocal = true; + } else if (refname.StartsWith(remotePrefix, StringComparison.Ordinal)) { + var name = refname.Substring(remotePrefix.Length); + branch.Remote = name.Substring(0, name.IndexOf('/')); + branch.Name = name; + branch.IsLocal = false; + remoteBranches.Add(refname); + } + + branch.FullName = refname; + branch.Head = match.Groups[2].Value; + branch.IsCurrent = match.Groups[3].Value == "*"; + branch.Upstream = match.Groups[4].Value; + branch.UpstreamTrack = ParseTrack(match.Groups[5].Value); + branch.HeadSubject = match.Groups[6].Value; + + branches.Add(branch); + }); + + // Fixed deleted remote branch + foreach (var b in branches) { + if (!string.IsNullOrEmpty(b.Upstream) && !remoteBranches.Contains(b.Upstream)) { + b.Upstream = null; + } + } + + return branches; + } + + /// + /// Create new branch. + /// + /// + /// + /// + public static void Create(Repository repo, string name, string startPoint) { + var errs = repo.RunCommand($"branch {name} {startPoint}", null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Rename branch + /// + /// + /// + public void Rename(Repository repo, string name) { + var errs = repo.RunCommand($"branch -M {Name} {name}", null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Delete branch. + /// + /// + public void Delete(Repository repo) { + string errs = null; + + if (!IsLocal) { + errs = repo.RunCommand($"-c credential.helper=manager push {Remote} --delete {Name.Substring(Name.IndexOf('/')+1)}", null); + } else { + errs = repo.RunCommand($"branch -D {Name}", null); + } + + if (errs != null) App.RaiseError(errs); + } + + private static string ParseTrack(string data) { + if (string.IsNullOrEmpty(data)) return ""; + + string track = ""; + + var ahead = AHEAD.Match(data); + if (ahead.Success) { + track += ahead.Groups[1].Value + "↑ "; + } + + var behind = BEHIND.Match(data); + if (behind.Success) { + track += behind.Groups[1].Value + "↓"; + } + + return track.Trim(); + } + } +} diff --git a/Git/Change.cs b/Git/Change.cs new file mode 100644 index 00000000..c5f9ca4a --- /dev/null +++ b/Git/Change.cs @@ -0,0 +1,147 @@ +using System.Text.RegularExpressions; + +namespace SourceGit.Git { + + /// + /// Changed file status. + /// + public class Change { + private static readonly Regex FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); + + /// + /// Status Code + /// + public enum Status { + None, + Modified, + Added, + Deleted, + Renamed, + Copied, + Unmerged, + Untracked, + } + + /// + /// Index status + /// + public Status Index { get; set; } + + /// + /// Work tree status. + /// + public Status WorkTree { get; set; } + + /// + /// Current file path. + /// + public string Path { get; set; } + + /// + /// Original file path before this revision. + /// + public string OriginalPath { get; set; } + + /// + /// Staged(added) in index? + /// + public bool IsAddedToIndex { + get { + if (Index == Status.None || Index == Status.Untracked) return false; + return true; + } + } + + /// + /// Is conflict? + /// + 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; + return false; + } + } + + /// + /// Parse change for `--name-status` data. + /// + /// Raw data. + /// Read from commit? + /// Parsed change instance. + public static Change Parse(string data, bool fromCommit = false) { + var match = FORMAT.Match(data); + if (!match.Success) return null; + + var change = new Change() { Path = match.Groups[2].Value }; + var status = match.Groups[1].Value; + + if (fromCommit) { + switch (status[0]) { + case 'M': change.Set(Status.Modified); break; + case 'A': change.Set(Status.Added); break; + case 'D': change.Set(Status.Deleted); break; + case 'R': change.Set(Status.Renamed); break; + case 'C': change.Set(Status.Copied); break; + default: return null; + } + } else { + switch (status) { + case " M": change.Set(Status.None, Status.Modified); break; + case " A": change.Set(Status.None, Status.Added); break; + case " D": change.Set(Status.None, Status.Deleted); break; + case " R": change.Set(Status.None, Status.Renamed); break; + case " C": change.Set(Status.None, Status.Copied); break; + case "M": change.Set(Status.Modified, Status.None); break; + case "MM": change.Set(Status.Modified, Status.Modified); break; + case "MD": change.Set(Status.Modified, Status.Deleted); break; + case "A": change.Set(Status.Added, Status.None); break; + case "AM": change.Set(Status.Added, Status.Modified); break; + case "AD": change.Set(Status.Added, Status.Deleted); break; + case "D": change.Set(Status.Deleted, Status.None); break; + case "R": change.Set(Status.Renamed, Status.None); break; + case "RM": change.Set(Status.Renamed, Status.Modified); break; + case "RD": change.Set(Status.Renamed, Status.Deleted); break; + case "C": change.Set(Status.Copied, Status.None); break; + case "CM": change.Set(Status.Copied, Status.Modified); break; + case "CD": change.Set(Status.Copied, Status.Deleted); break; + case "DR": change.Set(Status.Deleted, Status.Renamed); break; + case "DC": change.Set(Status.Deleted, Status.Copied); break; + case "DD": change.Set(Status.Deleted, Status.Deleted); break; + case "AU": change.Set(Status.Added, Status.Unmerged); break; + case "UD": change.Set(Status.Unmerged, Status.Deleted); break; + case "UA": change.Set(Status.Unmerged, Status.Added); break; + case "DU": change.Set(Status.Deleted, Status.Unmerged); break; + case "AA": change.Set(Status.Added, Status.Added); break; + case "UU": change.Set(Status.Unmerged, Status.Unmerged); break; + case "??": change.Set(Status.Untracked, Status.Untracked); break; + default: return null; + } + } + + if (change.Path[0] == '"') change.Path = change.Path.Substring(1, change.Path.Length - 2); + if (!string.IsNullOrEmpty(change.OriginalPath) && change.OriginalPath[0] == '"') change.OriginalPath = change.OriginalPath.Substring(1, change.OriginalPath.Length - 2); + return change; + } + + private void Set(Status index, Status workTree = Status.None) { + Index = index; + WorkTree = workTree; + + if (index == Status.Renamed || workTree == Status.Renamed) { + var idx = Path.IndexOf('\t'); + if (idx >= 0) { + OriginalPath = Path.Substring(0, idx); + Path = Path.Substring(idx + 1); + } else { + idx = Path.IndexOf(" -> "); + if (idx > 0) { + OriginalPath = Path.Substring(0, idx); + Path = Path.Substring(idx + 4); + } + } + } + } + } +} diff --git a/Git/Commit.cs b/Git/Commit.cs new file mode 100644 index 00000000..7cbafad4 --- /dev/null +++ b/Git/Commit.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; + +namespace SourceGit.Git { + + /// + /// Git commit information. + /// + public class Commit { + private static readonly string GPGSIG_START = "gpgsig -----BEGIN PGP SIGNATURE-----"; + private static readonly string GPGSIG_END = " -----END PGP SIGNATURE-----"; + + /// + /// SHA + /// + public string SHA { get; set; } + + /// + /// Short SHA. + /// + public string ShortSHA => SHA.Substring(0, 8); + + /// + /// Parent commit SHAs. + /// + public List Parents { get; set; } = new List(); + + /// + /// Author + /// + public User Author { get; set; } = new User(); + + /// + /// Committer. + /// + public User Committer { get; set; } = new User(); + + /// + /// Subject + /// + public string Subject { get; set; } = ""; + + /// + /// Extra message. + /// + public string Message { get; set; } = ""; + + /// + /// HEAD commit? + /// + public bool IsHEAD { get; set; } = false; + + /// + /// Merged in current branch? + /// + public bool IsMerged { get; set; } = false; + + /// + /// X offset in graph + /// + public double GraphOffset { get; set; } = 0; + + /// + /// Has decorators. + /// + public bool HasDecorators => Decorators.Count > 0; + + /// + /// Decorators. + /// + public List Decorators { get; set; } = new List(); + + /// + /// Read commits. + /// + /// Repository + /// Limitations + /// Parsed commits. + public static List Load(Repository repo, string limit) { + List commits = new List(); + Commit current = null; + bool bSkippingGpgsig = false; + bool findHead = false; + + repo.RunCommand("log --date-order --decorate=full --pretty=raw " + limit, line => { + if (bSkippingGpgsig) { + if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal)) bSkippingGpgsig = false; + return; + } else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal)) { + bSkippingGpgsig = true; + return; + } + + if (line.StartsWith("commit ", StringComparison.Ordinal)) { + if (current != null) { + current.Message = current.Message.TrimEnd(); + commits.Add(current); + } + + current = new Commit(); + ParseSHA(current, line.Substring("commit ".Length)); + if (!findHead) findHead = current.IsHEAD; + return; + } + + if (current == null) return; + + if (line.StartsWith("tree ", StringComparison.Ordinal)) { + return; + } else if (line.StartsWith("parent ", StringComparison.Ordinal)) { + current.Parents.Add(line.Substring("parent ".Length)); + } else if (line.StartsWith("author ", StringComparison.Ordinal)) { + current.Author.Parse(line); + } else if (line.StartsWith("committer ", StringComparison.Ordinal)) { + current.Committer.Parse(line); + } else if (string.IsNullOrEmpty(current.Subject)) { + current.Subject = line.Trim(); + } else { + current.Message += (line.Trim() + "\n"); + } + }); + + if (current != null) { + current.Message = current.Message.TrimEnd(); + commits.Add(current); + } + + if (!findHead && commits.Count > 0) { + var startInfo = new ProcessStartInfo(); + startInfo.FileName = Preference.Instance.GitExecutable; + startInfo.Arguments = $"merge-base --is-ancestor {commits[0].SHA} HEAD"; + startInfo.WorkingDirectory = repo.Path; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + startInfo.RedirectStandardOutput = false; + startInfo.RedirectStandardError = false; + + var proc = new Process() { StartInfo = startInfo }; + proc.Start(); + proc.WaitForExit(); + + commits[0].IsMerged = proc.ExitCode == 0; + proc.Close(); + } + + return commits; + } + + /// + /// Get changed file list. + /// + /// + /// + public List GetChanges(Repository repo) { + var changes = new List(); + var regex = new Regex(@"^[MADRC]\d*\s*.*$"); + + var errs = repo.RunCommand($"show --name-status {SHA}", line => { + if (!regex.IsMatch(line)) return; + + var change = Change.Parse(line, true); + if (change != null) changes.Add(change); + }); + + if (errs != null) App.RaiseError(errs); + return changes; + } + + /// + /// Get revision files. + /// + /// + /// + public List GetFiles(Repository repo) { + var files = new List(); + + var errs = repo.RunCommand($"ls-tree --name-only -r {SHA}", line => { + files.Add(line); + }); + + if (errs != null) App.RaiseError(errs); + return files; + } + + /// + /// Get file content. + /// + /// + /// + /// + public string GetTextFileContent(Repository repo, string file) { + var data = new List(); + var isBinary = false; + var count = 0; + + var errs = repo.RunCommand($"show {SHA}:\"{file}\"", line => { + if (isBinary) return; + + count++; + if (data.Count >= 1000) return; + + if (line.IndexOf('\0') >= 0) { + isBinary = true; + data.Clear(); + data.Add("BINARY FILE PREVIEW NOT SUPPORTED!"); + return; + } + + data.Add(line); + }); + + if (!isBinary && count > 1000) { + data.Add("..."); + data.Add($"Total {count} lines. Hide {count-1000} lines."); + } + + if (errs != null) App.RaiseError(errs); + return string.Join("\n", data); + } + + private static void ParseSHA(Commit commit, string data) { + var decoratorStart = data.IndexOf('('); + if (decoratorStart < 0) { + commit.SHA = data.Trim(); + return; + } + + commit.SHA = data.Substring(0, decoratorStart).Trim(); + + var subs = data.Substring(decoratorStart + 1).Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var sub in subs) { + var d = sub.Trim(); + if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal)) { + commit.Decorators.Add(new Decorator() { + Type = DecoratorType.Tag, + Name = d.Substring(15).Trim() + }); + } else if (d.EndsWith("/HEAD")) { + continue; + } else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal)) { + commit.IsHEAD = true; + commit.Decorators.Add(new Decorator() { + Type = DecoratorType.CurrentBranchHead, + Name = d.Substring(19).Trim() + }); + } else if (d.StartsWith("refs/heads/", StringComparison.Ordinal)) { + commit.Decorators.Add(new Decorator() { + Type = DecoratorType.LocalBranchHead, + Name = d.Substring(11).Trim() + }); + } else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal)) { + commit.Decorators.Add(new Decorator() { + Type = DecoratorType.RemoteBranchHead, + Name = d.Substring(13).Trim() + }); + } + } + } + } +} diff --git a/Git/Decorator.cs b/Git/Decorator.cs new file mode 100644 index 00000000..d9131712 --- /dev/null +++ b/Git/Decorator.cs @@ -0,0 +1,21 @@ +namespace SourceGit.Git { + + /// + /// Decorator type. + /// + public enum DecoratorType { + None, + CurrentBranchHead, + LocalBranchHead, + RemoteBranchHead, + Tag, + } + + /// + /// Commit decorator. + /// + public class Decorator { + public DecoratorType Type { get; set; } + public string Name { get; set; } + } +} diff --git a/Git/MergeTool.cs b/Git/MergeTool.cs new file mode 100644 index 00000000..8fda9492 --- /dev/null +++ b/Git/MergeTool.cs @@ -0,0 +1,202 @@ +using Microsoft.Win32; +using SourceGit.UI; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SourceGit.Git { + + /// + /// External merge tool + /// + public class MergeTool { + + /// + /// Display name + /// + public string Name { get; set; } + + /// + /// Executable file name. + /// + public string ExecutableName { get; set; } + + /// + /// Command line parameter. + /// + public string Parameter { get; set; } + + /// + /// Auto finder. + /// + public Func Finder { get; set; } + + /// + /// Is this merge tool configured. + /// + public bool IsConfigured => !string.IsNullOrEmpty(ExecutableName); + + /// + /// Supported merge tools. + /// + public static List Supported = new List() { + new MergeTool("--", "", "", FindInvalid), + new MergeTool("Araxis Merge", "Compare.exe", "/wait /merge /3 /a1 \"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", FindAraxisMerge), + new MergeTool("Beyond Compare 4", "BComp.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", FindBCompare), + new MergeTool("KDiff3", "kdiff3.exe", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", FindKDiff3), + new MergeTool("P4Merge", "p4merge.exe", "\"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", FindP4Merge), + new MergeTool("Tortoise Merge", "TortoiseMerge.exe", "-base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"", FindTortoiseMerge), + new MergeTool("Visual Studio 2017/2019", "vsDiffMerge.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\" //m", FindVSMerge), + new MergeTool("Visual Studio Code", "Code.exe", "-n --wait \"$MERGED\"", FindVSCode), + }; + + /// + /// Finder for invalid merge tool. + /// + /// + public static string FindInvalid() { + return "--"; + } + + /// + /// Find araxis merge tool install path. + /// + /// + public static string FindAraxisMerge() { + var path = @"C:\Program Files\Araxis\Araxis Merge\Compare.exe"; + if (File.Exists(path)) return path; + return ""; + } + + /// + /// Find kdiff3.exe by registry. + /// + /// + public 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; + } + + /// + /// Finder for p4merge + /// + /// + public static string FindP4Merge() { + var path = @"C:\Program Files\Perforce\p4merge.exe"; + if (File.Exists(path)) return path; + return ""; + } + + /// + /// Find BComp.exe by registry. + /// + /// + public 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"; + } + + /// + /// Find TortoiseMerge.exe by registry. + /// + /// + public static string FindTortoiseMerge() { + var root = RegistryKey.OpenBaseKey( + RegistryHive.LocalMachine, + Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); + + var tortoiseSVN = root.OpenSubKey("SOFTWARE\\TortoiseSVN"); + if (tortoiseSVN == null) return ""; + return tortoiseSVN.GetValue("TMergePath") as string; + } + + /// + /// Find vsDiffMerge.exe. + /// + /// + public 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"; + } + + /// + /// Find VSCode executable file path. + /// + /// + public 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 ""; + } + + /// + /// Constructor. + /// + /// + /// + /// + /// + public MergeTool(string name, string exe, string param, Func finder) { + Name = name; + ExecutableName = exe; + Parameter = param; + Finder = finder; + } + } +} diff --git a/Git/Preference.cs b/Git/Preference.cs new file mode 100644 index 00000000..2d223b53 --- /dev/null +++ b/Git/Preference.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Serialization; + +namespace SourceGit.Git { + + /// + /// User's preference settings. Serialized to + /// + public class Preference { + + /// + /// Group(Virtual folder) for watched repositories. + /// + public class Group { + /// + /// Unique ID of this group. + /// + public string Id { get; set; } + /// + /// Display name. + /// + public string Name { get; set; } + /// + /// Parent ID. + /// + public string ParentId { get; set; } + /// + /// Cache UI IsExpended status. + /// + public bool IsExpended { get; set; } + } + + #region STATICS + /// + /// Storage path for Preference. + /// + private static readonly string SAVE_PATH = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "SourceGit", + "preference.xml"); + /// + /// Runtime singleton instance. + /// + private static Preference instance = null; + public static Preference Instance { + get { + if (instance == null) Load(); + return instance; + } + set { + instance = value; + } + } + #endregion + + #region SETTING_GIT + /// + /// Git executable file path. + /// + public string GitExecutable { get; set; } + /// + /// Default clone directory. + /// + public string GitDefaultCloneDir { get; set; } + #endregion + + #region SETTING_MERGE_TOOL + /// + /// Selected merge tool. + /// + public int MergeTool { get; set; } = 0; + /// + /// Executable file path for merge tool. + /// + public string MergeExecutable { get; set; } = "--"; + #endregion + + #region SETTING_UI + /// + /// Main window's width + /// + public double UIMainWindowWidth { get; set; } + /// + /// Main window's height + /// + public double UIMainWindowHeight { get; set; } + /// + /// Use light color theme. + /// + public bool UIUseLightTheme { get; set; } + /// + /// Show/Hide tags' list view. + /// + public bool UIShowTags { get; set; } = true; + /// + /// Use horizontal layout for histories. + /// + public bool UIUseHorizontalLayout { get; set; } + /// + /// Use list instead of tree in unstaged view + /// + public bool UIUseListInUnstaged { get; set; } + /// + /// Use list instead of tree in staged view. + /// + public bool UIUseListInStaged { get; set; } + /// + /// Use list instead of tree in change view. + /// + public bool UIUseListInChanges { get; set; } + #endregion + + #region SETTING_REPOS + /// + /// Groups for repositories. + /// + public List Groups { get; set; } = new List(); + /// + /// Watched repositories. + /// + public List Repositories { get; set; } = new List(); + #endregion + + #region METHODS_LOAD_SAVE + /// + /// Load preference from disk. + /// + /// Loaded preference instance. + public static void Load() { + if (!File.Exists(SAVE_PATH)) { + instance = new Preference(); + return; + } + + var stream = new FileStream(SAVE_PATH, FileMode.Open); + var reader = new XmlSerializer(typeof(Preference)); + instance = (Preference)reader.Deserialize(stream); + stream.Close(); + } + + /// + /// Save current preference into disk. + /// + public static void Save() { + if (instance == null) return; + + var dir = Path.GetDirectoryName(SAVE_PATH); + if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); + + var stream = new FileStream(SAVE_PATH, FileMode.Create); + var writer = new XmlSerializer(typeof(Preference)); + writer.Serialize(stream, instance); + stream.Flush(); + stream.Close(); + } + #endregion + + #region METHODS_ON_GROUP + /// + /// Add new group(virtual folder). + /// + /// Display name. + /// Parent group ID. + /// Added group instance. + public Group AddGroup(string name, string parentId) { + var group = new Group() { + Name = name, + Id = Guid.NewGuid().ToString(), + ParentId = parentId, + IsExpended = false, + }; + + Groups.Add(group); + Groups.Sort((l, r) => l.Name.CompareTo(r.Name)); + + return group; + } + + /// + /// Find group by ID. + /// + /// Unique ID + /// Founded group's instance. + public Group FindGroup(string id) { + foreach (var group in Groups) { + if (group.Id == id) return group; + } + return null; + } + + /// + /// Rename group. + /// + /// Unique ID + /// New name. + public void RenameGroup(string id, string newName) { + foreach (var group in Groups) { + if (group.Id == id) { + group.Name = newName; + break; + } + } + + Groups.Sort((l, r) => l.Name.CompareTo(r.Name)); + } + + /// + /// Remove a group. + /// + /// Unique ID + public void RemoveGroup(string id) { + int removedIdx = -1; + + for (int i = 0; i < Groups.Count; i++) { + if (Groups[i].Id == id) { + removedIdx = i; + break; + } + } + + if (removedIdx >= 0) Groups.RemoveAt(removedIdx); + } + #endregion + + #region METHODS_ON_REPOS + /// + /// Add repository. + /// + /// Local storage path. + /// Group's ID + /// Added repository instance. + public Repository AddRepository(string path, string groupId) { + var repo = FindRepository(path); + if (repo != null) return repo; + + var dir = new DirectoryInfo(path); + repo = new Repository() { + Path = dir.FullName, + Name = dir.Name, + GroupId = groupId, + LastOpenTime = 0, + }; + + Repositories.Add(repo); + Repositories.Sort((l, r) => l.Name.CompareTo(r.Name)); + return repo; + } + + /// + /// Find repository by path. + /// + /// Local storage path. + /// Founded repository instance. + public Repository FindRepository(string path) { + var dir = new DirectoryInfo(path); + foreach (var repo in Repositories) { + if (repo.Path == dir.FullName) return repo; + } + return null; + } + + /// + /// Change a repository's display name in RepositoryManager. + /// + /// Local storage path. + /// New name + public void RenameRepository(string path, string newName) { + var repo = FindRepository(path); + if (repo == null) return; + + repo.Name = newName; + Repositories.Sort((l, r) => l.Name.CompareTo(r.Name)); + } + + /// + /// Remove a repository in RepositoryManager. + /// + /// Local storage path. + 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 + } +} diff --git a/Git/Remote.cs b/Git/Remote.cs new file mode 100644 index 00000000..4b411ff5 --- /dev/null +++ b/Git/Remote.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SourceGit.Git { + + /// + /// Git remote + /// + public class Remote { + private static readonly Regex FORMAT = new Regex(@"^([\w\.\-]+)\s*(\S+).*$"); + + /// + /// Name of this remote + /// + public string Name { get; set; } + + /// + /// URL + /// + public string URL { get; set; } + + /// + /// Parsing remote + /// + /// Repository + /// + public static List Load(Repository repo) { + var remotes = new List(); + var added = new List(); + + repo.RunCommand("remote -v", data => { + var match = FORMAT.Match(data); + if (!match.Success) return; + + var remote = new Remote() { + Name = match.Groups[1].Value, + URL = match.Groups[2].Value, + }; + + if (added.Contains(remote.Name)) return; + + added.Add(remote.Name); + remotes.Add(remote); + }); + + return remotes; + } + + /// + /// Add new remote + /// + /// + /// + /// + public static void Add(Repository repo, string name, string url) { + var errs = repo.RunCommand($"remote add {name} {url}", null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Delete remote. + /// + /// + /// + public static void Delete(Repository repo, string remote) { + var errs = repo.RunCommand($"remote remove {remote}", null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Edit remote. + /// + /// + /// + /// + public void Edit(Repository repo, string name, string url) { + string errs = null; + + if (name != Name) { + errs = repo.RunCommand($"remote rename {Name} {name}", null); + if (errs != null) { + App.RaiseError(errs); + return; + } + } + + if (url != URL) { + errs = repo.RunCommand($"remote set-url {name} {url}", null); + if (errs != null) App.RaiseError(errs); + } + } + } +} diff --git a/Git/Repository.cs b/Git/Repository.cs new file mode 100644 index 00000000..3d364b5b --- /dev/null +++ b/Git/Repository.cs @@ -0,0 +1,1059 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Threading; +using System.Xml.Serialization; + +namespace SourceGit.Git { + + /// + /// Git repository + /// + public class Repository { + + #region HOOKS + public static Action OnOpen = null; + public static Action OnClose = null; + [XmlIgnore] public Action OnNavigateCommit = null; + [XmlIgnore] public Action OnWorkingCopyChanged = null; + [XmlIgnore] public Action OnTagChanged = null; + [XmlIgnore] public Action OnStashChanged = null; + [XmlIgnore] public Action OnBranchChanged = null; + [XmlIgnore] public Action OnCommitsChanged = null; + #endregion + + #region PROPERTIES_SAVED + /// + /// Storage path. + /// + public string Path { get; set; } + /// + /// Display name. + /// + public string Name { get; set; } + /// + /// Owner group. + /// + public string GroupId { get; set; } + /// + /// Last open time(File time format). + /// + public long LastOpenTime { get; set; } + /// + /// Filters for logs. + /// + public List LogFilters { get; set; } = new List(); + /// + /// Last 10 Commit message. + /// + public List CommitMsgRecords { get; set; } = new List(); + #endregion + + #region PROPERTIES_RUNTIME + private List cachedRemotes = new List(); + private List cachedBranches = new List(); + private List cachedTags = new List(); + private FileSystemWatcher watcher = null; + private DispatcherTimer timer = null; + private bool isWatcherDisabled = false; + private long nextUpdateTags = 0; + private long nextUpdateLocalChanges = 0; + private long nextUpdateStashes = 0; + private long nextUpdateTree = 0; + + private string featurePrefix = null; + private string releasePrefix = null; + private string hotfixPrefix = null; + #endregion + + #region METHOD_PROCESS + /// + /// Read git config + /// + /// + /// + public string GetConfig(string key) { + var startInfo = new ProcessStartInfo(); + startInfo.FileName = Preference.Instance.GitExecutable; + startInfo.Arguments = $"config {key}"; + startInfo.WorkingDirectory = Path; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + startInfo.RedirectStandardOutput = true; + startInfo.StandardOutputEncoding = Encoding.UTF8; + + var proc = new Process() { StartInfo = startInfo }; + proc.Start(); + var output = proc.StandardOutput.ReadToEnd(); + proc.WaitForExit(); + proc.Close(); + + return output.Trim(); + } + + /// + /// Configure git. + /// + /// + /// + public void SetConfig(string key, string value) { + var startInfo = new ProcessStartInfo(); + startInfo.FileName = Preference.Instance.GitExecutable; + startInfo.Arguments = $"config {key} \"{value}\""; + startInfo.WorkingDirectory = Path; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + + var proc = new Process() { StartInfo = startInfo }; + proc.Start(); + proc.WaitForExit(); + proc.Close(); + } + + /// + /// Run git command without repository. + /// + /// Working directory. + /// Arguments for running git command. + /// Handler for output. + /// Handle error as output. + /// Errors if exists. + public static string RunCommand(string cwd, string args, Action outputHandler, bool includeError = false) { + var startInfo = new ProcessStartInfo(); + startInfo.FileName = Preference.Instance.GitExecutable; + startInfo.Arguments = "--no-pager -c core.quotepath=off " + args; + startInfo.WorkingDirectory = cwd; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + startInfo.StandardOutputEncoding = Encoding.UTF8; + startInfo.StandardErrorEncoding = Encoding.UTF8; + + var progressFilter = new Regex(@"\d+\%"); + var errs = new List(); + var proc = new Process() { StartInfo = startInfo }; + + proc.OutputDataReceived += (o, e) => { + if (e.Data == null) return; + outputHandler?.Invoke(e.Data); + }; + proc.ErrorDataReceived += (o, e) => { + if (e.Data == null) return; + if (includeError) outputHandler?.Invoke(e.Data); + if (string.IsNullOrEmpty(e.Data)) return; + if (progressFilter.IsMatch(e.Data)) return; + if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal)) return; + errs.Add(e.Data); + }; + + proc.Start(); + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + proc.WaitForExit(); + + int exitCode = proc.ExitCode; + proc.Close(); + + if (exitCode != 0 && errs.Count > 0) { + return string.Join("\n", errs); + } else { + return null; + } + } + + /// + /// Create process for reading outputs/errors using git.exe + /// + /// Arguments for running git command. + /// Handler for output. + /// Handle error as output. + /// Errors if exists. + public string RunCommand(string args, Action outputHandler, bool includeError = false) { + return RunCommand(Path, args, outputHandler, includeError); + } + + /// + /// Create process and redirect output to file. + /// + /// Git command arguments. + /// File path to redirect output into. + public void RunAndRedirect(string args, string redirectTo) { + var startInfo = new ProcessStartInfo(); + startInfo.FileName = Preference.Instance.GitExecutable; + startInfo.Arguments = "--no-pager " + args; + startInfo.WorkingDirectory = Path; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + + var proc = new Process() { StartInfo = startInfo }; + proc.Start(); + + using (var writer = new FileStream(redirectTo, FileMode.OpenOrCreate)) { + proc.StandardOutput.BaseStream.CopyTo(writer); + } + + proc.WaitForExit(); + proc.Close(); + } + + /// + /// Assert command result and then update branches and commits. + /// + /// + public void AssertCommand(string err) { + if (!string.IsNullOrEmpty(err)) { + App.RaiseError(err); + } else { + Branches(true); + OnBranchChanged?.Invoke(); + OnCommitsChanged?.Invoke(); + OnWorkingCopyChanged?.Invoke(); + } + + isWatcherDisabled = false; + } + #endregion + + #region METHOD_VALIDATIONS + /// + /// Is valid git directory. + /// + /// Local path. + /// + public static bool IsValid(string path) { + var startInfo = new ProcessStartInfo(); + startInfo.FileName = Preference.Instance.GitExecutable; + startInfo.Arguments = "rev-parse --git-dir"; + startInfo.WorkingDirectory = path; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + + try { + var proc = new Process() { StartInfo = startInfo }; + proc.Start(); + proc.WaitForExit(); + + var test = proc.ExitCode == 0; + proc.Close(); + return test; + } catch { + return false; + } + } + + /// + /// Is remote url valid. + /// + /// + /// + public static bool IsValidUrl(string url) { + return !string.IsNullOrEmpty(url) + && (url.StartsWith("http://", StringComparison.Ordinal) + || url.StartsWith("https://", StringComparison.Ordinal) + || url.StartsWith("git://", StringComparison.Ordinal) + || url.StartsWith("ssh://", StringComparison.Ordinal) + || url.StartsWith("file://", StringComparison.Ordinal)); + } + #endregion + + #region METHOD_OPEN_CLOSE + /// + /// Open repository. + /// + public void Open() { + LastOpenTime = DateTime.Now.ToFileTime(); + isWatcherDisabled = false; + + watcher = new FileSystemWatcher(); + watcher.Path = Path; + watcher.Filter = "*"; + watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName; + watcher.IncludeSubdirectories = true; + watcher.Created += OnFSChanged; + watcher.Renamed += OnFSChanged; + watcher.Changed += OnFSChanged; + watcher.Deleted += OnFSChanged; + watcher.EnableRaisingEvents = true; + + timer = new DispatcherTimer(); + timer.Tick += Tick; + timer.Interval = TimeSpan.FromSeconds(.1); + timer.Start(); + + featurePrefix = GetConfig("gitflow.prefix.feature"); + releasePrefix = GetConfig("gitflow.prefix.release"); + hotfixPrefix = GetConfig("gitflow.prefix.hotfix"); + + OnOpen?.Invoke(this); + } + + /// + /// Close repository. + /// + public void Close() { + OnBranchChanged = null; + OnCommitsChanged = null; + OnTagChanged = null; + OnStashChanged = null; + OnWorkingCopyChanged = null; + OnNavigateCommit = null; + + cachedBranches.Clear(); + cachedRemotes.Clear(); + cachedTags.Clear(); + + watcher.EnableRaisingEvents = false; + watcher.Dispose(); + timer.Stop(); + + watcher = null; + timer = null; + featurePrefix = null; + releasePrefix = null; + hotfixPrefix = null; + + OnClose?.Invoke(); + } + #endregion + + #region METHOD_WATCHER + public void SetWatcherEnabled(bool enabled) { + isWatcherDisabled = !enabled; + } + + private void Tick(object sender, EventArgs e) { + if (isWatcherDisabled) { + nextUpdateLocalChanges = 0; + nextUpdateStashes = 0; + nextUpdateTags = 0; + nextUpdateTree = 0; + return; + } + + var now = DateTime.Now.ToFileTime(); + if (nextUpdateLocalChanges > 0 && now >= nextUpdateLocalChanges) { + nextUpdateLocalChanges = 0; + OnWorkingCopyChanged?.Invoke(); + } + + if (nextUpdateTags > 0 && now >= nextUpdateTags) { + nextUpdateTags = 0; + OnTagChanged?.Invoke(); + } + + if (nextUpdateStashes > 0 && now >= nextUpdateStashes) { + nextUpdateStashes = 0; + OnStashChanged?.Invoke(); + } + + if (nextUpdateTree > 0 && now >= nextUpdateTree) { + nextUpdateTree = 0; + Branches(true); + OnBranchChanged?.Invoke(); + OnCommitsChanged?.Invoke(); + } + } + + private void OnFSChanged(object sender, FileSystemEventArgs e) { + if (e.Name.StartsWith(".git", StringComparison.Ordinal)) { + if (e.Name.Equals(".git") || e.Name.StartsWith(".git\\index")) return; + + if (e.Name.Equals(".gitignore") || e.Name.Equals(".gitattributes")) { + nextUpdateLocalChanges = DateTime.Now.AddSeconds(1.5).ToFileTime(); + } else if (e.Name.StartsWith(".git\\refs\\tags", StringComparison.Ordinal)) { + nextUpdateTags = DateTime.Now.AddSeconds(.5).ToFileTime(); + } else if (e.Name.StartsWith(".git\\refs\\stash", StringComparison.Ordinal)) { + nextUpdateStashes = DateTime.Now.AddSeconds(.5).ToFileTime(); + } else if (e.Name.EndsWith("_HEAD", StringComparison.Ordinal) || + e.Name.StartsWith(".git\\refs\\heads", StringComparison.Ordinal) || + e.Name.StartsWith(".git\\refs\\remotes", StringComparison.Ordinal)) { + nextUpdateTree = DateTime.Now.AddSeconds(.5).ToFileTime(); + } + } else { + nextUpdateLocalChanges = DateTime.Now.AddSeconds(1.5).ToFileTime(); + } + } + #endregion + + #region METHOD_GITCOMMANDS + /// + /// Clone repository. + /// + /// Remote repository URL + /// Folder to clone into + /// Local name + /// + /// + public static Repository Clone(string url, string folder, string name, Action onProgress) { + var errs = RunCommand(folder, $"-c credential.helper=manager clone --progress --verbose --recurse-submodules {url} {name}", line => { + if (line != null) onProgress?.Invoke(line); + }, true); + + if (errs != null) { + App.RaiseError(errs); + return null; + } + + var path = new DirectoryInfo(folder + "/" + name).FullName; + var repo = Preference.Instance.AddRepository(path, ""); + return repo; + } + + /// + /// Fetch remote changes + /// + /// + /// + /// + public void Fetch(Remote remote, bool prune, Action onProgress) { + isWatcherDisabled = true; + + var args = "-c credential.helper=manager fetch --progress --verbose "; + + if (prune) args += "--prune "; + + if (remote == null) { + args += "--all"; + } else { + args += remote.Name; + } + + var errs = RunCommand(args, line => { + if (line != null) onProgress?.Invoke(line); + }, true); + + AssertCommand(errs); + } + + /// + /// Pull remote changes. + /// + /// remote + /// branch + /// Progress message handler. + /// Use rebase instead of merge. + /// Auto stash local changes. + /// Progress message handler. + public void Pull(string remote, string branch, Action onProgress, bool rebase = false, bool autostash = false) { + isWatcherDisabled = true; + + var args = "-c credential.helper=manager pull --verbose --progress "; + var needPopStash = false; + + if (rebase) args += "--rebase "; + if (autostash) { + if (rebase) { + args += "--autostash "; + } else { + var changes = LocalChanges(); + if (changes.Count > 0) { + var fatal = RunCommand("stash push -u -m \"PULL_AUTO_STASH\"", null); + if (fatal != null) { + App.RaiseError(fatal); + isWatcherDisabled = false; + return; + } + needPopStash = true; + } + } + } + + var errs = RunCommand(args + remote + " " + branch, line => { + if (line != null) onProgress?.Invoke(line); + }, true); + + AssertCommand(errs); + + if (needPopStash) RunCommand("stash pop -q stash@{0}", null); + } + + /// + /// Push local branch to remote. + /// + /// Remote + /// Local branch name + /// Remote branch name + /// Progress message handler. + /// Push tags + /// Create track reference + /// Force push + public void Push(string remote, string localBranch, string remoteBranch, Action onProgress, bool withTags = false, bool track = false, bool force = false) { + isWatcherDisabled = true; + + var args = "-c credential.helper=manager push --progress --verbose "; + + if (withTags) args += "--tags "; + if (track) args += "-u "; + if (force) args += "--force-with-lease "; + + var errs = RunCommand(args + remote + " " + localBranch + ":" + remoteBranch, line => { + if (line != null) onProgress?.Invoke(line); + }, true); + + AssertCommand(errs); + } + + /// + /// Apply patch. + /// + /// + /// + public void Apply(string patch, string whitespaceMode) { + isWatcherDisabled = true; + + var errs = RunCommand($"apply --whitespace={whitespaceMode} \"{patch}\"", null); + if (errs != null) { + App.RaiseError(errs); + } else { + OnWorkingCopyChanged?.Invoke(); + } + + isWatcherDisabled = false; + } + + /// + /// Revert given commit. + /// + /// + /// + public void Revert(string commit, bool autoCommit) { + isWatcherDisabled = true; + + var errs = RunCommand($"revert {commit} --no-edit" + (autoCommit ? "" : " --no-commit"), null); + AssertCommand(errs); + } + + /// + /// Checkout + /// + /// Options. + public void Checkout(string option) { + isWatcherDisabled = true; + + var errs = RunCommand($"checkout {option}", null); + AssertCommand(errs); + } + + /// + /// Merge given branch into current. + /// + /// + /// + public void Merge(string branch, string option) { + isWatcherDisabled = true; + + var errs = RunCommand($"merge {branch} {option}", null); + AssertCommand(errs); + } + + /// + /// Rebase current branch to revision + /// + /// + /// + public void Rebase(string revision, bool autoStash) { + isWatcherDisabled = true; + + var args = $"rebase "; + if (autoStash) args += "--autostash "; + args += revision; + + var errs = RunCommand(args, null); + AssertCommand(errs); + } + + /// + /// Reset. + /// + /// + /// + public void Reset(string revision, string mode = "") { + isWatcherDisabled = true; + + var errs = RunCommand($"reset {mode} {revision}", null); + AssertCommand(errs); + } + + /// + /// Cherry pick commit. + /// + /// + /// + public void CherryPick(string commit, bool noCommit) { + isWatcherDisabled = true; + + var args = "cherry-pick "; + args += noCommit ? "-n " : "--ff "; + args += commit; + + var errs = RunCommand(args, null); + AssertCommand(errs); + } + + /// + /// Stage(add) files to index. + /// + /// + public void Stage(params string[] files) { + isWatcherDisabled = true; + + var args = "add"; + if (files == null || files.Length == 0) { + args += " ."; + } else { + args += " --"; + foreach (var file in files) args += $" \"{file}\""; + } + + var errs = RunCommand(args, null); + if (errs != null) App.RaiseError(errs); + + OnWorkingCopyChanged?.Invoke(); + isWatcherDisabled = false; + } + + /// + /// Unstage files from index + /// + /// + public void Unstage(params string[] files) { + isWatcherDisabled = true; + + var args = "reset"; + if (files != null && files.Length > 0) { + args += " --"; + foreach (var file in files) args += $" \"{file}\""; + } + + var errs = RunCommand(args, null); + if (errs != null) App.RaiseError(errs); + + OnWorkingCopyChanged?.Invoke(); + isWatcherDisabled = false; + } + + /// + /// Discard changes. + /// + /// + public void Discard(List changes) { + isWatcherDisabled = true; + + if (changes == null || changes.Count == 0) { + var errs = RunCommand("reset --hard HEAD", null); + if (errs != null) { + App.RaiseError(errs); + isWatcherDisabled = false; + return; + } + + RunCommand("clean -qfd", null); + } else { + foreach (var change in changes) { + if (change.WorkTree == Change.Status.Untracked || change.WorkTree == Change.Status.Added) { + RunCommand($"clean -qfd -- \"{change.Path}\"", null); + } else { + RunCommand($"restore -- \"{change.Path}\"", null); + } + } + } + + OnWorkingCopyChanged?.Invoke(); + isWatcherDisabled = false; + } + + /// + /// Commit + /// + /// + /// + public bool DoCommit(string message, bool amend) { + isWatcherDisabled = true; + + var file = System.IO.Path.Combine(Path, ".git", "COMMITMESSAGE"); + File.WriteAllText(file, message); + + var args = $"commit --file=\"{file}\""; + if (amend) args += " --amend --no-edit"; + var errs = RunCommand(args, null); + AssertCommand(errs); + + var branch = CurrentBranch(); + OnNavigateCommit?.Invoke(branch.Head); + return string.IsNullOrEmpty(errs); + } + + /// + /// Get all remotes of this repository. + /// + /// Force reload + /// Remote collection + public List Remotes(bool bForceReload = false) { + if (cachedRemotes.Count == 0 || bForceReload) { + cachedRemotes = Remote.Load(this); + } + + return cachedRemotes; + } + + /// + /// Local changes in working copy. + /// + /// Changes. + public List LocalChanges() { + List changes = new List(); + RunCommand("status -uall --porcelain", line => { + if (!string.IsNullOrEmpty(line)) { + var change = Change.Parse(line); + if (change != null) changes.Add(change); + } + }); + return changes; + } + + /// + /// Get total commit count. + /// + /// Number of total commits. + public int TotalCommits() { + int count = 0; + RunCommand("rev-list --all --count", line => { + if (!string.IsNullOrEmpty(line)) count = int.Parse(line.Trim()); + }); + return count; + } + + /// + /// Load commits. + /// + /// Extra limit arguments for `git log` + /// Commit collection + public List Commits(string limit = null) { + return Commit.Load(this, (limit == null ? "" : limit)); ; + } + + /// + /// Load all branches. + /// + /// Force reload. + /// Branches collection. + public List Branches(bool bForceReload = false) { + if (cachedBranches.Count == 0 || bForceReload) { + cachedBranches = Branch.Load(this); + } + + if (IsGitFlowEnabled()) { + foreach (var b in cachedBranches) { + if (b.IsLocal) { + if (b.Name.StartsWith(featurePrefix)) { + b.Kind = Branch.Type.Feature; + } else if (b.Name.StartsWith(releasePrefix)) { + b.Kind = Branch.Type.Release; + } else if (b.Name.StartsWith(hotfixPrefix)) { + b.Kind = Branch.Type.Hotfix; + } + } + } + } + + return cachedBranches; + } + + /// + /// Get current branch + /// + /// + public Branch CurrentBranch() { + foreach (var b in cachedBranches) { + if (b.IsCurrent) return b; + } + + return null; + } + + /// + /// Load all tags. + /// + /// + /// + public List Tags(bool bForceReload = false) { + if (cachedTags.Count == 0 || bForceReload) { + cachedTags = Tag.Load(this); + } + + return cachedTags; + } + + /// + /// Get all stashes + /// + /// + public List Stashes() { + var reflog = new Regex(@"^Reflog: refs/(stash@\{\d+\}).*$"); + var stashes = new List(); + var current = null as Stash; + + var errs = RunCommand("stash list --pretty=raw", line => { + if (line.StartsWith("commit ")) { + if (current != null && !string.IsNullOrEmpty(current.Name)) stashes.Add(current); + current = new Stash() { SHA = line.Substring(7, 8) }; + return; + } + + if (current == null) return; + + if (line.StartsWith("Reflog: refs/stash@")) { + var match = reflog.Match(line); + if (match.Success) current.Name = match.Groups[1].Value; + } else if (line.StartsWith("Reflog message: ")) { + current.Message = line.Substring(16); + } else if (line.StartsWith("author ")) { + current.Author.Parse(line); + } + }); + + if (current != null) stashes.Add(current); + if (errs != null) App.RaiseError(errs); + return stashes; + } + + /// + /// Diff + /// + /// + /// + /// + /// + /// + public List Diff(string startRevision, string endRevision, string file, string orgFile = null) { + var args = $"diff --ignore-cr-at-eol {startRevision} {endRevision} -- "; + if (!string.IsNullOrEmpty(orgFile)) args += $"\"{orgFile}\" "; + args += $"\"{file}\""; + + var data = new List(); + var errs = RunCommand(args, line => data.Add(line)); + + if (errs != null) App.RaiseError(errs); + return data; + } + + /// + /// Blame file. + /// + /// + /// + /// + public Blame BlameFile(string file, string revision) { + var regex = new Regex(@"^\^?([0-9a-f]+)\s+.*\((.*)\s+(\d+)\s+[\-\+]?\d+\s+\d+\) (.*)"); + var blame = new Blame(); + var current = null as Blame.Block; + + var errs = RunCommand($"blame -t {revision} -- \"{file}\"", line => { + if (blame.IsBinary) return; + if (string.IsNullOrEmpty(line)) return; + + if (line.IndexOf('\0') >= 0) { + blame.IsBinary = true; + blame.Blocks.Clear(); + return; + } + + var match = regex.Match(line); + if (!match.Success) return; + + var commit = match.Groups[1].Value; + var data = match.Groups[4].Value; + if (current != null && current.CommitSHA == commit) { + current.Content = current.Content + "\n" + data; + } else { + var timestamp = int.Parse(match.Groups[3].Value); + var when = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); + + current = new Blame.Block() { + CommitSHA = commit, + Author = match.Groups[2].Value, + Time = when, + Content = data, + }; + + if (current.Author == null) current.Author = ""; + blame.Blocks.Add(current); + } + + blame.LineCount++; + }); + + if (errs != null) App.RaiseError(errs); + return blame; + } + #endregion + + #region METHOD_GITFLOW + /// + /// Check if git-flow feature enabled + /// + /// + public bool IsGitFlowEnabled() { + return !string.IsNullOrEmpty(featurePrefix) + && !string.IsNullOrEmpty(releasePrefix) + && !string.IsNullOrEmpty(hotfixPrefix); + } + + /// + /// Get git-flow branch prefix. + /// + /// + public string GetFeaturePrefix() { return featurePrefix; } + public string GetReleasePrefix() { return releasePrefix; } + public string GetHotfixPrefix() { return hotfixPrefix; } + + /// + /// Enable git-flow + /// + /// + /// + /// + /// + /// + /// + public void EnableGitFlow(string master, string develop, string feature, string release, string hotfix, string version = "") { + isWatcherDisabled = true; + + var branches = Branches(); + var masterBranch = branches.Find(b => b.Name == master); + var devBranch = branches.Find(b => b.Name == develop); + var refreshBranches = false; + + if (masterBranch == null) { + var errs = RunCommand($"branch --no-track {master}", null); + if (errs != null) { + App.RaiseError(errs); + isWatcherDisabled = false; + return; + } + + refreshBranches = true; + } + + if (devBranch == null) { + var errs = RunCommand($"branch --no-track {develop}", null); + if (errs != null) { + App.RaiseError(errs); + if (refreshBranches) { + Branches(true); + OnBranchChanged?.Invoke(); + OnCommitsChanged?.Invoke(); + OnWorkingCopyChanged?.Invoke(); + } + isWatcherDisabled = false; + return; + } + + refreshBranches = true; + } + + SetConfig("gitflow.branch.master", master); + SetConfig("gitflow.branch.develop", develop); + SetConfig("gitflow.prefix.feature", feature); + SetConfig("gitflow.prefix.bugfix", "bugfix"); + SetConfig("gitflow.prefix.release", release); + SetConfig("gitflow.prefix.hotfix", hotfix); + SetConfig("gitflow.prefix.support", "support"); + SetConfig("gitflow.prefix.versiontag", version); + + RunCommand("flow init -d", null); + + featurePrefix = GetConfig("gitflow.prefix.feature"); + releasePrefix = GetConfig("gitflow.prefix.release"); + hotfixPrefix = GetConfig("gitflow.prefix.hotfix"); + + if (!IsGitFlowEnabled()) App.RaiseError("Initialize Git-flow failed!"); + + if (refreshBranches) { + Branches(true); + OnBranchChanged?.Invoke(); + OnCommitsChanged?.Invoke(); + OnWorkingCopyChanged?.Invoke(); + } + + isWatcherDisabled = false; + } + + /// + /// Start git-flow branch + /// + /// + /// + public void StartGitFlowBranch(Branch.Type type, string name) { + isWatcherDisabled = true; + + string args; + switch (type) { + case Branch.Type.Feature: args = $"flow feature start {name}"; break; + case Branch.Type.Release: args = $"flow release start {name}"; break; + case Branch.Type.Hotfix: args = $"flow hotfix start {name}"; break; + default: + App.RaiseError("Bad git-flow branch type!"); + return; + } + + var errs = RunCommand(args, null); + AssertCommand(errs); + } + + /// + /// Finish git-flow branch + /// + /// + public void FinishGitFlowBranch(Branch branch) { + isWatcherDisabled = true; + + string args; + switch (branch.Kind) { + case Branch.Type.Feature: + args = $"flow feature finish {branch.Name.Substring(featurePrefix.Length)}"; + break; + case Branch.Type.Release: + var releaseName = branch.Name.Substring(releasePrefix.Length); + args = $"flow release finish {releaseName} -m \"Release done\""; + break; + case Branch.Type.Hotfix: + var hotfixName = branch.Name.Substring(hotfixPrefix.Length); + args = $"flow hotfix finish {hotfixName} -m \"Hotfix done\""; + break; + default: + App.RaiseError("Bad git-flow branch type!"); + return; + } + + var errs = RunCommand(args, null); + AssertCommand(errs); + OnTagChanged?.Invoke(); + } + #endregion + + #region METHOD_COMMITMSG + public void RecordCommitMessage(string message) { + if (string.IsNullOrEmpty(message)) return; + + int exists = CommitMsgRecords.Count; + if (exists > 0) { + var last = CommitMsgRecords[0]; + if (last == message) return; + } + + if (exists >= 10) { + CommitMsgRecords.RemoveRange(9, exists - 9); + } + + CommitMsgRecords.Insert(0, message); + } + #endregion + } +} diff --git a/Git/Stash.cs b/Git/Stash.cs new file mode 100644 index 00000000..d83f85f0 --- /dev/null +++ b/Git/Stash.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SourceGit.Git { + + /// + /// Git stash + /// + public class Stash { + + /// + /// SHA for this stash + /// + public string SHA { get; set; } + + /// + /// Name + /// + public string Name { get; set; } + + /// + /// Author + /// + public User Author { get; set; } = new User(); + + /// + /// Message + /// + public string Message { get; set; } + + /// + /// Stash push. + /// + /// + /// + /// + /// + public static void Push(Repository repo, bool includeUntracked, string message, List files) { + string specialFiles = ""; + + if (files.Count > 0) { + specialFiles = " --"; + foreach (var f in files) specialFiles += $" \"{f}\""; + } + + string args = "stash push "; + if (includeUntracked) args += "-u "; + if (!string.IsNullOrEmpty(message)) args += $"-m \"{message}\" "; + + var errs = repo.RunCommand(args + specialFiles, null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Get changed file list in this stash. + /// + /// + /// + public List GetChanges(Repository repo) { + List changes = new List(); + + var errs = repo.RunCommand($"diff --name-status --pretty=format: {SHA}^ {SHA}", line => { + var change = Change.Parse(line); + if (change != null) changes.Add(change); + }); + + if (errs != null) App.RaiseError(errs); + return changes; + } + + /// + /// Apply stash. + /// + /// + public void Apply(Repository repo) { + var errs = repo.RunCommand($"stash apply -q {Name}", null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Pop stash + /// + /// + public void Pop(Repository repo) { + var errs = repo.RunCommand($"stash pop -q {Name}", null); + if (errs != null) App.RaiseError(errs); + } + + /// + /// Drop stash + /// + /// + public void Drop(Repository repo) { + var errs = repo.RunCommand($"stash drop -q {Name}", null); + if (errs != null) App.RaiseError(errs); + } + } +} diff --git a/Git/Tag.cs b/Git/Tag.cs new file mode 100644 index 00000000..f329c4d4 --- /dev/null +++ b/Git/Tag.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace SourceGit.Git { + + /// + /// Git tag. + /// + public class Tag { + private static readonly Regex FORMAT = new Regex(@"\$(.*)\$(.*)\$(.*)"); + + /// + /// SHA + /// + public string SHA { get; set; } + + /// + /// Display name. + /// + public string Name { get; set; } + + /// + /// Enable filter in log histories. + /// + public bool IsFiltered { get; set; } + + /// + /// Load all tags + /// + /// + /// + public static List Load(Repository repo) { + var args = "for-each-ref --sort=-creatordate --format=\"$%(refname:short)$%(objectname)$%(*objectname)\" refs/tags"; + var tags = new List(); + + repo.RunCommand(args, line => { + var match = FORMAT.Match(line); + if (!match.Success) return; + + var name = match.Groups[1].Value; + var commit = match.Groups[2].Value; + var dereference = match.Groups[3].Value; + + if (string.IsNullOrEmpty(dereference)) { + tags.Add(new Tag() { + Name = name, + SHA = commit, + }); + } else { + tags.Add(new Tag() { + Name = name, + SHA = dereference, + }); + } + }); + + return tags; + } + + /// + /// Add new tag. + /// + /// + /// + /// + /// + public static void Add(Repository repo, string name, string startPoint, string message) { + var args = $"tag -a {name} {startPoint} "; + + if (!string.IsNullOrEmpty(message)) { + string temp = Path.GetTempFileName(); + File.WriteAllText(temp, message); + args += $"-F \"{temp}\""; + } else { + args += $"-m {name}"; + } + + var errs = repo.RunCommand(args, null); + if (errs != null) App.RaiseError(errs); + else repo.OnCommitsChanged?.Invoke(); + } + + /// + /// Delete tag. + /// + /// + /// + /// + public static void Delete(Repository repo, string name, bool push) { + var errs = repo.RunCommand($"tag --delete {name}", null); + if (errs != null) { + App.RaiseError(errs); + return; + } + + if (push) { + var remotes = repo.Remotes(); + foreach (var r in remotes) { + repo.RunCommand($"-c credential.helper=manager push --delete {r.Name} refs/tags/{name}", null); + } + } + + repo.OnCommitsChanged?.Invoke(); + } + + /// + /// Push tag to remote. + /// + /// + /// + /// + public static void Push(Repository repo, string name, string remote) { + var errs = repo.RunCommand($"-c credential.helper=manager push {remote} refs/tags/{name}", null); + if (errs != null) App.RaiseError(errs); + } + } +} diff --git a/Git/User.cs b/Git/User.cs new file mode 100644 index 00000000..0e1b4120 --- /dev/null +++ b/Git/User.cs @@ -0,0 +1,42 @@ +using System; +using System.Text.RegularExpressions; + +namespace SourceGit.Git { + + /// + /// Git user. + /// + public class User { + private static readonly Regex FORMAT = new Regex(@"\w+ (.*) <([\w\.\-_]+@[\w\.\-_]+)> (\d{10}) [\+\-]\d+"); + + /// + /// Name. + /// + public string Name { get; set; } = ""; + + /// + /// Email. + /// + public string Email { get; set; } = ""; + + /// + /// Operation time. + /// + public string Time { get; set; } = ""; + + /// + /// Parse user from raw string. + /// + /// Raw string + public void Parse(string data) { + var match = FORMAT.Match(data); + if (!match.Success) return; + + var time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(int.Parse(match.Groups[3].Value)); + + Name = match.Groups[1].Value; + Email = match.Groups[2].Value; + Time = time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); + } + } +} diff --git a/Helpers/CommitGraph.cs b/Helpers/CommitGraph.cs new file mode 100644 index 00000000..82c37f0d --- /dev/null +++ b/Helpers/CommitGraph.cs @@ -0,0 +1,274 @@ +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Media; + +namespace SourceGit.Helpers { + + /// + /// Tools to parse commit graph. + /// + public class CommitGraphMaker { + /// + /// Sizes + /// + public static readonly double UNIT_WIDTH = 12; + public static readonly double HALF_WIDTH = 6; + public static readonly double DOUBLE_WIDTH = 24; + public static readonly double UNIT_HEIGHT = 24; + public static readonly double HALF_HEIGHT = 12; + + /// + /// Colors + /// + public static Brush[] Colors = new Brush[] { + Brushes.Orange, + Brushes.ForestGreen, + Brushes.Gold, + Brushes.Magenta, + Brushes.Red, + Brushes.Gray, + Brushes.Turquoise, + Brushes.Olive, + }; + + /// + /// Helpers to draw lines. + /// + public class LineHelper { + private double lastX = 0; + private double lastY = 0; + + /// + /// Parent commit id. + /// + public string Next { get; set; } + + /// + /// Is merged into this tree. + /// + public bool IsMerged { get; set; } + + /// + /// Points in line + /// + public List Points { get; set; } + + /// + /// Brush to draw line + /// + public Brush Brush { get; set; } + + /// + /// Current horizontal offset. + /// + public double HorizontalOffset => lastX; + + /// + /// Constructor. + /// + /// Parent commit id + /// Is merged in tree + /// Color index + /// Start point + public LineHelper(string nextCommitId, bool isMerged, int colorIdx, Point startPoint) { + Next = nextCommitId; + IsMerged = isMerged; + Points = new List() { startPoint }; + Brush = Colors[colorIdx % Colors.Length]; + + lastX = startPoint.X; + lastY = startPoint.Y; + } + + /// + /// Line to. + /// + /// + /// + /// + public void AddPoint(double x, double y, bool isEnd = false) { + if (x > lastX) { + Points.Add(new Point(lastX, lastY)); + Points.Add(new Point(x, y - HALF_HEIGHT)); + } else if (x < lastX) { + Points.Add(new Point(lastX, lastY + HALF_HEIGHT)); + Points.Add(new Point(x, y)); + } + + lastX = x; + lastY = y; + + if (isEnd) { + var last = Points.Last(); + if (last.X != lastX || last.Y != lastY) Points.Add(new Point(lastX, lastY)); + } + } + } + + /// + /// Short link between two commits. + /// + public struct ShortLink { + public Point Start; + public Point Control; + public Point End; + public Brush Brush; + } + + /// + /// Dot + /// + public struct Dot { + public double X; + public double Y; + public Brush Color; + } + + /// + /// Independent lines in graph + /// + public List Lines { get; set; } = new List(); + + /// + /// Short links. + /// + public List Links { get; set; } = new List(); + + /// + /// All dots. + /// + public List Dots { get; set; } = new List(); + + /// + /// Highlight commit id. + /// + public string Highlight { get; set; } + + /// + /// Parse commits. + /// + /// + /// + public static CommitGraphMaker Parse(List commits) { + CommitGraphMaker maker = new CommitGraphMaker(); + + List unsolved = new List(); + List ended = new List(); + Dictionary currentMap = new Dictionary(); + double offsetY = -HALF_HEIGHT; + int colorIdx = 0; + + for (int i = 0; i < commits.Count; i++) { + Git.Commit commit = commits[i]; + LineHelper major = null; + bool isMerged = commit.IsHEAD || commit.IsMerged; + int oldCount = unsolved.Count; + + // 更新Y坐标 + offsetY += UNIT_HEIGHT; + + // 找到当前的分支的HEAD,用于默认选中 + if (maker.Highlight == null && commit.IsHEAD) { + maker.Highlight = commit.SHA; + } + + // 找到第一个依赖于本提交的树,将其他依赖于本提交的树标记为终止,并对已存在的线路调整(防止线重合) + 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 (!currentMap.ContainsKey(major.Next)) currentMap.Add(major.Next, major); + } else { + major.Next = "ENDED"; + } + + major.AddPoint(offsetX, offsetY); + } else { + ended.Add(l); + } + + isMerged = isMerged || l.IsMerged; + } else { + if (!currentMap.ContainsKey(l.Next)) currentMap.Add(l.Next, l); + offsetX += UNIT_WIDTH; + l.AddPoint(offsetX, offsetY); + } + } + + // 处理本提交为非当前分支HEAD的情况(创建新依赖线路) + if (major == null && commit.Parents.Count > 0) { + offsetX += UNIT_WIDTH; + major = new LineHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY)); + unsolved.Add(major); + colorIdx++; + } + + // 确定本提交的点的位置 + Point position = new Point(offsetX, offsetY); + if (major != null) { + major.IsMerged = isMerged; + position.X = major.HorizontalOffset; + position.Y = offsetY; + maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = major.Brush }); + } else { + maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = Brushes.Orange }); + } + + // 处理本提交的其他依赖 + for (int j = 1; j < commit.Parents.Count; j++) { + var parent = commit.Parents[j]; + if (currentMap.ContainsKey(parent)) { + var l = currentMap[parent]; + var link = new ShortLink(); + + link.Start = position; + link.End = new Point(l.HorizontalOffset, offsetY + HALF_HEIGHT); + link.Control = new Point(link.End.X, link.Start.Y); + link.Brush = l.Brush; + maker.Links.Add(link); + } else { + offsetX += UNIT_WIDTH; + unsolved.Add(new LineHelper(commit.Parents[j], isMerged, colorIdx, position)); + colorIdx++; + } + } + + // 处理已终止的线 + foreach (var l in ended) { + l.AddPoint(position.X, position.Y, true); + maker.Lines.Add(l); + unsolved.Remove(l); + } + + // 加入本次提交 + commit.IsMerged = isMerged; + commit.GraphOffset = System.Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH); + + // 清理临时数据 + ended.Clear(); + currentMap.Clear(); + } + + // 处理尚未终结的线 + for (int i = 0; i < unsolved.Count; i++) { + var path = unsolved[i]; + path.AddPoint((i + 0.5) * UNIT_WIDTH, (commits.Count - 0.5) * UNIT_HEIGHT, true); + maker.Lines.Add(path); + } + unsolved.Clear(); + + // 处理默认选中异常 + if (maker.Highlight == null && commits.Count > 0) { + maker.Highlight = commits[0].SHA; + } + + return maker; + } + } +} diff --git a/Helpers/TextBoxHelper.cs b/Helpers/TextBoxHelper.cs new file mode 100644 index 00000000..3f34aed8 --- /dev/null +++ b/Helpers/TextBoxHelper.cs @@ -0,0 +1,143 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace SourceGit.Helpers { + + /// + /// Attached properties to TextBox. + /// + public static class TextBoxHelper { + + /// + /// Placeholder property + /// + public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.RegisterAttached( + "Placeholder", + typeof(string), + typeof(TextBoxHelper), + new PropertyMetadata(string.Empty, OnPlaceholderChanged)); + + /// + /// Vertical alignment for placeholder. + /// + public static readonly DependencyProperty PlaceholderBaselineProperty = DependencyProperty.RegisterAttached( + "PlaceholderBaseline", + typeof(AlignmentY), + typeof(TextBoxHelper), + new PropertyMetadata(AlignmentY.Center)); + + /// + /// Property to store generated placeholder brush. + /// + public static readonly DependencyProperty PlaceholderBrushProperty = DependencyProperty.RegisterAttached( + "PlaceholderBrush", + typeof(Brush), + typeof(TextBoxHelper), + new PropertyMetadata(Brushes.Transparent)); + + /// + /// Triggered when placeholder changed. + /// + /// + /// + private static void OnPlaceholderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + var textBox = d as TextBox; + if (textBox != null) textBox.Loaded += OnTextLoaded; + } + + /// + /// Setter for Placeholder property + /// + /// + /// + public static void SetPlaceholder(UIElement element, string value) { + element.SetValue(PlaceholderProperty, value); + } + + /// + /// Getter for Placeholder property + /// + /// + /// + public static string GetPlaceholder(UIElement element) { + return (string)element.GetValue(PlaceholderProperty); + } + + /// + /// Setter for PlaceholderBaseline property + /// + /// + /// + public static void SetPlaceholderBaseline(UIElement element, AlignmentY align) { + element.SetValue(PlaceholderBaselineProperty, align); + } + + /// + /// Setter for PlaceholderBaseline property. + /// + /// + /// + public static AlignmentY GetPlaceholderBaseline(UIElement element) { + return (AlignmentY)element.GetValue(PlaceholderBaselineProperty); + } + + /// + /// Setter for PlaceholderBrush property. + /// + /// + /// + public static void SetPlaceholderBrush(UIElement element, Brush value) { + element.SetValue(PlaceholderBrushProperty, value); + } + + /// + /// Getter for PlaceholderBrush property. + /// + /// + /// + public static Brush GetPlaceholderBrush(UIElement element) { + return (Brush)element.GetValue(PlaceholderBrushProperty); + } + + /// + /// Set placeholder as background when TextBox was loaded. + /// + /// + /// + private static void OnTextLoaded(object sender, RoutedEventArgs e) { + var textBox = sender as TextBox; + if (textBox == null) return; + + Label placeholder = new Label(); + placeholder.Content = textBox.GetValue(PlaceholderProperty); + + VisualBrush brush = new VisualBrush(); + brush.AlignmentX = AlignmentX.Left; + brush.AlignmentY = GetPlaceholderBaseline(textBox); + brush.TileMode = TileMode.None; + brush.Stretch = Stretch.None; + brush.Opacity = 0.3; + brush.Visual = placeholder; + + textBox.SetValue(PlaceholderBrushProperty, brush); + textBox.Background = brush; + textBox.TextChanged += OnTextChanged; + OnTextChanged(textBox, null); + } + + /// + /// Dynamically hide/show placeholder. + /// + /// + /// + private static void OnTextChanged(object sender, RoutedEventArgs e) { + var textBox = sender as TextBox; + if (string.IsNullOrEmpty(textBox.Text)) { + textBox.Background = textBox.GetValue(PlaceholderBrushProperty) as Brush; + } else { + textBox.Background = Brushes.Transparent; + } + } + } +} diff --git a/Helpers/TreeViewHelper.cs b/Helpers/TreeViewHelper.cs new file mode 100644 index 00000000..d1b74bf2 --- /dev/null +++ b/Helpers/TreeViewHelper.cs @@ -0,0 +1,329 @@ +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace SourceGit.Helpers { + + /// + /// Helper class to enable multi-selection of TreeView + /// + public static class TreeViewHelper { + + /// + /// Definition of EnableMultiSelection property. + /// + public static readonly DependencyProperty EnableMultiSelectionProperty = + DependencyProperty.RegisterAttached( + "EnableMultiSelection", + typeof(bool), + typeof(TreeViewHelper), + new FrameworkPropertyMetadata(false, OnEnableMultiSelectionChanged)); + + /// + /// Getter of EnableMultiSelection + /// + /// + /// + public static bool GetEnableMultiSelection(DependencyObject obj) { + return (bool)obj.GetValue(EnableMultiSelectionProperty); + } + + /// + /// Setter of EnableMultiSelection + /// + /// + /// + public static void SetEnableMultiSelection(DependencyObject obj, bool value) { + obj.SetValue(EnableMultiSelectionProperty, value); + } + + /// + /// Definition of SelectedItems + /// + public static readonly DependencyProperty SelectedItemsProperty = + DependencyProperty.RegisterAttached( + "SelectedItems", + typeof(ObservableCollection), + typeof(TreeViewHelper), + new FrameworkPropertyMetadata(null)); + + /// + /// Getter of SelectedItems + /// + /// + /// + public static ObservableCollection GetSelectedItems(DependencyObject obj) { + return (ObservableCollection)obj.GetValue(SelectedItemsProperty); + } + + /// + /// Setter of SelectedItems + /// + /// + /// + public static void SetSelectedItems(DependencyObject obj, ObservableCollection value) { + obj.SetValue(SelectedItemsProperty, value); + } + + /// + /// Definition of IsChecked property. + /// + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.RegisterAttached( + "IsChecked", + typeof(bool), + typeof(TreeViewHelper), + new FrameworkPropertyMetadata(false)); + + /// + /// Getter of IsChecked Property. + /// + /// + /// + public static bool GetIsChecked(DependencyObject obj) { + return (bool)obj.GetValue(IsCheckedProperty); + } + + /// + /// Setter of IsChecked property + /// + /// + /// + public static void SetIsChecked(DependencyObject obj, bool value) { + obj.SetValue(IsCheckedProperty, value); + } + + /// + /// Definition of MultiSelectionChangedEvent + /// + public static readonly RoutedEvent MultiSelectionChangedEvent = + EventManager.RegisterRoutedEvent("MultiSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TreeViewHelper)); + + /// + /// Add handler for MultiSelectionChanged event. + /// + /// + /// + public static void AddMultiSelectionChangedHandler(DependencyObject d, RoutedEventHandler handler) { + var tree = d as TreeView; + if (tree != null) tree.AddHandler(MultiSelectionChangedEvent, handler); + } + + /// + /// Remove handler for MultiSelectionChanged event. + /// + /// + /// + public static void RemoveMultiSelectionChangedHandler(DependencyObject d, RoutedEventHandler handler) { + var tree = d as TreeView; + if (tree != null) tree.RemoveHandler(MultiSelectionChangedEvent, handler); + } + + /// + /// Select all items in tree. + /// + /// + public static void SelectWholeTree(TreeView tree) { + var selected = GetSelectedItems(tree); + selected.Clear(); + SelectAll(selected, tree); + tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); + } + + /// + /// Selected one item by DataContext + /// + /// + /// + public static void SelectOneByContext(TreeView tree, object obj) { + var item = FindTreeViewItemByDataContext(tree, obj); + if (item != null) { + var selected = GetSelectedItems(tree); + selected.Add(item); + item.SetValue(IsCheckedProperty, true); + tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); + } + } + + /// + /// Unselect the whole tree. + /// + /// + public static void UnselectTree(TreeView tree) { + var selected = GetSelectedItems(tree); + if (selected.Count == 0) return; + + foreach (var old in selected) old.SetValue(IsCheckedProperty, false); + selected.Clear(); + tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); + } + + /// + /// Hooks when EnableMultiSelection changed. + /// + /// + /// + private static void OnEnableMultiSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + var tree = d as TreeView; + if (tree != null && (bool)e.NewValue) { + tree.SetValue(SelectedItemsProperty, new ObservableCollection()); + tree.PreviewMouseDown += OnTreeMouseDown; + } + } + + /// + /// Preview mouse button select. + /// + /// + /// + private static void OnTreeMouseDown(object sender, MouseButtonEventArgs e) { + var tree = sender as TreeView; + if (tree == null) return; + + var hit = VisualTreeHelper.HitTest(tree, e.GetPosition(tree)); + if (hit == null || hit.VisualHit is null) return; + + var item = FindTreeViewItem(hit.VisualHit as UIElement); + if (item == null) return; + + var selected = GetSelectedItems(tree); + if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { + if (GetIsChecked(item)) { + selected.Remove(item); + item.SetValue(IsCheckedProperty, false); + } else { + selected.Add(item); + item.SetValue(IsCheckedProperty, true); + } + } else if ((Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) && selected.Count > 0) { + var last = selected.Last(); + if (last == item) return; + + var lastPos = last.PointToScreen(new Point(0, 0)); + var curPos = item.PointToScreen(new Point(0, 0)); + if (lastPos.Y > curPos.Y) { + SelectRange(selected, tree, item, last); + } else { + SelectRange(selected, tree, last, item); + } + + selected.Add(item); + item.SetValue(IsCheckedProperty, true); + } else if (e.RightButton == MouseButtonState.Pressed) { + if (GetIsChecked(item)) return; + + foreach (var old in selected) old.SetValue(IsCheckedProperty, false); + selected.Clear(); + selected.Add(item); + item.SetValue(IsCheckedProperty, true); + } else { + foreach (var old in selected) old.SetValue(IsCheckedProperty, false); + selected.Clear(); + selected.Add(item); + item.SetValue(IsCheckedProperty, true); + } + + tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); + } + + /// + /// Find TreeViewItem by child element. + /// + /// + /// + /// + private static TreeViewItem FindTreeViewItem(DependencyObject child) { + if (child == null) return null; + if (child is TreeViewItem) return child as TreeViewItem; + if (child is TreeView) return null; + return FindTreeViewItem(VisualTreeHelper.GetParent(child)); + } + + /// + /// Find TreeViewItem by DataContext + /// + /// + /// + /// + private static TreeViewItem FindTreeViewItemByDataContext(ItemsControl control, object obj) { + if (control == null) return null; + if (control.DataContext == obj) return control as TreeViewItem; + + for (int i = 0; i < control.Items.Count; i++) { + var child = control.ItemContainerGenerator.ContainerFromIndex(i) as ItemsControl; + var found = FindTreeViewItemByDataContext(child, obj); + if (found != null) return found; + } + + return null; + } + + /// + /// Select all items. + /// + /// + /// + private static void SelectAll(ObservableCollection selected, ItemsControl control) { + for (int i = 0; i < control.Items.Count; i++) { + var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; + if (child == null) continue; + + selected.Add(child); + child.SetValue(IsCheckedProperty, true); + SelectAll(selected, child); + } + } + + /// + /// Select range items between given. + /// + /// + /// + /// + /// + /// + private static int SelectRange(ObservableCollection selected, ItemsControl control, TreeViewItem from, TreeViewItem to, int matches = 0) { + for (int i = 0; i < control.Items.Count; i++) { + var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; + if (child == null) continue; + + if (matches == 1) { + if (child == to) return 2; + selected.Add(child); + child.SetValue(IsCheckedProperty, true); + if (TryEndRangeSelection(selected, child, to)) return 2; + } else if (child == from) { + matches = 1; + if (TryEndRangeSelection(selected, child, to)) return 2; + } else { + matches = SelectRange(selected, child, from, to, matches); + if (matches == 2) return 2; + } + } + + return matches; + } + + private static bool TryEndRangeSelection(ObservableCollection selected, TreeViewItem control, TreeViewItem end) { + for (int i = 0; i < control.Items.Count; i++) { + var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; + if (child == null) continue; + + if (child == end) { + return true; + } else { + selected.Add(child); + child.SetValue(IsCheckedProperty, true); + + var ended = TryEndRangeSelection(selected, child, end); + if (ended) return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Helpers/Validations.cs b/Helpers/Validations.cs new file mode 100644 index 00000000..ceaf48fc --- /dev/null +++ b/Helpers/Validations.cs @@ -0,0 +1,124 @@ +using System.Globalization; +using System.IO; +using System.Text.RegularExpressions; +using System.Windows.Controls; + +namespace SourceGit.Helpers { + + /// + /// Validate clone folder. + /// + public class CloneFolderRule : ValidationRule { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var badPath = "EXISTS and FULL ACCESS CONTROL needed"; + var path = value as string; + return Directory.Exists(path) ? ValidationResult.ValidResult : new ValidationResult(false, badPath); + } + } + + /// + /// Validate git remote URL + /// + public class RemoteUriRule : ValidationRule { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var badUrl = "Remote git URL not supported"; + return Git.Repository.IsValidUrl(value as string) ? ValidationResult.ValidResult : new ValidationResult(false, badUrl); + } + } + + /// + /// Validate tag name. + /// + public class RemoteNameRule : ValidationRule { + public Git.Repository Repo { get; set; } + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var regex = new Regex(@"^[\w\-\.]+$"); + var name = value as string; + var remotes = Repo.Remotes(); + + if (string.IsNullOrEmpty(name)) return new ValidationResult(false, "Remote name can NOT be null"); + if (!regex.IsMatch(name)) return new ValidationResult(false, $"Bad name for remote. Regex: ^[\\w\\-\\.]+$"); + + foreach (var t in remotes) { + if (t.Name == name) { + return new ValidationResult(false, $"Remote '{name}' already exists"); + } + } + + return ValidationResult.ValidResult; + } + } + + /// + /// Validate branch name. + /// + public class BranchNameRule : ValidationRule { + public Git.Repository Repo { get; set; } + public string Prefix { get; set; } = ""; + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var regex = new Regex(@"^[\w\-/\.]+$"); + var name = value as string; + var branches = Repo.Branches(); + + if (string.IsNullOrEmpty(name)) return new ValidationResult(false, "Branch name can NOT be null"); + if (!regex.IsMatch(name)) return new ValidationResult(false, $"Bad name for branch. Regex: ^[\\w\\-/\\.]+$"); + + name = Prefix + name; + + foreach (var b in branches) { + if (b.Name == name) { + return new ValidationResult(false, $"Branch '{name}' already exists"); + } + } + + return ValidationResult.ValidResult; + } + } + + /// + /// Validate tag name. + /// + public class TagNameRule : ValidationRule { + public Git.Repository Repo { get; set; } + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var regex = new Regex(@"^[\w\-\.]+$"); + var name = value as string; + var tags = Repo.Tags(); + + if (string.IsNullOrEmpty(name)) return new ValidationResult(false, "Tag name can NOT be null"); + if (!regex.IsMatch(name)) return new ValidationResult(false, $"Bad name for tag. Regex: ^[\\w\\-\\.]+$"); + + foreach (var t in tags) { + if (t.Name == name) { + return new ValidationResult(false, $"Tag '{name}' already exists"); + } + } + + return ValidationResult.ValidResult; + } + } + + /// + /// Required for commit subject. + /// + public class CommitSubjectRequiredRule : ValidationRule { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var subject = value as string; + return string.IsNullOrWhiteSpace(subject) ? new ValidationResult(false, "Commit subject can NOT be empty") : ValidationResult.ValidResult; + } + } + + /// + /// Required for patch file. + /// + public class PatchFileRequiredRule : ValidationRule { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) { + var path = value as string; + var succ = !string.IsNullOrEmpty(path) && File.Exists(path); + return !succ ? new ValidationResult(false, "Invalid path for patch file") : ValidationResult.ValidResult; + } + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..05cd0620 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 leo + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Preview_Dark.png b/Preview_Dark.png new file mode 100644 index 0000000000000000000000000000000000000000..64f70af00f9b7d03e0b59a293c0093fcdcb71a5d GIT binary patch literal 97317 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�NgG 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ℜ%B1i2tMcO8G{SqtHqXJ1%4Y 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 + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if ((resourceMan == null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SourceGit.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 00000000..86e4a784 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SourceGit.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 00000000..033d7a5e --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..fee0e5d7 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# SourceGit + +开源的Git客户端,仅用于Windows 10。单文件,无需安装,< 500KB。 + +* DarkTheme + +![Preview_Dark](./Preview_Dark.png) + +* LightTheme + +![Preview_Light](./Preview_Light.png) + diff --git a/Resources/Controls.xaml b/Resources/Controls.xaml new file mode 100644 index 00000000..03450990 --- /dev/null +++ b/Resources/Controls.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Icons.xaml b/Resources/Icons.xaml new file mode 100644 index 00000000..262eab32 --- /dev/null +++ b/Resources/Icons.xaml @@ -0,0 +1,59 @@ + + M1004.824 466.4L557.72 19.328c-25.728-25.76-67.488-25.76-93.28 0L360.568 123.2l78.176 78.176c12.544-5.984 26.56-9.376 41.376-9.376 53.024 0 96 42.976 96 96 0 14.816-3.36 28.864-9.376 41.376l127.968 127.968c12.544-5.984 26.56-9.376 41.376-9.376 53.024 0 96 42.976 96 96s-42.976 96-96 96-96-42.976-96-96c0-14.816 3.36-28.864 9.376-41.376L521.496 374.624a88.837 88.837 0 0 1-9.376 3.872v266.976c37.28 13.184 64 48.704 64 90.528 0 53.024-42.976 96-96 96s-96-42.976-96-96c0-41.792 26.72-77.344 64-90.528V378.496c-37.28-13.184-64-48.704-64-90.528 0-14.816 3.36-28.864 9.376-41.376l-78.176-78.176L19.416 464.288c-25.76 25.792-25.76 67.52 0 93.28l447.136 447.072c25.728 25.76 67.488 25.76 93.28 0l444.992-444.992c25.76-25.76 25.76-67.552 0-93.28z + + F1M0,6L0,9 9,9 9,6 0,6z + F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z + F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z + M810.666667 273.493333L750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512z + M512 597.33333332m-1.26648097 0a1.26648097 1.26648097 0 1 0 2.53296194 0 1.26648097 1.26648097 0 1 0-2.53296194 0ZM809.691429 392.777143L732.16 314.514286 447.634286 599.771429 292.571429 443.977143 214.308571 521.508571l155.794286 155.794286 77.531429 77.531429 362.057143-362.057143z + M511.680999 0C233.071131 0 6.524722 222.580887 0.12872 499.655715 6.013042 257.886821 189.834154 63.960025 415.740962 63.960025c229.61649 0 415.740162 200.450718 415.740162 447.720175 0 52.958901 42.981137 95.940037 95.940038 95.940037s95.940037-42.981137 95.940037-95.940037c0-282.57539-229.104809-511.6802-511.6802-511.6802z m0 1023.3604c278.609869 0 505.156277-222.580887 511.55228-499.655715-5.884322 241.768894-189.705434 435.69569-415.612242 435.69569-229.61649 0-415.740162-200.450718-415.740163-447.720175 0-52.958901-42.981137-95.940037-95.940037-95.940038s-95.940037 42.981137-95.940037 95.940038c0 282.57539 229.104809 511.6802 511.680199 511.6802z + M701.9062029 677.41589899L589.90712068 565.41681675a148.33953321 148.33953321 0 1 0-24.97646381 26.55648342L676.07895931 703.12160261z m-346.38891409-199.50786053a114.97681148 114.97681148 0 1 1 114.85527151 114.97681148A115.09835147 115.09835147 0 0 1 355.45651882 477.90803846z + M352 64h320L960 352v320L672 960h-320L64 672v-320L352 64z m161.28 362.688L344.128 256 259.584 341.312 428.736 512l-169.152 170.688L344.128 768 513.28 597.312 682.432 768l84.544-85.312L597.824 512l169.152-170.688L682.432 256 513.28 426.688z + + M51.2 204.8h102.4v102.4H51.2V204.8z m204.8 0h716.8v102.4H256V204.8zM51.2 460.8h102.4v102.4H51.2V460.8z m204.8 0h716.8v102.4H256V460.8z m-204.8 256h102.4v102.4H51.2v-102.4z m204.8 0h716.8v102.4H256v-102.4z + M912 737l0 150L362 887l0-100 0-50 0-150 0-150 0-150L112 287l0-150 450 0 0 150L412 287l0 150L912 437l0 150L412 587l0 150L912 737z + + M868 545.5L536.1 163c-12.7-14.7-35.5-14.7-48.3 0L156 545.5c-4.5 5.2-0.8 13.2 6 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z + M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861c12.7 14.7 35.5 14.7 48.3 0L868 478.5c4.5-5.2 0.8-13.2-6-13.2z + + M509.44 546.304l270.848-270.912 90.56 90.56-347.52 349.056-0.832-0.768-13.056 13.056-362.624-361.28 91.136-91.264z + M256 224l1e-8 115.2L512 544l255.99999999-204.8 1e-8-115.2-256 204.80000001L256 224zM512 684.8l-256-204.8L256 595.2 512 800 768 595.2l0-115.2L512 684.8z + M169.5 831l342.8-341.9L855.1 831l105.3-105.3-448.1-448.1L64.2 725.7 169.5 831z + M768 800V684.8L512 480 256 684.8V800l256-204.8L768 800zM512 339.2L768 544V428.8L512 224 256 428.8V544l256-204.8z + + M64.2 180.3h418.2v120.6H64.2zM64.2 461.7h358.5v120.6H64.2zM64.2 723.1h418.2v120.6H64.2zM601.9 180.3h358.5v120.6H601.9zM482.4 119.9h179.2v241.3H482.4zM303.2 401.4h179.2v241.3H303.2zM482.4 662.8h179.2v241.3H482.4zM540.3 461.7h420.1v120.6H540.3zM601.9 723.1h358.5v120.6H601.9z + M 38,19C 48.4934,19 57,27.5066 57,38C 57,48.4934 48.4934,57 38,57C 27.5066,57 19,48.4934 19,38C 19,27.5066 27.5066,19 38,19 Z M 33.25,33.25L 33.25,36.4167L 36.4166,36.4167L 36.4166,47.5L 33.25,47.5L 33.25,50.6667L 44.3333,50.6667L 44.3333,47.5L 41.1666,47.5L 41.1666,36.4167L 41.1666,33.25L 33.25,33.25 Z M 38.7917,25.3333C 37.48,25.3333 36.4167,26.3967 36.4167,27.7083C 36.4167,29.02 37.48,30.0833 38.7917,30.0833C 40.1033,30.0833 41.1667,29.02 41.1667,27.7083C 41.1667,26.3967 40.1033,25.3333 38.7917,25.3333 Z + M64 864h896V288h-396.224a64 64 0 0 1-57.242667-35.376L460.224 160H64v704z m-64 32V128a32 32 0 0 1 32-32h448a32 32 0 0 1 28.624 17.690667L563.776 224H992a32 32 0 0 1 32 32v640a32 32 0 0 1-32 32H32a32 32 0 0 1-32-32z + M448 64l128 128h448v768H0V64z + M832 960l192-512H192L0 960zM128 384L0 960V128h288l128 128h416v128z + M780.512477 870.01493 780.512477 512l89.502453 0-358.013907-358.01493L153.98507 512l89.503477 0 0 358.01493 179.007977 0L422.496523 735.759203c0-49.427736 40.075741-89.503477 89.503477-89.503477s89.503477 40.075741 89.503477 89.503477l0 134.255727L780.512477 870.01493z + M928 0c53.02 0 96 42.98 96 96v576c0 53.02-42.98 96-96 96H352c-53.02 0-96-42.98-96-96V96c0-53.02 42.98-96 96-96h576M352 832c-88.224 0-160-71.776-160-160V256H96c-53.02 0-96 42.98-96 96v576c0 53.02 42.98 96 96 96h576c53.02 0 96-42.98 96-96v-96H352z + M384 576H320V512h64v64z m0-192H320v64h64V384z m0-128H320v64h64V256z m0-128H320v64h64V128z m512-64v768c0 35.2-28.8 64-64 64H512v128l-96-96L320 1024v-128H192c-35.2 0-64-28.8-64-64V64c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64z m-64 640H192v128h128v-64h192v64h320v-128z m0-640H256v576h576V64z + M989.866667 512L689.493333 802.133333 614.4 729.6 839.68 512 614.4 294.4 689.493333 221.866667z + M958.656 320H960v639.936A64 64 0 0 1 896.128 1024H191.936A63.872 63.872 0 0 1 128 959.936V64.064A64 64 0 0 1 191.936 0H640v320.96h319.616L958.656 320zM320 544c0 17.152 14.464 32 32.192 32h383.552A32.384 32.384 0 0 0 768 544c0-17.152-14.464-32-32.256-32H352.192A32.448 32.448 0 0 0 320 544z m0 128c0 17.152 14.464 32 32.192 32h383.552a32.384 32.384 0 0 0 32.256-32c0-17.152-14.464-32-32.256-32H352.192a32.448 32.448 0 0 0-32.192 32z m0 128c0 17.152 14.464 32 32.192 32h383.552a32.384 32.384 0 0 0 32.256-32c0-17.152-14.464-32-32.256-32H352.192a32.448 32.448 0 0 0-32.192 32z + M854.2 306.6L611.3 72.9c-6-5.7-13.9-8.9-22.2-8.9H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h277l219 210.6V824c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V329.6c0-8.7-3.5-17-9.8-23zM553.4 201.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v704c0 17.7 14.3 32 32 32h512c17.7 0 32-14.3 32-32V397.3c0-8.5-3.4-16.6-9.4-22.6L553.4 201.4zM568 753c0 3.8-3.4 7-7.5 7h-225c-4.1 0-7.5-3.2-7.5-7v-42c0-3.8 3.4-7 7.5-7h225c4.1 0 7.5 3.2 7.5 7v42z m0-220c0 3.8-3.4 7-7.5 7H476v84.9c0 3.9-3.1 7.1-7 7.1h-42c-3.8 0-7-3.2-7-7.1V540h-84.5c-4.1 0-7.5-3.2-7.5-7v-42c0-3.9 3.4-7 7.5-7H420v-84.9c0-3.9 3.2-7.1 7-7.1h42c3.9 0 7 3.2 7 7.1V484h84.5c4.1 0 7.5 3.1 7.5 7v42z + M599.22969 424.769286 599.22969 657.383158 424.769286 831.844585 424.769286 424.769286 192.155415 192.155415 831.844585 192.155415Z + + M1024 1024H0V0h1024v1024z m-64-64V320H320V256h640V64H64v896h192V64h64v896z + M81.92 81.92v860.16h860.16V81.92H81.92z m802.304 57.856V322.56H139.776V139.776h744.448z m-744.448 240.64H322.56v503.808H139.776V380.416z m240.128 503.808V380.416h504.32v503.808H379.904z + + M1024 896v128H0V704h128v192h768V704h128v192zM576 554.688L810.688 320 896 405.312l-384 384-384-384L213.312 320 448 554.688V0h128v554.688z + M432 0h160c26.6 0 48 21.4 48 48v336h175.4c35.6 0 53.4 43 28.2 68.2L539.4 756.6c-15 15-39.6 15-54.6 0L180.2 452.2c-25.2-25.2-7.4-68.2 28.2-68.2H384V48c0-26.6 21.4-48 48-48z m592 752v224c0 26.6-21.4 48-48 48H48c-26.6 0-48-21.4-48-48V752c0-26.6 21.4-48 48-48h293.4l98 98c40.2 40.2 105 40.2 145.2 0l98-98H976c26.6 0 48 21.4 48 48z m-248 176c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z m128 0c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z + M592 768h-160c-26.6 0-48-21.4-48-48V384h-175.4c-35.6 0-53.4-43-28.2-68.2L484.6 11.4c15-15 39.6-15 54.6 0l304.4 304.4c25.2 25.2 7.4 68.2-28.2 68.2H640v336c0 26.6-21.4 48-48 48z m432-16v224c0 26.6-21.4 48-48 48H48c-26.6 0-48-21.4-48-48V752c0-26.6 21.4-48 48-48h272v16c0 61.8 50.2 112 112 112h160c61.8 0 112-50.2 112-112v-16h272c26.6 0 48 21.4 48 48z m-248 176c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z m128 0c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z + M961.3 319.6L512 577.3 62.7 319.6 512 62l449.3 257.6zM512 628.4L185.4 441.6 62.7 512 512 769.6 961.3 512l-122.7-70.4L512 628.4zM512 820.8L185.4 634 62.7 704.3 512 962l449.3-257.7L838.6 634 512 820.8z + M889.259 551.125c-39.638 0-74.112 21.163-93.483 52.608v-0.426c-16.896 30.378-64.597 27.52-81.28-0.811V319.488H445.781c-41.13-6.827-49.92-66.859-15.061-86.23h-0.384c31.445-19.37 52.608-53.845 52.608-93.482 0-60.8-49.28-110.123-110.08-110.123S262.699 78.976 262.699 139.776c0 39.637 21.162 74.112 52.608 93.483h-0.384c34.858 19.37 26.069 79.402-15.062 86.229H31.104v630.23H292.48c47.744 0 59.392-69.505 22.443-81.622h0.938c-44.757-21.376-75.904-66.688-75.904-119.595 0-73.386 59.478-132.864 132.864-132.864 73.387 0 132.864 59.478 132.864 132.864 0 52.907-31.146 98.219-75.904 119.595h0.939c-36.95 12.16-25.344 81.621 22.443 81.621h261.376V719.915c16.682-28.331 64.384-31.19 81.28-0.811v-0.384c19.37 31.445 53.845 52.608 93.482 52.608 60.8 0 110.08-49.28 110.08-110.123 0-60.8-49.322-110.08-110.122-110.08z + M89.6 806.4h844.8V217.6H89.6v588.8zM0 128h1024v768H0V128z m242.816 577.536L192 654.72l154.304-154.368L192 346.048l50.816-50.816L448 500.352 242.816 705.536z m584.32 13.248H512V640h315.072v78.72z + M508.928 556.125091l92.904727 148.759273h124.462546l-79.639273-79.173819 49.245091-49.524363 164.584727 163.700363-164.631273 163.002182-49.152-49.617454 79.36-78.568728h-162.955636l-95.650909-153.227636 41.472-65.349818z m186.973091-394.705455l164.584727 163.700364-164.631273 163.002182-49.152-49.617455L726.109091 359.936H529.687273l-135.540364 223.976727H139.636364v-69.818182h215.133091l135.586909-223.976727h235.938909l-79.639273-79.173818 49.245091-49.524364z + + M795.968 471.04A291.584 291.584 0 0 0 512 256a293.376 293.376 0 0 0-283.968 215.04H0v144h228.032A292.864 292.864 0 0 0 512 832a291.136 291.136 0 0 0 283.968-216.96H1024V471.04h-228.032M512 688A145.856 145.856 0 0 1 366.016 544 144.576 144.576 0 0 1 512 400c80 0 145.984 63.104 145.984 144A145.856 145.856 0 0 1 512 688 + M0 586.459429l403.968 118.784 497.517714-409.892572-385.536 441.490286-1.609143 250.587428 154.916572-204.580571 278.601143 83.456L1170.285714 36.571429z + M24.356571 512A488.155429 488.155429 0 0 1 512 24.356571 488.155429 488.155429 0 0 1 999.643429 512 488.155429 488.155429 0 0 1 512 999.643429 488.155429 488.155429 0 0 1 24.356571 512z m446.976-325.046857v326.656L242.614857 619.227429l51.126857 110.665142 299.52-138.24V186.953143H471.332571z + M714.624 253.648h-404.8l-57.808 57.328h520.48z m-491.568 85.984v200.624h578.336V339.632z m404.8 143.296h-28.88v-28.64H425.472v28.64h-28.912v-57.312h231.328v57.312z m-404.8 295.12h578.336V559.36H223.056z m173.504-132.704h231.328v57.328h-28.912v-28.656H425.472v28.656h-28.912v-57.328z + M868.736 144.96a144.64 144.64 0 1 0-289.408 0c0 56.064 32.64 107.008 83.456 130.624-4.928 95.552-76.608 128-201.088 174.592-52.48 19.712-110.336 41.6-159.744 74.432V276.16A144.448 144.448 0 0 0 241.664 0.192a144.64 144.64 0 0 0-144.64 144.768c0 58.24 34.688 108.288 84.352 131.2v461.184a144.32 144.32 0 0 0-84.416 131.2 144.704 144.704 0 1 0 289.472 0 144.32 144.32 0 0 0-83.52-130.688c4.992-95.488 76.672-127.936 201.152-174.592 122.368-45.952 273.792-103.168 279.744-286.784a144.64 144.64 0 0 0 84.928-131.52zM241.664 61.44a83.456 83.456 0 1 1 0 166.912 83.456 83.456 0 0 1 0-166.912z m0 890.56a83.52 83.52 0 1 1 0-167.04 83.52 83.52 0 0 1 0 167.04zM724.032 228.416a83.52 83.52 0 1 1 0-167.04 83.52 83.52 0 0 1 0 167.04z + M896 128h-64V64c0-35.2-28.8-64-64-64s-64 28.8-64 64v64h-64c-35.2 0-64 28.8-64 64s28.8 64 64 64h64v64c0 35.2 28.8 64 64 64s64-28.8 64-64V256h64c35.2 0 64-28.8 64-64s-28.8-64-64-64z m-203.52 307.2C672.64 480.64 628.48 512 576 512H448c-46.72 0-90.24 12.8-128 35.2V372.48C394.24 345.6 448 275.2 448 192c0-106.24-85.76-192-192-192S64 85.76 64 192c0 83.2 53.76 153.6 128 180.48v279.68c-74.24 25.6-128 96.64-128 179.84 0 106.24 85.76 192 192 192s192-85.76 192-192c0-66.56-33.92-124.8-84.48-159.36 22.4-19.84 51.84-32.64 84.48-32.64h128c121.6 0 223.36-85.12 248.96-199.04-18.56 4.48-37.12 7.04-56.96 7.04-26.24 0-51.2-5.12-75.52-12.8zM256 128c35.2 0 64 28.8 64 64s-28.8 64-64 64-64-28.8-64-64 28.8-64 64-64z m0 768c-35.2 0-64-28.8-64-64s28.8-64 64-64 64 28.8 64 64-28.8 64-64 64z + M901.802667 479.232v-1.024c0-133.461333-111.616-241.664-249.514667-241.664-105.813333 0-195.925333 63.829333-232.448 153.941333-27.989333-20.138667-62.464-32.426667-100.010667-32.426666-75.776 0-139.605333 49.152-159.744 116.053333-51.882667 36.522667-86.016 96.938667-86.016 165.205333 0 111.616 90.453333 201.728 201.728 201.728h503.466667c111.616 0 201.728-90.453333 201.728-201.728 0-65.194667-31.061333-123.221333-79.189333-160.085333z + M363.789474 512h67.368421v107.789474h107.789473v67.368421h-107.789473v107.789473h-67.368421v-107.789473h-107.789474v-67.368421h107.789474v-107.789474z m297.539368-64A106.671158 106.671158 0 0 1 768 554.671158C768 613.578105 719.548632 660.210526 660.210526 660.210526h-107.789473v-53.894737h-107.789474v-107.789473h-94.31579v107.789473h-94.315789c4.311579-21.194105 22.231579-46.807579 43.560421-50.755368l-0.889263-11.560421a74.671158 74.671158 0 0 1 71.248842-74.590316 128.053895 128.053895 0 0 1 238.605474-7.437473 106.172632 106.172632 0 0 1 52.816842-13.972211z + M177.311335 156.116617c-22.478967 4.729721-32.774451 17.336854-36.251645 36.893258-10.080589 56.697303-33.399691 257.604032-13.234419 277.769304l445.342858 445.341834c23.177885 23.177885 60.757782 23.178909 83.935668 0l246.019183-246.019183c23.177885-23.177885 23.177885-60.757782 0-83.935668l-445.341834-445.341834C437.419398 120.463606 231.004211 144.82034 177.311335 156.116617zM331.22375 344.221786c-26.195615 26.195615-68.667939 26.195615-94.863555 0-26.195615-26.195615-26.195615-68.666916 0-94.863555s68.667939-26.195615 94.862531 0C357.418342 275.55487 357.419366 318.02617 331.22375 344.221786z + M682.666667 536.576h-143.701334v-142.336h-142.336V283.306667H238.933333a44.032 44.032 0 0 0-40.96 40.96v170.666666a55.978667 55.978667 0 0 0 14.336 34.133334l320.512 320.512a40.96 40.96 0 0 0 57.685334 0l173.738666-173.738667a40.96 40.96 0 0 0 0-57.685333z m-341.333334-108.544a40.96 40.96 0 1 1 0-57.685333 40.96 40.96 0 0 1 0 57.685333zM649.216 284.330667V141.994667h-68.608v142.336h-142.336v68.266666h142.336v142.336h68.608v-142.336h142.336v-68.266666h-142.336z + \ No newline at end of file diff --git a/Resources/Styles/Border.xaml b/Resources/Styles/Border.xaml new file mode 100644 index 00000000..a6f9bcd5 --- /dev/null +++ b/Resources/Styles/Border.xaml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/Resources/Styles/Button.xaml b/Resources/Styles/Button.xaml new file mode 100644 index 00000000..49c6169b --- /dev/null +++ b/Resources/Styles/Button.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/CheckBox.xaml b/Resources/Styles/CheckBox.xaml new file mode 100644 index 00000000..b271d993 --- /dev/null +++ b/Resources/Styles/CheckBox.xaml @@ -0,0 +1,38 @@ + + + + \ No newline at end of file diff --git a/Resources/Styles/ComboBox.xaml b/Resources/Styles/ComboBox.xaml new file mode 100644 index 00000000..5fd3e228 --- /dev/null +++ b/Resources/Styles/ComboBox.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/ContextMenu.xaml b/Resources/Styles/ContextMenu.xaml new file mode 100644 index 00000000..c82d6fee --- /dev/null +++ b/Resources/Styles/ContextMenu.xaml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/DataGrid.xaml b/Resources/Styles/DataGrid.xaml new file mode 100644 index 00000000..88bc5450 --- /dev/null +++ b/Resources/Styles/DataGrid.xaml @@ -0,0 +1,44 @@ + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/HyperLink.xaml b/Resources/Styles/HyperLink.xaml new file mode 100644 index 00000000..db1e0330 --- /dev/null +++ b/Resources/Styles/HyperLink.xaml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/Resources/Styles/Label.xaml b/Resources/Styles/Label.xaml new file mode 100644 index 00000000..7fecb8de --- /dev/null +++ b/Resources/Styles/Label.xaml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/Resources/Styles/ListView.xaml b/Resources/Styles/ListView.xaml new file mode 100644 index 00000000..72ab8ba5 --- /dev/null +++ b/Resources/Styles/ListView.xaml @@ -0,0 +1,66 @@ + + + + + + \ No newline at end of file diff --git a/Resources/Styles/Path.xaml b/Resources/Styles/Path.xaml new file mode 100644 index 00000000..d3f643e6 --- /dev/null +++ b/Resources/Styles/Path.xaml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/Resources/Styles/RadioButton.xaml b/Resources/Styles/RadioButton.xaml new file mode 100644 index 00000000..d7b65747 --- /dev/null +++ b/Resources/Styles/RadioButton.xaml @@ -0,0 +1,52 @@ + + + \ No newline at end of file diff --git a/Resources/Styles/ScrollBar.xaml b/Resources/Styles/ScrollBar.xaml new file mode 100644 index 00000000..25c06ecf --- /dev/null +++ b/Resources/Styles/ScrollBar.xaml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/ScrollViewer.xaml b/Resources/Styles/ScrollViewer.xaml new file mode 100644 index 00000000..971545e7 --- /dev/null +++ b/Resources/Styles/ScrollViewer.xaml @@ -0,0 +1,54 @@ + + + \ No newline at end of file diff --git a/Resources/Styles/TabControl.xaml b/Resources/Styles/TabControl.xaml new file mode 100644 index 00000000..1a449039 --- /dev/null +++ b/Resources/Styles/TabControl.xaml @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/Resources/Styles/TextBox.xaml b/Resources/Styles/TextBox.xaml new file mode 100644 index 00000000..728587fd --- /dev/null +++ b/Resources/Styles/TextBox.xaml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/ToggleButton.xaml b/Resources/Styles/ToggleButton.xaml new file mode 100644 index 00000000..3920885e --- /dev/null +++ b/Resources/Styles/ToggleButton.xaml @@ -0,0 +1,111 @@ + + + + + + + + + \ No newline at end of file diff --git a/Resources/Styles/Tooltip.xaml b/Resources/Styles/Tooltip.xaml new file mode 100644 index 00000000..0e7f9d3e --- /dev/null +++ b/Resources/Styles/Tooltip.xaml @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/Resources/Styles/TreeView.xaml b/Resources/Styles/TreeView.xaml new file mode 100644 index 00000000..f8b9b802 --- /dev/null +++ b/Resources/Styles/TreeView.xaml @@ -0,0 +1,201 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Themes/Dark.xaml b/Resources/Themes/Dark.xaml new file mode 100644 index 00000000..12896ffb --- /dev/null +++ b/Resources/Themes/Dark.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/Themes/Light.xaml b/Resources/Themes/Light.xaml new file mode 100644 index 00000000..f2b3ae76 --- /dev/null +++ b/Resources/Themes/Light.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SourceGit.csproj b/SourceGit.csproj new file mode 100644 index 00000000..99e44228 --- /dev/null +++ b/SourceGit.csproj @@ -0,0 +1,515 @@ + + + + + Debug + AnyCPU + {6B38FAF0-57D6-44E6-9B21-9BEAC481EF9E} + WinExe + SourceGit + SourceGit + v4.6 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + On + + + SourceGit.App + + + App.ico + + + + Always + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + Apply.xaml + + + Blame.xaml + + + CherryPick.xaml + + + Clone.xaml + + + CommitViewer.xaml + + + CreateBranch.xaml + + + CreateTag.xaml + + + Dashboard.xaml + + + DeleteBranch.xaml + + + DeleteRemote.xaml + + + DeleteTag.xaml + + + DiffViewer.xaml + + + Discard.xaml + + + Fetch.xaml + + + FileHistories.xaml + + + GitFlowFinishBranch.xaml + + + GitFlowSetup.xaml + + + GitFlowStartBranch.xaml + + + Histories.xaml + + + Init.xaml + + + InteractiveRebase.xaml + + + Manager.xaml + + + Merge.xaml + + + PopupManager.xaml + + + Preference.xaml + + + Pull.xaml + + + Push.xaml + + + PushTag.xaml + + + Rebase.xaml + + + Remote.xaml + + + RenameBranch.xaml + + + Reset.xaml + + + Revert.xaml + + + Stash.xaml + + + Stashes.xaml + + + WorkingCopy.xaml + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + + + + + + + + + About.xaml + + + Launcher.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + @echo off &setlocal +setlocal enabledelayedexpansion + +set file=$(SolutionDir)Properties\AssemblyInfo.cs +set temp=$(SolutionDir)Properties\AssemblyInfo.cs.tmp +set bak=$(SolutionDir)Properties\AssemblyInfo.cs.bk + +echo BACKUP AssemblyInfo +copy /Y %25file%25 %25bak%25 + +echo Find Version +git describe > VERSION +set /P version=<VERSION + +set search=1.0.0.0 +(for /f "delims=" %25%25i in (%25file%25) do ( + set "line=%25%25i" + set "line=!line:%25search%25=%25version%25!" + echo(!line! +))>"%25temp%25" +del /f %25file%25 +del /f VERSION +move %25temp%25 %25file%25 +endlocal + + + del /f $(SolutionDir)Properties\AssemblyInfo.cs +move $(SolutionDir)Properties\AssemblyInfo.cs.bk $(SolutionDir)Properties\AssemblyInfo.cs + + \ No newline at end of file diff --git a/SourceGit.sln b/SourceGit.sln new file mode 100644 index 00000000..6ab4db41 --- /dev/null +++ b/SourceGit.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGit", "SourceGit.csproj", "{6B38FAF0-57D6-44E6-9B21-9BEAC481EF9E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6B38FAF0-57D6-44E6-9B21-9BEAC481EF9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B38FAF0-57D6-44E6-9B21-9BEAC481EF9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B38FAF0-57D6-44E6-9B21-9BEAC481EF9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B38FAF0-57D6-44E6-9B21-9BEAC481EF9E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {01F4EC04-5B3C-4D74-BB48-1C251B2D2853} + EndGlobalSection +EndGlobal diff --git a/UI/About.xaml b/UI/About.xaml new file mode 100644 index 00000000..35c8e273 --- /dev/null +++ b/UI/About.xaml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/About.xaml.cs b/UI/About.xaml.cs new file mode 100644 index 00000000..d3080e3d --- /dev/null +++ b/UI/About.xaml.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.Reflection; +using System.Windows; +using System.Windows.Navigation; + +namespace SourceGit.UI { + + /// + /// About dialog + /// + public partial class About : Window { + + /// + /// Current app version + /// + public string Version { + get { + return FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; + } + } + + /// + /// Constructor + /// + public About() { + InitializeComponent(); + } + + /// + /// Open source code link + /// + /// + /// + private void OpenSource(object sender, RequestNavigateEventArgs e) { + Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); + e.Handled = true; + } + + /// + /// Close this dialog + /// + private void Quit(object sender, RoutedEventArgs e) { + Close(); + } + } +} diff --git a/UI/Apply.xaml b/UI/Apply.xaml new file mode 100644 index 00000000..1533930f --- /dev/null +++ b/UI/Apply.xaml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + diff --git a/UI/Blame.xaml.cs b/UI/Blame.xaml.cs new file mode 100644 index 00000000..8f4bc3c0 --- /dev/null +++ b/UI/Blame.xaml.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace SourceGit.UI { + + /// + /// Viewer to show git-blame + /// + public partial class Blame : Window { + + /// + /// Background color for blocks. + /// + public static Brush[] BG = new Brush[] { + Brushes.Transparent, + new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)) + }; + + /// + /// Constructor + /// + /// + /// + /// + public Blame(Git.Repository repo, string file, string revision) { + InitializeComponent(); + + double minWidth = content.ActualWidth; + + // Move to center. + var parent = App.Current.MainWindow; + Left = parent.Left + (parent.Width - Width) * 0.5; + Top = parent.Top + (parent.Height - Height) * 0.5; + + // Show loading. + DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1)); + anim.RepeatBehavior = RepeatBehavior.Forever; + loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim); + loading.Visibility = Visibility.Visible; + + // Layout content + blameFile.Content = $"{file}@{revision.Substring(0, 8)}"; + Task.Run(() => { + var blame = repo.BlameFile(file, revision); + + Dispatcher.Invoke(() => { + content.Document.Blocks.Clear(); + + if (blame.IsBinary) { + lineNumber.Text = "0"; + + Paragraph p = new Paragraph(new Run("BINARY FILE BLAME NOT SUPPORTED!!!")); + p.Margin = new Thickness(0); + p.Padding = new Thickness(0); + p.LineHeight = 1; + p.Background = Brushes.Transparent; + p.Foreground = FindResource("Brush.FG") as SolidColorBrush; + p.FontStyle = FontStyles.Normal; + + content.Document.Blocks.Add(p); + } else { + List numbers = new List(); + for (int i = 0; i < blame.LineCount; i++) numbers.Add(i.ToString()); + lineNumber.Text = string.Join("\n", numbers); + numbers.Clear(); + + for (int i = 0; i < blame.Blocks.Count; i++) { + var frag = blame.Blocks[i]; + var idx = i; + + Paragraph p = new Paragraph(new Run(frag.Content)); + p.DataContext = frag; + p.Margin = new Thickness(0); + p.Padding = new Thickness(0); + p.LineHeight = 1; + p.Background = BG[i % 2]; + p.Foreground = FindResource("Brush.FG") as SolidColorBrush; + p.FontStyle = FontStyles.Normal; + p.MouseRightButtonDown += (sender, ev) => { + Hyperlink link = new Hyperlink(new Run(frag.CommitSHA)); + link.ToolTip = "CLICK TO GO"; + link.Click += (o, e) => { + repo.OnNavigateCommit?.Invoke(frag.CommitSHA); + e.Handled = true; + }; + + foreach (var block in content.Document.Blocks) { + var paragraph = block as Paragraph; + if ((paragraph.DataContext as Git.Blame.Block).CommitSHA == frag.CommitSHA) { + paragraph.Background = Brushes.Green; + } else { + paragraph.Background = BG[i % 2]; + } + } + + commitID.Content = link; + authorName.Content = frag.Author; + authorTime.Content = frag.Time; + popup.IsOpen = true; + }; + + var formatter = new FormattedText( + frag.Content, + CultureInfo.CurrentUICulture, + FlowDirection.LeftToRight, + new Typeface(content.FontFamily, p.FontStyle, p.FontWeight, p.FontStretch), + content.FontSize, + Brushes.Black, + new NumberSubstitution(), + TextFormattingMode.Ideal); + if (minWidth < formatter.Width) { + content.Document.PageWidth = formatter.Width + 16; + minWidth = formatter.Width; + } + + content.Document.Blocks.Add(p); + } + } + + // Hide loading. + loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null); + loading.Visibility = Visibility.Collapsed; + }); + }); + } + + /// + /// Click logo + /// + /// + /// + private void LogoMouseButtonDown(object sender, MouseButtonEventArgs e) { + var element = e.OriginalSource as FrameworkElement; + if (element == null) return; + + var pos = PointToScreen(new Point(0, 33)); + SystemCommands.ShowSystemMenu(this, pos); + } + + /// + /// Minimize + /// + private void Minimize(object sender, RoutedEventArgs e) { + SystemCommands.MinimizeWindow(this); + } + + /// + /// Maximize/Restore + /// + private void MaximizeOrRestore(object sender, RoutedEventArgs e) { + if (WindowState == WindowState.Normal) { + SystemCommands.MaximizeWindow(this); + } else { + SystemCommands.RestoreWindow(this); + } + } + + /// + /// Quit + /// + private void Quit(object sender, RoutedEventArgs e) { + Close(); + } + + /// + /// Sync scroll + /// + /// + /// + private void SyncScrollChanged(object sender, ScrollChangedEventArgs e) { + if (e.VerticalChange != 0) { + var margin = new Thickness(4, -e.VerticalOffset, 4, 0); + lineNumber.Margin = margin; + } + } + + /// + /// Mouse wheel + /// + /// + /// + private void MouseWheelOnContent(object sender, MouseWheelEventArgs e) { + if (e.Delta > 0) { + content.LineUp(); + } else { + content.LineDown(); + } + + e.Handled = true; + } + + /// + /// Content size changed. + /// + /// + /// + private void ContentSizeChanged(object sender, SizeChangedEventArgs e) { + if (content.Document.PageWidth < content.ActualWidth) { + content.Document.PageWidth = content.ActualWidth; + } + } + } +} diff --git a/UI/CherryPick.xaml b/UI/CherryPick.xaml new file mode 100644 index 00000000..c22fd9b7 --- /dev/null +++ b/UI/CherryPick.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +

#fK(^@mct}#4v=TJZPI7#Q3y;b!( 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 zpCultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + +#pragma warning disable CS7035 + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 00000000..9b5d30b8 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SourceGit.Properties { + + + ///