Skip to content

Commit 0f9c475

Browse files
committed
Minor adjustments to the worktree structure.
1 parent f8cc33c commit 0f9c475

File tree

23 files changed

+436
-443
lines changed

23 files changed

+436
-443
lines changed

gix-worktree/src/fs/cache/mod.rs renamed to gix-worktree/src/cache/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bstr::{BStr, ByteSlice};
55
use gix_hash::oid;
66

77
use super::Cache;
8-
use crate::fs::PathOidMapping;
8+
use crate::PathOidMapping;
99

1010
#[derive(Clone)]
1111
pub enum State {

gix-worktree/src/fs/cache/platform.rs renamed to gix-worktree/src/cache/platform.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::path::Path;
33
use bstr::ByteSlice;
44
use gix_hash::oid;
55

6-
use crate::fs::{
6+
use crate::{
77
cache::{Platform, State},
88
PathOidMapping,
99
};

gix-worktree/src/fs/cache/state/attributes.rs renamed to gix-worktree/src/cache/state/attributes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::fs::cache::state::{AttributeMatchGroup, Attributes};
1+
use crate::cache::state::{AttributeMatchGroup, Attributes};
22
use gix_glob::pattern::Case;
33
use std::path::PathBuf;
44

55
/// Decide where to read `.gitattributes` files from.
66
#[derive(Default, Debug, Clone, Copy)]
77
pub enum Source {
88
/// Retrieve attribute files from an attribute list, see
9-
/// [State::attribute_list_from_index()][crate::fs::cache::State::attribute_list_from_index()].
9+
/// [State::attribute_list_from_index()][crate::cache::State::attribute_list_from_index()].
1010
///
1111
/// The attribute list is typically produced from an index. If a tree should be the source, build an attribute list
1212
/// from a tree instead.

gix-worktree/src/fs/cache/state/ignore.rs renamed to gix-worktree/src/cache/state/ignore.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::fs::cache::state::IgnoreMatchGroup;
2-
use crate::fs::PathOidMapping;
1+
use crate::cache::state::IgnoreMatchGroup;
2+
use crate::PathOidMapping;
33
use bstr::{BStr, BString, ByteSlice};
44
use gix_glob::pattern::Case;
55
use gix_hash::oid;

gix-worktree/src/fs/cache/state/mod.rs renamed to gix-worktree/src/cache/state/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use bstr::ByteSlice;
22
use gix_glob::pattern::Case;
33
use std::path::PathBuf;
44

5-
use crate::fs::{cache::State, PathOidMapping};
5+
use crate::{cache::State, PathOidMapping};
66

77
type AttributeMatchGroup = gix_attributes::Search;
88
type IgnoreMatchGroup = gix_ignore::Search;

gix-worktree/src/fs/mod.rs

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use std::sync::atomic::{AtomicUsize, Ordering};
2+
3+
use bstr::BStr;
4+
use gix_features::progress::Progress;
5+
use gix_hash::oid;
6+
7+
use crate::index::checkout::entry;
8+
use crate::{index, index::checkout, Cache};
9+
10+
mod reduce {
11+
use std::{
12+
marker::PhantomData,
13+
sync::atomic::{AtomicUsize, Ordering},
14+
};
15+
16+
use gix_features::progress::Progress;
17+
18+
use crate::index::checkout;
19+
20+
pub struct Reduce<'a, 'entry, P1, P2, E> {
21+
pub files: &'a mut P1,
22+
pub bytes: &'a mut P2,
23+
pub num_files: &'a AtomicUsize,
24+
pub aggregate: super::Outcome<'entry>,
25+
pub marker: PhantomData<E>,
26+
}
27+
28+
impl<'a, 'entry, P1, P2, E> gix_features::parallel::Reduce for Reduce<'a, 'entry, P1, P2, E>
29+
where
30+
P1: Progress,
31+
P2: Progress,
32+
E: std::error::Error + Send + Sync + 'static,
33+
{
34+
type Input = Result<super::Outcome<'entry>, checkout::Error<E>>;
35+
type FeedProduce = ();
36+
type Output = super::Outcome<'entry>;
37+
type Error = checkout::Error<E>;
38+
39+
fn feed(&mut self, item: Self::Input) -> Result<Self::FeedProduce, Self::Error> {
40+
let item = item?;
41+
let super::Outcome {
42+
bytes_written,
43+
delayed,
44+
errors,
45+
collisions,
46+
} = item;
47+
self.aggregate.bytes_written += bytes_written;
48+
self.aggregate.delayed.extend(delayed);
49+
self.aggregate.errors.extend(errors);
50+
self.aggregate.collisions.extend(collisions);
51+
52+
self.bytes.set(self.aggregate.bytes_written as usize);
53+
self.files.set(self.num_files.load(Ordering::Relaxed));
54+
55+
Ok(())
56+
}
57+
58+
fn finalize(self) -> Result<Self::Output, Self::Error> {
59+
Ok(self.aggregate)
60+
}
61+
}
62+
}
63+
pub use reduce::Reduce;
64+
65+
#[derive(Default)]
66+
pub struct Outcome<'a> {
67+
pub collisions: Vec<checkout::Collision>,
68+
pub errors: Vec<checkout::ErrorRecord>,
69+
pub delayed: Vec<(&'a mut gix_index::Entry, &'a BStr)>,
70+
pub bytes_written: u64,
71+
}
72+
73+
#[derive(Clone)]
74+
pub struct Context<'a, Find: Clone> {
75+
pub find: Find,
76+
pub path_cache: Cache,
77+
pub buf: Vec<u8>,
78+
pub options: checkout::Options,
79+
/// We keep these shared so that there is the chance for printing numbers that aren't looking like
80+
/// multiple of chunk sizes. Purely cosmetic. Otherwise it's the same as `files`.
81+
pub num_files: &'a AtomicUsize,
82+
}
83+
84+
pub fn process<'entry, Find, E>(
85+
entries_with_paths: impl Iterator<Item = (&'entry mut gix_index::Entry, &'entry BStr)>,
86+
files: &mut impl Progress,
87+
bytes: &mut impl Progress,
88+
ctx: &mut Context<'_, Find>,
89+
) -> Result<Outcome<'entry>, checkout::Error<E>>
90+
where
91+
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
92+
E: std::error::Error + Send + Sync + 'static,
93+
{
94+
let mut delayed = Vec::new();
95+
let mut collisions = Vec::new();
96+
let mut errors = Vec::new();
97+
let mut bytes_written = 0;
98+
99+
for (entry, entry_path) in entries_with_paths {
100+
// TODO: write test for that
101+
if entry.flags.contains(gix_index::entry::Flags::SKIP_WORKTREE) {
102+
files.inc();
103+
continue;
104+
}
105+
106+
// Symlinks always have to be delayed on windows as they have to point to something that exists on creation.
107+
// And even if not, there is a distinction between file and directory symlinks, hence we have to check what the target is
108+
// before creating it.
109+
// And to keep things sane, we just do the same on non-windows as well which is similar to what git does and adds some safety
110+
// around writing through symlinks (even though we handle this).
111+
// This also means that we prefer content in files over symlinks in case of collisions, which probably is for the better, too.
112+
if entry.mode == gix_index::entry::Mode::SYMLINK {
113+
delayed.push((entry, entry_path));
114+
continue;
115+
}
116+
117+
bytes_written +=
118+
checkout_entry_handle_result(entry, entry_path, &mut errors, &mut collisions, files, bytes, ctx)? as u64;
119+
}
120+
121+
Ok(Outcome {
122+
bytes_written,
123+
errors,
124+
collisions,
125+
delayed,
126+
})
127+
}
128+
129+
pub fn checkout_entry_handle_result<Find, E>(
130+
entry: &mut gix_index::Entry,
131+
entry_path: &BStr,
132+
errors: &mut Vec<checkout::ErrorRecord>,
133+
collisions: &mut Vec<checkout::Collision>,
134+
files: &mut impl Progress,
135+
bytes: &mut impl Progress,
136+
Context {
137+
find,
138+
path_cache,
139+
buf,
140+
options,
141+
num_files,
142+
}: &mut Context<'_, Find>,
143+
) -> Result<usize, checkout::Error<E>>
144+
where
145+
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
146+
E: std::error::Error + Send + Sync + 'static,
147+
{
148+
let res = entry::checkout(
149+
entry,
150+
entry_path,
151+
entry::Context { find, path_cache, buf },
152+
options.clone(),
153+
);
154+
files.inc();
155+
num_files.fetch_add(1, Ordering::SeqCst);
156+
match res {
157+
Ok(object_size) => {
158+
bytes.inc_by(object_size);
159+
Ok(object_size)
160+
}
161+
Err(index::checkout::Error::Io(err)) if gix_fs::symlink::is_collision_error(&err) => {
162+
// We are here because a file existed or was blocked by a directory which shouldn't be possible unless
163+
// we are on a file insensitive file system.
164+
files.fail(format!("{}: collided ({:?})", entry_path, err.kind()));
165+
collisions.push(checkout::Collision {
166+
path: entry_path.into(),
167+
error_kind: err.kind(),
168+
});
169+
Ok(0)
170+
}
171+
Err(err) => {
172+
if options.keep_going {
173+
errors.push(checkout::ErrorRecord {
174+
path: entry_path.into(),
175+
error: Box::new(err),
176+
});
177+
Ok(0)
178+
} else {
179+
Err(err)
180+
}
181+
}
182+
}
183+
}

gix-worktree/src/index/entry.rs renamed to gix-worktree/src/index/checkout/entry.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ use gix_index::entry::Stat;
66
use gix_index::Entry;
77
use io_close::Close;
88

9-
use crate::{fs, index};
9+
use crate::{index, Cache};
1010

1111
pub struct Context<'a, Find> {
1212
pub find: &'a mut Find,
13-
pub path_cache: &'a mut fs::Cache,
13+
pub path_cache: &'a mut Cache,
1414
pub buf: &'a mut Vec<u8>,
1515
}
1616

0 commit comments

Comments
 (0)