150 lines
4.5 KiB
Rust
150 lines
4.5 KiB
Rust
use std::boxed::Box;
|
|
use std::env::{set_current_dir as cd, current_dir as pwd};
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
use crate::backend::{Backend, Repo};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Git;
|
|
|
|
impl Backend for Git {
|
|
fn download(&self, source: &str, dest: &Path) -> bool {
|
|
Command::new("git")
|
|
.arg("clone")
|
|
.arg("--recursive")
|
|
.arg(source)
|
|
.arg(dest)
|
|
.output() // To suppress (capture) output
|
|
.expect("PROCESS ERROR!")
|
|
.status
|
|
.success()
|
|
}
|
|
|
|
fn submodules(&self) -> Vec<PathBuf> {
|
|
let cmd = Command::new("git")
|
|
.arg("config")
|
|
.arg("--file")
|
|
.arg(".gitmodules")
|
|
.arg("--get-regexp")
|
|
.arg("path")
|
|
.output()
|
|
.expect("WTF");
|
|
if cmd.status.success() {
|
|
// Command succeded, split by the first space character to find the path to the submodule
|
|
let out = std::string::String::from_utf8(cmd.stdout).expect("Wrong unicode");
|
|
let mut results = Vec::new();
|
|
for line in out.lines() {
|
|
results.push(PathBuf::from(line.split_once(" ").expect("Misformed .gitmodules").1));
|
|
}
|
|
return results;
|
|
} else {
|
|
// No submodules found (even if .gitmodules exist but is empty)
|
|
return Vec::new();
|
|
}
|
|
}
|
|
|
|
fn branch(&self) -> String {
|
|
let output = Command::new("git")
|
|
.arg("rev-parse")
|
|
.arg("--abbrev-ref")
|
|
.arg("HEAD")
|
|
.output()
|
|
.expect("WTF");
|
|
if !output.status.success() {
|
|
panic!("Corrupted git repository???");
|
|
}
|
|
String::from_utf8(output.stdout).unwrap().trim().to_string()
|
|
}
|
|
|
|
fn checkout(&self, target: &str) -> bool {
|
|
let status = Command::new("git")
|
|
.arg("checkout")
|
|
.arg(target)
|
|
.status()
|
|
.expect("PROCESS ERROR!");
|
|
status.success()
|
|
}
|
|
|
|
fn has_updates(&self) -> bool {
|
|
// Refresh remote
|
|
if !Command::new("git")
|
|
.arg("fetch")
|
|
.arg("--quiet")
|
|
.arg("origin")
|
|
.status()
|
|
.expect("WTF")
|
|
.success()
|
|
{
|
|
// FAILED, no internet??
|
|
// TODO: This should be a forgebuild error message
|
|
eprintln!("Fetching updates failed");
|
|
return false;
|
|
}
|
|
let branch = self.branch();
|
|
if Command::new("git")
|
|
.arg("diff")
|
|
.arg("--quiet")
|
|
.arg(&format!("remotes/origin/{}", &branch))
|
|
.status()
|
|
.expect("WTF")
|
|
.success()
|
|
{
|
|
// Command succeeeded, no updates
|
|
return false;
|
|
}
|
|
// Updates
|
|
return true;
|
|
}
|
|
|
|
fn update(&self) -> bool {
|
|
if Command::new("git")
|
|
.arg("pull")
|
|
.arg("--ff-only")
|
|
.arg("origin")
|
|
.arg(self.branch())
|
|
.status()
|
|
.expect("WTF")
|
|
.success()
|
|
{
|
|
// Main updates succeeded. If new submodules were added, initialize them
|
|
if !Command::new("git")
|
|
.arg("submodule")
|
|
.arg("update")
|
|
.arg("--init")
|
|
.arg("--recursive")
|
|
.status()
|
|
.expect("WTF")
|
|
.success()
|
|
{
|
|
// TODO: Should be forgebuild error message
|
|
eprintln!("Failed to initialize submodules which were added to the repo");
|
|
}
|
|
return true;
|
|
} else {
|
|
// Main updates failed
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// TODO: Maybe this should be a generic implementation part of the trait?
|
|
fn subupdate(&self) -> bool {
|
|
let mut found_subupdates = false;
|
|
let prev_dir = pwd().expect("failed to pwd");
|
|
for subpath in self.submodules() {
|
|
// Move into the submodule
|
|
cd(subpath).unwrap();
|
|
// Generate a Repo instance for the submodule but don't enable subupdates
|
|
// Just in case someone would trigger an infinite loop by accident
|
|
let subrepo = Repo::new(Box::new(Git), "irrelevant", &pwd().expect("failed to pwd"), false);
|
|
if subrepo.backend.update() {
|
|
found_subupdates = true;
|
|
}
|
|
// Move back into main repo
|
|
cd(&prev_dir).unwrap();
|
|
}
|
|
found_subupdates
|
|
}
|
|
|
|
}
|