use crate::error::{Error, Result};
use crate::objects::ObjectId;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FetchHeadLine {
pub oid: ObjectId,
pub for_merge: bool,
pub description: String,
}
pub(crate) fn write_fetch_head(git_dir: &Path, lines: &[FetchHeadLine]) -> Result<()> {
let mut body = String::new();
for line in lines {
if line.for_merge {
body.push_str(&format!("{}\t\t{}\n", line.oid.to_hex(), line.description));
} else {
body.push_str(&format!(
"{}\tnot-for-merge\t{}\n",
line.oid.to_hex(),
line.description
));
}
}
std::fs::write(git_dir.join("FETCH_HEAD"), body).map_err(Error::Io)
}
#[must_use]
pub fn merge_object_ids_hex(input: &str) -> Vec<String> {
let mut out = Vec::new();
for line in input.lines() {
if line.trim().is_empty() {
continue;
}
let Some(first_tab) = line.find('\t') else {
continue;
};
let oid = &line[..first_tab];
if !ObjectId::is_full_hex(oid) {
continue;
}
let rest = &line[first_tab + 1..];
if rest.starts_with("not-for-merge") {
continue;
}
let desc = rest.strip_prefix('\t').unwrap_or(rest);
if desc.is_empty() {
continue;
}
out.push(oid.to_owned());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_fetch_head_for_merge_uses_double_tab() {
let tmp = tempfile::tempdir().expect("tempdir");
let git_dir = tmp.path().join(".git");
std::fs::create_dir_all(&git_dir).unwrap();
let oid = ObjectId::from_hex(&"a".repeat(40)).unwrap();
write_fetch_head(
&git_dir,
&[FetchHeadLine {
oid,
for_merge: true,
description: "../repo".to_owned(),
}],
)
.unwrap();
let text = std::fs::read_to_string(git_dir.join("FETCH_HEAD")).unwrap();
assert_eq!(text, format!("{}\t\t../repo\n", oid.to_hex()));
}
#[test]
fn double_tab_for_merge_branch() {
let input = "a".repeat(40);
let line = format!("{input}\t\tbranch 'main' of ../repo2\n");
let oids = merge_object_ids_hex(&line);
assert_eq!(oids, vec![input]);
}
#[test]
fn not_for_merge_skipped() {
let input = format!(
"{}\tnot-for-merge\tbranch 'other' of ../x\n",
"b".repeat(40)
);
assert!(merge_object_ids_hex(&input).is_empty());
}
#[test]
fn bare_url_line_for_merge() {
let oid = "c".repeat(40);
let line = format!("{oid}\t\t../repo2\n");
assert_eq!(merge_object_ids_hex(&line), vec![oid]);
}
}