Skip to content

Commit 30596f4

Browse files
muirdmmeta-codesync[bot]
authored andcommitted
scmstore: honor skip_lfs in FileStore fetch pipeline
Summary: When FetchContext::skip_lfs() is true, the FileStore fetch pipeline skips fetching LFS blobs from the LFS store(s). The items will be reported as "missing" in the results since they weren't fetched. Reviewed By: zzl0 Differential Revision: D98202371 fbshipit-source-id: 9a4996d162e705ecda2d5c44fa6671cd448bb7aa
1 parent 878c1c0 commit 30596f4

File tree

1 file changed

+126
-20
lines changed
  • eden/scm/lib/revisionstore/src/scmstore

1 file changed

+126
-20
lines changed

eden/scm/lib/revisionstore/src/scmstore/file.rs

Lines changed: 126 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -300,20 +300,22 @@ impl FileStore {
300300

301301
fctx.inc_local(fetched_since_last_time(&state));
302302

303-
if let Some(lfs_cache) = lfs_client.as_ref().map(|c| c.shared.as_ref()) {
304-
assert!(
305-
format == SerializationFormat::Hg,
306-
"LFS cannot be used with non-Hg serialization format"
307-
);
308-
state.fetch_lfs(lfs_cache, StoreLocation::Cache);
309-
}
310-
311-
if let Some(lfs_local) = lfs_client.as_ref().and_then(|c| c.local.as_ref()) {
312-
assert!(
313-
format == SerializationFormat::Hg,
314-
"LFS cannot be used with non-Hg serialization format"
315-
);
316-
state.fetch_lfs(lfs_local, StoreLocation::Local);
303+
if !fctx.skip_lfs() {
304+
if let Some(lfs_cache) = lfs_client.as_ref().map(|c| c.shared.as_ref()) {
305+
assert!(
306+
format == SerializationFormat::Hg,
307+
"LFS cannot be used with non-Hg serialization format"
308+
);
309+
state.fetch_lfs(lfs_cache, StoreLocation::Cache);
310+
}
311+
312+
if let Some(lfs_local) = lfs_client.as_ref().and_then(|c| c.local.as_ref()) {
313+
assert!(
314+
format == SerializationFormat::Hg,
315+
"LFS cannot be used with non-Hg serialization format"
316+
);
317+
state.fetch_lfs(lfs_local, StoreLocation::Local);
318+
}
317319
}
318320

319321
fctx.inc_local(fetched_since_last_time(&state));
@@ -329,12 +331,14 @@ impl FileStore {
329331
);
330332
}
331333

332-
if let Some(ref lfs_client) = lfs_client {
333-
assert!(
334-
format == SerializationFormat::Hg,
335-
"LFS cannot be used with non-Hg serialization format"
336-
);
337-
state.fetch_lfs_remote(lfs_client, lfs_buffer_in_memory);
334+
if !fctx.skip_lfs() {
335+
if let Some(ref lfs_client) = lfs_client {
336+
assert!(
337+
format == SerializationFormat::Hg,
338+
"LFS cannot be used with non-Hg serialization format"
339+
);
340+
state.fetch_lfs_remote(lfs_client, lfs_buffer_in_memory);
341+
}
338342
}
339343

340344
fctx.inc_remote(fetched_since_last_time(&state));
@@ -708,3 +712,105 @@ impl HgIdMutableDeltaStore for FileStore {
708712
Ok(None)
709713
}
710714
}
715+
716+
#[cfg(test)]
717+
mod tests {
718+
use std::collections::BTreeMap;
719+
720+
use ::types::RepoPathBuf;
721+
use ::types::fetch_cause::FetchCause;
722+
use ::types::fetch_mode::FetchMode;
723+
use tempfile::TempDir;
724+
725+
use super::*;
726+
use crate::StoreType;
727+
use crate::indexedlogdatastore::IndexedLogHgIdDataStoreConfig;
728+
729+
fn make_indexedlog(tempdir: &TempDir) -> Arc<IndexedLogHgIdDataStore> {
730+
let config = IndexedLogHgIdDataStoreConfig {
731+
max_log_count: None,
732+
max_bytes_per_log: None,
733+
max_bytes: None,
734+
btrfs_compression: false,
735+
};
736+
Arc::new(
737+
IndexedLogHgIdDataStore::new(
738+
&BTreeMap::<&str, &str>::new(),
739+
tempdir,
740+
&config,
741+
StoreType::Rotated,
742+
SerializationFormat::Hg,
743+
)
744+
.unwrap(),
745+
)
746+
}
747+
748+
#[test]
749+
fn test_skip_lfs_skips_lfs_fetch() {
750+
let il_dir = TempDir::new().unwrap();
751+
let indexedlog = make_indexedlog(&il_dir);
752+
753+
let lfs_key = Key::new(
754+
RepoPathBuf::from_string("large.bin".to_string()).unwrap(),
755+
HgId::from_hex(b"2222222222222222222222222222222222222222").unwrap(),
756+
);
757+
let content = Bytes::from_static(b"large file content here");
758+
759+
// Write file content to indexedlog with LFS flag set.
760+
let lfs_meta = Metadata {
761+
flags: Some(Metadata::LFS_FLAG),
762+
size: None,
763+
};
764+
indexedlog
765+
.put_entry(Entry::new(lfs_key.hgid, content.clone(), lfs_meta))
766+
.unwrap();
767+
768+
// Set up LFS store with the actual blob.
769+
let lfs_dir = TempDir::new().unwrap();
770+
let server = mockito::Server::new();
771+
let lfs_config = crate::testutil::make_lfs_config(&server, &lfs_dir, "skip_lfs");
772+
let lfs_store = Arc::new(crate::lfs::LfsStore::rotated(&lfs_dir, &lfs_config).unwrap());
773+
lfs_store
774+
.add_blob_and_pointer(lfs_key.clone(), content)
775+
.unwrap();
776+
lfs_store.flush().unwrap();
777+
let lfs_client = crate::lfs::LfsClient::new(lfs_store, None, &lfs_config).unwrap();
778+
779+
let make_store = || {
780+
let mut store = FileStore::empty();
781+
store.indexedlog_local = Some(indexedlog.clone());
782+
store.lfs_client = Some(lfs_client.clone());
783+
store.lfs_threshold_bytes = Some(1);
784+
store
785+
};
786+
787+
// Without skip_lfs, the LFS key resolves successfully.
788+
let fctx = FetchContext::new_with_mode_and_cause(
789+
FetchMode::LocalOnly,
790+
FetchCause::EdenWalkPrefetch,
791+
);
792+
let results: Vec<_> = make_store()
793+
.fetch(fctx, vec![lfs_key.clone()], FileAttributes::CONTENT)
794+
.into_iter()
795+
.collect();
796+
assert!(
797+
results[0].is_ok(),
798+
"LFS key should resolve without skip_lfs"
799+
);
800+
801+
// With skip_lfs, the LFS fetch is skipped so the key is not found.
802+
let fctx = FetchContext::new_with_mode_and_cause(
803+
FetchMode::LocalOnly,
804+
FetchCause::EdenWalkPrefetch,
805+
)
806+
.with_skip_lfs(true);
807+
let results: Vec<_> = make_store()
808+
.fetch(fctx, vec![lfs_key.clone()], FileAttributes::CONTENT)
809+
.into_iter()
810+
.collect();
811+
assert!(
812+
results[0].is_err(),
813+
"LFS key should not be found when skip_lfs=true"
814+
);
815+
}
816+
}

0 commit comments

Comments
 (0)