Fix auto-refresh: watch .git directory instead of just files

Watching individual files (.git/index, .git/HEAD) fails reliably
because git replaces them via atomic rename, which removes inotify
watches. Watch the .git directory itself via directoryChanged signal
which fires on any create/delete/rename inside it. Keep file watches
as fallback. Also watch refs/heads for branch tip changes.
This commit is contained in:
Levi Neely 2026-07-16 04:28:24 +02:00
parent a7e7542d70
commit 3febe2f184
1 changed files with 23 additions and 12 deletions

View File

@ -515,8 +515,7 @@ void GitPlusPlusView::setupFileWatcher()
delete m_watcher;
m_watcher = new QFileSystemWatcher(this);
// Watch .git/index (changes on stage/unstage/commit/pull/merge)
// and .git/HEAD (changes on branch switch)
// Determine the actual .git directory
// For worktrees, .git is a file pointing to the real git dir
QString gitDir;
QString dotGit = m_gitDir + QStringLiteral("/.git");
@ -536,24 +535,36 @@ void GitPlusPlusView::setupFileWatcher()
if (gitDir.isEmpty()) return;
// Watch the git directory itself — picks up renames of index, HEAD, refs, etc.
// This is more robust than watching individual files which get replaced atomically.
QStringList watchPaths;
watchPaths << gitDir;
// Also watch refs/heads for branch changes
QString refsHeads = gitDir + QStringLiteral("/refs/heads");
if (QFileInfo::exists(refsHeads))
watchPaths << refsHeads;
m_watcher->addPaths(watchPaths);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &) {
m_fsDebounce.start();
});
// Still watch individual files as a fallback for systems where dir watching is coarse
QString indexFile = gitDir + QStringLiteral("/index");
QString headFile = gitDir + QStringLiteral("/HEAD");
if (QFileInfo::exists(indexFile)) watchPaths << indexFile;
if (QFileInfo::exists(headFile)) watchPaths << headFile;
if (!watchPaths.isEmpty()) {
m_watcher->addPaths(watchPaths);
QStringList filePaths;
if (QFileInfo::exists(indexFile)) filePaths << indexFile;
if (QFileInfo::exists(headFile)) filePaths << headFile;
if (!filePaths.isEmpty()) {
m_watcher->addPaths(filePaths);
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) {
// Re-add the path: QFileSystemWatcher drops watches when files are
// replaced atomically (git writes a temp file then renames over it)
// Re-add: atomic renames remove the watch
if (!m_watcher->files().contains(path)) {
QTimer::singleShot(100, this, [this, path]() {
if (QFileInfo::exists(path))
QTimer::singleShot(200, this, [this, path]() {
if (QFileInfo::exists(path) && m_watcher)
m_watcher->addPath(path);
});
}
// Debounce: multiple changes can fire rapidly
m_fsDebounce.start();
});
}