Skip to content

Commit 8920229

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

File tree

31 files changed

+496
-524
lines changed

31 files changed

+496
-524
lines changed

gitoxide-core/src/index/checkout.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
};
55

66
use anyhow::bail;
7-
use gix::{odb::FindExt, worktree::index::checkout, Progress};
7+
use gix::{odb::FindExt, worktree::checkout, Progress};
88

99
use crate::{
1010
index,
@@ -55,7 +55,7 @@ pub fn checkout_exclusive(
5555
progress.info(format!("Skipping {} DIR/SYMLINK/COMMIT entries", num_skipped));
5656
}
5757

58-
let opts = gix::worktree::index::checkout::Options {
58+
let opts = gix::worktree::checkout::Options {
5959
fs: gix::fs::Capabilities::probe(dest_directory),
6060

6161
destination_is_initially_empty: true,
@@ -80,7 +80,7 @@ pub fn checkout_exclusive(
8080
files_updated,
8181
bytes_written,
8282
} = match repo {
83-
Some(repo) => gix::worktree::index::checkout(
83+
Some(repo) => gix::worktree::checkout(
8484
&mut index,
8585
dest_directory,
8686
{
@@ -103,7 +103,7 @@ pub fn checkout_exclusive(
103103
should_interrupt,
104104
opts,
105105
),
106-
None => gix::worktree::index::checkout(
106+
None => gix::worktree::checkout(
107107
&mut index,
108108
dest_directory,
109109
|_, buf| {

gitoxide-core/src/repository/clone.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub(crate) mod function {
9898
}
9999
};
100100

101-
if let Some(gix::worktree::index::checkout::Outcome { collisions, errors, .. }) = outcome {
101+
if let Some(gix::worktree::checkout::Outcome { collisions, errors, .. }) = outcome {
102102
if !(collisions.is_empty() && errors.is_empty()) {
103103
let mut messages = Vec::new();
104104
if !errors.is_empty() {

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/checkout/chunk.rs

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::checkout::entry;
8+
use crate::{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::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(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/checkout/entry.rs

Lines changed: 9 additions & 9 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::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

@@ -19,7 +19,7 @@ pub fn checkout<Find, E>(
1919
entry: &mut Entry,
2020
entry_path: &BStr,
2121
Context { find, path_cache, buf }: Context<'_, Find>,
22-
index::checkout::Options {
22+
crate::checkout::Options {
2323
fs: gix_fs::Capabilities {
2424
symlink,
2525
executable_bit,
@@ -28,21 +28,21 @@ pub fn checkout<Find, E>(
2828
destination_is_initially_empty,
2929
overwrite_existing,
3030
..
31-
}: index::checkout::Options,
32-
) -> Result<usize, index::checkout::Error<E>>
31+
}: crate::checkout::Options,
32+
) -> Result<usize, crate::checkout::Error<E>>
3333
where
3434
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E>,
3535
E: std::error::Error + Send + Sync + 'static,
3636
{
37-
let dest_relative = gix_path::try_from_bstr(entry_path).map_err(|_| index::checkout::Error::IllformedUtf8 {
37+
let dest_relative = gix_path::try_from_bstr(entry_path).map_err(|_| crate::checkout::Error::IllformedUtf8 {
3838
path: entry_path.to_owned(),
3939
})?;
4040
let is_dir = Some(entry.mode == gix_index::entry::Mode::COMMIT || entry.mode == gix_index::entry::Mode::DIR);
4141
let dest = path_cache.at_path(dest_relative, is_dir, &mut *find)?.path();
4242

4343
let object_size = match entry.mode {
4444
gix_index::entry::Mode::FILE | gix_index::entry::Mode::FILE_EXECUTABLE => {
45-
let obj = find(&entry.id, buf).map_err(|err| index::checkout::Error::Find {
45+
let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
4646
err,
4747
oid: entry.id,
4848
path: dest.to_path_buf(),
@@ -77,13 +77,13 @@ where
7777
obj.data.len()
7878
}
7979
gix_index::entry::Mode::SYMLINK => {
80-
let obj = find(&entry.id, buf).map_err(|err| index::checkout::Error::Find {
80+
let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
8181
err,
8282
oid: entry.id,
8383
path: dest.to_path_buf(),
8484
})?;
8585
let symlink_destination = gix_path::try_from_byte_slice(obj.data)
86-
.map_err(|_| index::checkout::Error::IllformedUtf8 { path: obj.data.into() })?;
86+
.map_err(|_| crate::checkout::Error::IllformedUtf8 { path: obj.data.into() })?;
8787

8888
if symlink {
8989
try_write_or_unlink(dest, overwrite_existing, |p| {

0 commit comments

Comments
 (0)