History

Merge branch 'status'

Sebastian Thiel committed 2 years ago

Commit 45edd2e2 parents 52efa90 2e06a0020 files · +136 −58Browse files

Unified · Split

20 files
  1. gitoxide-core/src/index/information.rs +5 −5
  2. gitoxide-core/src/repository/attributes/validate_baseline.rs +7 −0
  3. gitoxide-core/src/repository/index/entries.rs +5 −5
  4. gix-config/src/file/includes/mod.rs +8 −1
  5. gix-config/src/file/includes/types.rs +5 −2
  6. gix-index/src/access/mod.rs +10 −9
  7. gix-index/src/entry/flags.rs +17 −0
  8. gix-index/src/entry/mod.rs +27 −2
  9. gix-index/src/extension/fs_monitor.rs +1 −0
  10. gix-index/src/lib.rs +5 −5
  11. gix-index/tests/index/access.rs +10 −9
  12. gix-status/src/index_as_worktree/function.rs +4 −4
  13. gix-worktree/src/stack/platform.rs +1 −0
  14. gix-worktree/src/stack/state/mod.rs +1 −1
  15. gix/src/attribute_stack.rs +1 −0
  16. gix/src/repository/attributes.rs +1 −0
  17. gix/src/repository/index.rs +5 −0
  18. gix/src/revision/spec/parse/delegate/navigate.rs +14 −8
  19. gix/src/revision/spec/parse/types.rs +1 −1
  20. gix/src/status/index_worktree.rs +8 −6
