@@ -160,8 +160,32 @@ impl SnapshotManager {
160160
161161 match entry. entry_type {
162162 EntryType :: Blob => {
163- // Restore file from blob object
164- self . store . copy_file ( & entry. hash , & entry_path) . await ?;
163+ // Check if file already exists with same content
164+ let should_copy = if entry_path. exists ( ) {
165+ // Compare metadata first (fast check)
166+ if let Ok ( metadata) = fs:: metadata ( & entry_path) . await {
167+ if metadata. len ( ) == entry. size
168+ && metadata. modified ( ) . ok ( ) == Some ( entry. modified ) {
169+ // Size and mtime match - likely unchanged, skip copy
170+ false
171+ } else {
172+ // Metadata differs - need to verify with hash
173+ let file_hash = self . store . put_file ( & entry_path) . await ?;
174+ file_hash != entry. hash
175+ }
176+ } else {
177+ true
178+ }
179+ } else {
180+ true
181+ } ;
182+
183+ if should_copy {
184+ // Restore file from blob object
185+ self . store . copy_file ( & entry. hash , & entry_path) . await ?;
186+ }
187+
188+ // Always update metadata (cheap operation)
165189 set_mode ( & entry_path, entry. mode ) ?;
166190 set_mtime ( & entry_path, entry. modified ) ?;
167191 }
@@ -575,6 +599,52 @@ mod tests {
575599 ) ;
576600 }
577601
602+ #[ tokio:: test]
603+ async fn test_restore_skip_unchanged ( ) {
604+ use std:: time:: Duration ;
605+
606+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
607+ let store_dir = temp_dir. path ( ) . join ( "store" ) ;
608+ let workspace = temp_dir. path ( ) . join ( "workspace" ) ;
609+ let restore_dir = temp_dir. path ( ) . join ( "restored" ) ;
610+
611+ fs:: create_dir_all ( & workspace) . await . unwrap ( ) ;
612+ fs:: write ( workspace. join ( "file.txt" ) , "content" ) . await . unwrap ( ) ;
613+
614+ let manager = SnapshotManager :: new ( store_dir. clone ( ) ) . unwrap ( ) ;
615+ let snapshot_id = manager
616+ . create_snapshot ( & workspace, Some ( "Test" . to_string ( ) ) , None )
617+ . await
618+ . unwrap ( ) ;
619+
620+ // First restore
621+ manager. restore_snapshot ( & snapshot_id, & restore_dir) . await . unwrap ( ) ;
622+
623+ // Get file timestamp after first restore
624+ let first_timestamp = std:: fs:: metadata ( restore_dir. join ( "file.txt" ) )
625+ . unwrap ( )
626+ . modified ( )
627+ . unwrap ( ) ;
628+
629+ // Wait to ensure timestamp would differ if file was rewritten
630+ tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
631+
632+ // Second restore to same location
633+ manager. restore_snapshot ( & snapshot_id, & restore_dir) . await . unwrap ( ) ;
634+
635+ // File should NOT be rewritten (timestamp unchanged)
636+ let second_timestamp = std:: fs:: metadata ( restore_dir. join ( "file.txt" ) )
637+ . unwrap ( )
638+ . modified ( )
639+ . unwrap ( ) ;
640+
641+ // Timestamps should match (file was not recopied)
642+ assert_eq ! (
643+ first_timestamp, second_timestamp,
644+ "File should not be recopied if unchanged"
645+ ) ;
646+ }
647+
578648 #[ tokio:: test]
579649 #[ cfg( unix) ]
580650 async fn test_symlinks ( ) {
0 commit comments