Skip to content

Commit 9c8bc03

Browse files
committed
feat: add new gix cat command.
It only prints things without fuzz. Inspired by https://youtu.be/JYH5ILv5g1g?si=bHLBPFJiZyRUTl6u&t=211.
1 parent c76e6b4 commit 9c8bc03

File tree

5 files changed

+80
-55
lines changed

5 files changed

+80
-55
lines changed

Diff for: gitoxide-core/src/repository/cat.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use crate::repository::revision::resolve::{BlobFormat, TreeMode};
2+
use anyhow::{anyhow, Context};
3+
use gix::diff::blob::ResourceKind;
4+
use gix::filter::plumbing::driver::apply::Delay;
5+
use gix::revision::Spec;
6+
7+
pub fn display_object(
8+
repo: &gix::Repository,
9+
spec: Spec<'_>,
10+
tree_mode: TreeMode,
11+
cache: Option<(BlobFormat, &mut gix::diff::blob::Platform)>,
12+
mut out: impl std::io::Write,
13+
) -> anyhow::Result<()> {
14+
let id = spec.single().context("rev-spec must resolve to a single object")?;
15+
let header = id.header()?;
16+
match header.kind() {
17+
gix::object::Kind::Tree if matches!(tree_mode, TreeMode::Pretty) => {
18+
for entry in id.object()?.into_tree().iter() {
19+
writeln!(out, "{}", entry?)?;
20+
}
21+
}
22+
gix::object::Kind::Blob if cache.is_some() && spec.path_and_mode().is_some() => {
23+
let (path, mode) = spec.path_and_mode().expect("is present");
24+
match cache.expect("is some") {
25+
(BlobFormat::Git, _) => unreachable!("no need for a cache when querying object db"),
26+
(BlobFormat::Worktree, cache) => {
27+
let platform = cache.attr_stack.at_entry(path, Some(mode.into()), &repo.objects)?;
28+
let object = id.object()?;
29+
let mut converted = cache.filter.worktree_filter.convert_to_worktree(
30+
&object.data,
31+
path,
32+
&mut |_path, attrs| {
33+
let _ = platform.matching_attributes(attrs);
34+
},
35+
Delay::Forbid,
36+
)?;
37+
std::io::copy(&mut converted, &mut out)?;
38+
}
39+
(BlobFormat::Diff | BlobFormat::DiffOrGit, cache) => {
40+
cache.set_resource(id.detach(), mode.kind(), path, ResourceKind::OldOrSource, &repo.objects)?;
41+
let resource = cache.resource(ResourceKind::OldOrSource).expect("just set");
42+
let data = resource
43+
.data
44+
.as_slice()
45+
.ok_or_else(|| anyhow!("Binary data at {} cannot be diffed", path))?;
46+
out.write_all(data)?;
47+
}
48+
}
49+
}
50+
_ => out.write_all(&id.object()?.data)?,
51+
}
52+
Ok(())
53+
}
54+
55+
pub(super) mod function {
56+
use crate::repository::revision::resolve::TreeMode;
57+
58+
pub fn cat(repo: gix::Repository, revspec: &str, out: impl std::io::Write) -> anyhow::Result<()> {
59+
super::display_object(&repo, repo.rev_parse(revspec)?, TreeMode::Pretty, None, out)?;
60+
Ok(())
61+
}
62+
}

