diff options
| author | tv <tv@krebsco.de> | 2026-11-05 05:09:24 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-05 05:09:24 +0100 |
| commit | 6386aa1358819d77ff2f58e613d52301be1014a7 (patch) | |
| tree | caab7cf22164f6329329ee2aad18e005d3fcf3e1 /src/document | |
| parent | 3db1bf5014036087c2df6a746759c4df290330c4 (diff) | |
pushed have is the senders heads
Diffstat (limited to 'src/document')
| -rw-r--r-- | src/document/sync.rs | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/src/document/sync.rs b/src/document/sync.rs index 9415bd0..a46e059 100644 --- a/src/document/sync.rs +++ b/src/document/sync.rs @@ -54,16 +54,14 @@ impl Received { /// Changes pushed along a writer's tree (`SyncBody::Changes`), as the Automerge sync /// message that would have brought them: the sender's `heads`, nothing needed, and - /// as what it has, everything we had (`ours`, our heads before: it takes the - /// message as delivered, so it had all of them). + /// as what it has, the history of those heads (nothing beyond them: its heads are + /// all it has). Where the changes wait for predecessors and some of those heads stay + /// unknown, they give way to the heads we share (`known_last_sync`), as for an + /// overtaken message. /// /// No heads stand for the tips of the changes (`SyncSession::push`). Changes that /// don't decode leave the heads out; applying them fails as it would have anyway. - pub(crate) fn pushed( - heads: Vec<crate::document::ChangeHash>, - changes: Vec<Vec<u8>>, - ours: Vec<crate::document::ChangeHash>, - ) -> Self { + pub(crate) fn pushed(heads: Vec<crate::document::ChangeHash>, changes: Vec<Vec<u8>>) -> Self { let heads = if heads.is_empty() { let decoded: Vec<automerge::Change> = changes .iter() @@ -74,10 +72,10 @@ impl Received { heads }; Self(sync::Message { - heads, + heads: heads.clone(), need: Vec::new(), have: vec![sync::Have { - last_sync: ours, + last_sync: heads, bloom: sync::BloomFilter::default(), }], changes: changes.into(), @@ -1501,7 +1499,7 @@ mod tests { assert!(a.push(&ours, 1 << 20).is_none(), "sent already"); let before = theirs.get_heads(); - let message = Received::pushed(heads, changes, before.clone()); + let message = Received::pushed(heads, changes); assert_eq!(message.heads(), ours.get_heads(), "found in the changes"); b.receive(&mut theirs, message).unwrap(); b.came_from_peer(&theirs, &before); @@ -1513,6 +1511,37 @@ mod tests { ); } + /// A push says the sender has its heads, no more: what we have beyond them goes + /// back with the answer, as it would to an Automerge message. + #[test] + fn what_the_pusher_lacks_goes_back() { + use crate::document::testing::change; + use automerge::{ROOT, transaction::Transactable}; + let (mut ours, mut theirs) = (Document::new(), Document::new()); + change(&mut ours, |doc| doc.put(ROOT, "early", 1).unwrap()); + let (mut a, mut b) = in_step(&mut ours, &mut theirs); + let (now, bytes) = (Instant::now(), SyncBytes::default()); + + change(&mut theirs, |doc| doc.put(ROOT, "theirs", 3).unwrap()); + let only_theirs = theirs.get_heads(); + change(&mut ours, |doc| doc.put(ROOT, "news", 2).unwrap()); + let (heads, changes) = a.push(&ours, 1 << 20).expect("news to push"); + a.assume_delivered(&ours.get_heads()); + a.send_succeeded(a.id()); + + let before = theirs.get_heads(); + b.receive(&mut theirs, Received::pushed(heads, changes)) + .unwrap(); + b.came_from_peer(&theirs, &before); + assert!(!b.answered_as_delivered(&theirs, &theirs.get_heads())); + let answer = b.generate(&theirs, now, bytes).expect("an answer"); + let answer = decode(&answer).unwrap(); + assert!(answer.carries_changes(), "with what the pusher lacks"); + a.receive(&mut ours, answer).unwrap(); + assert!(ours.get_change_by_hash(&only_theirs[0]).is_some()); + assert_eq!(ours.get_heads(), theirs.get_heads()); + } + /// Tips are the changes none of the others depend on, sorted as heads are. #[test] fn tips_are_what_nothing_depends_on() { |
