diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index c997f8221..b89f0f3d1 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -1023,6 +1023,7 @@
Template: ${0}$
WORKSPACE:
Configure Workspaces...
+ Fetch All Repositories
WORKTREE
BRANCH
Copy Path
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index cf27832e7..cd82711e6 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -1027,6 +1027,7 @@
模板:${0}$
工作区:
配置工作区...
+ 拉取工作区所有仓库
本地工作树
連結分支
复制工作树路径
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index fcf0003dd..73a2ec861 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -1027,6 +1027,7 @@
範本: ${0}$
工作區:
設定工作區...
+ 提取工作區所有存放庫
本機工作區
分支
複製工作區路徑
diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs
index 68e3db43d..cc1c37ad9 100644
--- a/src/ViewModels/Launcher.cs
+++ b/src/ViewModels/Launcher.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Text;
+using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Threading;
@@ -116,6 +118,26 @@ public void CloseAll()
_ignoreIndexChange = false;
}
+ public async Task FetchAllRepositoriesAsync()
+ {
+ // avoid collection was modified while enumerating.
+ var pages = new List();
+ pages.AddRange(Pages);
+
+ var count = 0;
+ foreach (var page in pages)
+ {
+ if (page.Data is Repository repo)
+ {
+ if (await repo.FetchAllRemotesAsync())
+ count++;
+ }
+ }
+
+ if (count > 0)
+ Models.Notification.Send(null, $"Fetched {count} repositories");
+ }
+
public void SwitchWorkspace(Workspace to)
{
if (to == null || to.IsActive)
diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs
index 8c08dbe7f..55abefd85 100644
--- a/src/ViewModels/Repository.cs
+++ b/src/ViewModels/Repository.cs
@@ -675,6 +675,40 @@ public async Task FetchAsync(bool autoStart)
ShowPopup(new Fetch(this));
}
+ public async Task FetchAllRemotesAsync()
+ {
+ if (IsAutoFetching)
+ return false;
+
+ CommandLog log = null;
+
+ try
+ {
+ var lockFile = Path.Combine(GitDir, "index.lock");
+ if (File.Exists(lockFile))
+ return false;
+
+ if (_remotes.Count == 0)
+ return false;
+
+ IsAutoFetching = true;
+ log = CreateLog("Fetch");
+
+ foreach (var remote in _remotes)
+ await new Commands.Fetch(FullPath, remote.Name).Use(log).RunAsync();
+
+ _lastFetchTime = DateTime.Now;
+ }
+ catch
+ {
+ // Ignore all exceptions.
+ }
+
+ IsAutoFetching = false;
+ log?.Complete();
+ return true;
+ }
+
public async Task PullAsync(bool autoStart)
{
if (IsBare || !CanCreatePopup())
diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs
index 27b213c28..e67108ed1 100644
--- a/src/Views/Launcher.axaml.cs
+++ b/src/Views/Launcher.axaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Avalonia;
using Avalonia.Controls;
@@ -395,6 +396,19 @@ private void OnOpenWorkspaceMenu(object sender, RoutedEventArgs e)
menu.Items.Add(new MenuItem() { Header = "-" });
+ var hasRepos = launcher.Pages.Any(p => p.Data is ViewModels.Repository);
+
+ var fetchAll = new MenuItem();
+ fetchAll.Header = App.Text("Workspace.FetchAllRepositories");
+ fetchAll.Icon = this.CreateMenuIcon("Icons.Fetch");
+ fetchAll.IsEnabled = hasRepos;
+ fetchAll.Click += async (_, ev) =>
+ {
+ await launcher.FetchAllRepositoriesAsync();
+ ev.Handled = true;
+ };
+ menu.Items.Add(fetchAll);
+
var configure = new MenuItem();
configure.Header = App.Text("Workspace.Configure");
configure.Click += async (_, ev) =>