Skip to content

Commit

Permalink
Merge pull request #1410 from GitoxideLabs/status
Browse files Browse the repository at this point in the history
tree -> index diff for status
  • Loading branch information
Byron authored Jan 3, 2025
2 parents 7ddf283 + d6ed2e2 commit 0ab4f64
Show file tree
Hide file tree
Showing 64 changed files with 3,863 additions and 673 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

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

54 changes: 43 additions & 11 deletions gitoxide-core/src/repository/status.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::bail;
use gix::bstr::{BStr, BString, ByteSlice};
use gix::status::index_worktree::iter::Item;
use gix::status::{self, index_worktree};
use gix_status::index_as_worktree::{Change, Conflict, EntryStatus};
use std::path::Path;

Expand Down Expand Up @@ -109,21 +109,54 @@ pub fn show(
}
None => gix::status::Submodule::AsConfigured { check_dirty: false },
})
.into_index_worktree_iter(pathspecs)?;
.into_iter(pathspecs)?;

for item in iter.by_ref() {
let item = item?;
match item {
Item::Modification {
status::Item::TreeIndex(change) => {
let (location, _, _, _) = change.fields();
let status = match change {
gix::diff::index::Change::Addition { .. } => "A",
gix::diff::index::Change::Deletion { .. } => "D",
gix::diff::index::Change::Modification { .. } => "M",
gix::diff::index::Change::Rewrite {
ref source_location, ..
} => {
let source_location = gix::path::from_bstr(source_location.as_ref());
let source_location = gix::path::relativize_with_prefix(&source_location, prefix);
writeln!(
out,
"{status: >2} {source_rela_path} → {dest_rela_path}",
status = "R",
source_rela_path = source_location.display(),
dest_rela_path =
gix::path::relativize_with_prefix(&gix::path::from_bstr(location), prefix).display(),
)?;
continue;
}
gix::diff::index::Change::Unmerged { .. } => {
// Unmerged entries from the worktree-index are displayed as part of the index-worktree comparison.
// Here we have nothing to do with them and can ignore.
continue;
}
};
writeln!(
out,
"{status: >2} {rela_path}",
rela_path = gix::path::relativize_with_prefix(&gix::path::from_bstr(location), prefix).display(),
)?;
}
status::Item::IndexWorktree(index_worktree::Item::Modification {
entry: _,
entry_index: _,
rela_path,
status,
} => print_index_entry_status(&mut out, prefix, rela_path.as_ref(), status)?,
Item::DirectoryContents {
}) => print_index_entry_status(&mut out, prefix, rela_path.as_ref(), status)?,
status::Item::IndexWorktree(index_worktree::Item::DirectoryContents {
entry,
collapsed_directory_status,
} => {
}) => {
if collapsed_directory_status.is_none() {
writeln!(
out,
Expand All @@ -139,12 +172,12 @@ pub fn show(
)?;
}
}
Item::Rewrite {
status::Item::IndexWorktree(index_worktree::Item::Rewrite {
source,
dirwalk_entry,
copy: _, // TODO: how to visualize copies?
..
} => {
}) => {
// TODO: handle multi-status characters, there can also be modifications at the same time as determined by their ID and potentially diffstats.
writeln!(
out,
Expand Down Expand Up @@ -175,9 +208,8 @@ pub fn show(
writeln!(err, "{outcome:#?}", outcome = out.index_worktree).ok();
}

writeln!(err, "\nhead -> index isn't implemented yet")?;
progress.init(Some(out.index.entries().len()), gix::progress::count("files"));
progress.set(out.index.entries().len());
progress.init(Some(out.worktree_index.entries().len()), gix::progress::count("files"));
progress.set(out.worktree_index.entries().len());
progress.show_throughput(start);
Ok(())
}
Expand Down
40 changes: 23 additions & 17 deletions gitoxide-core/src/repository/tree.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use std::{borrow::Cow, io};

use anyhow::bail;
use gix::Tree;
use std::io::BufWriter;
use std::{borrow::Cow, io};

use crate::OutputFormat;

mod entries {
use std::collections::VecDeque;

use gix::{
bstr::{BStr, BString, ByteSlice, ByteVec},
objs::tree::EntryRef,
traverse::tree::visit::Action,
};
use std::collections::VecDeque;

use crate::repository::tree::format_entry;

Expand Down Expand Up @@ -58,6 +57,9 @@ mod entries {
}

fn push_element(&mut self, name: &BStr) {
if name.is_empty() {
return;
}
if !self.path.is_empty() {
self.path.push(b'/');
}
Expand All @@ -66,6 +68,10 @@ mod entries {
}

impl gix::traverse::tree::Visit for Traverse<'_, '_> {
fn pop_back_tracked_path_and_set_current(&mut self) {
self.path = self.path_deque.pop_back().unwrap_or_default();
}

fn pop_front_tracked_path_and_set_current(&mut self) {
self.path = self.path_deque.pop_front().expect("every parent is set only once");
}
Expand All @@ -91,12 +97,12 @@ mod entries {
fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> Action {
let size = self
.repo
.and_then(|repo| repo.find_object(entry.oid).map(|o| o.data.len()).ok());
.and_then(|repo| repo.find_header(entry.oid).map(|h| h.size()).ok());
if let Some(out) = &mut self.out {
format_entry(out, entry, self.path.as_bstr(), size).ok();
}
if let Some(size) = size {
self.stats.num_bytes += size as u64;
self.stats.num_bytes += size;
}

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

if recursive {
let mut delegate = entries::Traverse::new(extended.then_some(&repo), Some(&mut out));
tree.traverse().breadthfirst(&mut delegate)?;
let mut write = BufWriter::new(out);
let mut delegate = entries::Traverse::new(extended.then_some(&repo), Some(&mut write));
tree.traverse().depthfirst(&mut delegate)?;
} else {
for entry in tree.iter() {
let entry = entry?;
format_entry(
&mut out,
&entry.inner,
entry.inner.filename,
extended
.then(|| entry.id().object().map(|o| o.data.len()))
.transpose()?,
extended.then(|| entry.id().header().map(|o| o.size())).transpose()?,
)?;
}
}
Expand All @@ -182,12 +187,12 @@ fn format_entry(
mut out: impl io::Write,
entry: &gix::objs::tree::EntryRef<'_>,
filename: &gix::bstr::BStr,
size: Option<usize>,
size: Option<u64>,
) -> std::io::Result<()> {
use gix::objs::tree::EntryKind::*;
writeln!(
write!(
out,
"{} {}{} {}",
"{} {}{} ",
match entry.mode.kind() {
Tree => "TREE",
Blob => "BLOB",
Expand All @@ -196,7 +201,8 @@ fn format_entry(
Commit => "SUBM",
},
entry.oid,
size.map_or_else(|| "".into(), |s| Cow::Owned(format!(" {s}"))),
filename
)
size.map_or_else(|| "".into(), |s| Cow::Owned(format!(" {s}")))
)?;
out.write_all(filename)?;
out.write_all(b"\n")
}
11 changes: 8 additions & 3 deletions gix-diff/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ rust-version = "1.65"
autotests = false

[features]
default = ["blob"]
## Enable diffing of blobs using imara-diff, which also allows for a generic rewrite tracking implementation.
default = ["blob", "index"]
## Enable diffing of blobs using imara-diff.
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"]
## Enable diffing of two indices, which also allows for a generic rewrite tracking implementation.
index = ["dep:gix-index", "dep:gix-pathspec", "dep:gix-attributes"]
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
serde = ["dep:serde", "gix-hash/serde", "gix-object/serde"]
serde = ["dep:serde", "gix-hash/serde", "gix-object/serde", "gix-index?/serde"]
## Make it possible to compile to the `wasm32-unknown-unknown` target.
wasm = ["dep:getrandom"]

[lib]
doctest = false

[dependencies]
gix-index = { version = "^0.37.0", path = "../gix-index", optional = true }
gix-pathspec = { version = "^0.8.1", path = "../gix-pathspec", optional = true }
gix-attributes = { version = "^0.23.1", path = "../gix-attributes", optional = true }
gix-hash = { version = "^0.15.1", path = "../gix-hash" }
gix-object = { version = "^0.46.1", path = "../gix-object" }
gix-filter = { version = "^0.16.0", path = "../gix-filter", optional = true }
Expand Down
Loading

0 comments on commit 0ab4f64

Please sign in to comment.