Skip to content

Commit

Permalink
fsmonitor: save Watchman clock to disk
Browse files Browse the repository at this point in the history
  • Loading branch information
arxanas committed Jun 11, 2022
1 parent 4eb4d0e commit 04f37f5
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 8 deletions.
37 changes: 34 additions & 3 deletions lib/src/fsmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
#![warn(missing_docs)]

use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;

use itertools::Itertools;
use thiserror::Error;
use watchman_client::prelude::{NameOnly, QueryRequestCommon, QueryResult};
use watchman_client::prelude::{Clock, ClockSpec, NameOnly, QueryRequestCommon, QueryResult};

/// Represents an instance in time from the perspective of the filesystem
/// monitor.
Expand All @@ -21,7 +22,36 @@ use watchman_client::prelude::{NameOnly, QueryRequestCommon, QueryResult};
/// informs the filesystem monitor that we only wish to get changed files since
/// the previous point in time.
#[derive(Clone, Debug)]
pub struct FsmonitorClock(watchman_client::pdu::Clock);
pub struct FsmonitorClock(Clock);

impl ToString for FsmonitorClock {
fn to_string(&self) -> String {
let Self(clock) = self;
match clock {
Clock::Spec(spec) => match spec {
ClockSpec::StringClock(s) => s.clone(),
ClockSpec::UnixTimestamp(i) => i.to_string(),
},
Clock::ScmAware(_) => {
unimplemented!("SCM-aware Watchman clocks not supported")
}
}
}
}

impl FromStr for FsmonitorClock {
type Err = <i64 as FromStr>::Err;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let spec = if s.starts_with("c:") {
ClockSpec::StringClock(s.to_owned())
} else {
let timestamp: i64 = s.parse()?;
ClockSpec::UnixTimestamp(timestamp)
};
Ok(FsmonitorClock(Clock::Spec(spec)))
}
}

#[allow(missing_docs)]
#[derive(Debug, Error)]
Expand Down Expand Up @@ -72,7 +102,7 @@ impl Fsmonitor {
/// Query for changed files since the previous point in time.
pub async fn query_changed_files(
&self,
_previous_clock: Option<FsmonitorClock>,
previous_clock: Option<FsmonitorClock>,
) -> Result<(FsmonitorClock, Option<Vec<PathBuf>>), FsmonitorError> {
let QueryResult {
version: _,
Expand All @@ -89,6 +119,7 @@ impl Fsmonitor {
.query(
&self.resolved_root,
QueryRequestCommon {
since: previous_clock.map(|FsmonitorClock(clock)| clock),
..Default::default()
},
)
Expand Down
1 change: 1 addition & 0 deletions lib/src/protos/working_copy.proto
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ message TreeState {
bytes tree_id = 1;
map<string, FileState> file_states = 2;
SparsePatterns sparse_patterns = 3;
string fsmonitor_clock = 4;
}

message Checkout {
Expand Down
30 changes: 25 additions & 5 deletions lib/src/working_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct TreeState {
// Currently only path prefixes
sparse_patterns: Vec<RepoPath>,
own_mtime: MillisSinceEpoch,
fsmonitor_clock: Option<FsmonitorClock>,
}

fn file_state_from_proto(proto: &crate::protos::working_copy::FileState) -> FileState {
Expand Down Expand Up @@ -300,6 +301,7 @@ impl TreeState {
file_states: BTreeMap::new(),
sparse_patterns: vec![RepoPath::root()],
own_mtime: MillisSinceEpoch(0),
fsmonitor_clock: None,
}
}

Expand Down Expand Up @@ -332,6 +334,7 @@ impl TreeState {
self.tree_id = TreeId::new(proto.tree_id.clone());
self.file_states = file_states_from_proto(&proto);
self.sparse_patterns = sparse_patterns_from_proto(&proto);
self.fsmonitor_clock = proto.fsmonitor_clock.parse().ok();
}

fn save(&mut self) {
Expand All @@ -350,6 +353,9 @@ impl TreeState {
.push(path.to_internal_file_string());
}
proto.sparse_patterns = MessageField::some(sparse_patterns);
if let Some(fsmonitor_clock) = &self.fsmonitor_clock {
proto.fsmonitor_clock = fsmonitor_clock.to_string();
}

let mut temp_file = NamedTempFile::new_in(&self.state_path).unwrap();
proto.write_to_writer(temp_file.as_file_mut()).unwrap();
Expand Down Expand Up @@ -399,11 +405,11 @@ impl TreeState {
#[tokio::main]
async fn query_fsmonitor(
&self,
previous_clock: Option<FsmonitorClock>,
) -> Result<(FsmonitorClock, Option<Vec<PathBuf>>), FsmonitorError> {
let working_copy_path = self.working_copy_path.to_owned();
tokio::spawn(async move {
let fsmonitor = Fsmonitor::init(&working_copy_path).await?;
let previous_clock = None;
fsmonitor.query_changed_files(previous_clock).await
})
.await
Expand All @@ -417,11 +423,11 @@ impl TreeState {
base_ignores: Arc<GitIgnoreFile>,
should_use_fsmonitor: bool,
) -> Result<TreeId, SnapshotError> {
let (_fsmonitor_clock, changed_files) = if should_use_fsmonitor {
match self.query_fsmonitor() {
let (fsmonitor_clock, changed_files) = if should_use_fsmonitor {
match self.query_fsmonitor(self.fsmonitor_clock.clone()) {
Ok((fsmonitor_clock, changed_files)) => (Some(fsmonitor_clock), changed_files),
Err(err) => {
eprintln!("Failed to query fsmonitor: {}", err);
eprintln!("Failed to query filesystem monitor: {}", err);
(None, None)
}
}
Expand Down Expand Up @@ -487,7 +493,18 @@ impl TreeState {
}
let git_ignore = git_ignore
.chain_with_file(&dir.to_internal_dir_string(), disk_dir.join(".gitignore"));
for maybe_entry in disk_dir.read_dir().unwrap() {

let entries = match disk_dir.read_dir() {
Ok(entries) => entries,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => continue,
Err(err) => {
return Err(SnapshotError::FileOpenError {
path: disk_dir,
err,
})
}
};
for maybe_entry in entries {
let entry = maybe_entry.unwrap();
let file_type = entry.file_type().unwrap();
let file_name = entry.file_name();
Expand Down Expand Up @@ -536,6 +553,9 @@ impl TreeState {
tree_builder.remove(file.clone());
}
self.tree_id = tree_builder.write_tree();
if fsmonitor_clock.is_some() {
self.fsmonitor_clock = fsmonitor_clock;
}
Ok(self.tree_id.clone())
}

Expand Down

0 comments on commit 04f37f5

Please sign in to comment.