@@ -289,15 +289,14 @@ private void Import(PwGroup targetGroup, string path, string password, string ty
289289 /// <summary>
290290 /// Synchronizes the target group with the source group from the shared database.
291291 ///
292- /// WARNING: This is a destructive operation that will overwrite all entries and subgroups
293- /// in the target group with content from the source. Any local modifications to entries
294- /// within a KeeShare group will be lost on each sync.
292+ /// For "Import" mode: Performs a destructive replace - clears target and copies all
293+ /// content from source. Any local modifications will be lost.
295294 ///
296- /// This behavior is intentional for "Import " mode (one-time import), but for "Synchronize"
297- /// mode, a proper merge implementation would be preferable to preserve local modifications
298- /// that haven't been synced back to the shared database .
295+ /// For "Synchronize " mode: Performs a non-destructive merge - adds new entries/groups
296+ /// from source, and updates existing entries/groups if the source version is newer
297+ /// (based on LastModificationTime). Local entries not in source are preserved .
299298 ///
300- /// The following properties of the target group are preserved:
299+ /// The following properties of the target group are always preserved:
301300 /// - UUID (group identity)
302301 /// - Parent (group hierarchy)
303302 /// - Name (local group name)
@@ -309,11 +308,26 @@ private void Import(PwGroup targetGroup, string path, string password, string ty
309308 /// <param name="type">KeeShare type: "Import" or "Synchronize"</param>
310309 private void SyncGroups ( PwGroup source , PwGroup target , string type )
311310 {
312- // TODO: For "Synchronize" mode, consider implementing proper merge logic using
313- // KeePassLib's merge functionality (e.g., PwDatabase.MergeIn) to preserve local
314- // modifications and handle conflicts appropriately.
311+ if ( type == "Synchronize" )
312+ {
313+ // Non-destructive merge: add new items, update existing if newer
314+ MergeGroupContents ( source , target ) ;
315+ }
316+ else
317+ {
318+ // Import mode: destructive replace
319+ ImportGroupContents ( source , target ) ;
320+ }
315321
316- // Clear entries and subgroups (destructive operation)
322+ target . Touch ( true , false ) ;
323+ }
324+
325+ /// <summary>
326+ /// Performs a destructive import: clears target and copies all content from source.
327+ /// </summary>
328+ private void ImportGroupContents ( PwGroup source , PwGroup target )
329+ {
330+ // Clear entries and subgroups
317331 target . Entries . Clear ( ) ;
318332 target . Groups . Clear ( ) ;
319333
@@ -328,11 +342,47 @@ private void SyncGroups(PwGroup source, PwGroup target, string type)
328342 {
329343 target . AddGroup ( group . CloneDeep ( ) , true ) ;
330344 }
345+ }
346+
347+ /// <summary>
348+ /// Performs a non-destructive merge: adds new entries/groups from source,
349+ /// updates existing if source is newer. Local items not in source are preserved.
350+ /// </summary>
351+ private void MergeGroupContents ( PwGroup source , PwGroup target )
352+ {
353+ // Merge entries
354+ foreach ( var sourceEntry in source . Entries )
355+ {
356+ var targetEntry = target . FindEntry ( sourceEntry . Uuid , false ) ;
357+ if ( targetEntry == null )
358+ {
359+ // Entry doesn't exist in target - add it
360+ target . AddEntry ( sourceEntry . CloneDeep ( ) , true ) ;
361+ }
362+ else
363+ {
364+ // Entry exists - update if source is newer
365+ // AssignProperties with bOnlyIfNewer=true will only update if source.LastMod > target.LastMod
366+ targetEntry . AssignProperties ( sourceEntry , true , false , false ) ;
367+ }
368+ }
331369
332- // Note: We preserve Name/Icon/Notes of the target group to maintain local identity
333- // Only the content (entries and subgroups) is synchronized from the shared database.
334-
335- target . Touch ( true , false ) ;
370+ // Merge subgroups recursively
371+ foreach ( var sourceGroup in source . Groups )
372+ {
373+ var targetGroup = target . FindGroup ( sourceGroup . Uuid , false ) ;
374+ if ( targetGroup == null )
375+ {
376+ // Group doesn't exist in target - add it
377+ target . AddGroup ( sourceGroup . CloneDeep ( ) , true ) ;
378+ }
379+ else
380+ {
381+ // Group exists - update properties if source is newer, then merge contents
382+ targetGroup . AssignProperties ( sourceGroup , true , false ) ;
383+ MergeGroupContents ( sourceGroup , targetGroup ) ;
384+ }
385+ }
336386 }
337387
338388 private IOConnectionInfo ResolvePath ( string path )
0 commit comments