Skip to content

tree ➡ index diff for status #1317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions gitoxide-core/src/index/information.rs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ pub struct Options {

#[cfg(feature = "serde")]
mod serde_only {
use gix::index::entry::Stage;

mod ext {
#[derive(serde::Serialize)]
@@ -115,11 +116,10 @@ mod serde_only {
let (mut intent_to_add, mut skip_worktree) = (0, 0);
for entry in f.entries() {
match entry.flags.stage() {
0 => stage_0_merged += 1,
1 => stage_1_base += 1,
2 => stage_2_ours += 1,
3 => stage_3_theirs += 1,
invalid => anyhow::bail!("Invalid stage {} encountered", invalid),
Stage::Unconflicted => stage_0_merged += 1,
Stage::Base => stage_1_base += 1,
Stage::Ours => stage_2_ours += 1,
Stage::Theirs => stage_3_theirs += 1,
}
match entry.mode {
gix::index::entry::Mode::DIR => dir += 1,
7 changes: 7 additions & 0 deletions gitoxide-core/src/repository/attributes/validate_baseline.rs
Original file line number Diff line number Diff line change
@@ -262,13 +262,18 @@ pub(crate) mod function {
}

#[derive(Debug)]
// See note on `Mismatch`
#[allow(dead_code)]
pub struct ExcludeLocation {
pub line: usize,
pub rela_source_file: String,
pub pattern: String,
}

#[derive(Debug)]
// We debug-print this structure, which makes all fields 'used', but it doesn't count.
// TODO: find a way to not have to do more work, but make the warning go away.
#[allow(dead_code)]
pub enum Mismatch {
Attributes {
actual: Vec<gix::attrs::Assignment>,
@@ -281,6 +286,8 @@ pub(crate) mod function {
}

#[derive(Debug)]
// See note on `Mismatch`
#[allow(dead_code)]
pub struct ExcludeMatch {
pub pattern: gix::glob::Pattern,
pub source: Option<PathBuf>,
10 changes: 5 additions & 5 deletions gitoxide-core/src/repository/index/entries.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ pub(crate) mod function {
io::{BufWriter, Write},
};

use gix::index::entry::Stage;
use gix::{
bstr::{BStr, BString},
worktree::IndexPersistedOrInMemory,
@@ -392,11 +393,10 @@ pub(crate) mod function {
out,
"{} {}{:?} {} {}{}{}",
match entry.flags.stage() {
0 => " ",
1 => "BASE ",
2 => "OURS ",
3 => "THEIRS ",
_ => "UNKNOWN",
Stage::Unconflicted => " ",
Stage::Base => "BASE ",
Stage::Ours => "OURS ",
Stage::Theirs => "THEIRS ",
},
if entry.flags.is_empty() {
"".to_string()
9 changes: 8 additions & 1 deletion gix-config/src/file/includes/mod.rs
Original file line number Diff line number Diff line change
@@ -99,7 +99,14 @@ fn append_followed_includes_recursively(
}

buf.clear();
std::io::copy(&mut std::fs::File::open(&config_path)?, buf)?;
std::io::copy(
&mut std::fs::File::open(&config_path).map_err(|err| Error::Io {
source: err,
path: config_path.to_owned(),
})?,
buf,
)
.map_err(Error::CopyBuffer)?;
let config_meta = Metadata {
path: Some(config_path),
trust: meta.trust,
7 changes: 5 additions & 2 deletions gix-config/src/file/includes/types.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::{parse, path::interpolate};
use std::path::PathBuf;

/// The error returned when following includes.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Failed to copy configuration file into buffer")]
CopyBuffer(#[source] std::io::Error),
#[error("Could not read included configuration file at '{}'", path.display())]
Io { path: PathBuf, source: std::io::Error },
#[error(transparent)]
Parse(#[from] parse::Error),
#[error(transparent)]
19 changes: 10 additions & 9 deletions gix-index/src/access/mod.rs
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ use std::{cmp::Ordering, ops::Range};
use bstr::{BStr, ByteSlice, ByteVec};
use filetime::FileTime;

use crate::entry::{Stage, StageRaw};
use crate::{entry, extension, AccelerateLookup, Entry, PathStorage, PathStorageRef, State, Version};

// TODO: integrate this somehow, somewhere, depending on later usage.
@@ -81,7 +82,7 @@ impl State {
res
})
.ok()?;
self.entry_index_by_idx_and_stage(path, idx, stage, stage_cmp)
self.entry_index_by_idx_and_stage(path, idx, stage as StageRaw, stage_cmp)
}

/// Walk as far in `direction` as possible, with [`Ordering::Greater`] towards higher stages, and [`Ordering::Less`]
@@ -112,7 +113,7 @@ impl State {
&self,
path: &BStr,
idx: usize,
wanted_stage: entry::Stage,
wanted_stage: entry::StageRaw,
stage_cmp: Ordering,
) -> Option<usize> {
match stage_cmp {
@@ -121,15 +122,15 @@ impl State {
.enumerate()
.rev()
.take_while(|(_, e)| e.path(self) == path)
.find_map(|(idx, e)| (e.stage() == wanted_stage).then_some(idx)),
.find_map(|(idx, e)| (e.stage_raw() == wanted_stage).then_some(idx)),
Ordering::Equal => Some(idx),
Ordering::Less => self
.entries
.get(idx + 1..)?
.iter()
.enumerate()
.take_while(|(_, e)| e.path(self) == path)
.find_map(|(ofs, e)| (e.stage() == wanted_stage).then_some(idx + ofs + 1)),
.find_map(|(ofs, e)| (e.stage_raw() == wanted_stage).then_some(idx + ofs + 1)),
}
}

@@ -291,15 +292,15 @@ impl State {
.binary_search_by(|e| {
let res = e.path(self).cmp(path);
if res.is_eq() {
stage_at_index = e.stage();
stage_at_index = e.stage_raw();
}
res
})
.ok()?;
let idx = if stage_at_index == 0 || stage_at_index == 2 {
idx
} else {
self.entry_index_by_idx_and_stage(path, idx, 2, stage_at_index.cmp(&2))?
self.entry_index_by_idx_and_stage(path, idx, Stage::Ours as StageRaw, stage_at_index.cmp(&2))?
};
Some(&self.entries[idx])
}
@@ -334,13 +335,13 @@ impl State {
+ self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix));

let low_entry = &self.entries.get(low)?;
if low_entry.stage() != 0 {
if low_entry.stage_raw() != 0 {
low = self
.walk_entry_stages(low_entry.path(self), low, Ordering::Less)
.unwrap_or(low);
}
if let Some(high_entry) = self.entries.get(high) {
if high_entry.stage() != 0 {
if high_entry.stage_raw() != 0 {
high = self
.walk_entry_stages(high_entry.path(self), high, Ordering::Less)
.unwrap_or(high);
@@ -374,7 +375,7 @@ impl State {
.binary_search_by(|e| {
let res = e.path(self).cmp(path);
if res.is_eq() {
stage_at_index = e.stage();
stage_at_index = e.stage_raw();
}
res
})
17 changes: 17 additions & 0 deletions gix-index/src/entry/flags.rs
Original file line number Diff line number Diff line change
@@ -62,6 +62,23 @@ bitflags! {
impl Flags {
/// Return the stage as extracted from the bits of this instance.
pub fn stage(&self) -> Stage {
match self.stage_raw() {
0 => Stage::Unconflicted,
1 => Stage::Base,
2 => Stage::Ours,
3 => Stage::Theirs,
_ => unreachable!("BUG: Flags::STAGE_MASK is two bits, whose 4 possible values we have covered"),
}
}

/// Return an entry's stage as raw number between 0 and 4.
/// Possible values are:
///
/// * 0 = no conflict,
/// * 1 = base,
/// * 2 = ours,
/// * 3 = theirs
pub fn stage_raw(&self) -> u32 {
(*self & Flags::STAGE_MASK).bits() >> 12
}

29 changes: 27 additions & 2 deletions gix-index/src/entry/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
/// The stage of an entry, one of…
/// The stage of an entry.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum Stage {
/// This is the default, and most entries are in this stage.
#[default]
Unconflicted = 0,
/// The entry is the common base between 'our' change and 'their' change, for comparison.
Base = 1,
/// The entry represents our change.
Ours = 2,
/// The entry represents their change.
Theirs = 3,
}

// The stage of an entry, one of…
/// * 0 = no conflict,
/// * 1 = base,
/// * 2 = ours,
/// * 3 = theirs
pub type Stage = u32;
pub type StageRaw = u32;

///
#[allow(clippy::empty_docs)]
@@ -78,6 +92,17 @@ mod access {
pub fn stage(&self) -> entry::Stage {
self.flags.stage()
}

/// Return an entry's stage as raw number between 0 and 4.
/// Possible values are:
///
/// * 0 = no conflict,
/// * 1 = base,
/// * 2 = ours,
/// * 3 = theirs
pub fn stage_raw(&self) -> u32 {
self.flags.stage_raw()
}
}
}

1 change: 1 addition & 0 deletions gix-index/src/extension/fs_monitor.rs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ use crate::{
};

#[derive(Clone)]
#[allow(dead_code)]
pub enum Token {
V1 { nanos_since_1970: u64 },
V2 { token: BString },
10 changes: 5 additions & 5 deletions gix-index/src/lib.rs
Original file line number Diff line number Diff line change
@@ -144,6 +144,7 @@ pub struct State {
}

mod impls {
use crate::entry::Stage;
use std::fmt::{Debug, Formatter};

use crate::State;
@@ -155,11 +156,10 @@ mod impls {
f,
"{} {}{:?} {} {}",
match entry.flags.stage() {
0 => " ",
1 => "BASE ",
2 => "OURS ",
3 => "THEIRS ",
_ => "UNKNOWN",
Stage::Unconflicted => " ",
Stage::Base => "BASE ",
Stage::Ours => "OURS ",
Stage::Theirs => "THEIRS ",
},
if entry.flags.is_empty() {
"".to_string()
19 changes: 10 additions & 9 deletions gix-index/tests/index/access.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::index::Fixture;
use bstr::{BString, ByteSlice};
use gix_index::entry::Stage;

fn icase_fixture() -> gix_index::File {
Fixture::Generated("v2_icase_name_clashes").open()
@@ -11,7 +12,7 @@ fn entry_by_path() {
for entry in file.entries() {
let path = entry.path(&file);
assert_eq!(file.entry_by_path(path), Some(entry));
assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry));
assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry));
}
}

@@ -140,27 +141,27 @@ fn entry_by_path_and_stage() {
for entry in file.entries() {
let path = entry.path(&file);
assert_eq!(
file.entry_index_by_path_and_stage(path, 0)
file.entry_index_by_path_and_stage(path, Stage::Unconflicted)
.map(|idx| &file.entries()[idx]),
Some(entry)
);
assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry));
assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry));
}
}

#[test]
fn entry_by_path_with_conflicting_file() {
let file = Fixture::Loose("conflicting-file").open();
for expected_stage in [1 /* common ancestor */, 2 /* ours */, 3 /* theirs */] {
for expected_stage in [Stage::Base, Stage::Ours, Stage::Theirs] {
assert!(
file.entry_by_path_and_stage("file".into(), expected_stage).is_some(),
"we have no stage 0 during a conflict, but all other ones. Missed {expected_stage}"
"we have no stage 0 during a conflict, but all other ones. Missed {expected_stage:?}"
);
}

assert_eq!(
file.entry_by_path("file".into()).expect("found").stage(),
2,
Stage::Ours,
"we always find our stage while in a merge"
);
}
@@ -226,13 +227,13 @@ fn sort_entries() {

for (idx, entry) in file.entries()[..valid_entries].iter().enumerate() {
assert_eq!(
file.entry_index_by_path_and_stage_bounded(entry.path(&file), 0, valid_entries),
file.entry_index_by_path_and_stage_bounded(entry.path(&file), Stage::Unconflicted, valid_entries),
Some(idx),
"we can still find entries in the correctly sorted region"
);
}
assert_eq!(
file.entry_by_path_and_stage(new_entry_path, 0),
file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted),
None,
"new entry can't be found due to incorrect order"
);
@@ -241,7 +242,7 @@ fn sort_entries() {
assert!(file.verify_entries().is_ok(), "sorting of entries restores invariants");

assert_eq!(
file.entry_by_path_and_stage(new_entry_path, 0)
file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted)
.expect("can be found")
.path(&file),
new_entry_path,
Loading