дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
fix(vcs): break the .git/index self-refresh loop that froze the UI (#339)
With a project open, the app became slow and then unresponsive after 30-60 minutes. Git status writes refreshed stat data back to .git/index; the watcher reported that write as a file change and a VCS change; the UI scheduled another status ~250 ms later, which wrote the index again. With any stat-dirty file this never settled: a full status, IPC traffic and decoration rebuilds in an endless loop. Watcher batches now carry .git internals separately (FsBatch.vcs), so they no longer appear as worktree changes and no fsChanged is sent for .git-only batches. Status records the identity (size, mtime) of the index it writes, and the host skips VcsChanged only for that write; external index changes (git add / reset / stash from a terminal) and HEAD / refs / packed-refs changes still refresh as before. The UI's fsChanged handler also ignores .git-only batches. Fixes #311 Co-authored-by: a-rookie-of-C-language <[email protected]>
Цей коміт міститься в:
1 джерело
0a27d02f4d
коміт
ab6fca01c4
8 files changed
+163
-20
No files matched your search
@@ -234,7 +234,7 @@ impl Host {
|
||||
// A repository was cloned / initialised / removed below the project: its
|
||||
// `.git/HEAD` (the watcher reports nothing else of `.git`) appeared or went.
|
||||
let known = repos.read().roots();
|
||||
let git_dirs = changed.iter().chain(&batch.removed).any(|p| {
|
||||
let git_dirs = changed.iter().chain(&batch.removed).chain(&batch.vcs).any(|p| {
|
||||
let Some(dir) = p.ends_with(".git/HEAD").then(|| p.parent().and_then(Path::parent)).flatten()
|
||||
else {
|
||||
return false;
|
||||
@@ -244,7 +244,10 @@ impl Host {
|
||||
if git_dirs {
|
||||
scan_repositories(&root, &repos, &settings, &sink);
|
||||
}
|
||||
sink.emit(IdeEvent::VcsChanged { root: root.clone().into() });
|
||||
let vcs_moved = batch.vcs.iter().any(|p| vcs_path_changed(p));
|
||||
if git_dirs || vcs_moved {
|
||||
sink.emit(IdeEvent::VcsChanged { root: root.clone().into() });
|
||||
}
|
||||
}),
|
||||
)?
|
||||
};
|
||||
@@ -1008,3 +1011,49 @@ mod maven_linkage_tests {
|
||||
assert!(model.maven.unwrap().projects.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
fn vcs_path_changed(path: &Path) -> bool {
|
||||
!path.ends_with(".git/index") || !ide_vcs::is_own_index_write(path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod index_event_tests {
|
||||
use super::vcs_path_changed;
|
||||
|
||||
#[test]
|
||||
fn status_write_is_suppressed_but_external_index_and_refs_refresh() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dir = tmp.path();
|
||||
let git = |args: &[&str]| {
|
||||
let output = std::process::Command::new("git").current_dir(dir).args(args).output().unwrap();
|
||||
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
|
||||
};
|
||||
git(&["init", "-q"]);
|
||||
let file = dir.join("file.txt");
|
||||
std::fs::write(&file, "original\n").unwrap();
|
||||
git(&["add", "file.txt"]);
|
||||
// Same contents, different stat data: status must refresh the index.
|
||||
std::fs::File::options()
|
||||
.write(true)
|
||||
.open(&file)
|
||||
.unwrap()
|
||||
.set_modified(std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1))
|
||||
.unwrap();
|
||||
let index = dir.join(".git/index");
|
||||
let before = std::fs::read(&index).unwrap();
|
||||
let repo = ide_vcs::Repo::discover(dir).unwrap().unwrap();
|
||||
repo.status().unwrap();
|
||||
assert_ne!(before, std::fs::read(&index).unwrap(), "status must actually write the index");
|
||||
assert!(!vcs_path_changed(&index));
|
||||
assert!(!vcs_path_changed(&index), "duplicate notifications for the same identity");
|
||||
for path in [".git/HEAD", ".git/refs/heads/main", ".git/packed-refs"] {
|
||||
assert!(vcs_path_changed(&dir.join(path)));
|
||||
}
|
||||
std::fs::write(&file, "external staged change\n").unwrap();
|
||||
git(&["add", "file.txt"]);
|
||||
assert!(vcs_path_changed(&index));
|
||||
assert!(vcs_path_changed(&index));
|
||||
std::fs::remove_file(&index).unwrap();
|
||||
assert!(vcs_path_changed(&index));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Заблокувати користувача