Something went wrong. Try again.
Automatically mirror remote git pushes
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495use std::io::{self, BufRead};use std::process::Command;
use anyhow::{Context, Result, bail};
fn main() { if let Err(e) = run() { eprintln!("relay: {e:#}"); std::process::exit(1); }}
fn run() -> Result<()> { let mirror_remote = get_mirror_remote()?; let refs = read_pushed_refs()?;
if refs.is_empty() { return Ok(()); }
push_to_mirror(&mirror_remote, &refs)}
fn get_mirror_remote() -> Result<String> { let output = Command::new("git") .args(["config", "relay.mirror-remote"]) .output() .context("failed to run git config")?;
if !output.status.success() { bail!( "relay.mirror-remote is not configured\n \ Set it with: git config relay.mirror-remote <remote-name>" ); }
Ok(String::from_utf8(output.stdout) .context("git config output is not valid UTF-8")? .trim() .to_string())}
struct PushedRef { local_ref: String, local_sha: String, remote_ref: String,}
fn read_pushed_refs() -> Result<Vec<PushedRef>> { let stdin = io::stdin(); let mut refs = Vec::new();
for line in stdin.lock().lines() { let line = line.context("failed to read stdin")?; let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() >= 4 { refs.push(PushedRef { local_ref: parts[0].to_string(), local_sha: parts[1].to_string(), remote_ref: parts[2].to_string(), }); } }
Ok(refs)}
fn push_to_mirror(remote: &str, refs: &[PushedRef]) -> Result<()> { let zero_sha = "0".repeat(40);
let refspecs: Vec<String> = refs .iter() .map(|r| { if r.local_sha == zero_sha { format!(":{}", r.remote_ref) } else { format!("{}:{}", r.local_ref, r.remote_ref) } }) .collect();
let status = Command::new("git") .arg("push") .arg(remote) .args(&refspecs) .status() .context("failed to execute git push")?;
if !status.success() { bail!("mirror push to '{}' failed", remote); }
Ok(())}