1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! Reachability checks for bundle connectivity verification.
use std::collections::{HashSet, VecDeque};
use crate::error::Result;
use crate::objects::{oid_hash_set, parse_commit, ObjectId, ObjectKind};
use crate::refs;
use crate::repo::Repository;
/// Returns every commit reachable from all refs under `refs/` (not `HEAD` alone).
///
/// Git's connectivity check uses `--all` (refs), not `HEAD` resolution: a symbolic `HEAD`
/// that points at `refs/heads/main` must not be treated as an extra root, or pushes that
/// only advance `main` would be misclassified as already connected.
fn commit_closure_from_all_refs(repo: &Repository) -> Result<HashSet<ObjectId>> {
let mut seeds = Vec::new();
for prefix in &[
"refs/heads/",
"refs/tags/",
"refs/remotes/",
"refs/bundles/",
] {
if let Ok(entries) = refs::list_refs(repo.git_dir(), prefix) {
for (_, oid) in entries {
seeds.push(oid);
}
}
}
let mut seen = oid_hash_set();
let mut queue: VecDeque<ObjectId> = VecDeque::new();
for oid in seeds {
if repo.odb().read_info(&oid).is_err() {
continue;
}
if let Some(commit) = repo.odb().peel_to_commit(oid)? {
if seen.insert(commit) {
queue.push_back(commit);
}
}
}
while let Some(c) = queue.pop_front() {
let obj = repo.odb().read(&c)?;
if obj.kind != ObjectKind::Commit {
continue;
}
let commit = parse_commit(&obj.data)?;
for p in commit.parents {
if seen.insert(p) {
queue.push_back(p);
}
}
}
Ok(seen.into_iter().collect())
}
/// Returns whether every bundle prerequisite commit is already reachable from existing refs.
///
/// Matches Git's `verify_bundle` connectivity check: prerequisite OIDs may exist loose in the ODB
/// but must still lie in the commit closure of `refs/heads/`, `refs/tags/`, `refs/remotes/`, and
/// `refs/bundles/` (so dangling objects do not satisfy prerequisites).
pub fn bundle_prerequisites_connected_to_refs(
repo: &Repository,
prerequisites: &[ObjectId],
) -> Result<bool> {
if prerequisites.is_empty() {
return Ok(true);
}
let closure = commit_closure_from_all_refs(repo)?;
Ok(prerequisites.iter().all(|p| closure.contains(p)))
}