-
-
Notifications
You must be signed in to change notification settings - Fork 341
/
Copy pathresolve.rs
160 lines (151 loc) · 5.81 KB
/
resolve.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::OutputFormat;
pub struct Options {
pub format: OutputFormat,
pub explain: bool,
pub cat_file: bool,
pub tree_mode: TreeMode,
pub blob_format: BlobFormat,
pub show_reference: bool,
}
pub enum TreeMode {
Raw,
Pretty,
}
#[derive(Copy, Clone)]
pub enum BlobFormat {
Git,
Worktree,
Diff,
DiffOrGit,
}
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::revision::resolve::BlobFormat;
use crate::{
repository::{revision, revision::resolve::TreeMode},
OutputFormat,
};
pub fn resolve(
mut repo: gix::Repository,
specs: Vec<OsString>,
mut out: impl std::io::Write,
Options {
format,
explain,
cat_file,
tree_mode,
blob_format,
show_reference,
}: Options,
) -> anyhow::Result<()> {
repo.object_cache_size_if_unset(1024 * 1024);
let mut cache = (!matches!(blob_format, BlobFormat::Git))
.then(|| {
repo.diff_resource_cache(
match blob_format {
BlobFormat::Git => {
unreachable!("checked before")
}
BlobFormat::Worktree | BlobFormat::Diff => {
gix::diff::blob::pipeline::Mode::ToWorktreeAndBinaryToText
}
BlobFormat::DiffOrGit => gix::diff::blob::pipeline::Mode::ToGitUnlessBinaryToTextIsPresent,
},
Default::default(),
)
})
.transpose()?;
match format {
OutputFormat::Human => {
for spec in specs {
if explain {
return revision::explain(spec, out);
}
let spec = gix::path::os_str_into_bstr(&spec)?;
let spec = repo.rev_parse(spec)?;
if cat_file {
return display_object(&repo, spec, tree_mode, cache.as_mut().map(|c| (blob_format, c)), out);
}
if let Some(r) = spec.first_reference().filter(|_| show_reference) {
writeln!(out, "{}", r.name)?;
}
if let Some(r) = spec.second_reference().filter(|_| show_reference) {
writeln!(out, "{}", r.name)?;
}
writeln!(out, "{spec}", spec = spec.detach())?;
}
}
#[cfg(feature = "serde")]
OutputFormat::Json => {
if explain {
anyhow::bail!("Explanations are only for human consumption")
}
serde_json::to_writer_pretty(
&mut out,
&specs
.into_iter()
.map(|spec| {
gix::path::os_str_into_bstr(&spec)
.map_err(anyhow::Error::from)
.and_then(|spec| repo.rev_parse(spec).map_err(Into::into))
.map(Spec::detach)
})
.collect::<Result<Vec<_>, _>>()?,
)?;
}
}
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(())
}
}