diff --git a/SourceGit/Git/Remote.cs b/SourceGit/Git/Remote.cs
index f7f4ccb2..ed32e810 100644
--- a/SourceGit/Git/Remote.cs
+++ b/SourceGit/Git/Remote.cs
@@ -1,97 +1,97 @@
-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);
- } else {
- repo.Fetch(null, true, null);
- }
- }
-
- ///
- /// 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);
- }
- }
- }
-}
+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);
+ } else {
+ repo.Fetch(new Remote() { Name = name }, true, null);
+ }
+ }
+
+ ///
+ /// 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/SourceGit/UI/Dashboard.xaml.cs b/SourceGit/UI/Dashboard.xaml.cs
index 45804eda..7833c681 100644
--- a/SourceGit/UI/Dashboard.xaml.cs
+++ b/SourceGit/UI/Dashboard.xaml.cs
@@ -1,984 +1,996 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Threading;
-
-namespace SourceGit.UI {
-
- ///
- /// Branch node in tree.
- ///
- public class BranchNode {
- public string Name { get; set; }
- public Git.Branch Branch { get; set; }
- public bool IsExpanded { get; set; }
- public bool IsCurrent => Branch != null ? Branch.IsCurrent : false;
- public bool IsFiltered => Branch != null ? Branch.IsFiltered : false;
- public string Track => Branch != null ? Branch.UpstreamTrack : "";
- public Visibility FilterVisibility => Branch == null ? Visibility.Collapsed : Visibility.Visible;
- public Visibility TrackVisibility => (Branch != null && !Branch.IsSameWithUpstream) ? Visibility.Visible : Visibility.Collapsed;
- public List Children { get; set; }
- }
-
- ///
- /// Remote node in tree.
- ///
- public class RemoteNode {
- public string Name { get; set; }
- public bool IsExpanded { get; set; }
- public List Children { get; set; }
- }
-
- ///
- /// Dashboard for opened repository.
- ///
- public partial class Dashboard : UserControl {
- private Git.Repository repo = null;
- private List cachedLocalBranches = new List();
- private List cachedRemotes = new List();
- private string abortCommand = null;
-
- ///
- /// Constructor.
- ///
- /// Opened repository.
- public Dashboard(Git.Repository opened) {
- opened.OnWorkingCopyChanged = UpdateLocalChanges;
- opened.OnTagChanged = UpdateTags;
- opened.OnStashChanged = UpdateStashes;
- opened.OnBranchChanged = () => UpdateBranches(false);
- opened.OnCommitsChanged = UpdateHistories;
- opened.OnNavigateCommit = commit => {
- Dispatcher.Invoke(() => {
- workspace.SelectedItem = historiesSwitch;
- histories.Navigate(commit);
- });
- };
-
- InitializeComponent();
-
- repo = opened;
- repoName.Content = repo.Name;
- histories.Repo = opened;
- commits.Repo = opened;
-
- UpdateBranches();
- UpdateHistories();
- UpdateLocalChanges();
- UpdateStashes();
- UpdateTags();
- }
-
- #region DATA_UPDATE
- private void UpdateHistories() {
- Dispatcher.Invoke(() => {
- histories.SetLoadingEnabled(true);
- });
-
- Task.Run(() => {
- var args = "-5000 ";
- if (repo.LogFilters.Count > 0) {
- args = args + string.Join(" ", repo.LogFilters);
- } else {
- args = args + "--branches --remotes --tags";
- }
-
- var commits = repo.Commits(args);
- histories.SetCommits(commits);
- });
- }
-
- private void UpdateLocalChanges() {
- Task.Run(() => {
- var changes = repo.LocalChanges();
- var conflicts = commits.SetData(changes);
-
- Dispatcher.Invoke(() => {
- localChangesBadge.Visibility = changes.Count == 0 ? Visibility.Collapsed : Visibility.Visible;
- localChangesCount.Content = changes.Count;
- btnContinue.Visibility = conflicts ? Visibility.Collapsed : Visibility.Visible;
- DetectMergeState();
- });
- });
- }
-
- private void UpdateStashes() {
- Task.Run(() => {
- var data = repo.Stashes();
- Dispatcher.Invoke(() => {
- stashBadge.Visibility = data.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
- stashCount.Content = data.Count;
- stashes.SetData(repo, data);
- });
- });
- }
-
- private void BackupBranchNodeExpandState(Dictionary states, List nodes, string prefix) {
- foreach (var node in nodes) {
- var path = prefix + "/" + node.Name;
- states.Add(path, node.IsExpanded);
- BackupBranchNodeExpandState(states, node.Children, path);
- }
- }
-
- private void MakeBranchNode(Git.Branch branch, List collection, Dictionary folders, Dictionary expandStates, string prefix) {
- var subs = branch.Name.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
- if (!branch.IsLocal) {
- if (subs.Length < 2) return;
- subs = subs.Skip(1).ToArray();
- }
-
- branch.IsFiltered = repo.LogFilters.Contains(branch.FullName);
-
- if (subs.Length == 1) {
- var node = new BranchNode() {
- Name = subs[0],
- Branch = branch,
- Children = new List(),
- };
- collection.Add(node);
- } else {
- BranchNode lastFolder = null;
- string path = prefix;
- for (int i = 0; i < subs.Length - 1; i++) {
- path = path + "/" + subs[i];
- if (folders.ContainsKey(path)) {
- lastFolder = folders[path];
- } else if (lastFolder == null) {
- lastFolder = new BranchNode() {
- Name = subs[i],
- IsExpanded = expandStates.ContainsKey(path) ? expandStates[path] : false,
- Children = new List(),
- };
- collection.Add(lastFolder);
- folders.Add(path, lastFolder);
- } else {
- var folder = new BranchNode() {
- Name = subs[i],
- IsExpanded = expandStates.ContainsKey(path) ? expandStates[path] : false,
- Children = new List(),
- };
- lastFolder.Children.Add(folder);
- folders.Add(path, folder);
- lastFolder = folder;
- }
- }
-
- BranchNode node = new BranchNode();
- node.Name = subs[subs.Length - 1];
- node.Branch = branch;
- node.Children = new List();
- lastFolder.Children.Add(node);
- }
- }
-
- private void SortBranchNodes(List collection) {
- collection.Sort((l, r) => {
- if (l.Branch != null) {
- return r.Branch != null ? l.Branch.Name.CompareTo(r.Branch.Name) : -1;
- } else {
- return r.Branch == null ? l.Name.CompareTo(r.Name) : 1;
- }
- });
-
- foreach (var sub in collection) {
- if (sub.Children.Count > 0) SortBranchNodes(sub.Children);
- }
- }
-
- private void UpdateBranches(bool force = true) {
- Task.Run(() => {
- var branches = repo.Branches(force);
- var localBranchNodes = new List();
- var remoteNodes = new List();
- var remoteMap = new Dictionary();
- var folders = new Dictionary();
- var states = new Dictionary();
-
- BackupBranchNodeExpandState(states, cachedLocalBranches, "locals");
- foreach (var r in cachedRemotes) {
- var prefix = $"remotes/{r.Name}";
- states.Add(prefix, r.IsExpanded);
- BackupBranchNodeExpandState(states, r.Children, prefix);
- }
-
- foreach (var b in branches) {
- if (b.IsLocal) {
- MakeBranchNode(b, localBranchNodes, folders, states, "locals");
- } else {
- RemoteNode remote = null;
-
- if (!remoteMap.ContainsKey(b.Remote)) {
- var key = "remotes/" + b.Remote;
- remote = new RemoteNode() {
- Name = b.Remote,
- IsExpanded = states.ContainsKey(key) ? states[key] : false,
- Children = new List(),
- };
- remoteNodes.Add(remote);
- remoteMap.Add(b.Remote, remote);
- } else {
- remote = remoteMap[b.Remote];
- }
-
- MakeBranchNode(b, remote.Children, folders, states, "remotes");
- }
- }
-
- SortBranchNodes(localBranchNodes);
- foreach (var r in remoteNodes) SortBranchNodes(r.Children);
-
- cachedLocalBranches = localBranchNodes;
- cachedRemotes = remoteNodes;
-
- Dispatcher.Invoke(() => {
- localBranchTree.ItemsSource = localBranchNodes;
- remoteBranchTree.ItemsSource = remoteNodes;
- });
- });
- }
-
- private void UpdateTags() {
- Task.Run(() => {
- var tags = repo.Tags(true);
- foreach (var t in tags) t.IsFiltered = repo.LogFilters.Contains(t.Name);
-
- Dispatcher.Invoke(() => {
- tagCount.Content = $"TAGS ({tags.Count})";
- tagList.ItemsSource = tags;
- });
- });
- }
-
- private void Cleanup(object sender, RoutedEventArgs e) {
- localBranchTree.ItemsSource = null;
- remoteBranchTree.ItemsSource = null;
- tagList.ItemsSource = null;
- cachedLocalBranches.Clear();
- cachedRemotes.Clear();
- }
- #endregion
-
- #region TOOLBAR
- private void Close(object sender, RoutedEventArgs e) {
- if (PopupManager.IsLocked()) return;
- PopupManager.Close();
-
- cachedLocalBranches.Clear();
- cachedRemotes.Clear();
-
- repo.Close();
- }
-
- private void OpenFetch(object sender, RoutedEventArgs e) {
- Fetch.Show(repo);
- }
-
- private void OpenPull(object sender, RoutedEventArgs e) {
- Pull.Show(repo);
- }
-
- private void OpenPush(object sender, RoutedEventArgs e) {
- Push.Show(repo);
- }
-
- private void OpenStash(object sender, RoutedEventArgs e) {
- Stash.Show(repo, new List());
- }
-
- private void OpenApply(object sender, RoutedEventArgs e) {
- Apply.Show(repo);
- }
-
- private void OpenSearch(object sender, RoutedEventArgs e) {
- workspace.SelectedItem = historiesSwitch;
- if (histories.searchBar.Margin.Top == 0) {
- histories.HideSearchBar();
- } else {
- histories.OpenSearchBar();
- }
- }
-
- private void OpenExplorer(object sender, RoutedEventArgs e) {
- Process.Start(repo.Path);
- }
-
- private void OpenTerminal(object sender, RoutedEventArgs e) {
- var bash = Path.Combine(App.Preference.GitExecutable, "..", "bash.exe");
- if (!File.Exists(bash)) {
- App.RaiseError("Can NOT locate bash.exe. Make sure bash.exe exists under the same folder with git.exe");
- return;
- }
-
- var start = new ProcessStartInfo();
- start.WorkingDirectory = repo.Path;
- start.FileName = bash;
- Process.Start(start);
- }
- #endregion
-
- #region HOT_KEYS
- public void OpenSearchBar(object sender, ExecutedRoutedEventArgs e) {
- workspace.SelectedItem = historiesSwitch;
- histories.OpenSearchBar();
- }
-
- public void HideSearchBar(object sender, ExecutedRoutedEventArgs e) {
- if (histories.Visibility == Visibility.Visible) {
- histories.HideSearchBar();
- }
- }
- #endregion
-
- #region MERGE_ABORTS
- public void DetectMergeState() {
- var gitDir = Path.Combine(repo.Path, ".git");
- var cherryPickMerge = Path.Combine(gitDir, "CHERRY_PICK_HEAD");
- var rebaseMerge = Path.Combine(gitDir, "REBASE_HEAD");
- var revertMerge = Path.Combine(gitDir, "REVERT_HEAD");
- var otherMerge = Path.Combine(gitDir, "MERGE_HEAD");
-
- if (File.Exists(cherryPickMerge)) {
- abortCommand = "cherry-pick";
- txtMergeProcessing.Content = "Cherry-Pick merge request detected! Press 'Abort' to restore original HEAD";
- } else if (File.Exists(rebaseMerge)) {
- abortCommand = "rebase";
- txtMergeProcessing.Content = "Rebase merge request detected! Press 'Abort' to restore original HEAD";
- } else if (File.Exists(revertMerge)) {
- abortCommand = "revert";
- txtMergeProcessing.Content = "Revert merge request detected! Press 'Abort' to restore original HEAD";
- } else if (File.Exists(otherMerge)) {
- abortCommand = "merge";
- txtMergeProcessing.Content = "Merge request detected! Press 'Abort' to restore original HEAD";
- } else {
- abortCommand = null;
- }
-
- if (abortCommand != null) {
- abortPanel.Visibility = Visibility.Visible;
- if (commits.Visibility == Visibility.Visible) {
- btnResolve.Visibility = Visibility.Collapsed;
- } else {
- btnResolve.Visibility = Visibility.Visible;
- }
-
- commits.LoadMergeMessage();
- } else {
- abortPanel.Visibility = Visibility.Collapsed;
- }
- }
-
- private void Resolve(object sender, RoutedEventArgs e) {
- workspace.SelectedItem = workingCopySwitch;
- }
-
- private async void Continue(object sender, RoutedEventArgs e) {
- if (abortCommand == null) return;
-
- await Task.Run(() => {
- repo.SetWatcherEnabled(false);
- var errs = repo.RunCommand($"-c core.editor=true {abortCommand} --continue", null);
- repo.AssertCommand(errs);
- });
-
- commits.ClearMessage();
- }
-
- private async void Abort(object sender, RoutedEventArgs e) {
- if (abortCommand == null) return;
-
- await Task.Run(() => {
- repo.SetWatcherEnabled(false);
- var errs = repo.RunCommand($"{abortCommand} --abort", null);
- repo.AssertCommand(errs);
- });
-
- commits.ClearMessage();
- }
- #endregion
-
- #region WORKSPACE
- private void SwitchWorkingCopy(object sender, RoutedEventArgs e) {
- if (commits == null || histories == null || stashes == null) return;
-
- commits.Visibility = Visibility.Visible;
- histories.Visibility = Visibility.Collapsed;
- stashes.Visibility = Visibility.Collapsed;
-
- if (abortPanel.Visibility == Visibility.Visible) {
- btnResolve.Visibility = Visibility.Collapsed;
- }
- }
-
- private void SwitchHistories(object sender, RoutedEventArgs e) {
- if (commits == null || histories == null || stashes == null) return;
-
- commits.Visibility = Visibility.Collapsed;
- histories.Visibility = Visibility.Visible;
- stashes.Visibility = Visibility.Collapsed;
-
- if (abortPanel.Visibility == Visibility.Visible) {
- btnResolve.Visibility = Visibility.Visible;
- }
- }
-
- private void SwitchStashes(object sender, RoutedEventArgs e) {
- if (commits == null || histories == null || stashes == null) return;
-
- commits.Visibility = Visibility.Collapsed;
- histories.Visibility = Visibility.Collapsed;
- stashes.Visibility = Visibility.Visible;
-
- if (abortPanel.Visibility == Visibility.Visible) {
- btnResolve.Visibility = Visibility.Visible;
- }
- }
- #endregion
-
- #region LOCAL_BRANCHES
- private void OpenNewBranch(object sender, RoutedEventArgs e) {
- CreateBranch.Show(repo);
- }
-
- private void OpenGitFlow(object sender, RoutedEventArgs ev) {
- var button = sender as Button;
- if (button.ContextMenu == null) {
- button.ContextMenu = new ContextMenu();
- button.ContextMenu.PlacementTarget = button;
- button.ContextMenu.Placement = PlacementMode.Bottom;
- button.ContextMenu.StaysOpen = false;
- button.ContextMenu.Focusable = true;
- } else {
- button.ContextMenu.Items.Clear();
- }
-
- if (repo.IsGitFlowEnabled()) {
- var startFeature = new MenuItem();
- startFeature.Header = "Start Feature ...";
- startFeature.Click += (o, e) => {
- GitFlowStartBranch.Show(repo, Git.Branch.Type.Feature);
- e.Handled = true;
- };
-
- var startRelease = new MenuItem();
- startRelease.Header = "Start Release ...";
- startRelease.Click += (o, e) => {
- GitFlowStartBranch.Show(repo, Git.Branch.Type.Release);
- e.Handled = true;
- };
-
- var startHotfix = new MenuItem();
- startHotfix.Header = "Start Hotfix ...";
- startHotfix.Click += (o, e) => {
- GitFlowStartBranch.Show(repo, Git.Branch.Type.Hotfix);
- e.Handled = true;
- };
-
- button.ContextMenu.Items.Add(startFeature);
- button.ContextMenu.Items.Add(startRelease);
- button.ContextMenu.Items.Add(startHotfix);
- } else {
- var init = new MenuItem();
- init.Header = "Initialize Git-Flow";
- init.Click += (o, e) => {
- GitFlowSetup.Show(repo);
- e.Handled = true;
- };
- button.ContextMenu.Items.Add(init);
- }
-
- button.ContextMenu.IsOpen = true;
- ev.Handled = true;
- }
-
- private void LocalBranchSelected(object sender, RoutedPropertyChangedEventArgs