aboutsummaryrefslogtreecommitdiffstats
path: root/src/document/sync.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/document/sync.rs')
-rw-r--r--src/document/sync.rs49
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() {