git_index/
init.rs

1mod from_tree {
2    use std::collections::VecDeque;
3
4    use bstr::{BStr, BString, ByteSlice, ByteVec};
5    use git_object::{
6        tree::{self, EntryMode},
7        TreeRefIter,
8    };
9    use git_traverse::tree::{breadthfirst, visit::Action, Visit};
10
11    use crate::{
12        entry::{Flags, Mode, Stat},
13        Entry, PathStorage, State, Version,
14    };
15
16    /// Initialization
17    impl State {
18        /// Return a new and empty in-memory index assuming the given `object_hash`.
19        pub fn new(object_hash: git_hash::Kind) -> Self {
20            State {
21                object_hash,
22                timestamp: filetime::FileTime::now(),
23                version: Version::V2,
24                entries: vec![],
25                path_backing: vec![],
26                is_sparse: false,
27                tree: None,
28                link: None,
29                resolve_undo: None,
30                untracked: None,
31                fs_monitor: None,
32            }
33        }
34        /// Create an index [`State`][crate::State] by traversing `tree` recursively, accessing sub-trees
35        /// with `find`.
36        ///
37        /// **No extension data is currently produced**.
38        pub fn from_tree<Find>(tree: &git_hash::oid, mut find: Find) -> Result<Self, breadthfirst::Error>
39        where
40            Find: for<'a> FnMut(&git_hash::oid, &'a mut Vec<u8>) -> Option<TreeRefIter<'a>>,
41        {
42            let mut buf = Vec::new();
43            let root = find(tree, &mut buf).ok_or(breadthfirst::Error::NotFound { oid: tree.into() })?;
44
45            let mut delegate = CollectEntries::new();
46            breadthfirst(root, breadthfirst::State::default(), &mut find, &mut delegate)?;
47
48            let CollectEntries {
49                mut entries,
50                path_backing,
51                path: _,
52                path_deque: _,
53            } = delegate;
54
55            entries.sort_by(|a, b| Entry::cmp_filepaths(a.path_in(&path_backing), b.path_in(&path_backing)));
56
57            Ok(State {
58                object_hash: tree.kind(),
59                timestamp: filetime::FileTime::now(),
60                version: Version::V2,
61                entries,
62                path_backing,
63                is_sparse: false,
64                tree: None,
65                link: None,
66                resolve_undo: None,
67                untracked: None,
68                fs_monitor: None,
69            })
70        }
71    }
72
73    struct CollectEntries {
74        entries: Vec<Entry>,
75        path_backing: PathStorage,
76        path: BString,
77        path_deque: VecDeque<BString>,
78    }
79
80    impl CollectEntries {
81        pub fn new() -> CollectEntries {
82            CollectEntries {
83                entries: Vec::new(),
84                path_backing: Vec::new(),
85                path: BString::default(),
86                path_deque: VecDeque::new(),
87            }
88        }
89
90        fn push_element(&mut self, name: &BStr) {
91            if !self.path.is_empty() {
92                self.path.push(b'/');
93            }
94            self.path.push_str(name);
95        }
96
97        pub fn add_entry(&mut self, entry: &tree::EntryRef<'_>) {
98            let mode = match entry.mode {
99                EntryMode::Tree => unreachable!("visit_non_tree() called us"),
100                EntryMode::Blob => Mode::FILE,
101                EntryMode::BlobExecutable => Mode::FILE_EXECUTABLE,
102                EntryMode::Link => Mode::SYMLINK,
103                EntryMode::Commit => Mode::COMMIT,
104            };
105
106            let path_start = self.path_backing.len();
107            self.path_backing.extend_from_slice(&self.path);
108
109            let new_entry = Entry {
110                stat: Stat::default(),
111                id: entry.oid.into(),
112                flags: Flags::empty(),
113                mode,
114                path: path_start..self.path_backing.len(),
115            };
116
117            self.entries.push(new_entry);
118        }
119    }
120
121    impl Visit for CollectEntries {
122        fn pop_front_tracked_path_and_set_current(&mut self) {
123            self.path = self
124                .path_deque
125                .pop_front()
126                .expect("every call is matched with push_tracked_path_component");
127        }
128
129        fn push_back_tracked_path_component(&mut self, component: &bstr::BStr) {
130            self.push_element(component);
131            self.path_deque.push_back(self.path.clone());
132        }
133
134        fn push_path_component(&mut self, component: &bstr::BStr) {
135            self.push_element(component);
136        }
137
138        fn pop_path_component(&mut self) {
139            if let Some(pos) = self.path.rfind_byte(b'/') {
140                self.path.resize(pos, 0);
141            } else {
142                self.path.clear();
143            }
144        }
145
146        fn visit_tree(&mut self, _entry: &git_object::tree::EntryRef<'_>) -> git_traverse::tree::visit::Action {
147            Action::Continue
148        }
149
150        fn visit_nontree(&mut self, entry: &git_object::tree::EntryRef<'_>) -> git_traverse::tree::visit::Action {
151            self.add_entry(entry);
152            Action::Continue
153        }
154    }
155}