use std::collections::{BTreeSet, HashMap};
use crate::error::{Error, Result};
use crate::index::{Index, IndexEntry};
use crate::merge_base::is_ancestor;
use crate::objects::{parse_tree, ObjectId, ObjectKind};
use crate::repo::Repository;
pub fn tree_to_index_entries(
repo: &Repository,
oid: &ObjectId,
prefix: &str,
) -> Result<Vec<IndexEntry>> {
let obj = repo.odb.read(oid)?;
if obj.kind != ObjectKind::Tree {
return Err(Error::Message(format!("expected tree, got {}", obj.kind)));
}
let entries = parse_tree(&obj.data)?;
let mut result = Vec::new();
for te in entries {
let name = String::from_utf8_lossy(&te.name).into_owned();
let path = if prefix.is_empty() {
name.clone()
} else {
format!("{prefix}/{name}")
};
if te.mode == 0o040000 {
let sub = tree_to_index_entries(repo, &te.oid, &path)?;
result.extend(sub);
} else {
let path_bytes = path.into_bytes();
result.push(IndexEntry {
ctime_sec: 0,
ctime_nsec: 0,
mtime_sec: 0,
mtime_nsec: 0,
dev: 0,
ino: 0,
mode: te.mode,
uid: 0,
gid: 0,
size: 0,
oid: te.oid,
flags: path_bytes.len().min(0xFFF) as u16,
flags_extended: None,
path: path_bytes,
base_index_pos: 0,
});
}
}
Ok(result)
}
#[must_use]
pub fn tree_to_map(entries: Vec<IndexEntry>) -> HashMap<Vec<u8>, IndexEntry> {
let mut out = HashMap::new();
for e in entries {
out.insert(e.path.clone(), e);
}
out
}
pub fn compose_fast_forward_index(
repo: &Repository,
target_tree: ObjectId,
head_tree: ObjectId,
current_index: &Index,
) -> Result<Index> {
let mut new_entries = tree_to_index_entries(repo, &target_tree, "")?;
let target_paths: BTreeSet<Vec<u8>> = new_entries.iter().map(|e| e.path.clone()).collect();
let head_entries = tree_to_map(tree_to_index_entries(repo, &head_tree, "")?);
for e in ¤t_index.entries {
if e.stage() != 0 {
continue;
}
if target_paths.contains(&e.path) {
continue;
}
if !head_entries.contains_key(&e.path) {
new_entries.push(e.clone());
}
}
let mut index = Index::new();
index.entries = new_entries;
index.sort();
index.dedup_paths_keep_last();
Ok(index)
}
pub fn compose_octopus_final_index(pre_merge_index: &Index, final_index: &mut Index) {
let final_paths: BTreeSet<Vec<u8>> = final_index
.entries
.iter()
.filter(|e| e.stage() == 0)
.map(|e| e.path.clone())
.collect();
for e in &pre_merge_index.entries {
if e.stage() != 0 {
continue;
}
if final_paths.contains(&e.path) {
continue;
}
final_index.entries.push(e.clone());
}
final_index.sort();
}
pub fn reduce_octopus_merge_heads(
repo: &Repository,
merge_oids: &[ObjectId],
merge_names: &[String],
) -> Result<(Vec<ObjectId>, Vec<String>)> {
debug_assert_eq!(merge_oids.len(), merge_names.len());
let mut out_oids = Vec::with_capacity(merge_oids.len());
let mut out_names = Vec::with_capacity(merge_names.len());
for i in 0..merge_oids.len() {
let oid = merge_oids[i];
let redundant = merge_oids
.iter()
.enumerate()
.any(|(j, &other)| j != i && is_ancestor(repo, oid, other).unwrap_or(false));
if !redundant {
out_oids.push(oid);
out_names.push(merge_names[i].clone());
}
}
Ok((out_oids, out_names))
}