Skip to content

gix-status improvements #1155

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 4 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ members = [
"gix-diff",
"gix-date",
"gix-traverse",
"gix-dir",
"gix-index",
"gix-bitmap",
"gix-worktree",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ is usable to some extent.
* `gitoxide-core`
* **very early** _(possibly without any documentation and many rough edges)_
* [gix-date](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-date)
* [gix-dir](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-dir)
* **idea** _(just a name placeholder)_
* [gix-note](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-note)
* [gix-fetchhead](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-fetchhead)
Expand Down
15 changes: 14 additions & 1 deletion crate-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,18 @@ A plumbing crate with shared functionality regarding EWAH compressed bitmaps, as
* [x] decode on-disk representation
* [ ] encode on-disk representation

### gix-dir

A git directory walk.

* [ ] list untracked files
- [ ] `normal` - files and directories
- [ ] `all` - expand to untracked files in untracked directories
* [ ] list ignored files
- [ ] `matching` mode (show every ignored file, do not aggregate into parent directory)
- [ ] `traditional` mode (aggregate all ignored files of a folder into ignoring the folder itself)
* [ ] accelerated walk with `untracked`-cache (as provided by `UNTR` extension of `gix_index::File`)

### gix-index

The git staging area.
Expand All @@ -629,6 +641,7 @@ The git staging area.
* [x] 'link' base indices to take information from, split index
* [x] 'sdir' [sparse directory entries](https://github.blog/2021-08-16-highlights-from-git-2-33/) - marker
* [x] verification of entries and extensions as well as checksum
* [ ] expand sparse directory entries using information of the tree itself
* write
* [x] V2
* [x] V3 - extension bits
Expand All @@ -655,7 +668,7 @@ The git staging area.
* [ ] IEOT index entry offset table
* [ ] 'link' base indices to take information from, split index
* [ ] 'sdir' sparse directory entries
* add and remove entries
* [ ] add and remove entries
* [x] API documentation
* [ ] Some examples

Expand Down
6 changes: 6 additions & 0 deletions gix-dir/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
14 changes: 14 additions & 0 deletions gix-dir/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "gix-dir"
version = "0.0.0"
repository = "https://github.com/Byron/gitoxide"
license = "MIT OR Apache-2.0"
description = "A crate of the gitoxide project dealing with directory walks"
authors = ["Sebastian Thiel <[email protected]>"]
edition = "2021"
rust-version = "1.65"

[lib]
doctest = false

[dependencies]
1 change: 1 addition & 0 deletions gix-dir/LICENSE-APACHE
1 change: 1 addition & 0 deletions gix-dir/LICENSE-MIT
3 changes: 3 additions & 0 deletions gix-dir/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! A crate for handling a git-style directory walk.
#![deny(rust_2018_idioms)]
#![forbid(unsafe_code)]
1 change: 1 addition & 0 deletions gix-ignore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ serde = ["dep:serde", "bstr/serde", "gix-glob/serde"]
[dependencies]
gix-glob = { version = "^0.15.1", path = "../gix-glob" }
gix-path = { version = "^0.10.3", path = "../gix-path" }
gix-trace = { version = "^0.1.6", path = "../gix-trace" }

bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"]}
unicode-bom = "2.0.2"
Expand Down
19 changes: 19 additions & 0 deletions gix-ignore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ pub struct Search {
pub patterns: Vec<gix_glob::search::pattern::List<search::Ignore>>,
}

/// The kind of *ignored* item.
///
/// This classification is obtained when checking if a path matches an ignore pattern.
#[derive(Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum Kind {
/// The item is ignored and will be removed to make place for tracked items that are to be checked out.
///
/// This is the default for ignored items.
/// Another way of thinking about this class is to consider these files *trashable*, or talk about them as `ignored-and-expendable`.
#[default]
Expendable,
/// An ignored file was additionally marked as *precious* using the `$` prefix to indicate the file shall be kept.
///
/// This means that precious files are treated like untracked files, which also must not be removed, but won't show up by default
/// as they are also ignored.
/// One can also talk about them as `ignored-and-precious`.
Precious,
}

///
pub mod parse;

Expand Down
40 changes: 33 additions & 7 deletions gix-ignore/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,43 @@ impl<'a> Lines<'a> {
}

impl<'a> Iterator for Lines<'a> {
type Item = (gix_glob::Pattern, usize);
type Item = (gix_glob::Pattern, usize, crate::Kind);

fn next(&mut self) -> Option<Self::Item> {
for line in self.lines.by_ref() {
for mut line in self.lines.by_ref() {
self.line_no += 1;
if line.first() == Some(&b'#') {
continue;
}
match gix_glob::Pattern::from_bytes(truncate_non_escaped_trailing_spaces(line)) {
let first = match line.first().copied() {
Some(b'#') | None => continue,
Some(c) => c,
};
let (kind, can_negate) = if first == b'$' {
line = &line[1..];
(crate::Kind::Precious, false)
} else {
let second = line.get(1);
if first == b'!' && second == Some(&b'$') {
gix_trace::error!(
"Line {} starts with !$ which is not allowed ('{}')",
self.line_no,
line.as_bstr()
);
continue;
}
if first == b'\\' && second == Some(&b'$') {
line = &line[1..];
}
(crate::Kind::Expendable, true)
};

line = truncate_non_escaped_trailing_spaces(line);
let res = if can_negate {
gix_glob::Pattern::from_bytes(line)
} else {
gix_glob::Pattern::from_bytes_without_negation(line)
};
match res {
None => continue,
Some(pattern) => return Some((pattern, self.line_no)),
Some(pattern) => return Some((pattern, self.line_no, kind)),
}
}
None
Expand Down
25 changes: 15 additions & 10 deletions gix-ignore/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub struct Match<'a> {
pub pattern: &'a gix_glob::Pattern,
/// The path to the source from which the pattern was loaded, or `None` if it was specified by other means.
pub source: Option<&'a Path>,
/// The kind of pattern this match represents.
pub kind: crate::Kind,
/// The line at which the pattern was found in its `source` file, or the occurrence in which it was provided.
pub sequence_number: usize,
}
Expand All @@ -24,13 +26,13 @@ pub struct Match<'a> {
pub struct Ignore;

impl Pattern for Ignore {
type Value = ();
type Value = crate::Kind;

fn bytes_to_patterns(bytes: &[u8], _source: &std::path::Path) -> Vec<pattern::Mapping<Self::Value>> {
crate::parse(bytes)
.map(|(pattern, line_number)| pattern::Mapping {
.map(|(pattern, line_number, kind)| pattern::Mapping {
pattern,
value: (),
value: kind,
sequence_number: line_number,
})
.collect()
Expand Down Expand Up @@ -61,7 +63,7 @@ impl Search {
Ok(group)
}

/// Parse a list of patterns, using slashes as path separators
/// Parse a list of ignore patterns, using slashes as path separators.
pub fn from_overrides(patterns: impl IntoIterator<Item = impl Into<OsString>>) -> Self {
Self::from_overrides_inner(&mut patterns.into_iter().map(Into::into))
}
Expand All @@ -73,11 +75,13 @@ impl Search {
.enumerate()
.filter_map(|(seq_id, pattern)| {
let pattern = gix_path::try_into_bstr(PathBuf::from(pattern)).ok()?;
gix_glob::parse(pattern.as_ref()).map(|p| pattern::Mapping {
pattern: p,
value: (),
sequence_number: seq_id,
})
crate::parse(pattern.as_ref())
.next()
.map(|(p, _seq_id, kind)| pattern::Mapping {
pattern: p,
value: kind,
sequence_number: seq_id + 1,
})
})
.collect(),
source: None,
Expand Down Expand Up @@ -112,7 +116,7 @@ pub fn pattern_matching_relative_path<'a>(
list.patterns.iter().rev().find_map(
|pattern::Mapping {
pattern,
value: (),
value: kind,
sequence_number,
}| {
pattern
Expand All @@ -125,6 +129,7 @@ pub fn pattern_matching_relative_path<'a>(
)
.then_some(Match {
pattern,
kind: *kind,
source: list.source.as_deref(),
sequence_number: *sequence_number,
})
Expand Down
12 changes: 12 additions & 0 deletions gix-ignore/tests/fixtures/ignore/precious.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$.config
\$starts-with-dollar
# html files are now precious and won't be discarded
$*.html

!foo.html

# this isn't allowed and ignored
!$foo.html

# but this is a literal !/* that is precious
$!/*
34 changes: 28 additions & 6 deletions gix-ignore/tests/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ use bstr::BString;
use gix_glob::{pattern::Mode, Pattern};
use gix_testtools::fixture_bytes;

#[test]
fn precious() {
let input = fixture_bytes("ignore/precious.txt");
let actual: Vec<_> = gix_ignore::parse(&input).map(flat_map).collect();
assert_eq!(
actual,
vec![
pat_precious(".config", Mode::NO_SUB_DIR, 1),
pat("$starts-with-dollar", Mode::NO_SUB_DIR, 2),
pat_precious("*.html", Mode::NO_SUB_DIR | Mode::ENDS_WITH, 4),
pat("foo.html", Mode::NO_SUB_DIR | Mode::NEGATIVE, 6),
pat_precious("!/*", Mode::empty(), 12),
]
);
}

#[test]
fn byte_order_marks_are_no_patterns() {
assert_eq!(
Expand Down Expand Up @@ -58,7 +74,7 @@ fn backslashes_before_hashes_are_no_comments() {

#[test]
fn trailing_spaces_can_be_escaped_to_be_literal() {
fn parse_one(input: &str) -> (BString, Mode, usize) {
fn parse_one(input: &str) -> (BString, Mode, usize, gix_ignore::Kind) {
let actual: Vec<_> = gix_ignore::parse(input.as_bytes()).map(flat_map).collect();
assert_eq!(actual.len(), 1, "{input:?} should match");
actual.into_iter().next().expect("present")
Expand Down Expand Up @@ -101,14 +117,20 @@ fn trailing_spaces_can_be_escaped_to_be_literal() {
);
}

fn flatten(input: Option<(Pattern, usize)>) -> Option<(BString, gix_glob::pattern::Mode, usize)> {
fn flatten(
input: Option<(Pattern, usize, gix_ignore::Kind)>,
) -> Option<(BString, gix_glob::pattern::Mode, usize, gix_ignore::Kind)> {
input.map(flat_map)
}

fn flat_map(input: (Pattern, usize)) -> (BString, gix_glob::pattern::Mode, usize) {
(input.0.text, input.0.mode, input.1)
fn flat_map(input: (Pattern, usize, gix_ignore::Kind)) -> (BString, gix_glob::pattern::Mode, usize, gix_ignore::Kind) {
(input.0.text, input.0.mode, input.1, input.2)
}

fn pat(pattern: &str, mode: Mode, pos: usize) -> (BString, Mode, usize, gix_ignore::Kind) {
(pattern.into(), mode, pos, gix_ignore::Kind::Expendable)
}

fn pat(pattern: &str, mode: Mode, pos: usize) -> (BString, Mode, usize) {
(pattern.into(), mode, pos)
fn pat_precious(pattern: &str, mode: Mode, pos: usize) -> (BString, Mode, usize, gix_ignore::Kind) {
(pattern.into(), mode, pos, gix_ignore::Kind::Precious)
}
Loading