98 lines · 2.8 KB · Rust
RawDownload
use anyhow::{Context, anyhow, bail};use gix::{    bstr::{BString, ByteSlice},    merge::tree::TreatAsUnresolved,    prelude::Write,}; use super::tree::Options;use crate::OutputFormat; pub fn commit(    mut repo: gix::Repository,    out: &mut dyn std::io::Write,    err: &mut dyn std::io::Write,    ours: BString,    theirs: BString,    Options {        format,        file_favor,        tree_favor,        in_memory,        debug,        message: _,        update_head: _,    }: Options,) -> anyhow::Result<()> {    if format != OutputFormat::Human {        bail!("JSON output isn't implemented yet");    }    repo.object_cache_size_if_unset(repo.compute_object_cache_size_for_tree_diffs(&**repo.index_or_empty()?));    if in_memory {        repo.objects.enable_object_memory();    }    let (ours_ref, ours_id) = refname_and_commit(&repo, ours)?;    let (theirs_ref, theirs_id) = refname_and_commit(&repo, theirs)?;     let options = repo        .tree_merge_options()?        .with_file_favor(file_favor)        .with_tree_favor(tree_favor);    let ours_id_str = ours_id.to_string();    let theirs_id_str = theirs_id.to_string();    let labels = gix::merge::blob::builtin_driver::text::Labels {        ancestor: None,        current: ours_ref            .as_ref()            .map_or(ours_id_str.as_str().into(), |n| n.as_bstr())            .into(),        other: theirs_ref            .as_ref()            .map_or(theirs_id_str.as_str().into(), |n| n.as_bstr())            .into(),    };    let res = repo        .merge_commits(ours_id, theirs_id, labels, options.into())?        .tree_merge;    let has_conflicts = res.conflicts.is_empty();    let has_unresolved_conflicts = res.has_unresolved_conflicts(TreatAsUnresolved::default());    {        let _span = gix::trace::detail!("Writing merged tree");        let mut written = 0;        let tree_id = res            .tree            .detach()            .write(|tree| {                written += 1;                repo.write(tree)            })            .map_err(|err| anyhow!("{err}"))?;        writeln!(out, "{tree_id} (wrote {written} trees)")?;    }     if debug {        writeln!(err, "{:#?}", res.conflicts)?;    }    if !has_conflicts {        writeln!(err, "{} possibly resolved conflicts", res.conflicts.len())?;    }    if has_unresolved_conflicts {        bail!("Tree conflicted")    }    Ok(())} fn refname_and_commit(    repo: &gix::Repository,    revspec: BString,) -> anyhow::Result<(Option<BString>, gix::hash::ObjectId)> {    let spec = repo.rev_parse(revspec.as_bstr())?;    let commit_id = spec        .single()        .context("Expected revspec to expand to a single rev only")?        .object()?        .peel_to_commit()?        .id;    let refname = spec.first_reference().map(|r| r.name.shorten().as_bstr().to_owned());    Ok((refname, commit_id))}