diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index bbef234a..8090e576 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -88,6 +88,7 @@ M960 146v91C960 318 759 384 512 384S64 318 64 238V146C64 66 265 0 512 0s448 66 448 146zM960 352v206C960 638 759 704 512 704S64 638 64 558V352c96 66 272 97 448 97S864 418 960 352zm0 320v206C960 958 759 1024 512 1024S64 958 64 878V672c96 66 272 97 448 97S864 738 960 672z M883 567l-128-128c-17-17-43-17-60 0l-128 128c-17 17-17 43 0 60 17 17 43 17 60 0l55-55V683c0 21-21 43-43 43H418c-13-38-43-64-77-77V375c51-17 85-64 85-119 0-73-60-128-128-128-73 0-128 55-128 128 0 55 34 102 85 119v269c-51 17-85 64-85 119 0 73 55 128 128 128 55 0 102-34 119-85H640c73 0 128-55 128-128v-111l55 55c9 9 17 13 30 13 13 0 21-4 30-13 17-13 17-43 0-55zM299 213c26 0 43 17 43 43 0 21-21 43-43 43-26 0-43-21-43-43 0-26 17-43 43-43zm0 597c-26 0-43-21-43-43 0-26 17-43 43-43s43 17 43 43c0 21-17 43-43 43zM725 384c-73 0-128-60-128-128 0-73 55-128 128-128s128 55 128 128c0 68-55 128-128 128zm0-171c-26 0-43 17-43 43s17 43 43 43 43-17 43-43-17-43-43-43z M293 122v244h439V146l171 175V829a73 73 0 01-73 73h-98V536H293v366H195a73 73 0 01-73-73V195a73 73 0 0173-73h98zm366 512v268H366V634h293zm-49 49h-195v73h195v-73zm49-561v171H366V122h293z + M0 551V472c0-11 9-19 19-19h984c11 0 19 9 19 19v79c0 11-9 19-19 19H19c-11 0-19-9-19-19zM114 154v240c0 11-9 19-19 19H19C9 413 0 404 0 394V79C0 35 35 0 79 0h315c11 0 19 9 19 19v75c0 11-9 19-19 19H154c-21 0-39 18-39 39zm795 0c0-22-17-39-39-39h-240c-11 0-19-9-19-19V19c0-11 9-19 19-19h315c43 0 79 35 79 79v315c0 11-9 19-19 19h-75c-11 0-19-9-19-19l-1-240zm0 716v-240c0-11 9-19 19-19h75c11 0 19 9 19 19v315c0 43-35 79-79 79h-315c-11 0-19-9-19-19v-75c0-11 9-19 19-19H870c21-1 39-18 39-40zm-795 0c0 21 18 39 39 39h240c11 0 19 9 19 19v75c0 11-9 19-19 19H79C35 1023 0 988 0 945v-315c0-11 9-19 19-19h75c11 0 19 9 19 19V870z M702 677 590 565a148 148 0 10-25 27L676 703zm-346-200a115 115 0 11115 115A115 115 0 01355 478z M928 500a21 21 0 00-19-20L858 472a11 11 0 01-9-9c-1-6-2-13-3-19a11 11 0 015-12l46-25a21 21 0 0010-26l-8-22a21 21 0 00-24-13l-51 10a11 11 0 01-12-6c-3-6-6-11-10-17a11 11 0 011-13l34-39a21 21 0 001-28l-15-18a20 20 0 00-27-4l-45 27a11 11 0 01-13-1c-5-4-10-9-15-12a11 11 0 01-3-12l19-49a21 21 0 00-9-26l-20-12a21 21 0 00-27 6L650 193a9 9 0 01-11 3c-1-1-12-5-20-7a11 11 0 01-7-10l1-52a21 21 0 00-17-22l-23-4a21 21 0 00-24 14L532 164a11 11 0 01-11 7h-20a11 11 0 01-11-7l-17-49a21 21 0 00-24-15l-23 4a21 21 0 00-17 22l1 52a11 11 0 01-8 11c-5 2-15 6-19 7c-4 1-8 0-12-4l-33-40A21 21 0 00313 146l-20 12A21 21 0 00285 184l19 49a11 11 0 01-3 12c-5 4-10 8-15 12a11 11 0 01-13 1L228 231a21 21 0 00-27 4L186 253a21 21 0 001 28L221 320a11 11 0 011 13c-3 5-7 11-10 17a11 11 0 01-12 6l-51-10a21 21 0 00-24 13l-8 22a21 21 0 0010 26l46 25a11 11 0 015 12l0 3c-1 6-2 11-3 16a11 11 0 01-9 9l-51 8A21 21 0 0096 500v23A21 21 0 00114 544l51 8a11 11 0 019 9c1 6 2 13 3 19a11 11 0 01-5 12l-46 25a21 21 0 00-10 26l8 22a21 21 0 0024 13l51-10a11 11 0 0112 6c3 6 6 11 10 17a11 11 0 01-1 13l-34 39a21 21 0 00-1 28l15 18a20 20 0 0027 4l45-27a11 11 0 0113 1c5 4 10 9 15 12a11 11 0 013 12l-19 49a21 21 0 009 26l20 12a21 21 0 0027-6L374 832c3-3 7-5 10-4c7 3 12 5 20 7a11 11 0 018 10l-1 52a21 21 0 0017 22l23 4a21 21 0 0024-14l17-50a11 11 0 0111-7h20a11 11 0 0111 7l17 49a21 21 0 0020 15a19 19 0 004 0l23-4a21 21 0 0017-22l-1-52a11 11 0 018-10c8-3 13-5 18-7l1 0c6-2 9 0 11 3l34 41A21 21 0 00710 878l20-12a21 21 0 009-26l-18-49a11 11 0 013-12c5-4 10-8 15-12a11 11 0 0113-1l45 27a21 21 0 0027-4l15-18a21 21 0 00-1-28l-34-39a11 11 0 01-1-13c3-5 7-11 10-17a11 11 0 0112-6l51 10a21 21 0 0024-13l8-22a21 21 0 00-10-26l-46-25a11 11 0 01-5-12l0-3c1-6 2-11 3-16a11 11 0 019-9l51-8a21 21 0 0018-21v-23zm-565 188a32 32 0 01-51 5a270 270 0 011-363a32 32 0 0151 6l91 161a32 32 0 010 31zM512 782a270 270 0 01-57-6a32 32 0 01-20-47l92-160a32 32 0 0127-16h184a32 32 0 0130 41c-35 109-137 188-257 188zm15-328L436 294a32 32 0 0121-47a268 268 0 0155-6c120 0 222 79 257 188a32 32 0 01-30 41h-184a32 32 0 01-28-16z M900 287c40 69 60 144 60 225s-20 156-60 225c-40 69-94 123-163 163-69 40-144 60-225 60s-156-20-225-60c-69-40-123-94-163-163C84 668 64 593 64 512s20-156 60-225 94-123 163-163c69-40 144-60 225-60s156 20 225 60 123 94 163 163zM762 512c0-9-3-16-9-22L578 315l-44-44c-6-6-13-9-22-9s-16 3-22 9l-44 44-176 176c-6 6-9 13-9 22s3 16 9 22l44 44c6 6 13 9 22 9s16-3 22-9l92-92v269c0 9 3 16 9 22 6 6 13 9 22 9h62c8 0 16-3 22-9 6-6 9-13 9-22V486l92 92c6 6 13 9 22 9 8 0 16-3 22-9l44-44c6-6 9-13 9-22z diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 95773c46..ab29d6eb 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -526,6 +526,8 @@ SAVE Save As... Patch has been saved successfully! + Scan Repositories + Root Dir: Check for Updates... New version of this software is available: Check for updates failed! @@ -589,6 +591,7 @@ Open All Repositories Open Repository Open Terminal + Rescan Repositories in Default Clone Dir Search Repositories... Sort Changes diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index dedfa015..3986e358 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -528,6 +528,8 @@ 保 存 另存为... 补丁已成功保存! + 扫描仓库 + 根路径 : 检测更新... 检测到软件有版本更新: 获取最新版本信息失败! @@ -591,6 +593,7 @@ 打开所有包含仓库 打开本地仓库 打开终端 + 重新扫描默认克隆路径下的仓库 快速查找仓库... 排序 本地更改 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 205975a2..57f87926 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -529,6 +529,8 @@ 儲存 另存新檔... 修補檔已成功儲存! + 掃描存放庫 + 頂層目錄 : 檢查更新... 軟體有版本更新: 獲取最新版本資訊失敗! @@ -593,6 +595,7 @@ 開啟本機存放庫 開啟終端機 快速搜尋存放庫... + 重新掃描預設複製目錄下的存放庫 排序 本機變更 加入至 .gitignore 忽略清單 diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs new file mode 100644 index 00000000..7b2ce475 --- /dev/null +++ b/src/ViewModels/ScanRepositories.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Avalonia.Threading; + +namespace SourceGit.ViewModels +{ + public class ScanRepositories : Popup + { + public string RootDir + { + get; + private set; + } = string.Empty; + + public ScanRepositories(string rootDir) + { + GetManagedRepositories(Preference.Instance.RepositoryNodes, _managed); + + RootDir = rootDir; + View = new Views.ScanRepositories() { DataContext = this }; + } + + public override Task Sure() + { + ProgressDescription = $"Scan repositories under '{RootDir}' ..."; + + return Task.Run(() => + { + // If it is too fast, the panel will dispear very quickly, the we'll have a bad experience. + Task.Delay(500).Wait(); + + var founded = new List(); + GetUnmanagedRepositories(new DirectoryInfo(RootDir), founded, new EnumerationOptions() + { + AttributesToSkip = FileAttributes.Hidden | FileAttributes.System, + IgnoreInaccessible = true, + }); + + Dispatcher.UIThread.Invoke(() => + { + foreach (var f in founded) + Preference.Instance.FindOrAddNodeByRepositoryPath(f, null, false); + Welcome.Instance.Refresh(); + }); + + return true; + }); + } + + private void GetManagedRepositories(List group, HashSet repos) + { + foreach (RepositoryNode node in group) + { + if (node.IsRepository) + repos.Add(node.Id); + else + GetManagedRepositories(node.SubNodes, repos); + } + } + + private void GetUnmanagedRepositories(DirectoryInfo dir, List outs, EnumerationOptions opts, int depth = 0) + { + var subdirs = dir.GetDirectories("*", opts); + foreach (var subdir in subdirs) + { + SetProgressDescription($"Scanning {subdir.FullName}..."); + + var normalizedSelf = subdir.FullName.Replace("\\", "/"); + if (_managed.Contains(normalizedSelf)) + continue; + + var gitDir = Path.Combine(subdir.FullName, ".git"); + if (Directory.Exists(gitDir) || File.Exists(gitDir)) + { + var test = new Commands.QueryRepositoryRootPath(subdir.FullName).ReadToEnd(); + if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut)) + { + var normalized = test.StdOut.Trim().Replace("\\", "/"); + if (!_managed.Contains(normalizedSelf)) + outs.Add(normalized); + + continue; + } + } + + if (depth < 8) + GetUnmanagedRepositories(subdir, outs, opts, depth + 1); + } + } + + private HashSet _managed = new HashSet(); + } +} diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index c6919d7b..b193d5a4 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using Avalonia.Collections; using Avalonia.Controls; @@ -114,6 +115,17 @@ namespace SourceGit.ViewModels Native.OS.OpenTerminal(null); } + public void ScanDefaultCloneDir() + { + var defaultCloneDir = Preference.Instance.GitDefaultCloneDir; + if (string.IsNullOrEmpty(defaultCloneDir)) + App.RaiseException(PopupHost.Active.GetId(), "The default clone dir haven't been configured!"); + else if (!Directory.Exists(defaultCloneDir)) + App.RaiseException(PopupHost.Active.GetId(), $"The default clone dir '{defaultCloneDir}' is not exists!"); + else if (PopupHost.CanCreatePopup()) + PopupHost.ShowAndStartPopup(new ScanRepositories(defaultCloneDir)); + } + public void ClearSearchFilter() { SearchFilter = string.Empty; diff --git a/src/Views/ScanRepositories.axaml b/src/Views/ScanRepositories.axaml new file mode 100644 index 00000000..90274700 --- /dev/null +++ b/src/Views/ScanRepositories.axaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/src/Views/ScanRepositories.axaml.cs b/src/Views/ScanRepositories.axaml.cs new file mode 100644 index 00000000..4848112a --- /dev/null +++ b/src/Views/ScanRepositories.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class ScanRepositories : UserControl + { + public ScanRepositories() + { + InitializeComponent(); + } + } +} diff --git a/src/Views/WelcomeToolbar.axaml b/src/Views/WelcomeToolbar.axaml index 8c1c8a8e..59802241 100644 --- a/src/Views/WelcomeToolbar.axaml +++ b/src/Views/WelcomeToolbar.axaml @@ -21,9 +21,15 @@ - + + + + +