diff --git a/uSync.BackOffice/Models/SyncMergeOptions.cs b/uSync.BackOffice/Models/SyncMergeOptions.cs index a6159f878..bf1c762a9 100644 --- a/uSync.BackOffice/Models/SyncMergeOptions.cs +++ b/uSync.BackOffice/Models/SyncMergeOptions.cs @@ -1,4 +1,5 @@ using uSync.BackOffice.SyncHandlers.Interfaces; +using uSync.Core.Roots.Models; namespace uSync.BackOffice.Models; @@ -28,4 +29,9 @@ public SyncMergeOptions(SyncUpdateCallback? callback) /// Callback use to pass info to the UI. /// public SyncUpdateCallback? UpdateCallback { get; set; } -} \ No newline at end of file + + /// + /// what type of merging are we going to do. + /// + public SyncMergeStrategy MergeStrategy { get; set; } = SyncMergeStrategy.Magic; +} diff --git a/uSync.BackOffice/Services/ISyncFileService.cs b/uSync.BackOffice/Services/ISyncFileService.cs index 839886777..475df0dd0 100644 --- a/uSync.BackOffice/Services/ISyncFileService.cs +++ b/uSync.BackOffice/Services/ISyncFileService.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using System.Xml.Linq; - +using uSync.BackOffice.Models; +using uSync.Core.Roots.Models; using uSync.Core.Tracking; namespace uSync.BackOffice.Services; @@ -91,10 +93,18 @@ public interface ISyncFileService /// Task> GetAllNodesAsync(string[] filePaths); + /// + /// legacy merge (fancy) - will be removed in v19 + /// + [Obsolete("Will be removed in v19 - pass the options for greater control")] + XElement? GetDifferences(List nodes, ISyncTrackerBase? trackerBase) + => GetDifferences(nodes, trackerBase, new SyncFileMergeOptions()); + + /// /// get a XML representation of the differences between two files /// - XElement? GetDifferences(List nodes, ISyncTrackerBase? trackerBase); + XElement? GetDifferences(List nodes, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options); /// /// list the folders inside a folder @@ -135,10 +145,24 @@ public interface ISyncFileService /// Task LoadXElementAsync(string file); + /// + /// legacy merge (fancy) - will be removed in v19 + /// + [Obsolete("Will be removed in v19 - pass the options for greater control")] + Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension) + => MakeSingleExportFromFolders(folders, itemType, trackerBase, fileName, extension, new SyncFileMergeOptions()); + /// /// merge all the files in the given folders into a single xml node, that can be bulk imported /// - Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension); + Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension, SyncFileMergeOptions options); + + /// + /// legacy merge (fancy) - will be removed in v19 + /// + [Obsolete("Will be removed in v19 - pass the options for greater control")] + Task MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase) + => MergeFilesAsync(filenames, trackerBase, new SyncFileMergeOptions()); /// /// merge a list of files into a single XElement @@ -146,7 +170,15 @@ public interface ISyncFileService /// /// depending on the tracker this can do clever things like merge bits of doctypes together. /// - Task MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase); + Task MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options); + + /// + /// legacy merge (fancy) - will be removed in v19 + /// + [Obsolete("Will be removed in v19 - pass the options for greater control")] + Task> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase) + => MergeFoldersAsync(folders, extension, trackerBase, new SyncFileMergeOptions()); + /// /// Merge a number of uSync folders into a single 'usync source' @@ -161,7 +193,8 @@ public interface ISyncFileService /// the doctype tracker merges properties so you can have /// property level root values for doctypes. /// - Task> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase); + Task> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase, + SyncFileMergeOptions options); /// diff --git a/uSync.BackOffice/Services/ISyncService.cs b/uSync.BackOffice/Services/ISyncService.cs index 9ccce0492..64a5f2815 100644 --- a/uSync.BackOffice/Services/ISyncService.cs +++ b/uSync.BackOffice/Services/ISyncService.cs @@ -7,6 +7,7 @@ using uSync.BackOffice.Models; using uSync.BackOffice.SyncHandlers.Interfaces; using uSync.BackOffice.SyncHandlers.Models; +using uSync.Core.Roots.Models; namespace uSync.BackOffice; @@ -158,7 +159,8 @@ public interface ISyncService Task FinishBulkProcessAsync(HandlerActions action, IEnumerable actions); /// - /// merge the given folders in single 'production' files for each handler. + /// Merge the given folders in single 'production' files for each handler. + /// [Obsolete: Use overload with SyncFileMergeOptions for greater control] /// Task MergeExportFolder(string[] paths, IEnumerable handlers); diff --git a/uSync.BackOffice/Services/SyncFileService.cs b/uSync.BackOffice/Services/SyncFileService.cs index 9d79aa015..075357c72 100644 --- a/uSync.BackOffice/Services/SyncFileService.cs +++ b/uSync.BackOffice/Services/SyncFileService.cs @@ -12,8 +12,8 @@ using System.Xml.Linq; using Umbraco.Cms.Core.Extensions; - using uSync.Core; +using uSync.Core.Roots.Models; using uSync.Core.Tracking; namespace uSync.BackOffice.Services; @@ -324,9 +324,9 @@ public void CopyFolder(string source, string target) } } - public async Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string filename, string extension) + public async Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string filename, string extension, SyncFileMergeOptions options) { - var merged = await MergeFoldersAsync(folders, extension, trackerBase); + var merged = await MergeFoldersAsync(folders, extension, trackerBase, options); var megaNode = new XElement(itemType + "s"); int count = 0; @@ -407,7 +407,7 @@ static string GetShortFileName(string file) #region roots /// - public async Task> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase) + public async Task> MergeFoldersAsync(string[] folders, string extension, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options) { var elements = new Dictionary(); var cleanElements = new Dictionary(); @@ -436,7 +436,7 @@ public async Task> MergeFoldersAsync(string[] folde if (elements.TryGetValue(item.Key, out var value)) { // merge these files. - item.Value.SetNode(MergeNodes(value.Node, item.Value.Node, trackerBase)); + item.Value.SetNode(MergeNodes(value.Node, item.Value.Node, trackerBase, options)); item.Value.SetFileName($"{uSyncConstants.MergedFolderName}/{Path.GetFileName(item.Value.FileName)}"); } } @@ -468,7 +468,7 @@ public async Task> MergeFoldersAsync(string[] folde } /// - public async Task MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase) + public async Task MergeFilesAsync(string[] filenames, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options) { if (filenames.Length == 0) return null; var latest = await LoadXElementSafeAsync(filenames[0]); @@ -478,13 +478,20 @@ public async Task> MergeFoldersAsync(string[] folde { var node = await LoadXElementSafeAsync(filenames[n]); if (node is null) continue; - latest = MergeNodes(latest, node, trackerBase); + latest = MergeNodes(latest, node, trackerBase, options); } return latest; } - private static XElement MergeNodes(XElement source, XElement target, ISyncTrackerBase? trackerBase) - => trackerBase is null ? target : trackerBase.MergeFiles(source, target) ?? target; + private static XElement MergeNodes(XElement source, XElement target, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options) + { + if (trackerBase is ISyncTrackerOptionsBase optionsBase) + { + return optionsBase.MergeFiles(source, target, options) ?? target; + } + + return trackerBase is null ? target : trackerBase.MergeFiles(source, target) ?? target; + } private async Task>> GetFolderItemsAsync(string folder, string extension) { @@ -525,7 +532,7 @@ private async Task>> GetFolder } /// - public XElement? GetDifferences(List nodes, ISyncTrackerBase? trackerBase) + public XElement? GetDifferences(List nodes, ISyncTrackerBase? trackerBase, SyncFileMergeOptions options) { try { @@ -534,6 +541,11 @@ private async Task>> GetFolder if (trackerBase is null) return SyncRootMergerHelper.GetDifferencesByFileContents(nodes); + if (trackerBase is ISyncTrackerOptionsBase optionsBase) + { + return optionsBase.GetDifferences(nodes, options); + } + return trackerBase?.GetDifferences(nodes); } catch (Exception ex) diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index 1d23577be..cbbabdd2c 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -6,8 +6,11 @@ using System.IO.Compression; using System.Linq; using System.Threading.Tasks; - +using uSync.BackOffice.Configuration; using uSync.BackOffice.SyncHandlers.Models; +using uSync.Core.Roots.Models; + +using CoreConstants = uSync.Core.uSyncConstants; namespace uSync.BackOffice; @@ -130,7 +133,14 @@ public async Task MergeExportFolder(string[] paths, IEnumerable( + CoreConstants.DefaultSettings.MergeStrategy, + CoreConstants.DefaultSettings.MergeStrategy_Default) + }; + + totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, baseTracker, targetFileName, _uSyncConfig.Settings.DefaultExtension, handlerMergeOptions); } return totalMerged; diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index c32451040..418125cc3 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -26,9 +26,12 @@ using uSync.Core; using uSync.Core.Dependency; using uSync.Core.Models; +using uSync.Core.Roots.Models; using uSync.Core.Serialization; using uSync.Core.Tracking; +using CoreConstants = uSync.Core.uSyncConstants; + namespace uSync.BackOffice.SyncHandlers; /// @@ -346,8 +349,13 @@ public async Task> FetchAllNodesAsync(string[] fo /// protected virtual async Task> GetMergedItemsAsync(string[] folders, SyncMergeOptions options) { + var fileMergeOptions = new SyncFileMergeOptions + { + MergeStrategy = options.MergeStrategy + }; + var baseTracker = trackers.FirstOrDefault() as ISyncTrackerBase; - return [.. (await syncFileService.MergeFoldersAsync(folders, uSyncConfig.Settings.DefaultExtension, baseTracker))]; + return [.. (await syncFileService.MergeFoldersAsync(folders, uSyncConfig.Settings.DefaultExtension, baseTracker, fileMergeOptions))]; } /// @@ -360,14 +368,14 @@ protected virtual async Task> GetMergedItemsAsync /// /// given a file path, will give you the merged values across all folders. /// - protected virtual async Task GetMergedNodeAsync(string filePath) + protected virtual async Task GetMergedNodeAsync(string filePath, SyncFileMergeOptions options) { var allFiles = uSyncConfig.GetFolders() .Select(x => syncFileService.GetAbsPath($"{x}/{this.DefaultFolder}/{filePath}")) .ToArray(); var baseTracker = trackers.FirstOrDefault() as ISyncTrackerBase; - return await syncFileService.MergeFilesAsync(allFiles, baseTracker); + return await syncFileService.MergeFilesAsync(allFiles, baseTracker, options); } @@ -434,7 +442,7 @@ virtual public async Task> ImportAsync(string file, Han if (file.InvariantStartsWith($"{uSyncConstants.MergedFolderName}/")) { - var node = await GetMergedNodeAsync(file.Substring(uSyncConstants.MergedFolderName.Length + 1)); + var node = await GetMergedNodeAsync(file.Substring(uSyncConstants.MergedFolderName.Length + 1), new SyncFileMergeOptions()); if (node is not null) return await ImportElementAsync(node, file, config, options); else @@ -859,7 +867,7 @@ public async Task> ExportAsync(Udi udi, string[] folder return [uSyncAction.Fail(nameof(udi), this.handlerType, this.ItemType, ChangeType.Fail, $"Item not found {udi}", new KeyNotFoundException(nameof(udi)))]; - + } /// @@ -926,7 +934,15 @@ protected virtual async Task> Export_DoExportAsync(TObject if (nodes.Count > 0) { nodes.Add(attempt.Item); - var differences = syncFileService.GetDifferences(nodes, trackers.FirstOrDefault()); + + var mergeOptions = new SyncFileMergeOptions + { + MergeStrategy = config.GetSetting( + CoreConstants.DefaultSettings.MergeStrategy, + CoreConstants.DefaultSettings.MergeStrategy_Default) + }; + + var differences = syncFileService.GetDifferences(nodes, trackers.FirstOrDefault(), mergeOptions); if (differences is not null && differences.HasElements) { if (config.FullFileOnDifference) @@ -1309,7 +1325,7 @@ public virtual async Task> ReportElementSingleAsync(XEl { return [uSyncActionHelper .ReportActionFail(Path.GetFileName(node.GetAlias()), $"format error {fex.Message}")]; - + } } diff --git a/uSync.Core/Roots/Configs/BlockGridConfigMerger.cs b/uSync.Core/Roots/Configs/BlockGridConfigMerger.cs index 18d1c2ecb..e612ab20f 100644 --- a/uSync.Core/Roots/Configs/BlockGridConfigMerger.cs +++ b/uSync.Core/Roots/Configs/BlockGridConfigMerger.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core; using uSync.Core.Extensions; +using uSync.Core.Roots.Models; namespace uSync.Core.Roots.Configs; @@ -30,7 +31,7 @@ public virtual object GetMergedConfig(string root, string target) return MergeJsonProperties(rootConfig, targetConfig, "_"); } - public virtual object GetDifferenceConfig(string root, string target) + public virtual object GetDifferenceConfig(string root, string target, SyncFileMergeOptions options) { var rootConfig = root.DeserializeJson(); var targetConfig = target.DeserializeJson(); @@ -38,6 +39,6 @@ public virtual object GetDifferenceConfig(string root, string target) if (targetConfig is null) return target; if (rootConfig is null) return target; - return GetJsonPropertyDifferences(rootConfig, targetConfig, "_"); + return GetJsonPropertyDifferences(rootConfig, targetConfig, "_", options); } } diff --git a/uSync.Core/Roots/Configs/BlockListConfigMerger.cs b/uSync.Core/Roots/Configs/BlockListConfigMerger.cs index 67a3871ee..b4c7c34c4 100644 --- a/uSync.Core/Roots/Configs/BlockListConfigMerger.cs +++ b/uSync.Core/Roots/Configs/BlockListConfigMerger.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core; using uSync.Core.Extensions; +using uSync.Core.Roots.Models; namespace uSync.Core.Roots.Configs; @@ -30,7 +31,7 @@ public virtual object GetMergedConfig(string root, string target) return MergeJsonProperties(rootConfig, targetConfig, "_"); } - public virtual object GetDifferenceConfig(string root, string target) + public virtual object GetDifferenceConfig(string root, string target, SyncFileMergeOptions options) { var rootConfig = root.DeserializeJson(); var targetConfig = target.DeserializeJson(); @@ -38,7 +39,7 @@ public virtual object GetDifferenceConfig(string root, string target) if (targetConfig is null) return target; if (rootConfig is null) return target; - return GetJsonPropertyDifferences(rootConfig, targetConfig, "_"); + return GetJsonPropertyDifferences(rootConfig, targetConfig, "_", options); } } \ No newline at end of file diff --git a/uSync.Core/Roots/Configs/ISyncConfigMerger.cs b/uSync.Core/Roots/Configs/ISyncConfigMerger.cs index b43da38e4..302da4f0d 100644 --- a/uSync.Core/Roots/Configs/ISyncConfigMerger.cs +++ b/uSync.Core/Roots/Configs/ISyncConfigMerger.cs @@ -1,9 +1,16 @@ -namespace uSync.Core.Roots.Configs; +using uSync.Core.Roots.Models; + +namespace uSync.Core.Roots.Configs; public interface ISyncConfigMerger { string[] Editors { get; } object? GetMergedConfig(string root, string target); - object? GetDifferenceConfig(string root, string target); + + [Obsolete("Use GetDifferenceConfig(string root, string target, SyncFileMergeOptions options) instead. Will be removed in v19.")] + object? GetDifferenceConfig(string root, string target) + => GetDifferenceConfig(root, target, new SyncFileMergeOptions()); + + object? GetDifferenceConfig(string root, string target, SyncFileMergeOptions options); } diff --git a/uSync.Core/Roots/Configs/ImageCropperConfigMerger.cs b/uSync.Core/Roots/Configs/ImageCropperConfigMerger.cs index b17489ed8..93b22a678 100644 --- a/uSync.Core/Roots/Configs/ImageCropperConfigMerger.cs +++ b/uSync.Core/Roots/Configs/ImageCropperConfigMerger.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core; using uSync.Core.Extensions; +using uSync.Core.Roots.Models; namespace uSync.Core.Roots.Configs; internal class ImageCropperConfigMerger : SyncConfigMergerBase, ISyncConfigMerger @@ -27,7 +28,7 @@ internal class ImageCropperConfigMerger : SyncConfigMergerBase, ISyncConfigMerge return MergeJsonProperties(rootConfig, targetConfig, "_"); } - public object? GetDifferenceConfig(string root, string target) + public object? GetDifferenceConfig(string root, string target, SyncFileMergeOptions options) { var rootConfig = root.DeserializeJson(); var targetConfig = target.DeserializeJson(); @@ -35,6 +36,6 @@ internal class ImageCropperConfigMerger : SyncConfigMergerBase, ISyncConfigMerge if (targetConfig is null) return target; if (rootConfig is null) return target; - return GetJsonPropertyDifferences(rootConfig, targetConfig, "_"); + return GetJsonPropertyDifferences(rootConfig, targetConfig, "_", options); } } diff --git a/uSync.Core/Roots/Configs/SyncConfigMergerBase.cs b/uSync.Core/Roots/Configs/SyncConfigMergerBase.cs index 0157708ed..334f53ccd 100644 --- a/uSync.Core/Roots/Configs/SyncConfigMergerBase.cs +++ b/uSync.Core/Roots/Configs/SyncConfigMergerBase.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; using uSync.Core.Extensions; +using uSync.Core.Roots.Models; namespace uSync.Core.Roots.Configs; @@ -72,7 +73,7 @@ protected static TObject[] GetObjectDifferences(TObject[]? rootOb return [.. remaining]; } - protected JsonArray? GetJsonArrayDifferences(JsonArray? sourceArray, JsonArray? targetArray, string key, string removeProperty) + protected JsonArray? GetJsonArrayDifferences(JsonArray? sourceArray, JsonArray? targetArray, string key, string removeProperty, SyncFileMergeOptions options) { // if target is blank the difference is nothing? if (targetArray is null) return []; @@ -98,7 +99,7 @@ protected static TObject[] GetObjectDifferences(TObject[]? rootOb if (block.Value.IsJsonEqual(sourceItem) is false) { // the values are different, so we go property by property to see if we can merge them. - var target = GetJsonPropertyDifferences(sourceItem, block.Value, key); + var target = GetJsonPropertyDifferences(sourceItem, block.Value, key, options); targetOnly.Add(target); } } @@ -113,7 +114,7 @@ protected static TObject[] GetObjectDifferences(TObject[]? rootOb return targetOnly.ToJsonArray(); } - public JsonObject GetJsonPropertyDifferences(JsonObject sourceObject, JsonObject targetObject, string propertyKey) + public JsonObject GetJsonPropertyDifferences(JsonObject sourceObject, JsonObject targetObject, string propertyKey, SyncFileMergeOptions options) { foreach (var property in sourceObject) { @@ -130,6 +131,9 @@ public JsonObject GetJsonPropertyDifferences(JsonObject sourceObject, JsonObject if (property.Value.IsJsonEqual(targetValue) is false) { + // we don't merge past the top level unless we are magic merging. + if (options.MergeStrategy < SyncMergeStrategy.Magic) continue; + // target is an update so we keep this value. // unless its an array, and then we have to merge deeper. switch (targetValue.GetValueKind()) @@ -141,7 +145,7 @@ public JsonObject GetJsonPropertyDifferences(JsonObject sourceObject, JsonObject // this assumes we know what they key should be based on our array of well known array keys. // if the array is new or generic we fall back to 'key'; var (arrayKey, arrayLabel) = _knownArrayKeys.GetValueOrDefault(property.Key, (key: "key", label: "label")); - targetObject[property.Key] = GetJsonArrayDifferences(sourceArray, targetArray, arrayKey, arrayLabel); + targetObject[property.Key] = GetJsonArrayDifferences(sourceArray, targetArray, arrayKey, arrayLabel, options); break; case JsonValueKind.Object: // i am not sure we ever hit this in the block config, but it's here should the block or json store @@ -149,7 +153,7 @@ public JsonObject GetJsonPropertyDifferences(JsonObject sourceObject, JsonObject // wouldn't have a key. if (property.Value is JsonObject sourceObj && targetValue is JsonObject targetObj) { - targetObject[property.Key] = GetJsonPropertyDifferences(sourceObj, targetObj, string.Empty); + targetObject[property.Key] = GetJsonPropertyDifferences(sourceObj, targetObj, string.Empty, options); } break; } diff --git a/uSync.Core/Roots/Models/SyncMergeOptions.cs b/uSync.Core/Roots/Models/SyncMergeOptions.cs new file mode 100644 index 000000000..859992403 --- /dev/null +++ b/uSync.Core/Roots/Models/SyncMergeOptions.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace uSync.Core.Roots.Models; + +public enum SyncMergeStrategy +{ + /// + /// don't merge the value in the lowest folder wins. + /// + None, + + /// + /// merge at a 'block' level, new properties merge, new blocks or values at the top level + /// + Fancy, + + /// + /// merge down to the property level, so a property in a block will be merged individually. + /// + Magic, +} + +public class SyncFileMergeOptions +{ + /// + /// what type of merging are we going to do. + /// + /// This determines how the merging process will handle conflicts and overlapping content. + /// + public SyncMergeStrategy MergeStrategy { get; set; } = SyncMergeStrategy.Magic; +} \ No newline at end of file diff --git a/uSync.Core/Tracking/ISyncTracker.cs b/uSync.Core/Tracking/ISyncTracker.cs index 7de703fb6..05e59d5d7 100644 --- a/uSync.Core/Tracking/ISyncTracker.cs +++ b/uSync.Core/Tracking/ISyncTracker.cs @@ -1,6 +1,7 @@ using System.Xml.Linq; using uSync.Core.Models; +using uSync.Core.Roots.Models; using uSync.Core.Serialization; namespace uSync.Core.Tracking; @@ -13,10 +14,17 @@ public interface ISyncTrackerBase XElement? GetDifferences(List nodes) => nodes.Count > 0 ? nodes[^1] : null; +} + +public interface ISyncTrackerOptionsBase : ISyncTrackerBase +{ + XElement? MergeFiles(XElement a, XElement b, SyncFileMergeOptions options); + XElement? GetDifferences(List nodes, SyncFileMergeOptions options); } -public interface ISyncTracker : ISyncTrackerBase + +public interface ISyncTracker : ISyncTrackerOptionsBase { Task> GetChangesAsync(XElement target, XElement source, SyncSerializerOptions options); Task> GetChangesAsync(XElement node, SyncSerializerOptions options); diff --git a/uSync.Core/Tracking/Impliment/DataTypeTracker.cs b/uSync.Core/Tracking/Impliment/DataTypeTracker.cs index 37c29878e..7af1a001a 100644 --- a/uSync.Core/Tracking/Impliment/DataTypeTracker.cs +++ b/uSync.Core/Tracking/Impliment/DataTypeTracker.cs @@ -4,6 +4,7 @@ using uSync.Core.Extensions; using uSync.Core.Roots.Configs; +using uSync.Core.Roots.Models; using uSync.Core.Serialization; namespace uSync.Core.Tracking.Impliment; @@ -56,21 +57,21 @@ public DataTypeTracker( return base.MergeFiles(a, b); } - public override XElement? GetDifferences(List nodes) + public override XElement? GetDifferences(List nodes, SyncFileMergeOptions options) { - if (nodes.Count <= 1) return base.GetDifferences(nodes); + if (nodes.Count <= 1) return base.GetDifferences(nodes, options); var editorAlias = GetEditorAlias(nodes[0]); var merger = GetConfigMerger(editorAlias); if (!string.IsNullOrEmpty(editorAlias) && merger != null) { - return GetDifferences(nodes[0], nodes[1], merger); + return GetDifferences(nodes[0], nodes[1], merger, options); } - return SyncRootMergerHelper.GetDifferences(nodes, TrackingItems); + return SyncRootMergerHelper.GetDifferences(nodes, TrackingItems, options); } - public XElement? GetDifferences(XElement root, XElement target, ISyncConfigMerger merger) + protected XElement? GetDifferences(XElement root, XElement target, ISyncConfigMerger merger, SyncFileMergeOptions options) { var rootConfig = root.Element("Config").ValueOrDefault(string.Empty); @@ -79,7 +80,7 @@ public DataTypeTracker( if (!string.IsNullOrEmpty(rootConfig) && !string.IsNullOrEmpty(targetConfig)) { // calculate config differences. - var difference = merger.GetDifferenceConfig(rootConfig, targetConfig); + var difference = merger.GetDifferenceConfig(rootConfig, targetConfig, options); if (difference != null) { root.Element("Config")?.ReplaceNodes(new XCData(SerializeConfig(difference))); @@ -89,7 +90,7 @@ public DataTypeTracker( } - return SyncRootMergerHelper.GetDifferences([root, target], TrackingItems); + return SyncRootMergerHelper.GetDifferences([root, target], TrackingItems, options); } private string GetEditorAlias(XElement node) diff --git a/uSync.Core/Tracking/SyncRootMergerHelper.cs b/uSync.Core/Tracking/SyncRootMergerHelper.cs index de3de4934..c5a89f3f1 100644 --- a/uSync.Core/Tracking/SyncRootMergerHelper.cs +++ b/uSync.Core/Tracking/SyncRootMergerHelper.cs @@ -1,24 +1,27 @@ using System.Xml.Linq; using System.Xml.XPath; +using uSync.Core.Roots.Models; namespace uSync.Core.Tracking; public class SyncRootMergerHelper { - public static XElement GetDifferences(List nodes, IList trackedNodes) + public static XElement GetDifferences(List nodes, IList trackedNodes, SyncFileMergeOptions options) { - var (_, differences) = CompareNodes(nodes, trackedNodes); + var (_, differences) = CompareNodes(nodes, trackedNodes, options); return differences; } - public static XElement GetCombined(List nodes, IList trackedNodes) + public static XElement GetCombined(List nodes, IList trackedNodes, SyncFileMergeOptions options) { - var (combined, _) = CompareNodes(nodes, trackedNodes); + var (combined, _) = CompareNodes(nodes, trackedNodes, options); return combined; } public static XElement? GetDifferencesByFileContents(List nodes) { + /// this is the fallback, and it basically does a 'none' merge where the latest difference is the one you get. + /// work out what is the 'latest' version of the node we are using for comparison. /// // Node1, Node2, Node3 @@ -52,7 +55,7 @@ public static XElement GetCombined(List nodes, IList tra return target; } - public static (XElement combined, XElement differences) CompareNodes(List nodes, IList trackedNodes) + public static (XElement combined, XElement differences) CompareNodes(List nodes, IList trackedNodes, SyncFileMergeOptions options) { var differences = XElement.Parse(nodes[^1].ToString()); var combined = XElement.Parse(nodes[^1].ToString()); diff --git a/uSync.Core/Tracking/SyncXmlTrackAndMerger.cs b/uSync.Core/Tracking/SyncXmlTrackAndMerger.cs index fb442075a..f950ac2f5 100644 --- a/uSync.Core/Tracking/SyncXmlTrackAndMerger.cs +++ b/uSync.Core/Tracking/SyncXmlTrackAndMerger.cs @@ -1,5 +1,5 @@ using System.Xml.Linq; - +using uSync.Core.Roots.Models; using uSync.Core.Serialization; namespace uSync.Core.Tracking; @@ -12,10 +12,20 @@ public SyncXmlTrackAndMerger(SyncSerializerCollection serializers) { } - public override XElement? MergeFiles(XElement a, XElement b) - => SyncRootMergerHelper.GetCombined([a, b], TrackingItems); + public virtual XElement? MergeFiles(XElement a, XElement b, SyncFileMergeOptions options) + { + if (options.MergeStrategy == SyncMergeStrategy.None) + return base.MergeFiles(a, b); + + return SyncRootMergerHelper.GetCombined([a, b], TrackingItems, options); + } - public override XElement? GetDifferences(List nodes) - => SyncRootMergerHelper.GetDifferences(nodes, TrackingItems); + public virtual XElement? GetDifferences(List nodes, SyncFileMergeOptions options) + { + if (options.MergeStrategy == SyncMergeStrategy.None) + return base.GetDifferences(nodes); + + return SyncRootMergerHelper.GetDifferences(nodes, TrackingItems, options); + } } diff --git a/uSync.Core/Tracking/SyncXmlTracker.cs b/uSync.Core/Tracking/SyncXmlTracker.cs index 43a826a15..772ae9d36 100644 --- a/uSync.Core/Tracking/SyncXmlTracker.cs +++ b/uSync.Core/Tracking/SyncXmlTracker.cs @@ -6,6 +6,7 @@ using uSync.Core.Extensions; using uSync.Core.Models; +using uSync.Core.Roots.Models; using uSync.Core.Serialization; namespace uSync.Core.Tracking; @@ -345,11 +346,11 @@ private static List CompareNode(XElement target, XElement source, s }; } - public virtual XElement? MergeFiles(XElement a, XElement b) => b; + public virtual XElement? MergeFiles(XElement a, XElement b) + => b; public virtual XElement? GetDifferences(List nodes) => nodes?.Count > 0 ? nodes[^1] : null; - } public class TrackingItem diff --git a/uSync.Core/uSyncConstants.cs b/uSync.Core/uSyncConstants.cs index 3057ba3d6..ea3a348db 100644 --- a/uSync.Core/uSyncConstants.cs +++ b/uSync.Core/uSyncConstants.cs @@ -1,4 +1,6 @@ -namespace uSync.Core; +using uSync.Core.Roots.Models; + +namespace uSync.Core; public static partial class uSyncConstants { @@ -143,6 +145,9 @@ public static class DefaultSettings public const string UsingRazorViews = "UsingRazorViews"; public const bool UsingRazorViews_Default = false; + public const string MergeStrategy = "MergeStrategy"; + public const SyncMergeStrategy MergeStrategy_Default = SyncMergeStrategy.Magic; + } public const int DependencyCountMax = 204800;