Skip to content

Commit

Permalink
collect filemonitor events into lists to avoid excessive recomputation.
Browse files Browse the repository at this point in the history
Previously, each file change both in `.git` as well as in the worktree would
cause a complete recomputation. This computation included opening a git
repository at least once (probaby more often), to make an 'is-ignored' check.

The latter is very expensive in `git2` and gets more expensive the more
files there are.

Now the repository is opened when needed, and we re-use it for all applicable
file paths.
  • Loading branch information
Byron committed Apr 13, 2024
1 parent e5f7e9e commit b025e4e
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 12 deletions.
2 changes: 2 additions & 0 deletions crates/gitbutler-core/src/projects/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub struct Project {
pub id: ProjectId,
pub title: String,
pub description: Option<String>,
// TODO(ST): Keep track of the `git_dir` separately and use it, particularly in `file_monitor.rs` (#3062)
/// The worktree path of the projects repository.
pub path: path::PathBuf,
#[serde(default)]
pub preferred_key: AuthKey,
Expand Down
36 changes: 24 additions & 12 deletions crates/gitbutler-tauri/src/watcher/file_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use gitbutler_core::{git, projects::ProjectId};
use notify::Watcher;
use notify_debouncer_full::new_debouncer;
use tokio::task;
use tracing::Level;

/// The timeout for debouncing file change events.
/// This is used to prevent multiple events from being sent for a single file change.
Expand Down Expand Up @@ -66,29 +67,41 @@ pub fn spawn(
})
.context("failed to start watcher")?;

let repo = git::Repository::open(worktree_path).context(format!(
"failed to open project repository: {}",
worktree_path.display()
))?;

tracing::debug!(%project_id, "file watcher started");

let path = worktree_path.to_owned();
let worktree_path = worktree_path.to_owned();
task::spawn_blocking(move || {
tracing::debug!(%project_id, "file watcher started");
let _debouncer = debouncer;
let _runtime = tracing::span!(Level::DEBUG, "file monitor", %project_id ).entered();
'outer: for result in notify_rx {
let _span = tracing::span!(Level::DEBUG, "handle debounced events").entered();
match result {
Err(err) => {
tracing::error!(?err, "file watcher error");
tracing::error!(?err, "ignored file watcher error");
}
Ok(events) => {
let maybe_repo = git::Repository::open(&worktree_path).with_context(
|| {
format!(
"failed to open project repository: {}",
worktree_path.display()
)
},
).map(Some).unwrap_or_else(|err| {
tracing::error!(?err, "will consider changes to all files as repository couldn't be opened");
None
});

let file_paths = events
.into_iter()
.filter(|event| is_interesting_kind(event.kind))
.flat_map(|event| event.event.paths)
.filter(|file| is_interesting_file(&repo, file));
.filter(|file| {
maybe_repo
.as_ref()
.map_or(true, |repo| is_interesting_file(repo, file))
});
for file_path in file_paths {
match file_path.strip_prefix(&path) {
match file_path.strip_prefix(&worktree_path) {
Ok(relative_file_path) => {
if relative_file_path.as_os_str().is_empty() {
continue;
Expand Down Expand Up @@ -116,7 +129,6 @@ pub fn spawn(
}
}
}
tracing::debug!(%project_id, "file watcher stopped");
});
Ok(())
}
Expand Down

0 comments on commit b025e4e

Please sign in to comment.