diff --git a/crates/gitbutler-core/src/ops/mod.rs b/crates/gitbutler-core/src/ops/mod.rs index 892299d1d..85ba841e7 100644 --- a/crates/gitbutler-core/src/ops/mod.rs +++ b/crates/gitbutler-core/src/ops/mod.rs @@ -3,3 +3,5 @@ pub mod oplog; mod reflog; pub mod snapshot; mod state; + +pub const OPLOG_FILE_NAME: &str = "operations-log.toml"; diff --git a/crates/gitbutler-core/src/ops/state.rs b/crates/gitbutler-core/src/ops/state.rs index 947d94f51..4697216fd 100644 --- a/crates/gitbutler-core/src/ops/state.rs +++ b/crates/gitbutler-core/src/ops/state.rs @@ -7,6 +7,8 @@ use std::{ use serde::{Deserialize, Serialize}; +use super::OPLOG_FILE_NAME; + /// This tracks the head of the oplog, persisted in operations-log.toml. #[derive(Serialize, Deserialize, Debug, Default)] pub struct Oplog { @@ -22,7 +24,7 @@ pub struct OplogHandle { impl OplogHandle { /// Creates a new concurrency-safe handle to the state of the oplog. pub fn new(base_path: &Path) -> Self { - let file_path = base_path.join("operations-log.toml"); + let file_path = base_path.join(OPLOG_FILE_NAME); Self { file_path } } diff --git a/crates/gitbutler-watcher/src/events.rs b/crates/gitbutler-watcher/src/events.rs index fe81fec63..9dba82251 100644 --- a/crates/gitbutler-watcher/src/events.rs +++ b/crates/gitbutler-watcher/src/events.rs @@ -13,6 +13,8 @@ pub(super) enum InternalEvent { // From file monitor GitFilesChange(ProjectId, Vec), ProjectFilesChange(ProjectId, Vec), + // Triggered on change in the `.git/gitbutler` directory + GitButlerOplogChange(ProjectId), } /// This type captures all operations that can be fed into a watcher that runs in the background. @@ -52,6 +54,9 @@ impl Display for InternalEvent { comma_separated_paths(paths) ) } + InternalEvent::GitButlerOplogChange(project_id) => { + write!(f, "GitButlerOplogChange({})", project_id) + } InternalEvent::ProjectFilesChange(project_id, paths) => { write!( f, diff --git a/crates/gitbutler-watcher/src/file_monitor.rs b/crates/gitbutler-watcher/src/file_monitor.rs index e67413ff1..979430ce8 100644 --- a/crates/gitbutler-watcher/src/file_monitor.rs +++ b/crates/gitbutler-watcher/src/file_monitor.rs @@ -10,6 +10,7 @@ use notify_debouncer_full::new_debouncer; use tokio::task; use tracing::Level; +use gitbutler_core::ops::OPLOG_FILE_NAME; /// The timeout for debouncing file change events. /// This is used to prevent multiple events from being sent for a single file change. const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100); @@ -114,12 +115,16 @@ pub fn spawn( .map_or(FileKind::Project, |repo| classify_file(repo, &file)); (file, kind) }); + let mut oplog_changed = false; let (mut stripped_git_paths, mut worktree_relative_paths) = (HashSet::new(), HashSet::new()); for (file_path, kind) in classified_file_paths { match kind { FileKind::ProjectIgnored => ignored += 1, FileKind::GitUninteresting => git_noop += 1, + FileKind::GitButlerOplog => { + oplog_changed = true; + } FileKind::Project | FileKind::Git => match file_path .strip_prefix(&worktree_path) { @@ -165,6 +170,13 @@ pub fn spawn( break 'outer; } } + if oplog_changed { + let event = InternalEvent::GitButlerOplogChange(project_id); + if out.send(event).is_err() { + tracing::info!("channel closed - stopping file watcher"); + break 'outer; + } + } } } } @@ -201,6 +213,8 @@ enum FileKind { Project, /// A file that was ignored in the project, and thus shouldn't trigger a computation. ProjectIgnored, + /// GitButler oplog file (`.git/gitbutler/operations-log.toml`) + GitButlerOplog, } fn classify_file(git_repo: &git::Repository, file_path: &Path) -> FileKind { @@ -212,6 +226,8 @@ fn classify_file(git_repo: &git::Repository, file_path: &Path) -> FileKind { || check_file_path == Path::new("index") { FileKind::Git + } else if check_file_path == Path::new("gitbutler").join(OPLOG_FILE_NAME) { + FileKind::GitButlerOplog } else { FileKind::GitUninteresting } diff --git a/crates/gitbutler-watcher/src/handler/mod.rs b/crates/gitbutler-watcher/src/handler/mod.rs index 0d32affea..9ade2d85e 100644 --- a/crates/gitbutler-watcher/src/handler/mod.rs +++ b/crates/gitbutler-watcher/src/handler/mod.rs @@ -60,6 +60,11 @@ impl Handler { .await .context("failed to handle git file change event"), + events::InternalEvent::GitButlerOplogChange(project_id) => self + .gitbutler_oplog_change(project_id) + .await + .context("failed to handle gitbutler oplog change event"), + // This is only produced at the end of mutating Tauri commands to trigger a fresh state being served to the UI. events::InternalEvent::CalculateVirtualBranches(project_id) => self .calculate_virtual_branches(project_id) @@ -181,4 +186,8 @@ impl Handler { } Ok(()) } + async fn gitbutler_oplog_change(&self, _project_id: ProjectId) -> Result<()> { + // TODO: Queue up pushing of data here, if configured + Ok(()) + } }