radicle: fix diverging quorum
When calculating a quorum there was a case where two heads that were equal would result in double-counting, allowing the quorum to pass a threshold higher than the expected votes. To prevent this, each head is immediately counted as a direct vote. Then, when comparing the head to the rest of the set, if they are equal that iteration would be skipped. This is because the merge base of two equal commits is that commit, resulting in the double-counting. Note that the `skip` can also skip the current head, so `i + 1` is used. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
589c375624
commit
3260046c67
|
|
@ -925,11 +925,18 @@ pub fn quorum(
|
|||
for (i, head) in heads.iter().enumerate() {
|
||||
let head = Oid::from(*head);
|
||||
|
||||
for other in heads.iter().skip(i) {
|
||||
// Add a direct vote for this head.
|
||||
*candidates.entry(head).or_default() += 1;
|
||||
|
||||
// Compare this head to all other heads ahead of it in the list.
|
||||
for other in heads.iter().skip(i + 1) {
|
||||
// N.b. if heads are equal then skip it, otherwise it will end up as
|
||||
// a double vote.
|
||||
if *head == *other {
|
||||
continue;
|
||||
}
|
||||
let base = repo.merge_base(*head, *other)?;
|
||||
|
||||
// Nb. This also handles `head` == `other`, which happens once for every head,
|
||||
// as well as when there is more than one head with the same OID.
|
||||
if base == *other || base == *head {
|
||||
*candidates.entry(Oid::from(base)).or_default() += 1;
|
||||
}
|
||||
|
|
@ -1086,6 +1093,7 @@ mod tests {
|
|||
let c0: git::Oid = c0.into();
|
||||
let c1 = fixtures::commit("C1", &[*c0], &repo);
|
||||
let c2 = fixtures::commit("C2", &[*c1], &repo);
|
||||
let c3 = fixtures::commit("C3", &[*c1], &repo);
|
||||
let b2 = fixtures::commit("B2", &[*c1], &repo);
|
||||
let a1 = fixtures::commit("A1", &[*c0], &repo);
|
||||
let m1 = fixtures::commit("M1", &[*c2, *b2], &repo);
|
||||
|
|
@ -1094,6 +1102,7 @@ mod tests {
|
|||
eprintln!("C0: {c0}");
|
||||
eprintln!("C1: {c1}");
|
||||
eprintln!("C2: {c2}");
|
||||
eprintln!("C3: {c3}");
|
||||
eprintln!("B2: {b2}");
|
||||
eprintln!("A1: {a1}");
|
||||
eprintln!("M1: {m1}");
|
||||
|
|
@ -1145,6 +1154,25 @@ mod tests {
|
|||
Err(QuorumError::NoQuorum)
|
||||
);
|
||||
|
||||
// B2 C2 C3
|
||||
// \ | /
|
||||
// C1
|
||||
// |
|
||||
// C0
|
||||
assert_matches!(quorum(&[*b2, *c2, *c2], 2, &repo), Ok(c2));
|
||||
assert_matches!(
|
||||
quorum(&[*b2, *c2, *c2], 3, &repo),
|
||||
Err(QuorumError::NoQuorum)
|
||||
);
|
||||
assert_matches!(
|
||||
quorum(&[*b2, *c2, *b2, *c2], 3, &repo),
|
||||
Err(QuorumError::NoQuorum)
|
||||
);
|
||||
assert_matches!(
|
||||
quorum(&[*c3, *b2, *c2, *b2, *c2, *c3], 3, &repo),
|
||||
Err(QuorumError::NoQuorum)
|
||||
);
|
||||
|
||||
// B2 C2
|
||||
// \|
|
||||
// A1 C1
|
||||
|
|
|
|||
Loading…
Reference in New Issue