@@ -543,6 +543,50 @@ def sync(self, *keys: KeyPart, value: Any) -> Document:
543543 op = _core .Op .replace (value )
544544 return self ._apply_patches ([_core .Patch (route = route , operation = op )])
545545
546+ def merge (self , * keys : KeyPart , value : Any ) -> Document :
547+ """Merge a value into the mapping at path without removing extra keys.
548+
549+ Recursively updates matching keys and adds new keys, but never
550+ removes existing keys not present in *value*. Lists are replaced
551+ entirely (not merged element-wise).
552+ """
553+ from yamltrip .sync import _compute_patches # noqa: PLC0415
554+
555+ normalized = _normalize_keys (keys ) if keys else ()
556+
557+ # If path doesn't exist, delegate to upsert.
558+ if normalized :
559+ route = _make_route (normalized )
560+ if not self ._core_doc .query_exists (route ):
561+ return self .upsert (* normalized , value = value )
562+
563+ # Get current value and diff
564+ try :
565+ old_value = self ._core_doc .parse_value (_make_route (normalized ))
566+ except (ValueError , KeyError ):
567+ return self .upsert (* normalized , value = value )
568+
569+ # Pre-convert any flow sequences that will be modified.
570+ doc : Document = self
571+ flow_patches = _flow_seq_replacements (
572+ self ._core_doc , old_value , value , normalized
573+ )
574+ if flow_patches :
575+ doc = doc ._apply_patches (flow_patches )
576+ old_value = doc ._core_doc .parse_value (_make_route (normalized ))
577+
578+ patches = _compute_patches (old_value , value , normalized , remove_extra = False )
579+ if not patches :
580+ return doc
581+ try :
582+ return doc ._apply_patches (patches )
583+ except PatchError as e :
584+ if "expected BlockSequence" not in str (e ):
585+ raise
586+ route = _make_route (normalized )
587+ op = _core .Op .replace (value )
588+ return self ._apply_patches ([_core .Patch (route = route , operation = op )])
589+
546590 def find_index (self , * keys : KeyPart , where : dict [str , Any ]) -> int | None :
547591 """Return the index of the first list item matching all key/value pairs.
548592
0 commit comments