Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 97f8e96

Browse files
committed
Merge branch 'filter-programs'
2 parents f27ca12 + 198ce27 commit 97f8e96

22 files changed

Lines changed: 2013 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ members = [
233233
"gix-worktree",
234234
"gix-revision",
235235
"gix-packetline",
236+
"gix-packetline-blocking",
236237
"gix-mailmap",
237238
"gix-note",
238239
"gix-negotiate",

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ is usable to some extent.
6262
* [gix-validate](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-validate)
6363
* [gix-url](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-url)
6464
* [gix-packetline](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-packetline)
65+
* [gix-packetline-blocking](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-packetline)
6566
* [gix-transport](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-transport)
6667
* [gix-protocol](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-protocol)
6768
* [gix-pack](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-pack)

gix-filter/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ doctest = false
1515
gix-hash = { version = "^0.11.3", path = "../gix-hash" }
1616
gix-trace = { version = "^0.1.2", path = "../gix-trace" }
1717
gix-object = { version = "^0.32.0", path = "../gix-object" }
18+
gix-command = { version = "^0.2.6", path = "../gix-command" }
19+
gix-quote = { version = "^0.4.5", path = "../gix-quote" }
20+
gix-path = { version = "^0.8.2", path = "../gix-path" }
21+
gix-packetline = { package = "gix-packetline-blocking", version = "^0.16.3", path = "../gix-packetline-blocking" }
1822

1923
encoding_rs = "0.8.32"
2024
bstr = { version = "1.5.0", default-features = false, features = ["std"] }
2125
thiserror = "1.0.38"
26+
27+
28+
[dev-dependencies]
29+
once_cell = "1.18.0"
30+
gix-testtools = { path = "../tests/tools" }

gix-filter/examples/ident.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
use bstr::{ByteSlice, ByteVec};
2+
use gix_filter::driver::process;
3+
use std::io::{stdin, stdout, Read, Write};
4+
use std::time::Duration;
5+
6+
static PREFIX: &str = "➡";
7+
8+
fn main() -> Result<(), Box<dyn std::error::Error>> {
9+
let mut args = std::env::args();
10+
let sub_command = args.nth(1).ok_or("Need sub-command")?;
11+
let next_arg = args.next(); // possibly %f
12+
let needs_failure = next_arg.as_deref().map_or(false, |file| file.ends_with("fail"));
13+
if needs_failure {
14+
panic!("failure requested for {sub_command}");
15+
}
16+
17+
match sub_command.as_str() {
18+
"process" => {
19+
let disallow_delay = next_arg.as_deref().map_or(false, |arg| arg == "disallow-delay");
20+
let mut srv = gix_filter::driver::process::Server::handshake(
21+
stdin(),
22+
stdout(),
23+
"git-filter",
24+
|versions| versions.contains(&2).then_some(2),
25+
if disallow_delay {
26+
&["clean", "smudge"]
27+
} else {
28+
&["clean", "smudge", "delay"]
29+
},
30+
)?;
31+
32+
let mut next_smudge_aborts = false;
33+
let mut next_smudge_fails_permanently = false; // a test validates that we don't actually hang
34+
let mut delayed = Vec::new();
35+
while let Some(mut request) = srv.next_request()? {
36+
let needs_failure = request
37+
.meta
38+
.iter()
39+
.find_map(|(key, value)| (key == "pathname").then_some(value))
40+
.map_or(false, |path| path.ends_with(b"fail"));
41+
let pathname = request
42+
.meta
43+
.iter()
44+
.find_map(|(key, value)| (key == "pathname").then(|| value.clone()));
45+
if needs_failure {
46+
panic!("process failure requested: {:?}", request.meta);
47+
}
48+
let can_delay = request
49+
.meta
50+
.iter()
51+
.any(|(key, value)| key == "can-delay" && value == "1");
52+
match request.command.as_str() {
53+
"clean" => {
54+
let mut buf = Vec::new();
55+
request.as_read().read_to_end(&mut buf)?;
56+
request.write_status(if can_delay {
57+
process::Status::delayed()
58+
} else {
59+
process::Status::success()
60+
})?;
61+
62+
let lines = if let Some(delayed_lines) = buf
63+
.is_empty()
64+
.then(|| {
65+
delayed
66+
.iter()
67+
.position(|(cmd, path, _)| {
68+
*cmd == request.command.as_str() && Some(path) == pathname.as_ref()
69+
})
70+
.map(|pos| delayed.remove(pos).2)
71+
})
72+
.flatten()
73+
{
74+
delayed_lines
75+
} else {
76+
let mut lines = Vec::new();
77+
for mut line in buf.lines_with_terminator() {
78+
if line.starts_with(PREFIX.as_bytes()) {
79+
line = &line[PREFIX.len()..];
80+
}
81+
lines.push_str(line);
82+
}
83+
lines
84+
};
85+
if can_delay {
86+
delayed.push(("clean", pathname.expect("needed for delayed operation"), lines));
87+
} else {
88+
request.as_write().write_all(&lines)?;
89+
request.write_status(process::Status::Previous)?;
90+
}
91+
}
92+
"smudge" => {
93+
let mut buf = Vec::new();
94+
request.as_read().read_to_end(&mut buf)?;
95+
let status = if next_smudge_aborts {
96+
next_smudge_aborts = false;
97+
process::Status::abort()
98+
} else if next_smudge_fails_permanently {
99+
process::Status::exit()
100+
} else if can_delay {
101+
process::Status::delayed()
102+
} else {
103+
process::Status::success()
104+
};
105+
request.write_status(status)?;
106+
107+
let lines = if let Some(delayed_lines) = buf
108+
.is_empty()
109+
.then(|| {
110+
delayed
111+
.iter()
112+
.position(|(cmd, path, _)| {
113+
*cmd == request.command.as_str() && Some(path) == pathname.as_ref()
114+
})
115+
.map(|pos| delayed.remove(pos).2)
116+
})
117+
.flatten()
118+
{
119+
delayed_lines
120+
} else {
121+
let mut lines = Vec::new();
122+
for line in buf.lines_with_terminator() {
123+
if !line.starts_with(PREFIX.as_bytes()) {
124+
lines.push_str(PREFIX.as_bytes());
125+
}
126+
lines.push_str(line);
127+
}
128+
lines
129+
};
130+
131+
if can_delay {
132+
delayed.push(("smudge", pathname.expect("needed for delayed operation"), lines));
133+
} else {
134+
request.as_write().write_all(&lines)?;
135+
request.write_status(process::Status::Previous)?;
136+
}
137+
}
138+
"list_available_blobs" => {
139+
{
140+
let mut out = request.as_write();
141+
let mut last_cmd = None;
142+
let mut buf = Vec::<u8>::new();
143+
for (cmd, path, _) in &delayed {
144+
if last_cmd.get_or_insert(*cmd) != cmd {
145+
panic!("the API doesn't support mixing cmds as paths might not be unique anymore")
146+
}
147+
buf.clear();
148+
buf.push_str("pathname=");
149+
buf.extend_from_slice(path);
150+
out.write_all(&buf)?
151+
}
152+
}
153+
request.write_status(process::Status::success())?;
154+
}
155+
"wait-1-s" => {
156+
std::io::copy(&mut request.as_read(), &mut std::io::sink())?;
157+
request.write_status(process::Status::success())?;
158+
std::thread::sleep(Duration::from_secs(1));
159+
}
160+
"next-smudge-aborts" => {
161+
std::io::copy(&mut request.as_read(), &mut std::io::sink())?;
162+
request.write_status(process::Status::success())?;
163+
next_smudge_aborts = true;
164+
}
165+
"next-invocation-returns-strange-status-and-smudge-fails-permanently" => {
166+
std::io::copy(&mut request.as_read(), &mut std::io::sink())?;
167+
request.write_status(process::Status::success())?;
168+
next_smudge_fails_permanently = true;
169+
}
170+
unknown => panic!("Unknown capability requested: {unknown}"),
171+
}
172+
}
173+
}
174+
// simple filters actually don't support streaming - they have to first read all input, then produce all output,
175+
// but can't mix reading stdin and write to stdout at the same time as `git` (or `gitoxide`) don't read the output while
176+
// writing the input.
177+
"clean" => {
178+
let mut stdin = stdin().lock();
179+
let mut stdout = stdout().lock();
180+
let mut buf = Vec::new();
181+
std::io::copy(&mut stdin, &mut buf)?;
182+
for mut line in buf.lines_with_terminator() {
183+
if line.starts_with(PREFIX.as_bytes()) {
184+
line = &line[PREFIX.len()..];
185+
}
186+
stdout.write_all(line).map(|_| true)?;
187+
}
188+
}
189+
"smudge" => {
190+
let mut stdin = stdin().lock();
191+
let mut stdout = stdout().lock();
192+
let mut buf = Vec::new();
193+
std::io::copy(&mut stdin, &mut buf)?;
194+
for line in buf.lines_with_terminator() {
195+
if !line.starts_with(PREFIX.as_bytes()) {
196+
stdout.write_all(PREFIX.as_bytes())?;
197+
}
198+
stdout.write_all(line).map(|_| true)?;
199+
}
200+
}
201+
unknown => panic!("Unknown sub-command: {unknown}"),
202+
}
203+
Ok(())
204+
}

0 commit comments

Comments
 (0)