use crate::digest_tracker::DigestTracker;
use crate::git::GitRepo;
use anyhow::{Context, Result};
use std::path::Path;
pub struct SuccessorNavigator;
impl SuccessorNavigator {
pub fn find_branch_point(
repo: &GitRepo,
_output_dir: &Path,
new_layers: &[crate::extracted_image::Layer],
) -> Result<(Option<git2::Oid>, usize)> {
if new_layers.is_empty() {
return Ok((None, 0));
}
let mut current_commit: Option<git2::Oid> = None;
let mut layer_index = 0;
while layer_index < new_layers.len() {
let current_layer = &new_layers[layer_index];
let candidates = if let Some(commit) = current_commit {
repo.get_commit_successors(Some(commit))?
} else {
repo.get_commit_successors(None)?
};
let mut found_match = false;
for candidate_oid in candidates {
if Self::commit_has_layer_at_position(
repo,
candidate_oid,
layer_index,
current_layer,
)? {
current_commit = Some(candidate_oid);
layer_index += 1;
found_match = true;
break;
}
}
if !found_match {
return Ok((current_commit, layer_index));
}
}
Ok((current_commit, layer_index))
}
fn commit_has_layer_at_position(
repo: &GitRepo,
commit_oid: git2::Oid,
layer_position: usize,
expected_layer: &crate::extracted_image::Layer,
) -> Result<bool> {
let digest_tracker = Self::read_digests_from_commit(repo, commit_oid)?;
Ok(digest_tracker.layer_matches(layer_position, expected_layer))
}
fn read_digests_from_commit(repo: &GitRepo, commit_oid: git2::Oid) -> Result<DigestTracker> {
match repo.read_file_from_commit(commit_oid, "Image.md") {
Ok(content) => {
let image_metadata = crate::image_metadata::ImageMetadata::parse_markdown(&content)
.context("Failed to parse Image.md from commit")?;
Ok(DigestTracker {
layer_digests: image_metadata.layer_digests,
})
}
Err(_) => {
Ok(DigestTracker::new())
}
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
#[test]
fn test_root_commits_deduplication() {
let roots: HashSet<git2::Oid> = HashSet::new();
assert_eq!(roots.len(), 0);
}
}