Skip to content

Commit f2d6db1

Browse files
committed
An API and a test for replacement configuration (#364)
1 parent 6cb9ecc commit f2d6db1

File tree

6 files changed

+73
-6
lines changed

6 files changed

+73
-6
lines changed

git-odb/src/lib.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ pub struct Store {
100100
/// The source directory from which all content is loaded, and the central write lock for use when a directory refresh is needed.
101101
pub(crate) path: PathBuf,
102102

103+
/// A set of replacements that given a source OID return a destination OID. The vector is sorted.
104+
pub(crate) replacements: Vec<(git_hash::ObjectId, git_hash::ObjectId)>,
105+
103106
/// A list of indices keeping track of which slots are filled with data. These are usually, but not always, consecutive.
104107
pub(crate) index: ArcSwap<types::SlotMapIndex>,
105108

@@ -123,12 +126,19 @@ pub struct Store {
123126
}
124127

125128
/// Create a new cached handle to the object store with support for additional options.
126-
pub fn at_opts(objects_dir: impl Into<PathBuf>, options: store::init::Options) -> std::io::Result<Handle> {
127-
let handle = OwnShared::new(Store::at_opts(objects_dir, options)?).to_handle();
129+
///
130+
/// `replacements` is an iterator over pairs of old and new object ids for replacement support.
131+
/// This means that when asking for object `X`, one will receive object `X-replaced` given an iterator like `Some((X, X-replaced))`.
132+
pub fn at_opts(
133+
objects_dir: impl Into<PathBuf>,
134+
replacements: impl IntoIterator<Item = (git_hash::ObjectId, git_hash::ObjectId)>,
135+
options: store::init::Options,
136+
) -> std::io::Result<Handle> {
137+
let handle = OwnShared::new(Store::at_opts(objects_dir, replacements, options)?).to_handle();
128138
Ok(Cache::from(handle))
129139
}
130140

131141
/// Create a new cached handle to the object store.
132142
pub fn at(objects_dir: impl Into<PathBuf>) -> std::io::Result<Handle> {
133-
at_opts(objects_dir, Default::default())
143+
at_opts(objects_dir, Vec::new().into_iter(), Default::default())
134144
}

git-odb/src/store_impls/dynamic/access.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@ impl Store {
1515
pub fn use_multi_pack_index(&self) -> bool {
1616
self.use_multi_pack_index
1717
}
18+
19+
/// An iterator over replacements from object-ids `X` to `X-replaced` as `(X, X-replaced)`, sorted by the original id `X`.
20+
pub fn replacements(&self) -> impl Iterator<Item = (git_hash::ObjectId, git_hash::ObjectId)> + '_ {
21+
self.replacements.iter().cloned()
22+
}
1823
}

git-odb/src/store_impls/dynamic/handle.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ impl super::Store {
246246
super::Handle {
247247
store: self.clone(),
248248
refresh: RefreshMode::default(),
249+
ignore_replacements: false,
249250
token: Some(token),
250251
snapshot: RefCell::new(self.collect_snapshot()),
251252
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
@@ -260,6 +261,7 @@ impl super::Store {
260261
super::Handle {
261262
store: self.clone(),
262263
refresh: Default::default(),
264+
ignore_replacements: false,
263265
token: Some(token),
264266
snapshot: RefCell::new(self.collect_snapshot()),
265267
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
@@ -332,6 +334,7 @@ impl TryFrom<&super::Store> for super::Store {
332334
fn try_from(s: &super::Store) -> Result<Self, Self::Error> {
333335
super::Store::at_opts(
334336
s.path(),
337+
s.replacements(),
335338
crate::store::init::Options {
336339
slots: crate::store::init::Slots::Given(s.files.len().try_into().expect("BUG: too many slots")),
337340
object_hash: Default::default(),
@@ -367,6 +370,7 @@ where
367370
super::Handle {
368371
store: self.store.clone(),
369372
refresh: self.refresh,
373+
ignore_replacements: self.ignore_replacements,
370374
token: {
371375
let token = self.store.register_handle();
372376
match self.token.as_ref().expect("token is always set here ") {

git-odb/src/store_impls/dynamic/init.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ impl Store {
6363
/// Note that the `slots` isn't used for packs, these are included with their multi-index or index respectively.
6464
/// For example, In a repository with 250m objects and geometric packing one would expect 27 index/pack pairs,
6565
/// or a single multi-pack index.
66+
/// `replacements` is an iterator over pairs of old and new object ids for replacement support.
67+
/// This means that when asking for object `X`, one will receive object `X-replaced` given an iterator like `Some((X, X-replaced))`.
6668
pub fn at_opts(
6769
objects_dir: impl Into<PathBuf>,
70+
replacements: impl IntoIterator<Item = (git_hash::ObjectId, git_hash::ObjectId)>,
6871
Options {
6972
slots,
7073
object_hash,
@@ -97,8 +100,12 @@ impl Store {
97100
"Cannot use more than 1^15 slots",
98101
));
99102
}
103+
let mut replacements: Vec<_> = replacements.into_iter().collect();
104+
replacements.sort_by(|a, b| a.0.cmp(&b.0));
105+
100106
Ok(Store {
101107
write: Default::default(),
108+
replacements,
102109
path: objects_dir,
103110
files: Vec::from_iter(std::iter::repeat_with(MutableIndexAndPack::default).take(slot_count)),
104111
index: ArcSwap::new(Arc::new(SlotMapIndex::default())),

git-odb/src/store_impls/dynamic/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ where
1919
/// even more rare.
2020
pub max_recursion_depth: usize,
2121

22+
/// If true, replacements will not be performed even if these are available.
23+
pub ignore_replacements: bool,
24+
2225
pub(crate) token: Option<handle::Mode>,
2326
snapshot: RefCell<load_index::Snapshot>,
2427
}

git-odb/tests/odb/store/dynamic.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use git_hash::ObjectId;
12
use std::process::Command;
23

34
use git_odb::{store, Find, FindExt, Write};
@@ -205,13 +206,13 @@ fn write() -> crate::Result {
205206
}
206207

207208
#[test]
209+
#[ignore]
208210
fn object_replacement() {
209211
let dir = git_testtools::scripted_fixture_repo_read_only("make_replaced_history.sh").unwrap();
210212
let handle = git_odb::at(dir.join(".git/objects")).unwrap();
211213
let mut buf = Vec::new();
212-
let third_commit = handle
213-
.find_commit(hex_to_id("434e5a872d6738d1fffd1e11e52a1840b73668c6"), &mut buf)
214-
.unwrap();
214+
let short_history_link = hex_to_id("434e5a872d6738d1fffd1e11e52a1840b73668c6");
215+
let third_commit = handle.find_commit(short_history_link, &mut buf).unwrap();
215216

216217
let orphan_of_new_history = hex_to_id("0703c317e28068f39834ae61e7ab941b7d672322");
217218
assert_eq!(
@@ -223,6 +224,43 @@ fn object_replacement() {
223224
drop(third_commit);
224225
let orphan = handle.find_commit(orphan_of_new_history, &mut buf).unwrap();
225226
assert_eq!(orphan.parents.len(), 0);
227+
228+
let long_history_tip = hex_to_id("71f537d9d78bf6ae89a29a17e54b95a914d3d2ef");
229+
let unrelated_mapping = (
230+
ObjectId::null(handle.store_ref().object_hash()),
231+
ObjectId::null(handle.store_ref().object_hash()),
232+
);
233+
234+
let mut handle = git_odb::at_opts(
235+
dir.join(".git/objects"),
236+
vec![(short_history_link, long_history_tip), unrelated_mapping],
237+
git_odb::store::init::Options { ..Default::default() },
238+
)
239+
.unwrap();
240+
drop(orphan);
241+
242+
let replaced = handle.find_commit(short_history_link, &mut buf).unwrap();
243+
let long_history_second_id = hex_to_id("753ccf815e7b69c9147db5bbf633fe5f7da24ad7");
244+
assert_eq!(
245+
replaced.parents().collect::<Vec<_>>(),
246+
vec![long_history_second_id],
247+
"replacements are applied by default when present"
248+
);
249+
250+
handle.ignore_replacements = true;
251+
drop(replaced);
252+
let not_replaced = handle.find_commit(short_history_link, &mut buf).unwrap();
253+
assert_eq!(
254+
not_replaced.parents().collect::<Vec<_>>(),
255+
vec![orphan_of_new_history],
256+
"no replacements are made if explicitly disabled"
257+
);
258+
259+
assert_eq!(
260+
handle.store_ref().replacements().collect::<Vec<_>>(),
261+
vec![unrelated_mapping, (short_history_link, long_history_tip)],
262+
"one can query the list of replacements"
263+
);
226264
}
227265

228266
#[test]

0 commit comments

Comments
 (0)