Replace Git subprocesses with native APIs
This commit is contained in:
@@ -368,48 +368,31 @@ impl App {
|
||||
}
|
||||
|
||||
fn read_git_state(path: &Path) -> Option<GitState> {
|
||||
let path = path.to_str()?;
|
||||
let branches = std::process::Command::new("git")
|
||||
.args([
|
||||
"-C",
|
||||
path,
|
||||
"for-each-ref",
|
||||
"--format=%(refname:short)",
|
||||
"refs/heads",
|
||||
])
|
||||
.output()
|
||||
let repository = gix::discover(path).ok()?;
|
||||
let mut branches = repository
|
||||
.references()
|
||||
.ok()?
|
||||
.local_branches()
|
||||
.ok()?
|
||||
.map(|reference| {
|
||||
reference
|
||||
.map(|reference| String::from_utf8_lossy(reference.name().shorten()).into_owned())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.ok()?;
|
||||
if !branches.status.success() {
|
||||
return None;
|
||||
}
|
||||
let mut branches = String::from_utf8_lossy(&branches.stdout)
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|branch| !branch.is_empty())
|
||||
.map(str::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
branches.sort();
|
||||
branches.dedup();
|
||||
|
||||
let current = std::process::Command::new("git")
|
||||
.args(["-C", path, "symbolic-ref", "--quiet", "--short", "HEAD"])
|
||||
.output()
|
||||
.ok()
|
||||
.filter(|output| output.status.success())
|
||||
.map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned())
|
||||
.filter(|branch| !branch.is_empty());
|
||||
let current = repository
|
||||
.head_name()
|
||||
.ok()?
|
||||
.map(|name| String::from_utf8_lossy(name.shorten()).into_owned());
|
||||
let label = current.clone().unwrap_or_else(|| {
|
||||
std::process::Command::new("git")
|
||||
.args(["-C", path, "rev-parse", "--short", "HEAD"])
|
||||
.output()
|
||||
repository
|
||||
.head_id()
|
||||
.ok()
|
||||
.filter(|output| output.status.success())
|
||||
.map(|output| {
|
||||
format!(
|
||||
"detached @ {}",
|
||||
String::from_utf8_lossy(&output.stdout).trim()
|
||||
)
|
||||
})
|
||||
.and_then(|id| id.shorten().ok())
|
||||
.map(|id| format!("detached @ {id}"))
|
||||
.unwrap_or_else(|| "No branch".to_owned())
|
||||
});
|
||||
Some(GitState {
|
||||
@@ -420,22 +403,25 @@ fn read_git_state(path: &Path) -> Option<GitState> {
|
||||
}
|
||||
|
||||
fn git_switch(path: &Path, branch: &str) -> Result<(), String> {
|
||||
let output = std::process::Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(path)
|
||||
.args(["switch", "--", branch])
|
||||
.output()
|
||||
.map_err(|error| format!("Could not run Git: {error}"))?;
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let detail = String::from_utf8_lossy(&output.stderr).trim().to_owned();
|
||||
Err(if detail.is_empty() {
|
||||
"Could not switch Git branches.".into()
|
||||
} else {
|
||||
format!("Could not switch Git branches: {detail}")
|
||||
})
|
||||
let repository = git2::Repository::discover(path)
|
||||
.map_err(|error| format!("Could not switch Git branches: {error}"))?;
|
||||
let reference = format!("refs/heads/{branch}");
|
||||
let previous = repository
|
||||
.find_reference("HEAD")
|
||||
.ok()
|
||||
.map(|head| (head.symbolic_target().map(str::to_owned), head.target()));
|
||||
repository
|
||||
.set_head(&reference)
|
||||
.map_err(|error| format!("Could not switch Git branches: {error}"))?;
|
||||
if let Err(error) = repository.checkout_head(Some(git2::build::CheckoutBuilder::new().safe())) {
|
||||
if let Some((Some(reference), _)) = &previous {
|
||||
let _ = repository.set_head(reference);
|
||||
} else if let Some((_, Some(target))) = previous {
|
||||
let _ = repository.set_head_detached(target);
|
||||
}
|
||||
return Err(format!("Could not switch Git branches: {error}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn draft_title(projects: &[ProjectWithSessions], project_id: i32) -> String {
|
||||
@@ -450,7 +436,6 @@ pub(super) fn draft_title(projects: &[ProjectWithSessions], project_id: i32) ->
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::database::{Project, Session};
|
||||
use std::process::Command;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[test]
|
||||
@@ -541,37 +526,30 @@ mod tests {
|
||||
std::process::id()
|
||||
));
|
||||
std::fs::create_dir(&directory).unwrap();
|
||||
for arguments in [
|
||||
&["init", "-b", "main"][..],
|
||||
&["config", "user.name", "DS4Server Test"],
|
||||
&["config", "user.email", "test@ds4server.invalid"],
|
||||
] {
|
||||
assert!(
|
||||
Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(&directory)
|
||||
.args(arguments)
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
);
|
||||
}
|
||||
let mut options = git2::RepositoryInitOptions::new();
|
||||
options.initial_head("main");
|
||||
let repository = git2::Repository::init_opts(&directory, &options).unwrap();
|
||||
let mut config = repository.config().unwrap();
|
||||
config.set_str("user.name", "DS4Server Test").unwrap();
|
||||
config
|
||||
.set_str("user.email", "test@ds4server.invalid")
|
||||
.unwrap();
|
||||
drop(config);
|
||||
std::fs::write(directory.join("tracked.txt"), "test").unwrap();
|
||||
for arguments in [
|
||||
&["add", "tracked.txt"][..],
|
||||
&["commit", "-m", "Initial"],
|
||||
&["branch", "feature"],
|
||||
] {
|
||||
assert!(
|
||||
Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(&directory)
|
||||
.args(arguments)
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
);
|
||||
}
|
||||
let mut index = repository.index().unwrap();
|
||||
index.add_path(Path::new("tracked.txt")).unwrap();
|
||||
let tree_id = index.write_tree().unwrap();
|
||||
index.write().unwrap();
|
||||
let tree = repository.find_tree(tree_id).unwrap();
|
||||
let signature = repository.signature().unwrap();
|
||||
let commit = repository
|
||||
.commit(Some("HEAD"), &signature, &signature, "Initial", &tree, &[])
|
||||
.unwrap();
|
||||
let commit = repository.find_commit(commit).unwrap();
|
||||
repository.branch("feature", &commit, false).unwrap();
|
||||
drop(commit);
|
||||
drop(tree);
|
||||
drop(repository);
|
||||
|
||||
let state = read_git_state(&directory).unwrap();
|
||||
assert_eq!(state.current.as_deref(), Some("main"));
|
||||
|
||||
Reference in New Issue
Block a user