Skip to content

Commit 03b0650

Browse files
committed
chore: update gix-command to 0.3.4
This version of gix-command gains the ability to parse shebang lines on Windows. This replaces StGit code that did the same, but with perhaps more robustness. Refs: #407
1 parent 9329c3f commit 03b0650

File tree

3 files changed

+18
-109
lines changed

3 files changed

+18
-109
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ ctrlc = "3.4"
3535
encoding_rs = "0.8"
3636
flate2 = "1"
3737
gix = { version = "0.58", default-features = false, features = ["revision"] }
38-
gix-command = "0.3"
38+
gix-command = "0.3.4"
3939
indexmap = "2.1"
4040
is-terminal = "0.4"
4141
nom = { version = "7", default_features = false, features = ["std"] }

src/patch/edit/interactive.rs

+15-106
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
//! Functions for conducting interactive patch edit session.
44
55
use std::{
6-
ffi::{OsStr, OsString},
6+
ffi::OsString,
77
fs::File,
8-
io::{BufWriter, Read, Write},
8+
io::{BufWriter, Write},
99
path::Path,
1010
};
1111

1212
use anyhow::{anyhow, Context, Result};
13-
use bstr::{BString, ByteSlice};
13+
use bstr::BString;
1414

1515
use super::description::{EditablePatchDescription, EditedPatchDescription};
1616

@@ -89,15 +89,25 @@ pub(crate) fn call_editor<P: AsRef<Path>>(
8989
stderr.flush()?;
9090
}
9191

92-
let result = run_editor(&editor, path.as_ref());
92+
let status_result = gix_command::prepare(&editor)
93+
.arg(path.as_ref())
94+
.with_shell_allow_argument_splitting()
95+
.stdout(std::process::Stdio::inherit())
96+
.spawn()
97+
.with_context(|| format!("running editor: {}", editor.to_string_lossy()))
98+
.and_then(|mut child| {
99+
child
100+
.wait()
101+
.with_context(|| format!("waiting editor: {}", editor.to_string_lossy()))
102+
});
93103

94104
if use_advice && !is_dumb {
95105
let mut stderr = std::io::stderr();
96106
stderr.write_all("\r\x1b[K".as_bytes()).unwrap_or(());
97107
stderr.flush()?;
98108
}
99109

100-
let status = result?;
110+
let status = status_result?;
101111

102112
if !status.success() {
103113
return Err(anyhow!(
@@ -112,32 +122,6 @@ pub(crate) fn call_editor<P: AsRef<Path>>(
112122
Ok(buf)
113123
}
114124

115-
fn run_editor<P: AsRef<Path>>(editor: &OsStr, path: P) -> Result<std::process::ExitStatus> {
116-
let prep = gix_command::prepare(editor)
117-
.arg(path.as_ref())
118-
.with_shell_allow_argument_splitting()
119-
.stdout(std::process::Stdio::inherit());
120-
121-
let mut command = if cfg!(windows) && !prep.use_shell {
122-
if let Some(interpreter) = parse_interpreter(&prep.command) {
123-
let mut cmd = std::process::Command::new(interpreter);
124-
cmd.arg(prep.command).arg(path.as_ref());
125-
cmd
126-
} else {
127-
std::process::Command::from(prep)
128-
}
129-
} else {
130-
std::process::Command::from(prep)
131-
};
132-
let mut child = command
133-
.spawn()
134-
.with_context(|| format!("running editor: {}", editor.to_string_lossy()))?;
135-
136-
child
137-
.wait()
138-
.with_context(|| format!("waiting editor: {}", editor.to_string_lossy()))
139-
}
140-
141125
/// Determine user's editor of choice based on config and environment.
142126
fn get_editor(config: &gix::config::Snapshot) -> Result<OsString> {
143127
let editor = if let Some(editor) = std::env::var_os("GIT_EDITOR") {
@@ -163,78 +147,3 @@ fn get_editor(config: &gix::config::Snapshot) -> Result<OsString> {
163147
};
164148
Ok(editor)
165149
}
166-
167-
fn parse_interpreter(command: &OsStr) -> Option<OsString> {
168-
let command_path = Path::new(command);
169-
if command_path.extension().and_then(|ext| ext.to_str()) == Some("exe") {
170-
return None;
171-
}
172-
173-
let mut buffer = [0; 128];
174-
if let Some(n) = std::fs::File::open(command_path)
175-
.ok()
176-
.and_then(|mut file| file.read(&mut buffer).ok())
177-
{
178-
parse_shebang(&buffer[..n])
179-
.and_then(|bytes| bytes.to_os_str().ok())
180-
.map(|osstr| osstr.to_os_string())
181-
} else {
182-
None
183-
}
184-
}
185-
186-
fn parse_shebang(buffer: &[u8]) -> Option<&[u8]> {
187-
buffer
188-
.as_bstr()
189-
.lines()
190-
.next()
191-
.and_then(|line| line.strip_prefix(b"#!"))
192-
.and_then(|shebang| {
193-
shebang.rfind_byteset(b"/\\").map(|index| {
194-
if let Some(space_index) = shebang[index..].find_byte(b' ') {
195-
&shebang[..index + space_index]
196-
} else {
197-
shebang
198-
}
199-
})
200-
})
201-
}
202-
203-
#[cfg(test)]
204-
mod tests {
205-
use super::*;
206-
207-
#[test]
208-
fn plain_shebang() {
209-
assert_eq!(parse_shebang(b"#!/bin/sh\nsome stuff").unwrap(), b"/bin/sh");
210-
}
211-
212-
#[test]
213-
fn shebang_with_options() {
214-
assert_eq!(
215-
parse_shebang(b"#!/bin/sh -i -o -u\nsome stuff").unwrap(),
216-
b"/bin/sh"
217-
);
218-
}
219-
220-
#[test]
221-
fn shebang_with_backslashes() {
222-
assert_eq!(
223-
parse_shebang(b"#!C:\\Program Files\\Imashell.exe\nsome stuff").unwrap(),
224-
b"C:\\Program Files\\Imashell.exe"
225-
);
226-
}
227-
228-
#[test]
229-
fn shebang_with_trailing_space() {
230-
assert_eq!(
231-
parse_shebang(b"#!/bin/sh \nsome stuff").unwrap(),
232-
b"/bin/sh"
233-
);
234-
}
235-
236-
#[test]
237-
fn not_a_shebang() {
238-
assert!(parse_shebang(b"/bin/sh\nsome stuff").is_none());
239-
}
240-
}

0 commit comments

Comments
 (0)