From 84c45d2693dc98767f917cbd35e31b753b3546ed Mon Sep 17 00:00:00 2001 From: arimu1 Date: Thu, 23 Jul 2026 12:38:22 +0700 Subject: [PATCH] fix(watcher): don't panic when a subdirectory can't be watched gitui --watcher panics with "unexpected panic" whenever the recursive notify watch fails, e.g. when a subdirectory of the repo is owned by another user (root-owned, or created by a container) and returns PermissionDenied. The watcher thread's .expect("Watch error") turns that into a hard panic that tears down the terminal and closes gitui. Handle the watch() error gracefully instead: log it and skip live file watching for that thread, so a repo with a foreign-owned subdirectory still opens normally. gitui simply runs without live-refresh in that case, same as with --updater ticker. Fixes #2900 --- CHANGELOG.md | 1 + src/watcher.rs | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1ca0eaac2..8058692476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * crash when opening submodule ([#2895](https://github.com/gitui-org/gitui/issues/2895)) * when staging the last file in a directory, the first item after the directory is no longer skipped [[@Tillerino](https://github.com/Tillerino)] ([#2748](https://github.com/gitui-org/gitui/issues/2748)) * index-out-of-bounds panic when unstaging lines near the end of a diff ([#2953](https://github.com/gitui-org/gitui/issues/2953)) +* `--watcher` panic when a watched repo contains a directory owned by another user; live-refresh is now disabled instead of crashing ([#2900](https://github.com/gitui-org/gitui/issues/2900)) ## [0.28.1] - 2026-03-21 diff --git a/src/watcher.rs b/src/watcher.rs index ea30c7f135..9c4007caee 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -73,10 +73,38 @@ fn create_watcher( let mut bouncer = new_debouncer(timeout, tx).expect("Watch create error"); - bouncer + + match bouncer .watcher() .watch(Path::new(&workdir), RecursiveMode::Recursive) - .expect("Watch error"); + { + Ok(()) => std::mem::forget(bouncer), + Err(e) => { + // e.g. a subdirectory owned by another user can make the + // recursive watch fail with `PermissionDenied`. Live + // updates are disabled in that case, but gitui should + // still start up normally instead of crashing (#2900). + log::error!( + "failed to watch '{workdir}' for changes, live-refresh disabled: {e}" + ); + } + } +} - std::mem::forget(bouncer); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_watcher_does_not_panic_on_watch_error() { + // a path that cannot be watched (e.g. because it does not + // exist, or - as in #2900 - because a subdirectory is owned + // by another user) must not crash gitui. + let (tx, _rx) = std::sync::mpsc::channel(); + create_watcher( + Duration::from_millis(100), + tx, + "/nonexistent/gitui-watcher-test-path", + ); + } }