gitoxide-core/src/index/information.rs+5−5
@@ -6,6 +6,7 @@ pub struct Options { · 5 unmodified lines
6 6
7#[cfg(feature = "serde")]7#[cfg(feature = "serde")]
8mod serde_only {8mod serde_only {
9 use gix::index::entry::Stage;
9 10
10 mod ext {11 mod ext {
11 #[derive(serde::Serialize)]12 #[derive(serde::Serialize)]
@@ -115,11 +116,10 @@ mod serde_only { · 103 unmodified lines
115 let (mut intent_to_add, mut skip_worktree) = (0, 0);116 let (mut intent_to_add, mut skip_worktree) = (0, 0);
116 for entry in f.entries() {117 for entry in f.entries() {
117 match entry.flags.stage() {118 match entry.flags.stage() {
118 0 => stage_0_merged += 1,119 Stage::Unconflicted => stage_0_merged += 1,
119 1 => stage_1_base += 1,120 Stage::Base => stage_1_base += 1,
120 2 => stage_2_ours += 1,121 Stage::Ours => stage_2_ours += 1,
121 3 => stage_3_theirs += 1,122 Stage::Theirs => stage_3_theirs += 1,
122 invalid => anyhow::bail!("Invalid stage {} encountered", invalid),
123 }123 }
124 match entry.mode {124 match entry.mode {
125 gix::index::entry::Mode::DIR => dir += 1,125 gix::index::entry::Mode::DIR => dir += 1,
gitoxide-core/src/repository/attributes/validate_baseline.rs+7−0
@@ -262,6 +262,8 @@ pub(crate) mod function { · 261 unmodified lines
262 }262 }
263 263
264 #[derive(Debug)]264 #[derive(Debug)]
265 // See note on `Mismatch`
266 #[allow(dead_code)]
265 pub struct ExcludeLocation {267 pub struct ExcludeLocation {
266 pub line: usize,268 pub line: usize,
267 pub rela_source_file: String,269 pub rela_source_file: String,
@@ -269,6 +271,9 @@ pub(crate) mod function { · 1 unmodified line
269 }271 }
270 272
271 #[derive(Debug)]273 #[derive(Debug)]
274 // We debug-print this structure, which makes all fields 'used', but it doesn't count.
275 // TODO: find a way to not have to do more work, but make the warning go away.
276 #[allow(dead_code)]
272 pub enum Mismatch {277 pub enum Mismatch {
273 Attributes {278 Attributes {
274 actual: Vec<gix::attrs::Assignment>,279 actual: Vec<gix::attrs::Assignment>,
@@ -281,6 +286,8 @@ pub(crate) mod function { · 6 unmodified lines
281 }286 }
282 287
283 #[derive(Debug)]288 #[derive(Debug)]
289 // See note on `Mismatch`
290 #[allow(dead_code)]
284 pub struct ExcludeMatch {291 pub struct ExcludeMatch {
285 pub pattern: gix::glob::Pattern,292 pub pattern: gix::glob::Pattern,
286 pub source: Option<PathBuf>,293 pub source: Option<PathBuf>,
gitoxide-core/src/repository/index/entries.rs+5−5
@@ -23,6 +23,7 @@ pub(crate) mod function { · 22 unmodified lines
23 io::{BufWriter, Write},23 io::{BufWriter, Write},
24 };24 };
25 25
26 use gix::index::entry::Stage;
26 use gix::{27 use gix::{
27 bstr::{BStr, BString},28 bstr::{BStr, BString},
28 worktree::IndexPersistedOrInMemory,29 worktree::IndexPersistedOrInMemory,
@@ -392,11 +393,10 @@ pub(crate) mod function { · 363 unmodified lines
392 out,393 out,
393 "{} {}{:?} {} {}{}{}",394 "{} {}{:?} {} {}{}{}",
394 match entry.flags.stage() {395 match entry.flags.stage() {
395 0 => " ",396 Stage::Unconflicted => " ",
396 1 => "BASE ",397 Stage::Base => "BASE ",
397 2 => "OURS ",398 Stage::Ours => "OURS ",
398 3 => "THEIRS ",399 Stage::Theirs => "THEIRS ",
399 _ => "UNKNOWN",
400 },400 },
401 if entry.flags.is_empty() {401 if entry.flags.is_empty() {
402 "".to_string()402 "".to_string()
gix-config/src/file/includes/mod.rs+8−1
@@ -99,7 +99,14 @@ fn append_followed_includes_recursively( · 98 unmodified lines
99 }99 }
100 100
101 buf.clear();101 buf.clear();
102 std::io::copy(&mut std::fs::File::open(&config_path)?, buf)?;102 std::io::copy(
103 &mut std::fs::File::open(&config_path).map_err(|err| Error::Io {
104 source: err,
105 path: config_path.to_owned(),
106 })?,
107 buf,
108 )
109 .map_err(Error::CopyBuffer)?;
103 let config_meta = Metadata {110 let config_meta = Metadata {
104 path: Some(config_path),111 path: Some(config_path),
105 trust: meta.trust,112 trust: meta.trust,
gix-config/src/file/includes/types.rs+5−2
@@ -1,11 +1,14 @@
1use crate::{parse, path::interpolate};1use crate::{parse, path::interpolate};
2use std::path::PathBuf;
2 3
3/// The error returned when following includes.4/// The error returned when following includes.
4#[derive(Debug, thiserror::Error)]5#[derive(Debug, thiserror::Error)]
5#[allow(missing_docs)]6#[allow(missing_docs)]
6pub enum Error {7pub enum Error {
7 #[error(transparent)]8 #[error("Failed to copy configuration file into buffer")]
8 Io(#[from] std::io::Error),9 CopyBuffer(#[source] std::io::Error),
10 #[error("Could not read included configuration file at '{}'", path.display())]
11 Io { path: PathBuf, source: std::io::Error },
9 #[error(transparent)]12 #[error(transparent)]
10 Parse(#[from] parse::Error),13 Parse(#[from] parse::Error),
11 #[error(transparent)]14 #[error(transparent)]
gix-index/src/access/mod.rs+10−9
@@ -3,6 +3,7 @@ use std::{cmp::Ordering, ops::Range}; · 2 unmodified lines
3use bstr::{BStr, ByteSlice, ByteVec};3use bstr::{BStr, ByteSlice, ByteVec};
4use filetime::FileTime;4use filetime::FileTime;
5 5
6use crate::entry::{Stage, StageRaw};
6use crate::{entry, extension, AccelerateLookup, Entry, PathStorage, PathStorageRef, State, Version};7use crate::{entry, extension, AccelerateLookup, Entry, PathStorage, PathStorageRef, State, Version};
7 8
8// TODO: integrate this somehow, somewhere, depending on later usage.9// TODO: integrate this somehow, somewhere, depending on later usage.
@@ -81,7 +82,7 @@ impl State { · 72 unmodified lines
81 res82 res
82 })83 })
83 .ok()?;84 .ok()?;
84 self.entry_index_by_idx_and_stage(path, idx, stage, stage_cmp)85 self.entry_index_by_idx_and_stage(path, idx, stage as StageRaw, stage_cmp)
85 }86 }
86 87
87 /// Walk as far in `direction` as possible, with [`Ordering::Greater`] towards higher stages, and [`Ordering::Less`]88 /// Walk as far in `direction` as possible, with [`Ordering::Greater`] towards higher stages, and [`Ordering::Less`]
@@ -112,7 +113,7 @@ impl State { · 24 unmodified lines
112 &self,113 &self,
113 path: &BStr,114 path: &BStr,
114 idx: usize,115 idx: usize,
115 wanted_stage: entry::Stage,116 wanted_stage: entry::StageRaw,
116 stage_cmp: Ordering,117 stage_cmp: Ordering,
117 ) -> Option<usize> {118 ) -> Option<usize> {
118 match stage_cmp {119 match stage_cmp {
@@ -121,7 +122,7 @@ impl State { · 2 unmodified lines
121 .enumerate()122 .enumerate()
122 .rev()123 .rev()
123 .take_while(|(_, e)| e.path(self) == path)124 .take_while(|(_, e)| e.path(self) == path)
124 .find_map(|(idx, e)| (e.stage() == wanted_stage).then_some(idx)),125 .find_map(|(idx, e)| (e.stage_raw() == wanted_stage).then_some(idx)),
125 Ordering::Equal => Some(idx),126 Ordering::Equal => Some(idx),
126 Ordering::Less => self127 Ordering::Less => self
127 .entries128 .entries
@@ -129,7 +130,7 @@ impl State { · 1 unmodified line
129 .iter()130 .iter()
130 .enumerate()131 .enumerate()
131 .take_while(|(_, e)| e.path(self) == path)132 .take_while(|(_, e)| e.path(self) == path)
132 .find_map(|(ofs, e)| (e.stage() == wanted_stage).then_some(idx + ofs + 1)),133 .find_map(|(ofs, e)| (e.stage_raw() == wanted_stage).then_some(idx + ofs + 1)),
133 }134 }
134 }135 }
135 136
@@ -291,7 +292,7 @@ impl State { · 155 unmodified lines
291 .binary_search_by(|e| {292 .binary_search_by(|e| {
292 let res = e.path(self).cmp(path);293 let res = e.path(self).cmp(path);
293 if res.is_eq() {294 if res.is_eq() {
294 stage_at_index = e.stage();295 stage_at_index = e.stage_raw();
295 }296 }
296 res297 res
297 })298 })
@@ -299,7 +300,7 @@ impl State { · 1 unmodified line
299 let idx = if stage_at_index == 0 || stage_at_index == 2 {300 let idx = if stage_at_index == 0 || stage_at_index == 2 {
300 idx301 idx
301 } else {302 } else {
302 self.entry_index_by_idx_and_stage(path, idx, 2, stage_at_index.cmp(&2))?303 self.entry_index_by_idx_and_stage(path, idx, Stage::Ours as StageRaw, stage_at_index.cmp(&2))?
303 };304 };
304 Some(&self.entries[idx])305 Some(&self.entries[idx])
305 }306 }
@@ -334,13 +335,13 @@ impl State { · 28 unmodified lines
334 + self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix));335 + self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix));
335 336
336 let low_entry = &self.entries.get(low)?;337 let low_entry = &self.entries.get(low)?;
337 if low_entry.stage() != 0 {338 if low_entry.stage_raw() != 0 {
338 low = self339 low = self
339 .walk_entry_stages(low_entry.path(self), low, Ordering::Less)340 .walk_entry_stages(low_entry.path(self), low, Ordering::Less)
340 .unwrap_or(low);341 .unwrap_or(low);
341 }342 }
342 if let Some(high_entry) = self.entries.get(high) {343 if let Some(high_entry) = self.entries.get(high) {
343 if high_entry.stage() != 0 {344 if high_entry.stage_raw() != 0 {
344 high = self345 high = self
345 .walk_entry_stages(high_entry.path(self), high, Ordering::Less)346 .walk_entry_stages(high_entry.path(self), high, Ordering::Less)
346 .unwrap_or(high);347 .unwrap_or(high);
@@ -374,7 +375,7 @@ impl State { · 27 unmodified lines
374 .binary_search_by(|e| {375 .binary_search_by(|e| {
375 let res = e.path(self).cmp(path);376 let res = e.path(self).cmp(path);
376 if res.is_eq() {377 if res.is_eq() {
377 stage_at_index = e.stage();378 stage_at_index = e.stage_raw();
378 }379 }
379 res380 res
380 })381 })
gix-index/src/entry/flags.rs+17−0
@@ -62,6 +62,23 @@ bitflags! { · 61 unmodified lines
62impl Flags {62impl Flags {
63 /// Return the stage as extracted from the bits of this instance.63 /// Return the stage as extracted from the bits of this instance.
64 pub fn stage(&self) -> Stage {64 pub fn stage(&self) -> Stage {
65 match self.stage_raw() {
66 0 => Stage::Unconflicted,
67 1 => Stage::Base,
68 2 => Stage::Ours,
69 3 => Stage::Theirs,
70 _ => unreachable!("BUG: Flags::STAGE_MASK is two bits, whose 4 possible values we have covered"),
71 }
72 }
73
74 /// Return an entry's stage as raw number between 0 and 4.
75 /// Possible values are:
76 ///
77 /// * 0 = no conflict,
78 /// * 1 = base,
79 /// * 2 = ours,
80 /// * 3 = theirs
81 pub fn stage_raw(&self) -> u32 {
65 (*self & Flags::STAGE_MASK).bits() >> 1282 (*self & Flags::STAGE_MASK).bits() >> 12
66 }83 }
67 84
gix-index/src/entry/mod.rs+27−2
@@ -1,9 +1,23 @@
1/// The stage of an entry, one of…1/// The stage of an entry.
2#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
3pub enum Stage {
4 /// This is the default, and most entries are in this stage.
5 #[default]
6 Unconflicted = 0,
7 /// The entry is the common base between 'our' change and 'their' change, for comparison.
8 Base = 1,
9 /// The entry represents our change.
10 Ours = 2,
11 /// The entry represents their change.
12 Theirs = 3,
13}
14
15// The stage of an entry, one of…
2/// * 0 = no conflict,16/// * 0 = no conflict,
3/// * 1 = base,17/// * 1 = base,
4/// * 2 = ours,18/// * 2 = ours,
5/// * 3 = theirs19/// * 3 = theirs
6pub type Stage = u32;20pub type StageRaw = u32;
7 21
8///22///
9#[allow(clippy::empty_docs)]23#[allow(clippy::empty_docs)]
@@ -78,6 +92,17 @@ mod access { · 68 unmodified lines
78 pub fn stage(&self) -> entry::Stage {92 pub fn stage(&self) -> entry::Stage {
79 self.flags.stage()93 self.flags.stage()
80 }94 }
95
96 /// Return an entry's stage as raw number between 0 and 4.
97 /// Possible values are:
98 ///
99 /// * 0 = no conflict,
100 /// * 1 = base,
101 /// * 2 = ours,
102 /// * 3 = theirs
103 pub fn stage_raw(&self) -> u32 {
104 self.flags.stage_raw()
105 }
81 }106 }
82}107}
83 108
gix-index/src/extension/fs_monitor.rs+1−0
@@ -6,6 +6,7 @@ use crate::{ · 5 unmodified lines
6};6};
7 7
8#[derive(Clone)]8#[derive(Clone)]
9#[allow(dead_code)]
9pub enum Token {10pub enum Token {
10 V1 { nanos_since_1970: u64 },11 V1 { nanos_since_1970: u64 },
11 V2 { token: BString },12 V2 { token: BString },
gix-index/src/lib.rs+5−5
@@ -144,6 +144,7 @@ pub struct State { · 143 unmodified lines
144}144}
145 145
146mod impls {146mod impls {
147 use crate::entry::Stage;
147 use std::fmt::{Debug, Formatter};148 use std::fmt::{Debug, Formatter};
148 149
149 use crate::State;150 use crate::State;
@@ -155,11 +156,10 @@ mod impls { · 5 unmodified lines
155 f,156 f,
156 "{} {}{:?} {} {}",157 "{} {}{:?} {} {}",
157 match entry.flags.stage() {158 match entry.flags.stage() {
158 0 => " ",159 Stage::Unconflicted => " ",
159 1 => "BASE ",160 Stage::Base => "BASE ",
160 2 => "OURS ",161 Stage::Ours => "OURS ",
161 3 => "THEIRS ",162 Stage::Theirs => "THEIRS ",
162 _ => "UNKNOWN",
163 },163 },
164 if entry.flags.is_empty() {164 if entry.flags.is_empty() {
165 "".to_string()165 "".to_string()
gix-index/tests/index/access.rs+10−9
@@ -1,5 +1,6 @@
1use crate::index::Fixture;1use crate::index::Fixture;
2use bstr::{BString, ByteSlice};2use bstr::{BString, ByteSlice};
3use gix_index::entry::Stage;
3 4
4fn icase_fixture() -> gix_index::File {5fn icase_fixture() -> gix_index::File {
5 Fixture::Generated("v2_icase_name_clashes").open()6 Fixture::Generated("v2_icase_name_clashes").open()
@@ -11,7 +12,7 @@ fn entry_by_path() { · 5 unmodified lines
11 for entry in file.entries() {12 for entry in file.entries() {
12 let path = entry.path(&file);13 let path = entry.path(&file);
13 assert_eq!(file.entry_by_path(path), Some(entry));14 assert_eq!(file.entry_by_path(path), Some(entry));
14 assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry));15 assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry));
15 }16 }
16}17}
17 18
@@ -140,27 +141,27 @@ fn entry_by_path_and_stage() { · 122 unmodified lines
140 for entry in file.entries() {141 for entry in file.entries() {
141 let path = entry.path(&file);142 let path = entry.path(&file);
142 assert_eq!(143 assert_eq!(
143 file.entry_index_by_path_and_stage(path, 0)144 file.entry_index_by_path_and_stage(path, Stage::Unconflicted)
144 .map(|idx| &file.entries()[idx]),145 .map(|idx| &file.entries()[idx]),
145 Some(entry)146 Some(entry)
146 );147 );
147 assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry));148 assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry));
148 }149 }
149}150}
150 151
151#[test]152#[test]
152fn entry_by_path_with_conflicting_file() {153fn entry_by_path_with_conflicting_file() {
153 let file = Fixture::Loose("conflicting-file").open();154 let file = Fixture::Loose("conflicting-file").open();
154 for expected_stage in [1 /* common ancestor */, 2 /* ours */, 3 /* theirs */] {155 for expected_stage in [Stage::Base, Stage::Ours, Stage::Theirs] {
155 assert!(156 assert!(
156 file.entry_by_path_and_stage("file".into(), expected_stage).is_some(),157 file.entry_by_path_and_stage("file".into(), expected_stage).is_some(),
157 "we have no stage 0 during a conflict, but all other ones. Missed {expected_stage}"158 "we have no stage 0 during a conflict, but all other ones. Missed {expected_stage:?}"
158 );159 );
159 }160 }
160 161
161 assert_eq!(162 assert_eq!(
162 file.entry_by_path("file".into()).expect("found").stage(),163 file.entry_by_path("file".into()).expect("found").stage(),
163 2,164 Stage::Ours,
164 "we always find our stage while in a merge"165 "we always find our stage while in a merge"
165 );166 );
166}167}
@@ -226,13 +227,13 @@ fn sort_entries() { · 59 unmodified lines
226 227
227 for (idx, entry) in file.entries()[..valid_entries].iter().enumerate() {228 for (idx, entry) in file.entries()[..valid_entries].iter().enumerate() {
228 assert_eq!(229 assert_eq!(
229 file.entry_index_by_path_and_stage_bounded(entry.path(&file), 0, valid_entries),230 file.entry_index_by_path_and_stage_bounded(entry.path(&file), Stage::Unconflicted, valid_entries),
230 Some(idx),231 Some(idx),
231 "we can still find entries in the correctly sorted region"232 "we can still find entries in the correctly sorted region"
232 );233 );
233 }234 }
234 assert_eq!(235 assert_eq!(
235 file.entry_by_path_and_stage(new_entry_path, 0),236 file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted),
236 None,237 None,
237 "new entry can't be found due to incorrect order"238 "new entry can't be found due to incorrect order"
238 );239 );
@@ -241,7 +242,7 @@ fn sort_entries() { · 2 unmodified lines
241 assert!(file.verify_entries().is_ok(), "sorting of entries restores invariants");242 assert!(file.verify_entries().is_ok(), "sorting of entries restores invariants");
242 243
243 assert_eq!(244 assert_eq!(
244 file.entry_by_path_and_stage(new_entry_path, 0)245 file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted)
245 .expect("can be found")246 .expect("can be found")
246 .path(&file),247 .path(&file),
247 new_entry_path,248 new_entry_path,
gix-status/src/index_as_worktree/function.rs+4−4
@@ -157,11 +157,11 @@ where · 156 unmodified lines
157 let mut idx = 0;157 let mut idx = 0;
158 while let Some(entry) = chunk_entries.get(idx) {158 while let Some(entry) = chunk_entries.get(idx) {
159 let absolute_entry_index = entry_offset + idx;159 let absolute_entry_index = entry_offset + idx;
160 if idx == 0 && entry.stage() != 0 {160 if idx == 0 && entry.stage_raw() != 0 {
161 let offset = entry_offset.checked_sub(1).and_then(|prev_idx| {161 let offset = entry_offset.checked_sub(1).and_then(|prev_idx| {
162 let prev_entry = &all_entries[prev_idx];162 let prev_entry = &all_entries[prev_idx];
163 let entry_path = entry.path_in(state.path_backing);163 let entry_path = entry.path_in(state.path_backing);
164 if prev_entry.stage() == 0 || prev_entry.path_in(state.path_backing) != entry_path {164 if prev_entry.stage_raw() == 0 || prev_entry.path_in(state.path_backing) != entry_path {
165 // prev_entry (in previous chunk) does not belong to our conflict165 // prev_entry (in previous chunk) does not belong to our conflict
166 return None;166 return None;
167 }167 }
@@ -286,7 +286,7 @@ impl<'index> State<'_, 'index> { · 118 unmodified lines
286 self.skipped_by_pathspec.fetch_add(1, Ordering::Relaxed);286 self.skipped_by_pathspec.fetch_add(1, Ordering::Relaxed);
287 return None;287 return None;
288 }288 }
289 let status = if entry.stage() != 0 {289 let status = if entry.stage_raw() != 0 {
290 Ok(290 Ok(
291 Conflict::try_from_entry(entries, self.path_backing, entry_index, path).map(|(conflict, offset)| {291 Conflict::try_from_entry(entries, self.path_backing, entry_index, path).map(|(conflict, offset)| {
292 *outer_entry_index += offset; // let out loop skip over entries related to the conflict292 *outer_entry_index += offset; // let out loop skip over entries related to the conflict
@@ -604,7 +604,7 @@ impl Conflict { · 311 unmodified lines
604 let mut count = 0_usize;604 let mut count = 0_usize;
605 for stage in (start_index..(start_index + 3).min(entries.len())).filter_map(|idx| {605 for stage in (start_index..(start_index + 3).min(entries.len())).filter_map(|idx| {
606 let entry = &entries[idx];606 let entry = &entries[idx];
607 let stage = entry.stage();607 let stage = entry.stage_raw();
608 (stage > 0 && entry.path_in(path_backing) == entry_path).then_some(stage)608 (stage > 0 && entry.path_in(path_backing) == entry_path).then_some(stage)
609 }) {609 }) {
610 // This could be `1 << (stage - 1)` but let's be specific.610 // This could be `1 << (stage - 1)` but let's be specific.
gix-worktree/src/stack/platform.rs+1−0
@@ -20,6 +20,7 @@ impl<'a> Platform<'a> { · 19 unmodified lines
20 /// # Panics20 /// # Panics
21 ///21 ///
22 /// If the cache was configured without exclude patterns.22 /// If the cache was configured without exclude patterns.
23 #[doc(alias = "is_path_ignored", alias = "git2")]
23 pub fn is_excluded(&self) -> bool {24 pub fn is_excluded(&self) -> bool {
24 self.matching_exclude_pattern()25 self.matching_exclude_pattern()
25 .map_or(false, |m| !m.pattern.is_negative())26 .map_or(false, |m| !m.pattern.is_negative())
gix-worktree/src/stack/state/mod.rs+1−1
@@ -131,7 +131,7 @@ impl State { · 130 unmodified lines
131 131
132 // Stage 0 means there is no merge going on, stage 2 means it's 'our' side of the merge, but then132 // Stage 0 means there is no merge going on, stage 2 means it's 'our' side of the merge, but then
133 // there won't be a stage 0.133 // there won't be a stage 0.
134 if entry.mode == gix_index::entry::Mode::FILE && (entry.stage() == 0 || entry.stage() == 2) {134 if entry.mode == gix_index::entry::Mode::FILE && (entry.stage_raw() == 0 || entry.stage_raw() == 2) {
135 let basename = path.rfind_byte(b'/').map_or(path, |pos| path[pos + 1..].as_bstr());135 let basename = path.rfind_byte(b'/').map_or(path, |pos| path[pos + 1..].as_bstr());
136 let ignore_source = names.iter().find_map(|t| {136 let ignore_source = names.iter().find_map(|t| {
137 match case {137 match case {
gix/src/attribute_stack.rs+1−0
@@ -39,6 +39,7 @@ impl<'repo> AttributeStack<'repo> { · 38 unmodified lines
39 /// path is created as directory. If it's not known it is assumed to be a file.39 /// path is created as directory. If it's not known it is assumed to be a file.
40 ///40 ///
41 /// Provide access to cached information for that `relative` path via the returned platform.41 /// Provide access to cached information for that `relative` path via the returned platform.
42 #[doc(alias = "is_path_ignored", alias = "git2")]
42 pub fn at_path(43 pub fn at_path(
43 &mut self,44 &mut self,
44 relative: impl AsRef<std::path::Path>,45 relative: impl AsRef<std::path::Path>,
gix/src/repository/attributes.rs+1−0
@@ -106,6 +106,7 @@ impl Repository { · 105 unmodified lines
106 /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use106 /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use
107 /// [`Repository::attributes()`] for accessing both attributes and excludes.107 /// [`Repository::attributes()`] for accessing both attributes and excludes.
108 // TODO: test108 // TODO: test
109 #[doc(alias = "is_path_ignored", alias = "git2")]
109 #[cfg(feature = "excludes")]110 #[cfg(feature = "excludes")]
110 pub fn excludes(111 pub fn excludes(
111 &self,112 &self,
gix/src/repository/index.rs+5−0
@@ -40,6 +40,11 @@ impl crate::Repository { · 39 unmodified lines
40 /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file40 /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file
41 /// on disk has changed.41 /// on disk has changed.
42 ///42 ///
43 /// ### Notes
44 ///
45 /// * This will fail if the file doesn't exist, like in a newly initialized repository. If that is the case, use
46 /// [index_or_empty()](Self::index_or_empty) or [try_index()](Self::try_index) instead.
47 ///
43 /// The index file is shared across all clones of this repository.48 /// The index file is shared across all clones of this repository.
44 pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> {49 pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> {
45 self.try_index().and_then(|opt| match opt {50 self.try_index().and_then(|opt| match opt {
gix/src/revision/spec/parse/delegate/navigate.rs+14−8
@@ -1,6 +1,7 @@
1use std::collections::HashSet;1use std::collections::HashSet;
2 2
3use gix_hash::ObjectId;3use gix_hash::ObjectId;
4use gix_index::entry::Stage;
4use gix_revision::spec::parse::{5use gix_revision::spec::parse::{
5 delegate,6 delegate,
6 delegate::{PeelTo, Traversal},7 delegate::{PeelTo, Traversal},
@@ -305,9 +306,18 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { · 298 unmodified lines
305 }306 }
306 307
307 fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> {308 fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> {
309 let stage = match stage {
310 0 => Stage::Unconflicted,
311 1 => Stage::Base,
312 2 => Stage::Ours,
313 3 => Stage::Theirs,
314 _ => unreachable!(
315 "BUG: driver will not pass invalid stages (and it uses integer to avoid gix-index as dependency)"
316 ),
317 };
308 self.unset_disambiguate_call();318 self.unset_disambiguate_call();
309 match self.repo.index() {319 match self.repo.index() {
310 Ok(index) => match index.entry_by_path_and_stage(path, stage.into()) {320 Ok(index) => match index.entry_by_path_and_stage(path, stage) {
311 Some(entry) => {321 Some(entry) => {
312 self.objs[self.idx]322 self.objs[self.idx]
313 .get_or_insert_with(HashSet::default)323 .get_or_insert_with(HashSet::default)
@@ -323,21 +333,17 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { · 9 unmodified lines
323 Some(())333 Some(())
324 }334 }
325 None => {335 None => {
326 let stage_hint = [0, 1, 2]336 let stage_hint = [Stage::Unconflicted, Stage::Base, Stage::Ours]
327 .iter()337 .iter()
328 .filter(|our_stage| **our_stage != stage)338 .filter(|our_stage| **our_stage != stage)
329 .find_map(|stage| {339 .find_map(|stage| index.entry_index_by_path_and_stage(path, *stage).map(|_| *stage));
330 index
331 .entry_index_by_path_and_stage(path, (*stage).into())
332 .map(|_| (*stage).into())
333 });
334 let exists = self340 let exists = self
335 .repo341 .repo
336 .work_dir()342 .work_dir()
337 .map_or(false, |root| root.join(gix_path::from_bstr(path)).exists());343 .map_or(false, |root| root.join(gix_path::from_bstr(path)).exists());
338 self.err.push(Error::IndexLookup {344 self.err.push(Error::IndexLookup {
339 desired_path: path.into(),345 desired_path: path.into(),
340 desired_stage: stage.into(),346 desired_stage: stage,
341 exists,347 exists,
342 stage_hint,348 stage_hint,
343 });349 });
gix/src/revision/spec/parse/types.rs+1−1
@@ -98,7 +98,7 @@ pub enum Error { · 97 unmodified lines
98 desired: usize,98 desired: usize,
99 available: usize,99 available: usize,
100 },100 },
101 #[error("Path {desired_path:?} did not exist in index at stage {desired_stage}{}{}", stage_hint.map(|actual|format!(". It does exist at stage {actual}")).unwrap_or_default(), exists.then(|| ". It exists on disk").unwrap_or(". It does not exist on disk"))]101 #[error("Path {desired_path:?} did not exist in index at stage {}{}{}", *desired_stage as u8, stage_hint.map(|actual|format!(". It does exist at stage {}", actual as u8)).unwrap_or_default(), exists.then(|| ". It exists on disk").unwrap_or(". It does not exist on disk"))]
102 IndexLookup {102 IndexLookup {
103 desired_path: BString,103 desired_path: BString,
104 desired_stage: gix_index::entry::Stage,104 desired_stage: gix_index::entry::Stage,
gix/src/status/index_worktree.rs+8−6
@@ -199,10 +199,7 @@ mod submodule_status { · 198 unmodified lines
199 let local_repo = repo.to_thread_local();199 let local_repo = repo.to_thread_local();
200 let submodule_paths = match local_repo.submodules()? {200 let submodule_paths = match local_repo.submodules()? {
201 Some(sm) => {201 Some(sm) => {
202 let mut v: Vec<_> = sm202 let mut v: Vec<_> = sm.filter_map(|sm| sm.path().ok().map(Cow::into_owned)).collect();
203 .filter(|sm| sm.is_active().unwrap_or_default())
204 .filter_map(|sm| sm.path().ok().map(Cow::into_owned))
205 .collect();
206 v.sort();203 v.sort();
207 v204 v
208 }205 }
@@ -271,7 +268,8 @@ mod submodule_status { · 62 unmodified lines
271///268///
272/// ### Submodules269/// ### Submodules
273///270///
274/// Note that submodules can be set to 'inactive' which automatically excludes them from the status operation.271/// Note that submodules can be set to 'inactive', which will not exclude them from the status operation, similar to
272/// how `git status` includes them.
275///273///
276/// ### Index Changes274/// ### Index Changes
277///275///
@@ -613,7 +611,10 @@ pub mod iter { · 335 unmodified lines
613 ///611 ///
614 /// * `patterns`612 /// * `patterns`
615 /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered.613 /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered.
616 pub fn into_index_worktree_iter(self, patterns: Vec<BString>) -> Result<index_worktree::Iter, Error> {614 pub fn into_index_worktree_iter(
615 self,
616 patterns: impl IntoIterator<Item = BString>,
617 ) -> Result<index_worktree::Iter, Error> {
617 let index = match self.index {618 let index = match self.index {
618 None => IndexPersistedOrInMemory::Persisted(self.repo.index_or_empty()?),619 None => IndexPersistedOrInMemory::Persisted(self.repo.index_or_empty()?),
619 Some(index) => index,620 Some(index) => index,
@@ -634,6 +635,7 @@ pub mod iter { · 14 unmodified lines
634 {635 {
635 let (tx, rx) = std::sync::mpsc::channel();636 let (tx, rx) = std::sync::mpsc::channel();
636 let mut collect = Collect { tx };637 let mut collect = Collect { tx };
638 let patterns: Vec<_> = patterns.into_iter().collect();
637 let join = std::thread::Builder::new()639 let join = std::thread::Builder::new()
638 .name("gix::status::index_worktree::iter::producer".into())640 .name("gix::status::index_worktree::iter::producer".into())
639 .spawn({641 .spawn({