Struct git_worktree::fs::Cache

source ·
pub struct Cache<'paths> { /* private fields */ }
Expand description

A cache for efficiently executing operations on directories and files which are encountered in sorted order. That way, these operations can be re-used for subsequent invocations in the same directory.

This cache can be configured to create directories efficiently, read git-ignore files and git-attribute files, in any combination.

A cache for directory creation to reduce the amount of stat calls when creating directories safely, that is without following symlinks that might be on the way.

As a special case, it offers a ‘prefix’ which (by itself) is assumed to exist and may contain symlinks. Everything past that prefix boundary must not contain a symlink. We do this by allowing any input path.

Another added benefit is its ability to store the path of full path of the entry to which leading directories are to be created to avoid allocating memory.

For this to work, it remembers the last ‘good’ path to a directory and assumes that all components of it are still valid, too. As directories are created, the cache will be adjusted to reflect the latest seen directory.

The caching is only useful if consecutive calls to create a directory are using a sorted list of entries.

Implementations§

Available on debug-assertions enabled only.
Available on debug-assertions enabled only.
Available on debug-assertions enabled only.
Available on debug-assertions enabled only.

Create a new instance with worktree_root being the base for all future paths we handle, assuming it to be valid which includes symbolic links to be included in it as well. The case configures attribute and exclusion query case sensitivity.

Examples found in repository?
src/index/mod.rs (line 66)
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
fn checkout_inner<Find, E>(
    index: &mut git_index::State,
    paths: &git_index::PathStorage,
    dir: impl Into<std::path::PathBuf>,
    find: Find,
    files: &mut impl Progress,
    bytes: &mut impl Progress,
    should_interrupt: &AtomicBool,
    options: checkout::Options,
) -> Result<checkout::Outcome, checkout::Error<E>>
where
    Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, E> + Send + Clone,
    E: std::error::Error + Send + Sync + 'static,
{
    let num_files = AtomicUsize::default();
    let dir = dir.into();
    let case = if options.fs.ignore_case {
        git_glob::pattern::Case::Fold
    } else {
        git_glob::pattern::Case::Sensitive
    };
    let (chunk_size, thread_limit, num_threads) = git_features::parallel::optimize_chunk_size_and_thread_limit(
        100,
        index.entries().len().into(),
        options.thread_limit,
        None,
    );

    let state = fs::cache::State::for_checkout(options.overwrite_existing, options.attribute_globals.clone().into());
    let attribute_files = state.build_attribute_list(index, paths, case);
    let mut ctx = chunk::Context {
        buf: Vec::new(),
        path_cache: fs::Cache::new(dir, state, case, Vec::with_capacity(512), attribute_files),
        find,
        options,
        num_files: &num_files,
    };

    let chunk::Outcome {
        mut collisions,
        mut errors,
        mut bytes_written,
        delayed,
    } = if num_threads == 1 {
        let entries_with_paths = interrupt::Iter::new(index.entries_mut_with_paths_in(paths), should_interrupt);
        chunk::process(entries_with_paths, files, bytes, &mut ctx)?
    } else {
        let entries_with_paths = interrupt::Iter::new(index.entries_mut_with_paths_in(paths), should_interrupt);
        in_parallel(
            git_features::iter::Chunks {
                inner: entries_with_paths,
                size: chunk_size,
            },
            thread_limit,
            {
                let ctx = ctx.clone();
                move |_| (progress::Discard, progress::Discard, ctx.clone())
            },
            |chunk, (files, bytes, ctx)| chunk::process(chunk.into_iter(), files, bytes, ctx),
            chunk::Reduce {
                files,
                bytes,
                num_files: &num_files,
                aggregate: Default::default(),
                marker: Default::default(),
            },
        )?
    };

    for (entry, entry_path) in delayed {
        bytes_written += chunk::checkout_entry_handle_result(
            entry,
            entry_path,
            &mut errors,
            &mut collisions,
            files,
            bytes,
            &mut ctx,
        )? as u64;
    }

    Ok(checkout::Outcome {
        files_updated: num_files.load(Ordering::Relaxed),
        collisions,
        errors,
        bytes_written,
    })
}

Append the relative path to the root directory the cache contains and efficiently create leading directories unless is_dir is known (Some(…)) then relative points to a directory itself in which case the entire resulting path is created as directory. If it’s not known it is assumed to be a file.

Provide access to cached information for that relative entry via the platform returned.

