Skip to content

Commit f37a930

Browse files
committed
Merge branch 'index-entries-attrs'
2 parents 3456c84 + df28b7d commit f37a930

File tree

54 files changed

+1162
-461
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1162
-461
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,16 @@ is usable to some extent.
8383
* [gix-command](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-command)
8484
* [gix-prompt](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-prompt)
8585
* [gix-refspec](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-refspec)
86-
* `gitoxide-core`
87-
* **very early** _(possibly without any documentation and many rough edges)_
88-
* [gix-utils](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-utils)
8986
* [gix-fs](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-fs)
87+
* [gix-utils](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-utils)
88+
* [gix-hashtable](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-hashtable)
9089
* [gix-worktree](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-worktree)
9190
* [gix-bitmap](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-bitmap)
91+
* `gitoxide-core`
92+
* **very early** _(possibly without any documentation and many rough edges)_
9293
* [gix-date](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-date)
93-
* [gix-hashtable](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-hashtable)
94-
* **idea** _(just a name placeholder)_
9594
* [gix-archive](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-archive)
95+
* **idea** _(just a name placeholder)_
9696
* [gix-note](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-note)
9797
* [gix-fetchhead](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-fetchhead)
9898
* [gix-filter](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-filter)

crate-status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ See its [README.md](https://github.com/Byron/gitoxide/blob/main/gix-lock/README.
690690
* [x] proper handling of worktree related refs
691691
* [ ] create, move, remove, and repair
692692
* [x] access exclude information
693-
* [ ] access attribute information
693+
* [x] access attribute information
694694
* [x] respect `core.worktree` configuration
695695
- **deviation**
696696
* The delicate interplay between `GIT_COMMON_DIR` and `GIT_WORK_TREE` isn't implemented.

gitoxide-core/src/index/entries.rs

Lines changed: 0 additions & 94 deletions
This file was deleted.

gitoxide-core/src/index/mod.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ pub struct Options {
55
pub format: crate::OutputFormat,
66
}
77

8-
mod entries;
9-
pub use entries::entries;
10-
118
pub mod information;
129

1310
fn parse_file(index_path: impl AsRef<Path>, object_hash: gix::hash::Kind) -> anyhow::Result<gix::index::File> {

gitoxide-core/src/repository/exclude.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::io;
22

3-
use anyhow::{bail, Context};
3+
use anyhow::bail;
44
use gix::prelude::FindExt;
55

66
use crate::OutputFormat;
@@ -31,11 +31,12 @@ pub fn query(
3131
bail!("JSON output isn't implemented yet");
3232
}
3333

34-
let worktree = repo
35-
.worktree()
36-
.with_context(|| "Cannot check excludes without a current worktree")?;
37-
let index = worktree.index()?;
38-
let mut cache = worktree.excludes(&index, Some(gix::ignore::Search::from_overrides(overrides)))?;
34+
let index = repo.index()?;
35+
let mut cache = repo.excludes(
36+
&index,
37+
Some(gix::ignore::Search::from_overrides(overrides)),
38+
Default::default(),
39+
)?;
3940

4041
let prefix = repo.prefix().expect("worktree - we have an index by now")?;
4142

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#[derive(Debug)]
2+
pub struct Options {
3+
pub format: crate::OutputFormat,
4+
/// If true, also show attributes
5+
pub attributes: Option<Attributes>,
6+
pub statistics: bool,
7+
}
8+
9+
#[derive(Debug)]
10+
pub enum Attributes {
11+
/// Look at worktree attributes and index as fallback.
12+
WorktreeAndIndex,
13+
/// Look at attributes from index files only.
14+
Index,
15+
}
16+
17+
pub(crate) mod function {
18+
use crate::repository::index::entries::{Attributes, Options};
19+
use gix::attrs::State;
20+
use gix::bstr::ByteSlice;
21+
use gix::odb::FindExt;
22+
use std::borrow::Cow;
23+
use std::io::{BufWriter, Write};
24+
25+
pub fn entries(
26+
repo: gix::Repository,
27+
out: impl std::io::Write,
28+
mut err: impl std::io::Write,
29+
Options {
30+
format,
31+
attributes,
32+
statistics,
33+
}: Options,
34+
) -> anyhow::Result<()> {
35+
use crate::OutputFormat::*;
36+
let index = repo.index()?;
37+
let mut cache = attributes
38+
.map(|attrs| {
39+
repo.attributes(
40+
&index,
41+
match attrs {
42+
Attributes::WorktreeAndIndex => {
43+
gix::worktree::cache::state::attributes::Source::WorktreeThenIdMapping
44+
}
45+
Attributes::Index => gix::worktree::cache::state::attributes::Source::IdMapping,
46+
},
47+
match attrs {
48+
Attributes::WorktreeAndIndex => {
49+
gix::worktree::cache::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped
50+
}
51+
Attributes::Index => gix::worktree::cache::state::ignore::Source::IdMapping,
52+
},
53+
None,
54+
)
55+
.map(|cache| (cache.attribute_matches(), cache))
56+
})
57+
.transpose()?;
58+
let mut stats = Statistics {
59+
entries: index.entries().len(),
60+
..Default::default()
61+
};
62+
63+
let mut out = BufWriter::new(out);
64+
#[cfg(feature = "serde")]
65+
if let Json = format {
66+
out.write_all(b"[\n")?;
67+
}
68+
let mut entries = index.entries().iter().peekable();
69+
while let Some(entry) = entries.next() {
70+
let attrs = cache
71+
.as_mut()
72+
.map(|(attrs, cache)| {
73+
cache
74+
.at_entry(entry.path(&index), None, |id, buf| repo.objects.find_blob(id, buf))
75+
.map(|entry| {
76+
let is_excluded = entry.is_excluded();
77+
stats.excluded += usize::from(is_excluded);
78+
let attributes: Vec<_> = {
79+
entry.matching_attributes(attrs);
80+
attrs.iter().map(|m| m.assignment.to_owned()).collect()
81+
};
82+
stats.with_attributes += usize::from(!attributes.is_empty());
83+
Attrs {
84+
is_excluded,
85+
attributes,
86+
}
87+
})
88+
})
89+
.transpose()?;
90+
match format {
91+
Human => to_human(&mut out, &index, entry, attrs)?,
92+
#[cfg(feature = "serde")]
93+
Json => to_json(&mut out, &index, entry, attrs, entries.peek().is_none())?,
94+
}
95+
}
96+
97+
#[cfg(feature = "serde")]
98+
if format == Json {
99+
out.write_all(b"]\n")?;
100+
out.flush()?;
101+
if statistics {
102+
serde_json::to_writer_pretty(&mut err, &stats)?;
103+
}
104+
}
105+
if format == Human && statistics {
106+
out.flush()?;
107+
stats.cache = cache.map(|c| *c.1.statistics());
108+
writeln!(err, "{:#?}", stats)?;
109+
}
110+
Ok(())
111+
}
112+
113+
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
114+
struct Attrs {
115+
is_excluded: bool,
116+
attributes: Vec<gix::attrs::Assignment>,
117+
}
118+
119+
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
120+
#[derive(Default, Debug)]
121+
struct Statistics {
122+
#[allow(dead_code)] // Not really dead, but Debug doesn't count for it even though it's crucial.
123+
pub entries: usize,
124+
pub excluded: usize,
125+
pub with_attributes: usize,
126+
pub cache: Option<gix::worktree::cache::Statistics>,
127+
}
128+
129+
#[cfg(feature = "serde")]
130+
fn to_json(
131+
mut out: &mut impl std::io::Write,
132+
index: &gix::index::File,
133+
entry: &gix::index::Entry,
134+
attrs: Option<Attrs>,
135+
is_last: bool,
136+
) -> anyhow::Result<()> {
137+
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
138+
struct Entry<'a> {
139+
stat: &'a gix::index::entry::Stat,
140+
hex_id: String,
141+
flags: u32,
142+
mode: u32,
143+
path: std::borrow::Cow<'a, str>,
144+
meta: Option<Attrs>,
145+
}
146+
147+
serde_json::to_writer(
148+
&mut out,
149+
&Entry {
150+
stat: &entry.stat,
151+
hex_id: entry.id.to_hex().to_string(),
152+
flags: entry.flags.bits(),
153+
mode: entry.mode.bits(),
154+
path: entry.path(index).to_str_lossy(),
155+
meta: attrs,
156+
},
157+
)?;
158+
159+
if is_last {
160+
out.write_all(b"\n")?;
161+
} else {
162+
out.write_all(b",\n")?;
163+
}
164+
Ok(())
165+
}
166+
167+
fn to_human(
168+
out: &mut impl std::io::Write,
169+
file: &gix::index::File,
170+
entry: &gix::index::Entry,
171+
attrs: Option<Attrs>,
172+
) -> std::io::Result<()> {
173+
writeln!(
174+
out,
175+
"{} {}{:?} {} {}{}",
176+
match entry.flags.stage() {
177+
0 => "BASE ",
178+
1 => "OURS ",
179+
2 => "THEIRS ",
180+
_ => "UNKNOWN",
181+
},
182+
if entry.flags.is_empty() {
183+
"".to_string()
184+
} else {
185+
format!("{:?} ", entry.flags)
186+
},
187+
entry.mode,
188+
entry.id,
189+
entry.path(file),
190+
attrs
191+
.map(|a| {
192+
let mut buf = String::new();
193+
if a.is_excluded {
194+
buf.push_str(" ❌");
195+
}
196+
if !a.attributes.is_empty() {
197+
buf.push_str(" (");
198+
for assignment in a.attributes {
199+
match assignment.state {
200+
State::Set => {
201+
buf.push_str(assignment.name.as_str());
202+
}
203+
State::Unset => {
204+
buf.push('-');
205+
buf.push_str(assignment.name.as_str());
206+
}
207+
State::Value(v) => {
208+
buf.push_str(assignment.name.as_str());
209+
buf.push('=');
210+
buf.push_str(v.as_ref().as_bstr().to_str_lossy().as_ref());
211+
}
212+
State::Unspecified => {
213+
buf.push('!');
214+
buf.push_str(assignment.name.as_str());
215+
}
216+
}
217+
buf.push_str(", ");
218+
}
219+
buf.pop();
220+
buf.pop();
221+
buf.push(')');
222+
}
223+
buf.into()
224+
})
225+
.unwrap_or(Cow::Borrowed(""))
226+
)
227+
}
228+
}

gitoxide-core/src/repository/index.rs renamed to gitoxide-core/src/repository/index/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ pub fn from_tree(
3434

3535
Ok(())
3636
}
37+
38+
pub mod entries;
39+
pub use entries::function::entries;

0 commit comments

Comments
 (0)