Unified · Split
| @@ -6,6 +6,7 @@ pub struct Options { · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | #[cfg(feature = "serde")] |
| 8 | 8 | mod serde_only { |
| 9 | + use gix::index::entry::Stage; | |
| 9 | 10 | |
| 10 | 11 | mod ext { |
| 11 | 12 | #[derive(serde::Serialize)] |
| @@ -115,11 +116,10 @@ mod serde_only { · 103 unmodified lines | ||
| 115 | 116 | let (mut intent_to_add, mut skip_worktree) = (0, 0); |
| 116 | 117 | for entry in f.entries() { |
| 117 | 118 | match entry.flags.stage() { |
| 118 | − 0 => stage_0_merged += 1, | |
| 119 | − 1 => stage_1_base += 1, | |
| 120 | − 2 => stage_2_ours += 1, | |
| 121 | − 3 => stage_3_theirs += 1, | |
| 122 | − invalid => anyhow::bail!("Invalid stage {} encountered", invalid), | |
| 119 | + Stage::Unconflicted => stage_0_merged += 1, | |
| 120 | + Stage::Base => stage_1_base += 1, | |
| 121 | + Stage::Ours => stage_2_ours += 1, | |
| 122 | + Stage::Theirs => stage_3_theirs += 1, | |
| 123 | 123 | } |
| 124 | 124 | match entry.mode { |
| 125 | 125 | gix::index::entry::Mode::DIR => dir += 1, |
| @@ -262,6 +262,8 @@ pub(crate) mod function { · 261 unmodified lines | ||
| 262 | 262 | } |
| 263 | 263 | |
| 264 | 264 | #[derive(Debug)] |
| 265 | + // See note on `Mismatch` | |
| 266 | + #[allow(dead_code)] | |
| 265 | 267 | pub struct ExcludeLocation { |
| 266 | 268 | pub line: usize, |
| 267 | 269 | pub rela_source_file: String, |
| @@ -269,6 +271,9 @@ pub(crate) mod function { · 1 unmodified line | ||
| 269 | 271 | } |
| 270 | 272 | |
| 271 | 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 | 277 | pub enum Mismatch { |
| 273 | 278 | Attributes { |
| 274 | 279 | actual: Vec<gix::attrs::Assignment>, |
| @@ -281,6 +286,8 @@ pub(crate) mod function { · 6 unmodified lines | ||
| 281 | 286 | } |
| 282 | 287 | |
| 283 | 288 | #[derive(Debug)] |
| 289 | + // See note on `Mismatch` | |
| 290 | + #[allow(dead_code)] | |
| 284 | 291 | pub struct ExcludeMatch { |
| 285 | 292 | pub pattern: gix::glob::Pattern, |
| 286 | 293 | pub source: Option<PathBuf>, |
| @@ -23,6 +23,7 @@ pub(crate) mod function { · 22 unmodified lines | ||
| 23 | 23 | io::{BufWriter, Write}, |
| 24 | 24 | }; |
| 25 | 25 | |
| 26 | + use gix::index::entry::Stage; | |
| 26 | 27 | use gix::{ |
| 27 | 28 | bstr::{BStr, BString}, |
| 28 | 29 | worktree::IndexPersistedOrInMemory, |
| @@ -392,11 +393,10 @@ pub(crate) mod function { · 363 unmodified lines | ||
| 392 | 393 | out, |
| 393 | 394 | "{} {}{:?} {} {}{}{}", |
| 394 | 395 | match entry.flags.stage() { |
| 395 | − 0 => " ", | |
| 396 | − 1 => "BASE ", | |
| 397 | − 2 => "OURS ", | |
| 398 | − 3 => "THEIRS ", | |
| 399 | − _ => "UNKNOWN", | |
| 396 | + Stage::Unconflicted => " ", | |
| 397 | + Stage::Base => "BASE ", | |
| 398 | + Stage::Ours => "OURS ", | |
| 399 | + Stage::Theirs => "THEIRS ", | |
| 400 | 400 | }, |
| 401 | 401 | if entry.flags.is_empty() { |
| 402 | 402 | "".to_string() |
| @@ -99,7 +99,14 @@ fn append_followed_includes_recursively( · 98 unmodified lines | ||
| 99 | 99 | } |
| 100 | 100 | |
| 101 | 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 | 110 | let config_meta = Metadata { |
| 104 | 111 | path: Some(config_path), |
| 105 | 112 | trust: meta.trust, |
| @@ -1,11 +1,14 @@ | ||
| 1 | 1 | use crate::{parse, path::interpolate}; |
| 2 | +use std::path::PathBuf; | |
| 2 | 3 | |
| 3 | 4 | /// The error returned when following includes. |
| 4 | 5 | #[derive(Debug, thiserror::Error)] |
| 5 | 6 | #[allow(missing_docs)] |
| 6 | 7 | pub enum Error { |
| 7 | − #[error(transparent)] | |
| 8 | − Io(#[from] std::io::Error), | |
| 8 | + #[error("Failed to copy configuration file into buffer")] | |
| 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 | 12 | #[error(transparent)] |
| 10 | 13 | Parse(#[from] parse::Error), |
| 11 | 14 | #[error(transparent)] |
| @@ -3,6 +3,7 @@ use std::{cmp::Ordering, ops::Range}; · 2 unmodified lines | ||
| 3 | 3 | use bstr::{BStr, ByteSlice, ByteVec}; |
| 4 | 4 | use filetime::FileTime; |
| 5 | 5 | |
| 6 | +use crate::entry::{Stage, StageRaw}; | |
| 6 | 7 | use crate::{entry, extension, AccelerateLookup, Entry, PathStorage, PathStorageRef, State, Version}; |
| 7 | 8 | |
| 8 | 9 | // TODO: integrate this somehow, somewhere, depending on later usage. |
| @@ -81,7 +82,7 @@ impl State { · 72 unmodified lines | ||
| 81 | 82 | res |
| 82 | 83 | }) |
| 83 | 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 | 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 | 113 | &self, |
| 113 | 114 | path: &BStr, |
| 114 | 115 | idx: usize, |
| 115 | − wanted_stage: entry::Stage, | |
| 116 | + wanted_stage: entry::StageRaw, | |
| 116 | 117 | stage_cmp: Ordering, |
| 117 | 118 | ) -> Option<usize> { |
| 118 | 119 | match stage_cmp { |
| @@ -121,7 +122,7 @@ impl State { · 2 unmodified lines | ||
| 121 | 122 | .enumerate() |
| 122 | 123 | .rev() |
| 123 | 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 | 126 | Ordering::Equal => Some(idx), |
| 126 | 127 | Ordering::Less => self |
| 127 | 128 | .entries |
| @@ -129,7 +130,7 @@ impl State { · 1 unmodified line | ||
| 129 | 130 | .iter() |
| 130 | 131 | .enumerate() |
| 131 | 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 | 292 | .binary_search_by(|e| { |
| 292 | 293 | let res = e.path(self).cmp(path); |
| 293 | 294 | if res.is_eq() { |
| 294 | − stage_at_index = e.stage(); | |
| 295 | + stage_at_index = e.stage_raw(); | |
| 295 | 296 | } |
| 296 | 297 | res |
| 297 | 298 | }) |
| @@ -299,7 +300,7 @@ impl State { · 1 unmodified line | ||
| 299 | 300 | let idx = if stage_at_index == 0 || stage_at_index == 2 { |
| 300 | 301 | idx |
| 301 | 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 | 305 | Some(&self.entries[idx]) |
| 305 | 306 | } |
| @@ -334,13 +335,13 @@ impl State { · 28 unmodified lines | ||
| 334 | 335 | + self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix)); |
| 335 | 336 | |
| 336 | 337 | let low_entry = &self.entries.get(low)?; |
| 337 | − if low_entry.stage() != 0 { | |
| 338 | + if low_entry.stage_raw() != 0 { | |
| 338 | 339 | low = self |
| 339 | 340 | .walk_entry_stages(low_entry.path(self), low, Ordering::Less) |
| 340 | 341 | .unwrap_or(low); |
| 341 | 342 | } |
| 342 | 343 | if let Some(high_entry) = self.entries.get(high) { |
| 343 | − if high_entry.stage() != 0 { | |
| 344 | + if high_entry.stage_raw() != 0 { | |
| 344 | 345 | high = self |
| 345 | 346 | .walk_entry_stages(high_entry.path(self), high, Ordering::Less) |
| 346 | 347 | .unwrap_or(high); |
| @@ -374,7 +375,7 @@ impl State { · 27 unmodified lines | ||
| 374 | 375 | .binary_search_by(|e| { |
| 375 | 376 | let res = e.path(self).cmp(path); |
| 376 | 377 | if res.is_eq() { |
| 377 | − stage_at_index = e.stage(); | |
| 378 | + stage_at_index = e.stage_raw(); | |
| 378 | 379 | } |
| 379 | 380 | res |
| 380 | 381 | }) |
| @@ -62,6 +62,23 @@ bitflags! { · 61 unmodified lines | ||
| 62 | 62 | impl Flags { |
| 63 | 63 | /// Return the stage as extracted from the bits of this instance. |
| 64 | 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 | 82 | (*self & Flags::STAGE_MASK).bits() >> 12 |
| 66 | 83 | } |
| 67 | 84 | |
| @@ -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)] | |
| 3 | +pub 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 | 16 | /// * 0 = no conflict, |
| 3 | 17 | /// * 1 = base, |
| 4 | 18 | /// * 2 = ours, |
| 5 | 19 | /// * 3 = theirs |
| 6 | −pub type Stage = u32; | |
| 20 | +pub type StageRaw = u32; | |
| 7 | 21 | |
| 8 | 22 | /// |
| 9 | 23 | #[allow(clippy::empty_docs)] |
| @@ -78,6 +92,17 @@ mod access { · 68 unmodified lines | ||
| 78 | 92 | pub fn stage(&self) -> entry::Stage { |
| 79 | 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 | |
| @@ -6,6 +6,7 @@ use crate::{ · 5 unmodified lines | ||
| 6 | 6 | }; |
| 7 | 7 | |
| 8 | 8 | #[derive(Clone)] |
| 9 | +#[allow(dead_code)] | |
| 9 | 10 | pub enum Token { |
| 10 | 11 | V1 { nanos_since_1970: u64 }, |
| 11 | 12 | V2 { token: BString }, |
| @@ -144,6 +144,7 @@ pub struct State { · 143 unmodified lines | ||
| 144 | 144 | } |
| 145 | 145 | |
| 146 | 146 | mod impls { |
| 147 | + use crate::entry::Stage; | |
| 147 | 148 | use std::fmt::{Debug, Formatter}; |
| 148 | 149 | |
| 149 | 150 | use crate::State; |
| @@ -155,11 +156,10 @@ mod impls { · 5 unmodified lines | ||
| 155 | 156 | f, |
| 156 | 157 | "{} {}{:?} {} {}", |
| 157 | 158 | match entry.flags.stage() { |
| 158 | − 0 => " ", | |
| 159 | − 1 => "BASE ", | |
| 160 | − 2 => "OURS ", | |
| 161 | − 3 => "THEIRS ", | |
| 162 | − _ => "UNKNOWN", | |
| 159 | + Stage::Unconflicted => " ", | |
| 160 | + Stage::Base => "BASE ", | |
| 161 | + Stage::Ours => "OURS ", | |
| 162 | + Stage::Theirs => "THEIRS ", | |
| 163 | 163 | }, |
| 164 | 164 | if entry.flags.is_empty() { |
| 165 | 165 | "".to_string() |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | use crate::index::Fixture; |
| 2 | 2 | use bstr::{BString, ByteSlice}; |
| 3 | +use gix_index::entry::Stage; | |
| 3 | 4 | |
| 4 | 5 | fn icase_fixture() -> gix_index::File { |
| 5 | 6 | Fixture::Generated("v2_icase_name_clashes").open() |
| @@ -11,7 +12,7 @@ fn entry_by_path() { · 5 unmodified lines | ||
| 11 | 12 | for entry in file.entries() { |
| 12 | 13 | let path = entry.path(&file); |
| 13 | 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 | 141 | for entry in file.entries() { |
| 141 | 142 | let path = entry.path(&file); |
| 142 | 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 | 145 | .map(|idx| &file.entries()[idx]), |
| 145 | 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 | 152 | #[test] |
| 152 | 153 | fn entry_by_path_with_conflicting_file() { |
| 153 | 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 | 156 | assert!( |
| 156 | 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 | 162 | assert_eq!( |
| 162 | 163 | file.entry_by_path("file".into()).expect("found").stage(), |
| 163 | − 2, | |
| 164 | + Stage::Ours, | |
| 164 | 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 | 228 | for (idx, entry) in file.entries()[..valid_entries].iter().enumerate() { |
| 228 | 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 | 231 | Some(idx), |
| 231 | 232 | "we can still find entries in the correctly sorted region" |
| 232 | 233 | ); |
| 233 | 234 | } |
| 234 | 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 | 237 | None, |
| 237 | 238 | "new entry can't be found due to incorrect order" |
| 238 | 239 | ); |
| @@ -241,7 +242,7 @@ fn sort_entries() { · 2 unmodified lines | ||
| 241 | 242 | assert!(file.verify_entries().is_ok(), "sorting of entries restores invariants"); |
| 242 | 243 | |
| 243 | 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 | 246 | .expect("can be found") |
| 246 | 247 | .path(&file), |
| 247 | 248 | new_entry_path, |
| @@ -157,11 +157,11 @@ where · 156 unmodified lines | ||
| 157 | 157 | let mut idx = 0; |
| 158 | 158 | while let Some(entry) = chunk_entries.get(idx) { |
| 159 | 159 | let absolute_entry_index = entry_offset + idx; |
| 160 | − if idx == 0 && entry.stage() != 0 { | |
| 160 | + if idx == 0 && entry.stage_raw() != 0 { | |
| 161 | 161 | let offset = entry_offset.checked_sub(1).and_then(|prev_idx| { |
| 162 | 162 | let prev_entry = &all_entries[prev_idx]; |
| 163 | 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 | 165 | // prev_entry (in previous chunk) does not belong to our conflict |
| 166 | 166 | return None; |
| 167 | 167 | } |
| @@ -286,7 +286,7 @@ impl<'index> State<'_, 'index> { · 118 unmodified lines | ||
| 286 | 286 | self.skipped_by_pathspec.fetch_add(1, Ordering::Relaxed); |
| 287 | 287 | return None; |
| 288 | 288 | } |
| 289 | − let status = if entry.stage() != 0 { | |
| 289 | + let status = if entry.stage_raw() != 0 { | |
| 290 | 290 | Ok( |
| 291 | 291 | Conflict::try_from_entry(entries, self.path_backing, entry_index, path).map(|(conflict, offset)| { |
| 292 | 292 | *outer_entry_index += offset; // let out loop skip over entries related to the conflict |
| @@ -604,7 +604,7 @@ impl Conflict { · 311 unmodified lines | ||
| 604 | 604 | let mut count = 0_usize; |
| 605 | 605 | for stage in (start_index..(start_index + 3).min(entries.len())).filter_map(|idx| { |
| 606 | 606 | let entry = &entries[idx]; |
| 607 | − let stage = entry.stage(); | |
| 607 | + let stage = entry.stage_raw(); | |
| 608 | 608 | (stage > 0 && entry.path_in(path_backing) == entry_path).then_some(stage) |
| 609 | 609 | }) { |
| 610 | 610 | // This could be `1 << (stage - 1)` but let's be specific. |
| @@ -20,6 +20,7 @@ impl<'a> Platform<'a> { · 19 unmodified lines | ||
| 20 | 20 | /// # Panics |
| 21 | 21 | /// |
| 22 | 22 | /// If the cache was configured without exclude patterns. |
| 23 | + #[doc(alias = "is_path_ignored", alias = "git2")] | |
| 23 | 24 | pub fn is_excluded(&self) -> bool { |
| 24 | 25 | self.matching_exclude_pattern() |
| 25 | 26 | .map_or(false, |m| !m.pattern.is_negative()) |
| @@ -131,7 +131,7 @@ impl State { · 130 unmodified lines | ||
| 131 | 131 | |
| 132 | 132 | // Stage 0 means there is no merge going on, stage 2 means it's 'our' side of the merge, but then |
| 133 | 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 | 135 | let basename = path.rfind_byte(b'/').map_or(path, |pos| path[pos + 1..].as_bstr()); |
| 136 | 136 | let ignore_source = names.iter().find_map(|t| { |
| 137 | 137 | match case { |
| @@ -39,6 +39,7 @@ impl<'repo> AttributeStack<'repo> { · 38 unmodified lines | ||
| 39 | 39 | /// path is created as directory. If it's not known it is assumed to be a file. |
| 40 | 40 | /// |
| 41 | 41 | /// Provide access to cached information for that `relative` path via the returned platform. |
| 42 | + #[doc(alias = "is_path_ignored", alias = "git2")] | |
| 42 | 43 | pub fn at_path( |
| 43 | 44 | &mut self, |
| 44 | 45 | relative: impl AsRef<std::path::Path>, |
| @@ -106,6 +106,7 @@ impl Repository { · 105 unmodified lines | ||
| 106 | 106 | /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use |
| 107 | 107 | /// [`Repository::attributes()`] for accessing both attributes and excludes. |
| 108 | 108 | // TODO: test |
| 109 | + #[doc(alias = "is_path_ignored", alias = "git2")] | |
| 109 | 110 | #[cfg(feature = "excludes")] |
| 110 | 111 | pub fn excludes( |
| 111 | 112 | &self, |
| @@ -40,6 +40,11 @@ impl crate::Repository { · 39 unmodified lines | ||
| 40 | 40 | /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file |
| 41 | 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 | 48 | /// The index file is shared across all clones of this repository. |
| 44 | 49 | pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> { |
| 45 | 50 | self.try_index().and_then(|opt| match opt { |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | use std::collections::HashSet; |
| 2 | 2 | |
| 3 | 3 | use gix_hash::ObjectId; |
| 4 | +use gix_index::entry::Stage; | |
| 4 | 5 | use gix_revision::spec::parse::{ |
| 5 | 6 | delegate, |
| 6 | 7 | delegate::{PeelTo, Traversal}, |
| @@ -305,9 +306,18 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { · 298 unmodified lines | ||
| 305 | 306 | } |
| 306 | 307 | |
| 307 | 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 | 318 | self.unset_disambiguate_call(); |
| 309 | 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 | 321 | Some(entry) => { |
| 312 | 322 | self.objs[self.idx] |
| 313 | 323 | .get_or_insert_with(HashSet::default) |
| @@ -323,21 +333,17 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { · 9 unmodified lines | ||
| 323 | 333 | Some(()) |
| 324 | 334 | } |
| 325 | 335 | None => { |
| 326 | − let stage_hint = [0, 1, 2] | |
| 336 | + let stage_hint = [Stage::Unconflicted, Stage::Base, Stage::Ours] | |
| 327 | 337 | .iter() |
| 328 | 338 | .filter(|our_stage| **our_stage != stage) |
| 329 | − .find_map(|stage| { | |
| 330 | − index | |
| 331 | − .entry_index_by_path_and_stage(path, (*stage).into()) | |
| 332 | − .map(|_| (*stage).into()) | |
| 333 | − }); | |
| 339 | + .find_map(|stage| index.entry_index_by_path_and_stage(path, *stage).map(|_| *stage)); | |
| 334 | 340 | let exists = self |
| 335 | 341 | .repo |
| 336 | 342 | .work_dir() |
| 337 | 343 | .map_or(false, |root| root.join(gix_path::from_bstr(path)).exists()); |
| 338 | 344 | self.err.push(Error::IndexLookup { |
| 339 | 345 | desired_path: path.into(), |
| 340 | − desired_stage: stage.into(), | |
| 346 | + desired_stage: stage, | |
| 341 | 347 | exists, |
| 342 | 348 | stage_hint, |
| 343 | 349 | }); |
| @@ -98,7 +98,7 @@ pub enum Error { · 97 unmodified lines | ||
| 98 | 98 | desired: usize, |
| 99 | 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 | 102 | IndexLookup { |
| 103 | 103 | desired_path: BString, |
| 104 | 104 | desired_stage: gix_index::entry::Stage, |
| @@ -199,10 +199,7 @@ mod submodule_status { · 198 unmodified lines | ||
| 199 | 199 | let local_repo = repo.to_thread_local(); |
| 200 | 200 | let submodule_paths = match local_repo.submodules()? { |
| 201 | 201 | Some(sm) => { |
| 202 | − let mut v: Vec<_> = sm | |
| 203 | − .filter(|sm| sm.is_active().unwrap_or_default()) | |
| 204 | − .filter_map(|sm| sm.path().ok().map(Cow::into_owned)) | |
| 205 | − .collect(); | |
| 202 | + let mut v: Vec<_> = sm.filter_map(|sm| sm.path().ok().map(Cow::into_owned)).collect(); | |
| 206 | 203 | v.sort(); |
| 207 | 204 | v |
| 208 | 205 | } |
| @@ -271,7 +268,8 @@ mod submodule_status { · 62 unmodified lines | ||
| 271 | 268 | /// |
| 272 | 269 | /// ### 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 | 274 | /// ### Index Changes |
| 277 | 275 | /// |
| @@ -613,7 +611,10 @@ pub mod iter { · 335 unmodified lines | ||
| 613 | 611 | /// |
| 614 | 612 | /// * `patterns` |
| 615 | 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 | 618 | let index = match self.index { |
| 618 | 619 | None => IndexPersistedOrInMemory::Persisted(self.repo.index_or_empty()?), |
| 619 | 620 | Some(index) => index, |
| @@ -634,6 +635,7 @@ pub mod iter { · 14 unmodified lines | ||
| 634 | 635 | { |
| 635 | 636 | let (tx, rx) = std::sync::mpsc::channel(); |
| 636 | 637 | let mut collect = Collect { tx }; |
| 638 | + let patterns: Vec<_> = patterns.into_iter().collect(); | |
| 637 | 639 | let join = std::thread::Builder::new() |
| 638 | 640 | .name("gix::status::index_worktree::iter::producer".into()) |
| 639 | 641 | .spawn({ |