Skip to content

Commit 4fc84d7

Browse files
JohnTitorsapphi-red
authored andcommitted
fix(fsevent): set is: clone info for more IS_CLONE events
cherry-picked from notify-rs@7fec731
1 parent 8c21037 commit 4fc84d7

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

notify/src/fsevent.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,17 @@ fn translate_flags(flags: &StreamFlags, precise: bool, root_path_exists: bool) -
253253
});
254254
}
255255

256+
// `ITEM_CLONED` can be present alongside other flags (including create/modify/remove).
257+
// Preserve any existing `info` (like "root changed"), but annotate otherwise so downstream
258+
// can detect and filter clone-related events. See https://github.com/notify-rs/notify/issues/465.
259+
if flags.contains(StreamFlags::ITEM_CLONED) {
260+
for ev in &mut evs {
261+
if ev.info().is_none() {
262+
ev.attrs.set_info("is: clone");
263+
}
264+
}
265+
}
266+
256267
if flags.contains(StreamFlags::OWN_EVENT) {
257268
for ev in &mut evs {
258269
*ev = std::mem::take(ev).set_process_id(std::process::id());
@@ -853,6 +864,70 @@ mod tests {
853864
);
854865
}
855866

867+
#[test]
868+
fn translate_flags_ignores_is_file_only_events() {
869+
assert!(translate_flags(&StreamFlags::IS_FILE, true, false).is_empty());
870+
assert!(
871+
translate_flags(
872+
&(StreamFlags::IS_FILE | StreamFlags::ITEM_CLONED),
873+
true,
874+
false
875+
)
876+
.is_empty(),
877+
"type-only clone flags should not produce events"
878+
);
879+
}
880+
881+
#[test]
882+
fn translate_flags_sets_clone_info_for_file_events() {
883+
let create = translate_flags(
884+
&(StreamFlags::ITEM_CREATED | StreamFlags::IS_FILE | StreamFlags::ITEM_CLONED),
885+
true,
886+
false,
887+
);
888+
assert_eq!(create.len(), 1);
889+
assert_eq!(create[0].kind, EventKind::Create(CreateKind::File));
890+
assert_eq!(create[0].info(), Some("is: clone"));
891+
892+
let modify = translate_flags(
893+
&(StreamFlags::INODE_META_MOD
894+
| StreamFlags::ITEM_MODIFIED
895+
| StreamFlags::IS_FILE
896+
| StreamFlags::ITEM_CLONED),
897+
true,
898+
false,
899+
);
900+
assert_eq!(modify.len(), 2);
901+
assert!(
902+
modify
903+
.iter()
904+
.any(|e| matches!(e.kind, EventKind::Modify(ModifyKind::Metadata(_))))
905+
);
906+
assert!(
907+
modify
908+
.iter()
909+
.any(|e| matches!(e.kind, EventKind::Modify(ModifyKind::Data(_))))
910+
);
911+
assert!(
912+
modify.iter().all(|e| e.info() == Some("is: clone")),
913+
"all events should be annotated as clone-related: {modify:?}"
914+
);
915+
}
916+
917+
#[test]
918+
fn translate_flags_does_not_override_existing_info() {
919+
let evs = translate_flags(
920+
&(StreamFlags::ROOT_CHANGED
921+
| StreamFlags::ITEM_REMOVED
922+
| StreamFlags::IS_FILE
923+
| StreamFlags::ITEM_CLONED),
924+
true,
925+
false,
926+
);
927+
assert_eq!(evs.len(), 1);
928+
assert_eq!(evs[0].info(), Some("root changed"));
929+
}
930+
856931
#[test]
857932
fn does_not_crash_with_empty_path() {
858933
let mut watcher = FsEventWatcher::new(|_| {}, Config::default()).unwrap();

notify/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
//!
5353
//! On APFS, `std::fs::copy` may use copy-on-write cloning (`fclonefileat`/`clonefile`).
5454
//! This can update inode metadata on the source file, and FSEvents may report a metadata change
55-
//! for the source path (see [issue #259](https://github.com/notify-rs/notify/issues/259)).
55+
//! for the source path (see [issue #259](https://github.com/notify-rs/notify/issues/259) and
56+
//! [issue #465](https://github.com/notify-rs/notify/issues/465)).
5657
//!
5758
//! Workarounds are to avoid `std::fs::copy` (use `std::io::copy` or `read`/`write` instead), or
5859
//! filter out metadata-only events if they're not relevant (e.g. don't include

0 commit comments

Comments
 (0)