Examples found in repository?
src/fs/cache/mod.rs (lines 130-135)
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
    pub fn at_entry<'r, Find, E>(
        &mut self,
        relative: impl Into<&'r BStr>,
        is_dir: Option<bool>,
        find: Find,
    ) -> std::io::Result<Platform<'_, 'paths>>
    where
        Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, E>,
        E: std::error::Error + Send + Sync + 'static,
    {
        let relative = relative.into();
        let relative_path = git_path::from_bstr(relative);

        self.at_path(
            relative_path,
            is_dir.or_else(|| relative.ends_with_str("/").then(|| true)),
            // is_dir,
            find,
        )
    }
More examples
Hide additional examples
src/index/entry.rs (line 40)
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
pub fn checkout<Find, E>(
    entry: &mut Entry,
    entry_path: &BStr,
    Context { find, path_cache, buf }: Context<'_, '_, Find>,
    index::checkout::Options {
        fs: fs::Capabilities {
            symlink,
            executable_bit,
            ..
        },
        destination_is_initially_empty,
        overwrite_existing,
        ..
    }: index::checkout::Options,
) -> Result<usize, index::checkout::Error<E>>
where
    Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, E>,
    E: std::error::Error + Send + Sync + 'static,
{
    let dest_relative = git_path::try_from_bstr(entry_path).map_err(|_| index::checkout::Error::IllformedUtf8 {
        path: entry_path.to_owned(),
    })?;
    let is_dir = Some(entry.mode == git_index::entry::Mode::COMMIT || entry.mode == git_index::entry::Mode::DIR);
    let dest = path_cache.at_path(dest_relative, is_dir, &mut *find)?.path();

    let object_size = match entry.mode {
        git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => {
            let obj = find(&entry.id, buf).map_err(|err| index::checkout::Error::Find {
                err,
                oid: entry.id,
                path: dest.to_path_buf(),
            })?;

            #[cfg_attr(not(unix), allow(unused_mut))]
            let mut options = open_options(dest, destination_is_initially_empty, overwrite_existing);
            let needs_executable_bit = executable_bit && entry.mode == git_index::entry::Mode::FILE_EXECUTABLE;
            #[cfg(unix)]
            if needs_executable_bit && destination_is_initially_empty {
                use std::os::unix::fs::OpenOptionsExt;
                // Note that these only work if the file was newly created, but won't if it's already
                // existing, possibly without the executable bit set. Thus we do this only if the file is new.
                options.mode(0o777);
            }

            let mut file = try_write_or_unlink(dest, overwrite_existing, |p| options.open(p))?;
            file.write_all(obj.data)?;

            // For possibly existing, overwritten files, we must change the file mode explicitly.
            #[cfg(unix)]
            if needs_executable_bit && !destination_is_initially_empty {
                use std::os::unix::fs::PermissionsExt;
                let mut perm = std::fs::symlink_metadata(dest)?.permissions();
                perm.set_mode(0o777);
                std::fs::set_permissions(dest, perm)?;
            }
            // NOTE: we don't call `file.sync_all()` here knowing that some filesystems don't handle this well.
            //       revisit this once there is a bug to fix.
            update_fstat(entry, file.metadata()?)?;
            file.close()?;
            obj.data.len()
        }
        git_index::entry::Mode::SYMLINK => {
            let obj = find(&entry.id, buf).map_err(|err| index::checkout::Error::Find {
                err,
                oid: entry.id,
                path: dest.to_path_buf(),
            })?;
            let symlink_destination = git_path::try_from_byte_slice(obj.data)
                .map_err(|_| index::checkout::Error::IllformedUtf8 { path: obj.data.into() })?;

            if symlink {
                try_write_or_unlink(dest, overwrite_existing, |p| os::create_symlink(symlink_destination, p))?;
            } else {
                let mut file = try_write_or_unlink(dest, overwrite_existing, |p| {
                    open_options(p, destination_is_initially_empty, overwrite_existing).open(dest)
                })?;
                file.write_all(obj.data)?;
                file.close()?;
            }

            update_fstat(entry, std::fs::symlink_metadata(dest)?)?;
            obj.data.len()
        }
        git_index::entry::Mode::DIR => todo!(),
        git_index::entry::Mode::COMMIT => todo!(),
        _ => unreachable!(),
    };
    Ok(object_size)
}
source

pub fn at_entry<'r, Find, E>(
    &mut self,
    relative: impl Into<&'r BStr>,
    is_dir: Option<bool>,
    find: Find
) -> Result<Platform<'_, 'paths>>where
    Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<BlobRef<'a>, E>,
    E: Error + Send + Sync + 'static,

Panics on illformed UTF8 in relative

Return the base path against which all entries or paths should be relative to when querying.

Note that this path may not be canonicalized.

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.