diff --git a/src/Commands/Clone.cs b/src/Commands/Clone.cs index d4b3f2fc..a6bfe11f 100644 --- a/src/Commands/Clone.cs +++ b/src/Commands/Clone.cs @@ -8,12 +8,20 @@ namespace SourceGit.Commands { public class Clone : Command { private Action handler = null; - public Clone(string path, string url, string localName, string extraArgs, Action outputHandler) { + public Clone(string path, string url, string localName, string sshKey, string extraArgs, Action outputHandler) { Cwd = path; TraitErrorAsOutput = true; - Args = "-c credential.helper=manager clone --progress --verbose --recurse-submodules "; handler = outputHandler; + if (!string.IsNullOrEmpty(sshKey)) { + Environment.SetEnvironmentVariable("GIT_SSH_COMMAND", $"ssh -i '{sshKey}'"); + Args = ""; + } else { + Args = "-c credential.helper=manager "; + } + + Args += "clone --progress --verbose --recurse-submodules "; + if (!string.IsNullOrEmpty(extraArgs)) Args += $"{extraArgs} "; Args += $"{url} "; if (!string.IsNullOrEmpty(localName)) Args += localName; diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs index b61e53b3..c30787c7 100644 --- a/src/Commands/Fetch.cs +++ b/src/Commands/Fetch.cs @@ -13,7 +13,16 @@ namespace SourceGit.Commands { public Fetch(string repo, string remote, bool prune, Action outputHandler) { Cwd = repo; TraitErrorAsOutput = true; - Args = "-c credential.helper=manager fetch --progress --verbose "; + + var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); + if (!string.IsNullOrEmpty(sshKey)) { + Environment.SetEnvironmentVariable("GIT_SSH_COMMAND", $"ssh -i '{sshKey}'"); + Args = ""; + } else { + Args = "-c credential.helper=manager "; + } + + Args += "fetch --progress --verbose "; if (prune) Args += "--prune "; Args += remote; handler = outputHandler; diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs index 29d45322..f525a20f 100644 --- a/src/Commands/Pull.cs +++ b/src/Commands/Pull.cs @@ -11,10 +11,19 @@ namespace SourceGit.Commands { public Pull(string repo, string remote, string branch, bool useRebase, bool autoStash, Action onProgress) { Cwd = repo; - Args = "-c credential.helper=manager pull --verbose --progress --tags "; TraitErrorAsOutput = true; handler = onProgress; + var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); + if (!string.IsNullOrEmpty(sshKey)) { + Environment.SetEnvironmentVariable("GIT_SSH_COMMAND", $"ssh -i '{sshKey}'"); + Args = ""; + } else { + Args = "-c credential.helper=manager "; + } + + Args += "pull --verbose --progress --tags "; + if (useRebase) Args += "--rebase "; if (autoStash) { if (useRebase) Args += "--autostash "; diff --git a/src/Commands/Push.cs b/src/Commands/Push.cs index 2beafcb7..2742b564 100644 --- a/src/Commands/Push.cs +++ b/src/Commands/Push.cs @@ -11,7 +11,16 @@ namespace SourceGit.Commands { Cwd = repo; TraitErrorAsOutput = true; handler = onProgress; - Args = "-c credential.helper=manager push --progress --verbose "; + + var sshKey = new Config(repo).Get($"remote.{remote}.sshkey"); + if (!string.IsNullOrEmpty(sshKey)) { + Environment.SetEnvironmentVariable("GIT_SSH_COMMAND", $"ssh -i '{sshKey}'"); + Args = ""; + } else { + Args = "-c credential.helper=manager "; + } + + Args += "push --progress --verbose "; if (withTags) Args += "--tags "; if (track) Args += "-u "; diff --git a/src/Resources/Locales/en_US.xaml b/src/Resources/Locales/en_US.xaml index dbf55a68..35177b2f 100644 --- a/src/Resources/Locales/en_US.xaml +++ b/src/Resources/Locales/en_US.xaml @@ -22,6 +22,9 @@ Parent Folder : Relative foler to store this module. Optional. + SSH Private Key + Private SSH key store path + ABOUT SourceGit - OPEN SOURCE GIT CLIENT diff --git a/src/Resources/Locales/zh_CN.xaml b/src/Resources/Locales/zh_CN.xaml index 801fcf7f..666f530b 100644 --- a/src/Resources/Locales/zh_CN.xaml +++ b/src/Resources/Locales/zh_CN.xaml @@ -21,6 +21,9 @@ 本地目录 : 本地存放的父级目录,选填. + SSH密钥 + SSH密钥文件 + 关于软件 SourceGit - 开源Git图形客户端 diff --git a/src/Views/Popups/Clone.xaml b/src/Views/Popups/Clone.xaml index 3f425d74..f2f90b4c 100644 --- a/src/Views/Popups/Clone.xaml +++ b/src/Views/Popups/Clone.xaml @@ -11,6 +11,7 @@ + @@ -31,7 +32,8 @@ Grid.Row="0" Grid.Column="1" x:Name="txtUrl" Height="24" - Placeholder="{DynamicResource Text.Clone.RemoteURL.Placeholder}"> + Placeholder="{DynamicResource Text.Clone.RemoteURL.Placeholder}" + TextChanged="OnUrlChanged"> @@ -43,7 +45,7 @@ @@ -52,6 +54,33 @@ + + + + + + + + + + + + @@ -96,12 +125,12 @@ @@ -115,12 +144,12 @@ diff --git a/src/Views/Popups/Clone.xaml.cs b/src/Views/Popups/Clone.xaml.cs index a19fb3cb..04b9d879 100644 --- a/src/Views/Popups/Clone.xaml.cs +++ b/src/Views/Popups/Clone.xaml.cs @@ -1,5 +1,8 @@ +using Microsoft.Win32; +using System; using System.IO; using System.Threading.Tasks; +using System.Windows; using System.Windows.Controls; namespace SourceGit.Views.Popups { @@ -32,11 +35,13 @@ namespace SourceGit.Views.Popups { if (Validation.GetHasError(edit)) return null; } + var sshKey = txtSSHKey.Text; + return Task.Run(() => { var extras = string.IsNullOrEmpty(ExtraArgs) ? "" : ExtraArgs; if (!string.IsNullOrEmpty(RemoteName)) extras += $" --origin {RemoteName}"; - var succ = new Commands.Clone(Folder, Uri, LocalName, extras, UpdateProgress).Exec(); + var succ = new Commands.Clone(Folder, Uri, LocalName, sshKey, extras, UpdateProgress).Exec(); if (!succ) return false; var path = Folder; @@ -53,6 +58,13 @@ namespace SourceGit.Views.Popups { return false; } + if (!string.IsNullOrEmpty(sshKey)) { + var config = new Commands.Config(path); + var remote = "origin"; + if (!string.IsNullOrEmpty(RemoteName)) remote = RemoteName; + config.Set($"remote.{remote}.sshkey", sshKey); + } + var gitDir = new Commands.QueryGitDir(path).Result(); var repo = Models.Preference.Instance.AddRepository(path, gitDir, ""); if (repo != null) Dispatcher.Invoke(() => Models.Watcher.Open(repo)); @@ -60,12 +72,34 @@ namespace SourceGit.Views.Popups { }); } - private void OnFolderSelectorClick(object sender, System.Windows.RoutedEventArgs e) { + private void OnFolderSelectorClick(object sender, RoutedEventArgs e) { var dialog = new Controls.FolderDialog(); if (dialog.ShowDialog() == true) { Folder = dialog.SelectedPath; txtFolder.GetBindingExpression(TextBox.TextProperty).UpdateTarget(); } } + + private void OnSelectSSHKey(object sender, RoutedEventArgs e) { + var initPath = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "..", ".ssh")); + if (!Directory.Exists(initPath)) Directory.CreateDirectory(initPath); + + var dialog = new OpenFileDialog(); + dialog.Filter = $"SSH Private Key|*"; + dialog.Title = App.Text("SSHKey"); + dialog.InitialDirectory = initPath; + dialog.CheckFileExists = true; + dialog.Multiselect = false; + + if (dialog.ShowDialog() == true) txtSSHKey.Text = dialog.FileName; + } + + private void OnUrlChanged(object sender, TextChangedEventArgs e) { + if (!string.IsNullOrEmpty(txtUrl.Text)) { + rowSSHKey.Height = new GridLength(txtUrl.Text.StartsWith("git@") ? 32 : 0, GridUnitType.Pixel); + } else { + rowSSHKey.Height = new GridLength(0, GridUnitType.Pixel); + } + } } } diff --git a/src/Views/Popups/Fetch.xaml.cs b/src/Views/Popups/Fetch.xaml.cs index cb4110d3..a900b33b 100644 --- a/src/Views/Popups/Fetch.xaml.cs +++ b/src/Views/Popups/Fetch.xaml.cs @@ -28,13 +28,21 @@ namespace SourceGit.Views.Popups { public override Task Start() { var prune = chkPrune.IsChecked == true; var remote = (remotes.SelectedItem as Models.Remote).Name; - if (chkFetchAll.IsChecked == true) remote = "--all"; + var all = chkFetchAll.IsChecked == true; return Task.Run(() => { Models.Watcher.SetEnabled(repo, false); - var succ = new Commands.Fetch(repo, remote, prune, UpdateProgress).Exec(); + + if (all) { + foreach (var r in remotes.ItemsSource) { + new Commands.Fetch(repo, (r as Models.Remote).Name, prune, UpdateProgress).Exec(); + } + } else { + new Commands.Fetch(repo, remote, prune, UpdateProgress).Exec(); + } + Models.Watcher.SetEnabled(repo, true); - return succ; + return true; }); } } diff --git a/src/Views/Popups/Remote.xaml b/src/Views/Popups/Remote.xaml index 6ab14c53..4c2aaa2a 100644 --- a/src/Views/Popups/Remote.xaml +++ b/src/Views/Popups/Remote.xaml @@ -13,6 +13,7 @@ + @@ -48,6 +49,7 @@ Grid.Row="1" Grid.Column="1" x:Name="txtUrl" Height="24" + TextChanged="OnUrlChanged" Placeholder="{DynamicResource Text.Remote.URL.Placeholder}"> @@ -57,5 +59,32 @@ + + + + + + + + + + + + diff --git a/src/Views/Popups/Remote.xaml.cs b/src/Views/Popups/Remote.xaml.cs index 8c84c766..3d48b5e0 100644 --- a/src/Views/Popups/Remote.xaml.cs +++ b/src/Views/Popups/Remote.xaml.cs @@ -1,4 +1,8 @@ +using Microsoft.Win32; +using System; +using System.IO; using System.Threading.Tasks; +using System.Windows; using System.Windows.Controls; namespace SourceGit.Views.Popups { @@ -24,6 +28,11 @@ namespace SourceGit.Views.Popups { InitializeComponent(); ruleName.Repo = repo; + if (RemoteURL.StartsWith("git@")) { + txtSSHKey.Text = new Commands.Config(repo.Path).Get($"remote.{remote.Name}.sshkey"); + } else { + txtSSHKey.Text = ""; + } } public override string GetTitle() { @@ -39,11 +48,17 @@ namespace SourceGit.Views.Popups { txtUrl.GetBindingExpression(TextBox.TextProperty).UpdateSource(); if (Validation.GetHasError(txtUrl)) return null; + var sshKey = txtSSHKey.Text; + return Task.Run(() => { Models.Watcher.SetEnabled(repo.Path, false); if (remote == null) { var succ = new Commands.Remote(repo.Path).Add(RemoteName, RemoteURL); if (succ) new Commands.Fetch(repo.Path, RemoteName, true, UpdateProgress).Exec(); + + if (!string.IsNullOrEmpty(sshKey)) { + new Commands.Config(repo.Path).Set($"remote.{RemoteName}.sshkey", sshKey); + } } else { if (remote.URL != RemoteURL) { var succ = new Commands.Remote(repo.Path).SetURL(remote.Name, RemoteURL); @@ -54,10 +69,36 @@ namespace SourceGit.Views.Popups { var succ = new Commands.Remote(repo.Path).Rename(remote.Name, RemoteName); if (succ) remote.Name = RemoteName; } + + if (!string.IsNullOrEmpty(sshKey)) { + new Commands.Config(repo.Path).Set($"remote.{RemoteName}.sshkey", sshKey); + } } Models.Watcher.SetEnabled(repo.Path, true); return true; }); } + + private void OnSelectSSHKey(object sender, RoutedEventArgs e) { + var initPath = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "..", ".ssh")); + if (!Directory.Exists(initPath)) Directory.CreateDirectory(initPath); + + var dialog = new OpenFileDialog(); + dialog.Filter = $"SSH Private Key|*"; + dialog.Title = App.Text("SSHKey"); + dialog.InitialDirectory = initPath; + dialog.CheckFileExists = true; + dialog.Multiselect = false; + + if (dialog.ShowDialog() == true) txtSSHKey.Text = dialog.FileName; + } + + private void OnUrlChanged(object sender, TextChangedEventArgs e) { + if (!string.IsNullOrEmpty(txtUrl.Text)) { + rowSSHKey.Height = new GridLength(txtUrl.Text.StartsWith("git@") ? 32 : 0, GridUnitType.Pixel); + } else { + rowSSHKey.Height = new GridLength(0, GridUnitType.Pixel); + } + } } }