Diff for: gitoxide-core/src/repository/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub enum PathsOrPatterns {
1919

2020
#[cfg(feature = "archive")]
2121
pub mod archive;
22+
pub mod cat;
23+
pub use cat::function::cat;
2224
pub mod commit;
2325
pub mod config;
2426
mod credential;

Diff for: gitoxide-core/src/repository/revision/resolve.rs

+2-55
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,12 @@ pub enum BlobFormat {
2525
pub(crate) mod function {
2626
use std::ffi::OsString;
2727

28-
use anyhow::{anyhow, Context};
29-
use gix::diff::blob::ResourceKind;
30-
use gix::filter::plumbing::driver::apply::Delay;
3128
use gix::revision::Spec;
3229

3330
use super::Options;
31+
use crate::repository::cat::display_object;
3432
use crate::repository::revision::resolve::BlobFormat;
35-
use crate::{
36-
repository::{revision, revision::resolve::TreeMode},
37-
OutputFormat,
38-
};
33+
use crate::{repository::revision, OutputFormat};
3934

4035
pub fn resolve(
4136
mut repo: gix::Repository,
@@ -109,52 +104,4 @@ pub(crate) mod function {
109104
}
110105
Ok(())
111106
}
112-
113-
fn display_object(
114-
repo: &gix::Repository,
115-
spec: Spec<'_>,
116-
tree_mode: TreeMode,
117-
cache: Option<(BlobFormat, &mut gix::diff::blob::Platform)>,
118-
mut out: impl std::io::Write,
119-
) -> anyhow::Result<()> {
120-
let id = spec.single().context("rev-spec must resolve to a single object")?;
121-
let header = id.header()?;
122-
match header.kind() {
123-
gix::object::Kind::Tree if matches!(tree_mode, TreeMode::Pretty) => {
124-
for entry in id.object()?.into_tree().iter() {
125-
writeln!(out, "{}", entry?)?;
126-
}
127-
}
128-
gix::object::Kind::Blob if cache.is_some() && spec.path_and_mode().is_some() => {
129-
let (path, mode) = spec.path_and_mode().expect("is present");
130-
match cache.expect("is some") {
131-
(BlobFormat::Git, _) => unreachable!("no need for a cache when querying object db"),
132-
(BlobFormat::Worktree, cache) => {
133-
let platform = cache.attr_stack.at_entry(path, Some(mode.into()), &repo.objects)?;
134-
let object = id.object()?;
135-
let mut converted = cache.filter.worktree_filter.convert_to_worktree(
136-
&object.data,
137-
path,
138-
&mut |_path, attrs| {
139-
let _ = platform.matching_attributes(attrs);
140-
},
141-
Delay::Forbid,
142-
)?;
143-
std::io::copy(&mut converted, &mut out)?;
144-
}
145-
(BlobFormat::Diff | BlobFormat::DiffOrGit, cache) => {
146-
cache.set_resource(id.detach(), mode.kind(), path, ResourceKind::OldOrSource, &repo.objects)?;
147-
let resource = cache.resource(ResourceKind::OldOrSource).expect("just set");
148-
let data = resource
149-
.data
150-
.as_slice()
151-
.ok_or_else(|| anyhow!("Binary data at {} cannot be diffed", path))?;
152-
out.write_all(data)?;
153-
}
154-
}
155-
}
156-
_ => out.write_all(&id.object()?.data)?,
157-
}
158-
Ok(())
159-
}
160107
}

Diff for: src/plumbing/main.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,15 @@ pub fn main() -> Result<()> {
11171117
},
11181118
),
11191119
},
1120+
Subcommands::Cat { revspec } => prepare_and_run(
1121+
"cat",
1122+
trace,
1123+
verbose,
1124+
progress,
1125+
progress_keep_open,
1126+
None,
1127+
move |_progress, out, _err| core::repository::cat(repository(Mode::Lenient)?, &revspec, out),
1128+
),
11201129
Subcommands::Commit(cmd) => match cmd {
11211130
commit::Subcommands::Verify { rev_spec } => prepare_and_run(
11221131
"commit-verify",

Diff for: src/plumbing/options/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ pub enum Subcommands {
130130
/// Interact with submodules.
131131
#[clap(alias = "submodules")]
132132
Submodule(submodule::Platform),
133+
/// Show whatever object is at the given spec.
134+
Cat {
135+
/// The object to print to stdout.
136+
revspec: String,
137+
},
133138
IsClean,
134139
IsChanged,
135140
/// Show which git configuration values are used or planned.

0 commit comments

Comments
 (0)