Skip to content
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

tree -> index diff for status #1410

Merged
merged 17 commits into from
Jan 3, 2025
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
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
Loading