From 9efb306979e26790380b4411cca684d3b3cb1a64 Mon Sep 17 00:00:00 2001 From: netquick Date: Mon, 24 Aug 2026 16:31:56 +0200 Subject: [PATCH] Show the window before slow location probes and wrap git.exe for commit, diff, and merge. Co-authored-by: Cursor --- Backlog.md | 23 +- docs/Documentation.md | 18 +- src/Explorer.App/App.xaml.cs | 14 +- src/Explorer.App/AppServices.cs | 6 +- src/Explorer.App/GitChangesWindow.xaml | 80 ++++ src/Explorer.App/GitChangesWindow.xaml.cs | 147 +++++++ src/Explorer.App/GitCommitWindow.xaml | 43 ++ src/Explorer.App/GitCommitWindow.xaml.cs | 24 ++ src/Explorer.App/GitDiffWindow.xaml | 60 +++ src/Explorer.App/GitDiffWindow.xaml.cs | 14 + src/Explorer.App/MainWindow.xaml | 26 ++ src/Explorer.App/MainWindow.xaml.cs | 126 ++++++ src/Explorer.App/SettingsWindow.xaml | 2 +- src/Explorer.App/Themes/Dark.xaml | 5 + src/Explorer.App/Themes/Light.xaml | 5 + src/Explorer.Application/GitCommitPlanner.cs | 100 +++++ src/Explorer.Application/GitDiffParser.cs | 86 ++++ src/Explorer.Application/GitDiffPlanner.cs | 54 +++ .../GitPorcelainParser.cs | 230 ++++++++++- src/Explorer.Application/GitRepoDetector.cs | 90 ++++ .../IGitCommandProvider.cs | 30 ++ .../IGitStatusProvider.cs | 2 +- src/Explorer.Application/SourceManager.cs | 105 ++++- src/Explorer.Domain/BoundedWait.cs | 37 ++ src/Explorer.Domain/GitCommandResult.cs | 43 ++ src/Explorer.Domain/GitStatus.cs | 126 +++++- .../ViewModels/ExplorerPaneViewModel.cs | 6 + .../ViewModels/GitChangesViewModel.cs | 367 ++++++++++++++++ .../ViewModels/GitCommitViewModel.cs | 151 +++++++ .../ViewModels/GitDiffViewModel.cs | 21 + .../ViewModels/MainViewModel.cs | 114 ++++- .../ViewModels/NavigationTreeViewModel.cs | 18 + .../WindowsGitStatusProvider.cs | 391 +++++++++++++++++- src/Explorer.Windows/WindowsVolumeService.cs | 64 ++- .../WindowsWorkspaceLauncher.cs | 10 +- .../Explorer.Application.Tests.csproj | 1 + .../GitCommandIntegrationTests.cs | 171 ++++++++ .../GitCommitPlannerTests.cs | 114 +++++ .../GitDiffParserTests.cs | 86 ++++ .../GitPorcelainParserTests.cs | 126 +++++- .../SourceManagerTests.cs | 40 +- tests/Explorer.Domain.Tests/DomainTests.cs | 85 ++++ 42 files changed, 3184 insertions(+), 77 deletions(-) create mode 100644 src/Explorer.App/GitChangesWindow.xaml create mode 100644 src/Explorer.App/GitChangesWindow.xaml.cs create mode 100644 src/Explorer.App/GitCommitWindow.xaml create mode 100644 src/Explorer.App/GitCommitWindow.xaml.cs create mode 100644 src/Explorer.App/GitDiffWindow.xaml create mode 100644 src/Explorer.App/GitDiffWindow.xaml.cs create mode 100644 src/Explorer.Application/GitCommitPlanner.cs create mode 100644 src/Explorer.Application/GitDiffParser.cs create mode 100644 src/Explorer.Application/GitDiffPlanner.cs create mode 100644 src/Explorer.Application/IGitCommandProvider.cs create mode 100644 src/Explorer.Domain/BoundedWait.cs create mode 100644 src/Explorer.Domain/GitCommandResult.cs create mode 100644 src/Explorer.Presentation/ViewModels/GitChangesViewModel.cs create mode 100644 src/Explorer.Presentation/ViewModels/GitCommitViewModel.cs create mode 100644 src/Explorer.Presentation/ViewModels/GitDiffViewModel.cs create mode 100644 tests/Explorer.Application.Tests/GitCommandIntegrationTests.cs create mode 100644 tests/Explorer.Application.Tests/GitCommitPlannerTests.cs create mode 100644 tests/Explorer.Application.Tests/GitDiffParserTests.cs diff --git a/Backlog.md b/Backlog.md index bc1cfac..0edba29 100644 --- a/Backlog.md +++ b/Backlog.md @@ -590,12 +590,15 @@ Example: ## Actions -- [ ] Status -- [ ] View Changes -- [ ] Commit -- [ ] Pull -- [ ] Push -- [ ] Fetch +- [x] Status +- [x] View Changes +- [x] Diff +- [x] Commit +- [x] Stage / Unstage / Discard +- [x] Merge (ours / theirs / continue / abort) +- [x] Pull +- [x] Push +- [x] Fetch - [x] Open terminal here - [x] Open in Cursor @@ -696,13 +699,9 @@ Potential: ## Git -`WindowsGitStatusProvider` (`IGitStatusProvider`) +`WindowsGitStatusProvider` (`IGitStatusProvider`, `IGitCommandProvider`) -Discovery: Settings path, then Program Files, then PATH. Missing git.exe means no badge. Git is not bundled. Workbench does not commit, push, or pull. - -Potential later: - -`IGitCommandProvider` +Discovery: Settings path, then Program Files, then PATH. Missing git.exe means no badge. Git is not bundled. Status / View Changes lists porcelain paths. Diff is unified `git diff`. Commit stages checked files then `git commit --only`. Stage / unstage / discard, ours / theirs / mark resolved, continue / abort wrap `git.exe`. Fetch / fast-forward pull / merge pull / push use `GIT_TERMINAL_PROMPT=0`. No mergetool, stash, branch UI, or credential dialog. Failed fast-forward pull offers merge pull or a terminal. --- diff --git a/docs/Documentation.md b/docs/Documentation.md index 3017bde..f95bbda 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -24,18 +24,18 @@ Workbench **does**: Workbench **does not**: - Two-way sync -- Commit, push, or pull - Convert media (no FFmpeg in this build) - Hydrate online-only cloud files just to look at them - Change Windows drive mappings or cloud client folders +- Replace Git (no stash, branch UI, mergetool, or credential dialog) -Specialized tools still do specialized jobs. 7-Zip compresses. Git reports status. Workbench orchestrates. +Specialized tools still do specialized jobs. 7-Zip compresses. Git reports status, shows diffs, commits selected files, resolves conflicts, and runs fetch, pull, and push. Workbench orchestrates. --- ## First launch -1. This PC lists local and removable volumes Windows already knows. +The window opens immediately. Locations fill in a moment later — Workbench does not wait for slow network shares or a second instance locking the index. 2. Browsing works with an empty index. 3. Folder sizes, search, duplicates, and storage analysis need an index. Use the banner **Build index**, **Tools → Locations → Index this location**, or the toolbar **Index** control. 4. Data lives under `%LocalAppData%\ExplorerWorkbench\` — never beside the executable. @@ -273,9 +273,17 @@ Never auto-reorganizes. No MIME/content sniffing (that would hydrate cloud files ## Git -Workbench detects repositories and shows a badge (branch, modified, untracked, ahead/behind). **Tools → Development → Open terminal here** and **Open in Cursor** are available when a folder is in context. +Workbench detects repositories and shows a badge (branch, modified, untracked, ahead/behind, merging/rebasing). **Tools → Development** (and the folder context menu) offers **View changes…**, **Commit…**, **Fetch**, **Pull (fast-forward)**, **Pull (merge)**, **Push**, **Open terminal here**, and **Open in Cursor** when a folder is in a repository. -Missing `git.exe` means no badge. Path can be set in Settings. Git is not bundled. There is no commit, push, pull, or diff viewer. Profiles can require a clean working tree. +**View changes** lists staged, unstaged, untracked, and unmerged paths from `git status`. Double-click opens a unified **diff** (`git diff` / `git diff --cached`). **Open in Cursor** opens the file. Online-only cloud files are not opened or diffed (that would download them). Stage, unstage, and discard call the matching `git` commands. Discard asks first. + +**Commit** asks for a message and which files to include. Unmerged paths, online-only cloud files, and folders are skipped. A merge, rebase, cherry-pick, or revert in progress blocks a normal commit — use **Continue** or **Abort**. Workbench runs `git add` then `git commit --only` for the checked paths, so other staged files stay staged. + +**Merge** is conflict resolution, not a mergetool: **Use ours**, **Use theirs**, **Mark resolved**, then **Continue** (`git commit --no-edit` / `rebase --continue`). **Abort** restores the previous state. Incoming rebase/cherry-pick/revert from a terminal can be finished the same way. + +**Pull (fast-forward)** is `git pull --ff-only --no-rebase`. If that cannot fast-forward, Workbench offers **Pull (merge)** (`git pull --no-rebase`) or a terminal. Fetch and push are the matching `git` commands. There is no stash, branch UI, mergetool, or credential dialog (`GIT_TERMINAL_PROMPT=0`). + +Missing `git.exe` means no badge and no Git actions. Path can be set in Settings. Git is not bundled. Profiles can require a clean working tree. --- diff --git a/src/Explorer.App/App.xaml.cs b/src/Explorer.App/App.xaml.cs index 84bd34c..2a3edc5 100644 --- a/src/Explorer.App/App.xaml.cs +++ b/src/Explorer.App/App.xaml.cs @@ -35,11 +35,21 @@ public partial class App : System.Windows.Application .Build(); var vm = _host.Services.GetRequiredService(); - await vm.InitializeAsync().ConfigureAwait(true); - await _host.StartAsync().ConfigureAwait(true); var window = _host.Services.GetRequiredService(); + vm.PrepareUi(); window.DataContext = vm; window.Show(); + try + { + await vm.InitializeAsync().ConfigureAwait(true); + } + catch (Exception ex) + { + Log.Error(ex, "Startup initialization failed"); + vm.Footer = "Started with errors. See logs."; + } + + await _host.StartAsync().ConfigureAwait(true); } protected override async void OnExit(ExitEventArgs e) diff --git a/src/Explorer.App/AppServices.cs b/src/Explorer.App/AppServices.cs index ce721a5..9e0b229 100644 --- a/src/Explorer.App/AppServices.cs +++ b/src/Explorer.App/AppServices.cs @@ -42,7 +42,9 @@ public static class AppServices services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -115,7 +117,7 @@ public sealed class WatcherHostedService : BackgroundService await _hub.RefreshAsync(stoppingToken).ConfigureAwait(false); while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false)) { - await _sources.RefreshOnlineStateAsync(stoppingToken).ConfigureAwait(false); + await _sources.RefreshOnlineStateAsync(forceRefresh: true, stoppingToken).ConfigureAwait(false); _transfers.NotifyAvailability(); await _sync.TryAutoRunAsync(stoppingToken).ConfigureAwait(false); await _profiles.TryAutoRunAsync(stoppingToken).ConfigureAwait(false); diff --git a/src/Explorer.App/GitChangesWindow.xaml b/src/Explorer.App/GitChangesWindow.xaml new file mode 100644 index 0000000..3a13ac0 --- /dev/null +++ b/src/Explorer.App/GitChangesWindow.xaml @@ -0,0 +1,80 @@ + + + +