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

feat: add new gix cat command. #1616

Merged
merged 1 commit into from
Oct 6, 2024
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
62 changes: 62 additions & 0 deletions gitoxide-core/src/repository/cat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::repository::revision::resolve::{BlobFormat, TreeMode};
use anyhow::{anyhow, Context};
use gix::diff::blob::ResourceKind;
use gix::filter::plumbing::driver::apply::Delay;
use gix::revision::Spec;

pub fn display_object(
repo: &gix::Repository,
spec: Spec<'_>,
tree_mode: TreeMode,
cache: Option<(BlobFormat, &mut gix::diff::blob::Platform)>,
mut out: impl std::io::Write,
) -> anyhow::Result<()> {
let id = spec.single().context("rev-spec must resolve to a single object")?;
let header = id.header()?;
match header.kind() {
gix::object::Kind::Tree if matches!(tree_mode, TreeMode::Pretty) => {
for entry in id.object()?.into_tree().iter() {
writeln!(out, "{}", entry?)?;
}
}
gix::object::Kind::Blob if cache.is_some() && spec.path_and_mode().is_some() => {
let (path, mode) = spec.path_and_mode().expect("is present");
match cache.expect("is some") {
(BlobFormat::Git, _) => unreachable!("no need for a cache when querying object db"),
(BlobFormat::Worktree, cache) => {
let platform = cache.attr_stack.at_entry(path, Some(mode.into()), &repo.objects)?;
let object = id.object()?;
let mut converted = cache.filter.worktree_filter.convert_to_worktree(
&object.data,
path,
&mut |_path, attrs| {
let _ = platform.matching_attributes(attrs);
},
Delay::Forbid,
)?;
std::io::copy(&mut converted, &mut out)?;
}
(BlobFormat::Diff | BlobFormat::DiffOrGit, cache) => {
cache.set_resource(id.detach(), mode.kind(), path, ResourceKind::OldOrSource, &repo.objects)?;
let resource = cache.resource(ResourceKind::OldOrSource).expect("just set");
let data = resource
.data
.as_slice()
.ok_or_else(|| anyhow!("Binary data at {} cannot be diffed", path))?;
out.write_all(data)?;
}
}
}
_ => out.write_all(&id.object()?.data)?,
}
Ok(())
}

pub(super) mod function {
use crate::repository::revision::resolve::TreeMode;

pub fn cat(repo: gix::Repository, revspec: &str, out: impl std::io::Write) -> anyhow::Result<()> {
super::display_object(&repo, repo.rev_parse(revspec)?, TreeMode::Pretty, None, out)?;
Ok(())
}
}
2 changes: 2 additions & 0 deletions gitoxide-core/src/repository/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum PathsOrPatterns {

#[cfg(feature = "archive")]
pub mod archive;
pub mod cat;
pub use cat::function::cat;
pub mod commit;
pub mod config;
mod credential;
Expand Down
57 changes: 2 additions & 55 deletions gitoxide-core/src/repository/revision/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,12 @@ pub enum BlobFormat {
pub(crate) mod function {
use std::ffi::OsString;

use anyhow::{anyhow, Context};
use gix::diff::blob::ResourceKind;
use gix::filter::plumbing::driver::apply::Delay;
use gix::revision::Spec;

use super::Options;
use crate::repository::cat::display_object;
use crate::repository::revision::resolve::BlobFormat;
use crate::{
repository::{revision, revision::resolve::TreeMode},
OutputFormat,
};
use crate::{repository::revision, OutputFormat};

pub fn resolve(
mut repo: gix::Repository,
Expand Down Expand Up @@ -109,52 +104,4 @@ pub(crate) mod function {
}
Ok(())
}

fn display_object(
repo: &gix::Repository,
spec: Spec<'_>,
tree_mode: TreeMode,
cache: Option<(BlobFormat, &mut gix::diff::blob::Platform)>,
mut out: impl std::io::Write,
) -> anyhow::Result<()> {
let id = spec.single().context("rev-spec must resolve to a single object")?;
let header = id.header()?;
match header.kind() {
gix::object::Kind::Tree if matches!(tree_mode, TreeMode::Pretty) => {
for entry in id.object()?.into_tree().iter() {
writeln!(out, "{}", entry?)?;
}
}
gix::object::Kind::Blob if cache.is_some() && spec.path_and_mode().is_some() => {
let (path, mode) = spec.path_and_mode().expect("is present");
match cache.expect("is some") {
(BlobFormat::Git, _) => unreachable!("no need for a cache when querying object db"),
(BlobFormat::Worktree, cache) => {
let platform = cache.attr_stack.at_entry(path, Some(mode.into()), &repo.objects)?;
let object = id.object()?;
let mut converted = cache.filter.worktree_filter.convert_to_worktree(
&object.data,
path,
&mut |_path, attrs| {
let _ = platform.matching_attributes(attrs);
},
Delay::Forbid,
)?;
std::io::copy(&mut converted, &mut out)?;
}
(BlobFormat::Diff | BlobFormat::DiffOrGit, cache) => {
cache.set_resource(id.detach(), mode.kind(), path, ResourceKind::OldOrSource, &repo.objects)?;
let resource = cache.resource(ResourceKind::OldOrSource).expect("just set");
let data = resource
.data
.as_slice()
.ok_or_else(|| anyhow!("Binary data at {} cannot be diffed", path))?;
out.write_all(data)?;
}
}
}
_ => out.write_all(&id.object()?.data)?,
}
Ok(())
}
}
9 changes: 9 additions & 0 deletions src/plumbing/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,15 @@ pub fn main() -> Result<()> {
},
),
},
Subcommands::Cat { revspec } => prepare_and_run(
"cat",
trace,
verbose,
progress,
progress_keep_open,
None,
move |_progress, out, _err| core::repository::cat(repository(Mode::Lenient)?, &revspec, out),
),
Subcommands::Commit(cmd) => match cmd {
commit::Subcommands::Verify { rev_spec } => prepare_and_run(
"commit-verify",
Expand Down
5 changes: 5 additions & 0 deletions src/plumbing/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ pub enum Subcommands {
/// Interact with submodules.
#[clap(alias = "submodules")]
Submodule(submodule::Platform),
/// Show whatever object is at the given spec.
Cat {
/// The object to print to stdout.
revspec: String,
},
IsClean,
IsChanged,
/// Show which git configuration values are used or planned.
Expand Down
Loading