Skip to content

Commit 0ab4f64

Browse files
authored
Merge pull request #1410 from GitoxideLabs/status
tree -> index diff for status
2 parents 7ddf283 + d6ed2e2 commit 0ab4f64

File tree

64 files changed

+3863
-673
lines changed

Some content is hidden

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

64 files changed

+3863
-673
lines changed

Cargo.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gitoxide-core/src/repository/status.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anyhow::bail;
22
use gix::bstr::{BStr, BString, ByteSlice};
3-
use gix::status::index_worktree::iter::Item;
3+
use gix::status::{self, index_worktree};
44
use gix_status::index_as_worktree::{Change, Conflict, EntryStatus};
55
use std::path::Path;
66

@@ -109,21 +109,54 @@ pub fn show(
109109
}
110110
None => gix::status::Submodule::AsConfigured { check_dirty: false },
111111
})
112-
.into_index_worktree_iter(pathspecs)?;
112+
.into_iter(pathspecs)?;
113113

114114
for item in iter.by_ref() {
115115
let item = item?;
116116
match item {
117-
Item::Modification {
117+
status::Item::TreeIndex(change) => {
118+
let (location, _, _, _) = change.fields();
119+
let status = match change {
120+
gix::diff::index::Change::Addition { .. } => "A",
121+
gix::diff::index::Change::Deletion { .. } => "D",
122+
gix::diff::index::Change::Modification { .. } => "M",
123+
gix::diff::index::Change::Rewrite {
124+
ref source_location, ..
125+
} => {
126+
let source_location = gix::path::from_bstr(source_location.as_ref());
127+
let source_location = gix::path::relativize_with_prefix(&source_location, prefix);
128+
writeln!(
129+
out,
130+
"{status: >2} {source_rela_path} → {dest_rela_path}",
131+
status = "R",
132+
source_rela_path = source_location.display(),
133+
dest_rela_path =
134+
gix::path::relativize_with_prefix(&gix::path::from_bstr(location), prefix).display(),
135+
)?;
136+
continue;
137+
}
138+
gix::diff::index::Change::Unmerged { .. } => {
139+
// Unmerged entries from the worktree-index are displayed as part of the index-worktree comparison.
140+
// Here we have nothing to do with them and can ignore.
141+
continue;
142+
}
143+
};
144+
writeln!(
145+
out,
146+
"{status: >2} {rela_path}",
147+
rela_path = gix::path::relativize_with_prefix(&gix::path::from_bstr(location), prefix).display(),
148+
)?;
149+
}
150+
status::Item::IndexWorktree(index_worktree::Item::Modification {
118151
entry: _,
119152
entry_index: _,
120153
rela_path,
121154
status,
122-
} => print_index_entry_status(&mut out, prefix, rela_path.as_ref(), status)?,
123-
Item::DirectoryContents {
155+
}) => print_index_entry_status(&mut out, prefix, rela_path.as_ref(), status)?,
156+
status::Item::IndexWorktree(index_worktree::Item::DirectoryContents {
124157
entry,
125158
collapsed_directory_status,
126-
} => {
159+
}) => {
127160
if collapsed_directory_status.is_none() {
128161
writeln!(
129162
out,
@@ -139,12 +172,12 @@ pub fn show(
139172
)?;
140173
}
141174
}
142-
Item::Rewrite {
175+
status::Item::IndexWorktree(index_worktree::Item::Rewrite {
143176
source,
144177
dirwalk_entry,
145178
copy: _, // TODO: how to visualize copies?
146179
..
147-
} => {
180+
}) => {
148181
// TODO: handle multi-status characters, there can also be modifications at the same time as determined by their ID and potentially diffstats.
149182
writeln!(
150183
out,
@@ -175,9 +208,8 @@ pub fn show(
175208
writeln!(err, "{outcome:#?}", outcome = out.index_worktree).ok();
176209
}
177210

178-
writeln!(err, "\nhead -> index isn't implemented yet")?;
179-
progress.init(Some(out.index.entries().len()), gix::progress::count("files"));
180-
progress.set(out.index.entries().len());
211+
progress.init(Some(out.worktree_index.entries().len()), gix::progress::count("files"));
212+
progress.set(out.worktree_index.entries().len());
181213
progress.show_throughput(start);
182214
Ok(())
183215
}

gitoxide-core/src/repository/tree.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
use std::{borrow::Cow, io};
2-
31
use anyhow::bail;
42
use gix::Tree;
3+
use std::io::BufWriter;
4+
use std::{borrow::Cow, io};
55

66
use crate::OutputFormat;
77

88
mod entries {
9-
use std::collections::VecDeque;
10-
119
use gix::{
1210
bstr::{BStr, BString, ByteSlice, ByteVec},
1311
objs::tree::EntryRef,
1412
traverse::tree::visit::Action,
1513
};
14+
use std::collections::VecDeque;
1615

1716
use crate::repository::tree::format_entry;
1817

@@ -58,6 +57,9 @@ mod entries {
5857
}
5958

6059
fn push_element(&mut self, name: &BStr) {
60+
if name.is_empty() {
61+
return;
62+
}
6163
if !self.path.is_empty() {
6264
self.path.push(b'/');
6365
}
@@ -66,6 +68,10 @@ mod entries {
6668
}
6769

6870
impl gix::traverse::tree::Visit for Traverse<'_, '_> {
71+
fn pop_back_tracked_path_and_set_current(&mut self) {
72+
self.path = self.path_deque.pop_back().unwrap_or_default();
73+
}
74+
6975
fn pop_front_tracked_path_and_set_current(&mut self) {
7076
self.path = self.path_deque.pop_front().expect("every parent is set only once");
7177
}
@@ -91,12 +97,12 @@ mod entries {
9197
fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> Action {
9298
let size = self
9399
.repo
94-
.and_then(|repo| repo.find_object(entry.oid).map(|o| o.data.len()).ok());
100+
.and_then(|repo| repo.find_header(entry.oid).map(|h| h.size()).ok());
95101
if let Some(out) = &mut self.out {
96102
format_entry(out, entry, self.path.as_bstr(), size).ok();
97103
}
98104
if let Some(size) = size {
99-
self.stats.num_bytes += size as u64;
105+
self.stats.num_bytes += size;
100106
}
101107

102108
use gix::object::tree::EntryKind::*;
@@ -154,18 +160,17 @@ pub fn entries(
154160
let tree = treeish_to_tree(treeish, &repo)?;
155161

156162
if recursive {
157-
let mut delegate = entries::Traverse::new(extended.then_some(&repo), Some(&mut out));
158-
tree.traverse().breadthfirst(&mut delegate)?;
163+
let mut write = BufWriter::new(out);
164+
let mut delegate = entries::Traverse::new(extended.then_some(&repo), Some(&mut write));
165+
tree.traverse().depthfirst(&mut delegate)?;
159166
} else {
160167
for entry in tree.iter() {
161168
let entry = entry?;
162169
format_entry(
163170
&mut out,
164171
&entry.inner,
165172
entry.inner.filename,
166-
extended
167-
.then(|| entry.id().object().map(|o| o.data.len()))
168-
.transpose()?,
173+
extended.then(|| entry.id().header().map(|o| o.size())).transpose()?,
169174
)?;
170175
}
171176
}
@@ -182,12 +187,12 @@ fn format_entry(
182187
mut out: impl io::Write,
183188
entry: &gix::objs::tree::EntryRef<'_>,
184189
filename: &gix::bstr::BStr,
185-
size: Option<usize>,
190+
size: Option<u64>,
186191
) -> std::io::Result<()> {
187192
use gix::objs::tree::EntryKind::*;
188-
writeln!(
193+
write!(
189194
out,
190-
"{} {}{} {}",
195+
"{} {}{} ",
191196
match entry.mode.kind() {
192197
Tree => "TREE",
193198
Blob => "BLOB",
@@ -196,7 +201,8 @@ fn format_entry(
196201
Commit => "SUBM",
197202
},
198203
entry.oid,
199-
size.map_or_else(|| "".into(), |s| Cow::Owned(format!(" {s}"))),
200-
filename
201-
)
204+
size.map_or_else(|| "".into(), |s| Cow::Owned(format!(" {s}")))
205+
)?;
206+
out.write_all(filename)?;
207+
out.write_all(b"\n")
202208
}

gix-diff/Cargo.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ rust-version = "1.65"
1313
autotests = false
1414

1515
[features]
16-
default = ["blob"]
17-
## Enable diffing of blobs using imara-diff, which also allows for a generic rewrite tracking implementation.
16+
default = ["blob", "index"]
17+
## Enable diffing of blobs using imara-diff.
1818
blob = ["dep:imara-diff", "dep:gix-filter", "dep:gix-worktree", "dep:gix-path", "dep:gix-fs", "dep:gix-command", "dep:gix-tempfile", "dep:gix-trace", "dep:gix-traverse"]
19+
## Enable diffing of two indices, which also allows for a generic rewrite tracking implementation.
20+
index = ["dep:gix-index", "dep:gix-pathspec", "dep:gix-attributes"]
1921
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
20-
serde = ["dep:serde", "gix-hash/serde", "gix-object/serde"]
22+
serde = ["dep:serde", "gix-hash/serde", "gix-object/serde", "gix-index?/serde"]
2123
## Make it possible to compile to the `wasm32-unknown-unknown` target.
2224
wasm = ["dep:getrandom"]
2325

2426
[lib]
2527
doctest = false
2628

2729
[dependencies]
30+
gix-index = { version = "^0.37.0", path = "../gix-index", optional = true }
31+
gix-pathspec = { version = "^0.8.1", path = "../gix-pathspec", optional = true }
32+
gix-attributes = { version = "^0.23.1", path = "../gix-attributes", optional = true }
2833
gix-hash = { version = "^0.15.1", path = "../gix-hash" }
2934
gix-object = { version = "^0.46.1", path = "../gix-object" }
3035
gix-filter = { version = "^0.16.0", path = "../gix-filter", optional = true }

0 commit comments

Comments
 (0)