gix_worktree_state/checkout/
function.rs

1use std::sync::atomic::AtomicBool;
2
3use gix_features::{interrupt, parallel::in_parallel_with_finalize};
4use gix_worktree::{stack, Stack};
5
6use crate::checkout::chunk;
7
8/// Checkout the entire `index` into `dir`, and resolve objects found in index entries with `objects` to write their content to their
9/// respective path in `dir`.
10/// Use `files` to count each fully checked out file, and count the amount written `bytes`. If `should_interrupt` is `true`, the
11/// operation will abort.
12/// `options` provide a lot of context on how to perform the operation.
13///
14/// ### Handling the return value
15///
16/// Note that interruption still produce an `Ok(…)` value, so the caller should look at `should_interrupt` to communicate the outcome.
17///
18#[allow(clippy::too_many_arguments)]
19pub fn checkout<Find>(
20    index: &mut gix_index::State,
21    dir: impl Into<std::path::PathBuf>,
22    objects: Find,
23    files: &dyn gix_features::progress::Count,
24    bytes: &dyn gix_features::progress::Count,
25    should_interrupt: &AtomicBool,
26    options: crate::checkout::Options,
27) -> Result<crate::checkout::Outcome, crate::checkout::Error>
28where
29    Find: gix_object::Find + Send + Clone,
30{
31    let paths = index.take_path_backing();
32    let res = checkout_inner(index, &paths, dir, objects, files, bytes, should_interrupt, options);
33    index.return_path_backing(paths);
34    res
35}
36
37#[allow(clippy::too_many_arguments)]
38fn checkout_inner<Find>(
39    index: &mut gix_index::State,
40    paths: &gix_index::PathStorage,
41    dir: impl Into<std::path::PathBuf>,
42    objects: Find,
43    files: &dyn gix_features::progress::Count,
44    bytes: &dyn gix_features::progress::Count,
45    should_interrupt: &AtomicBool,
46    mut options: crate::checkout::Options,
47) -> Result<crate::checkout::Outcome, crate::checkout::Error>
48where
49    Find: gix_object::Find + Send + Clone,
50{
51    let num_files = files.counter();
52    let num_bytes = bytes.counter();
53    let dir = dir.into();
54    let (chunk_size, thread_limit, num_threads) = gix_features::parallel::optimize_chunk_size_and_thread_limit(
55        100,
56        index.entries().len().into(),
57        options.thread_limit,
58        None,
59    );
60
61    let mut ctx = chunk::Context {
62        buf: Vec::new(),
63        options: (&options).into(),
64        path_cache: Stack::from_state_and_ignore_case(
65            dir,
66            options.fs.ignore_case,
67            stack::State::for_checkout(
68                options.overwrite_existing,
69                options.validate,
70                std::mem::take(&mut options.attributes),
71            ),
72            index,
73            paths,
74        ),
75        filters: options.filters,
76        objects,
77    };
78
79    let chunk::Outcome {
80        mut collisions,
81        mut errors,
82        mut bytes_written,
83        files: files_updated,
84        delayed_symlinks,
85        delayed_paths_unknown,
86        delayed_paths_unprocessed,
87    } = if num_threads == 1 {
88        let entries_with_paths = interrupt::Iter::new(index.entries_mut_with_paths_in(paths), should_interrupt);
89        let mut delayed_filter_results = Vec::new();
90        let mut out = chunk::process(
91            entries_with_paths,
92            &num_files,
93            &num_bytes,
94            &mut delayed_filter_results,
95            &mut ctx,
96        )?;
97        chunk::process_delayed_filter_results(delayed_filter_results, &num_files, &num_bytes, &mut out, &mut ctx)?;
98        out
99    } else {
100        let entries_with_paths = interrupt::Iter::new(index.entries_mut_with_paths_in(paths), should_interrupt);
101        in_parallel_with_finalize(
102            gix_features::iter::Chunks {
103                inner: entries_with_paths,
104                size: chunk_size,
105            },
106            thread_limit,
107            {
108                let ctx = ctx.clone();
109                move |_| (Vec::new(), ctx)
110            },
111            |chunk, (delayed_filter_results, ctx)| {
112                chunk::process(chunk.into_iter(), &num_files, &num_bytes, delayed_filter_results, ctx)
113            },
114            |(delayed_filter_results, mut ctx)| {
115                let mut out = chunk::Outcome::default();
116                chunk::process_delayed_filter_results(
117                    delayed_filter_results,
118                    &num_files,
119                    &num_bytes,
120                    &mut out,
121                    &mut ctx,
122                )?;
123                Ok(out)
124            },
125            chunk::Reduce {
126                aggregate: Default::default(),
127            },
128        )?
129    };
130
131    for (entry, entry_path) in delayed_symlinks {
132        bytes_written += chunk::checkout_entry_handle_result(
133            entry,
134            entry_path,
135            &mut errors,
136            &mut collisions,
137            &num_files,
138            &num_bytes,
139            &mut ctx,
140        )?
141        .as_bytes()
142        .expect("only symlinks are delayed here, they are never filtered (or delayed again)")
143            as u64;
144    }
145
146    Ok(crate::checkout::Outcome {
147        files_updated,
148        collisions,
149        errors,
150        bytes_written,
151        delayed_paths_unknown,
152        delayed_paths_unprocessed,
153    })
154}