/// <summary> /// Initializes a new instance of the <see cref="AssetToImportMerge"/> class. /// </summary> /// <param name="previousItem">The previous item.</param> /// <param name="diff">The difference.</param> /// <param name="mergePreviewResult">The merge preview result.</param> internal AssetToImportMerge(AssetItem previousItem, AssetDiff diff, MergeResult mergePreviewResult) { PreviousItem = previousItem; this.Diff = diff; this.MergePreviewResult = mergePreviewResult; DependencyGroups = new List<AssetToImportMergeGroup>(); }
/// <summary> /// Merges the specified assets from <c>base</c> and <c>from2</c> into <c>from1</c>. /// </summary> /// <param name="assetDiff">A precomputed asset difference.</param> /// <param name="mergePolicy">The merge policy.</param> /// <param name="previewOnly">if set to <c>true</c> then the merge will not change the object.</param> /// <returns>MergePreviewResult.</returns> /// <exception cref="System.ArgumentNullException">assetDiff /// or /// mergePolicy</exception> public static MergeResult Merge(AssetDiff assetDiff, MergePolicyDelegate mergePolicy, bool previewOnly = false) { if (assetDiff == null) { throw new ArgumentNullException("assetDiff"); } if (mergePolicy == null) { throw new ArgumentNullException("mergePolicy"); } var allDiffs = assetDiff.Compute(); var diff3 = allDiffs.FindDifferences().ToList(); var result = new MergeResult(assetDiff.Asset1); // Try to merge foreach (var diff3Node in diff3) { Diff3ChangeType changeType; try { changeType = mergePolicy(diff3Node); if (changeType >= Diff3ChangeType.Conflict) { result.Error("Unresolved conflict [{0}] on node [{1}/{2}/{3}]", diff3Node.ChangeType, diff3Node.BaseNode, diff3Node.Asset1Node, diff3Node.Asset2Node); continue; } // If we are in preview only mode, just skip the update if (previewOnly) { continue; } // As we are merging into asset1, the only relevant changes can only come from asset2 if (changeType != Diff3ChangeType.MergeFromAsset2) { continue; } var dataInstance = diff3Node.Asset2Node != null ? diff3Node.Asset2Node.Instance : null; // Sets the value on the node diff3Node.ReplaceValue(dataInstance, node => node.Asset1Node, diff3Node.Asset2Node == null); } catch (Exception ex) { result.Error("Unexpected error while merging [{0}] on node [{1}]", ex, diff3Node.ChangeType, diff3Node.InstanceType); break; } } return(result); }
/// <summary> /// 3-way merge assets using an external diff tool. /// </summary> /// <param name="assetBase0">The asset base0.</param> /// <param name="assetFrom1">The asset from1.</param> /// <param name="assetFrom2">The asset from2.</param> /// <returns>The result of the merge.</returns> public static MergeResult MergeWithExternalTool(Asset assetBase0, Asset assetFrom1, Asset assetFrom2) { var result = new MergeResult(); // If they are all null, nothing to do if (assetBase0 == null && assetFrom1 == null && assetFrom2 == null) { return(result); } if (!File.Exists(DefaultMergeTool)) { result.Error("Unable to use external diff3 merge tool [{0}]. File not found", DefaultMergeTool); return(result); } var assetBase = (Asset)AssetCloner.Clone(assetBase0); var asset1 = (Asset)AssetCloner.Clone(assetFrom1); var asset2 = (Asset)AssetCloner.Clone(assetFrom2); // Clears base as we are not expecting to work with them directly // The real base must be passed by the assetBase0 parameter if (assetBase != null) { assetBase.Base = null; } if (asset1 != null) { asset1.Base = null; } if (asset2 != null) { asset2.Base = null; } var assetBasePath = Path.GetTempFileName(); var asset1Path = Path.GetTempFileName(); var asset2Path = Path.GetTempFileName(); try { AssetSerializer.Save(assetBasePath, assetBase); AssetSerializer.Save(asset1Path, asset1); AssetSerializer.Save(asset2Path, asset2); } catch (Exception exception) { result.Error("Unexpected error while serializing assets on disk before using diff tool", exception); return(result); } var outputPath = Path.GetTempFileName(); try { // TODO We need to access different diff tools command line // kdiff3.exe file1 file2 file3 -o outputfile var process = Process.Start(DefaultMergeTool, string.Format("{0} {1} {2} -o {3}", assetBasePath, asset1Path, asset2Path, outputPath)); if (process == null) { result.Error("Unable to launch diff3 tool exe from [{0}]", DefaultMergeTool); } else { process.WaitForExit(); if (process.ExitCode != 0) { result.Error("Error, failed to merge files"); } } } catch (Exception ex) { result.Error("Unable to launch diff3 tool exe from [{0}]", ex, DefaultMergeTool); } if (!result.HasErrors) { try { var mergedAsset = (Asset)AssetSerializer.Load(outputPath); if (mergedAsset != null) { if (assetFrom1 == null) { mergedAsset.Base = assetFrom2 == null ? assetBase0.Base : assetFrom2.Base; } else { mergedAsset.Base = assetFrom1.Base; } } result.Asset = mergedAsset; } catch (Exception ex) { result.Error("Unexpected exception while loading merged assets from [{0}]", ex, outputPath); } } return(result); }
/// <summary> /// Merges the specified assets from <c>base</c> and <c>from2</c> into <c>from1</c>. /// </summary> /// <param name="assetDiff">A precomputed asset difference.</param> /// <param name="mergePolicy">The merge policy.</param> /// <param name="previewOnly">if set to <c>true</c> then the merge will not change the object.</param> /// <returns>MergePreviewResult.</returns> /// <exception cref="System.ArgumentNullException">assetDiff /// or /// mergePolicy</exception> public static MergeResult Merge(AssetDiff assetDiff, MergePolicyDelegate mergePolicy, bool previewOnly = false) { if (assetDiff == null) { throw new ArgumentNullException("assetDiff"); } if (mergePolicy == null) { throw new ArgumentNullException("mergePolicy"); } var allDiffs = assetDiff.Compute(); var diff3 = allDiffs.FindDifferences().ToList(); var result = new MergeResult(assetDiff.Asset1); // Try to merge foreach (var diff3Node in diff3) { Diff3ChangeType changeType; try { changeType = mergePolicy(diff3Node); if (changeType >= Diff3ChangeType.Conflict) { result.Error("Unresolved conflict [{0}] on node [{1}/{2}/{3}]", diff3Node.ChangeType, diff3Node.BaseNode, diff3Node.Asset1Node, diff3Node.Asset2Node); continue; } // If we are in preview only mode, just skip the update if (previewOnly) { continue; } object dataInstance = null; bool replaceValue = false; switch (changeType) { case Diff3ChangeType.MergeFromAsset2: // Because for collection, the merge is performed by the MergeContainer // Skip any merge for individual items, as they should have been merged by MergeContainer // TODO: This is a workaround as FindDifferences().ToList() is giving changes inside collection while we rebuild collection with MergeContainer if (diff3Node.Parent == null || diff3Node.Parent.Type != Diff3NodeType.Collection) { // As we are merging into asset1, the only relevant changes can only come from asset2 dataInstance = diff3Node.Asset2Node?.Instance; replaceValue = true; } break; case Diff3ChangeType.Children: MergeContainer(diff3Node, out dataInstance); replaceValue = dataInstance != null; break; default: continue; } // Sets the value on the node if (replaceValue) { diff3Node.ReplaceValue(dataInstance, node => node.Asset1Node); } // Applies the override for this node diff3Node.ApplyOverride(); } catch (Exception ex) { result.Error("Unexpected error while merging [{0}] on node [{1}]", ex, diff3Node.ChangeType, diff3Node.InstanceType); break; } } if (!previewOnly) { foreach (var node in allDiffs.Asset1Node.Children(node => true)) { if (node.Instance is IDiffProxy) { ((IDiffProxy)node.Instance).ApplyChanges(); } } } return(result); }
/// <summary> /// Merges the specified assets from <c>base</c> and <c>from2</c> into <c>from1</c>. /// </summary> /// <param name="assetDiff">A precomputed asset difference.</param> /// <param name="mergePolicy">The merge policy.</param> /// <param name="previewOnly">if set to <c>true</c> then the merge will not change the object.</param> /// <returns>MergePreviewResult.</returns> /// <exception cref="System.ArgumentNullException">assetDiff /// or /// mergePolicy</exception> public static MergeResult Merge(AssetDiff assetDiff, MergePolicyDelegate mergePolicy, bool previewOnly = false) { if (assetDiff == null) throw new ArgumentNullException("assetDiff"); if (mergePolicy == null) throw new ArgumentNullException("mergePolicy"); var allDiffs = assetDiff.Compute(); var diff3 = allDiffs.FindDifferences().ToList(); var result = new MergeResult(assetDiff.Asset1); // Try to merge foreach (var diff3Node in diff3) { Diff3ChangeType changeType; try { changeType = mergePolicy(diff3Node); if (changeType >= Diff3ChangeType.Conflict) { result.Error("Unresolved conflict [{0}] on node [{1}/{2}/{3}]", diff3Node.ChangeType, diff3Node.BaseNode, diff3Node.Asset1Node, diff3Node.Asset2Node); continue; } // If we are in preview only mode, just skip the update if (previewOnly) { continue; } object dataInstance = null; bool replaceValue = false; switch (changeType) { case Diff3ChangeType.MergeFromAsset2: // Because for collection, the merge is performed by the MergeContainer // Skip any merge for individual items, as they should have been merged by MergeContainer // TODO: This is a workaround as FindDifferences().ToList() is giving changes inside collection while we rebuild collection with MergeContainer if (diff3Node.Parent == null || diff3Node.Parent.Type != Diff3NodeType.Collection) { // As we are merging into asset1, the only relevant changes can only come from asset2 dataInstance = diff3Node.Asset2Node?.Instance; replaceValue = true; } break; case Diff3ChangeType.Children: MergeContainer(diff3Node, out dataInstance); replaceValue = dataInstance != null; break; default: continue; } // Sets the value on the node if (replaceValue) diff3Node.ReplaceValue(dataInstance, node => node.Asset1Node); // Applies the override for this node diff3Node.ApplyOverride(); } catch (Exception ex) { result.Error("Unexpected error while merging [{0}] on node [{1}]", ex, diff3Node.ChangeType, diff3Node.InstanceType); break; } } if (!previewOnly) { foreach (var node in allDiffs.Asset1Node.Children(node => true)) { if (node.Instance is IDiffProxy) { ((IDiffProxy)node.Instance).ApplyChanges(); } } } return result; }
/// <summary> /// 3-way merge assets using an external diff tool. /// </summary> /// <param name="assetBase0">The asset base0.</param> /// <param name="assetFrom1">The asset from1.</param> /// <param name="assetFrom2">The asset from2.</param> /// <returns>The result of the merge.</returns> public static MergeResult MergeWithExternalTool(Asset assetBase0, Asset assetFrom1, Asset assetFrom2) { var result = new MergeResult(); // If they are all null, nothing to do if (assetBase0 == null && assetFrom1 == null && assetFrom2 == null) { return result; } if (!File.Exists(DefaultMergeTool)) { result.Error("Unable to use external diff3 merge tool [{0}]. File not found", DefaultMergeTool); return result; } var assetBase = (Asset)AssetCloner.Clone(assetBase0); var asset1 = (Asset)AssetCloner.Clone(assetFrom1); var asset2 = (Asset)AssetCloner.Clone(assetFrom2); // Clears base as we are not expecting to work with them directly // The real base must be passed by the assetBase0 parameter if (assetBase != null) { assetBase.Base = null; } if (asset1 != null) { asset1.Base = null; } if (asset2 != null) { asset2.Base = null; } var assetBasePath = Path.GetTempFileName(); var asset1Path = Path.GetTempFileName(); var asset2Path = Path.GetTempFileName(); try { AssetSerializer.Save(assetBasePath, assetBase); AssetSerializer.Save(asset1Path, asset1); AssetSerializer.Save(asset2Path, asset2); } catch (Exception exception) { result.Error("Unexpected error while serializing assets on disk before using diff tool", exception); return result; } var outputPath = Path.GetTempFileName(); try { // TODO We need to access different diff tools command line // kdiff3.exe file1 file2 file3 -o outputfile var process = Process.Start(DefaultMergeTool, string.Format("{0} {1} {2} -o {3}", assetBasePath, asset1Path, asset2Path, outputPath)); if (process == null) { result.Error("Unable to launch diff3 tool exe from [{0}]", DefaultMergeTool); } else { process.WaitForExit(); if (process.ExitCode != 0) { result.Error("Error, failed to merge files"); } } } catch (Exception ex) { result.Error("Unable to launch diff3 tool exe from [{0}]", ex, DefaultMergeTool); } if (!result.HasErrors) { try { bool aliasOccurred; var mergedAsset = (Asset)AssetSerializer.Load(outputPath, null, out aliasOccurred); if (mergedAsset != null) { if (assetFrom1 == null) { mergedAsset.Base = assetFrom2 == null ? assetBase0.Base : assetFrom2.Base; } else { mergedAsset.Base = assetFrom1.Base; } } result.Asset = mergedAsset; } catch (Exception ex) { result.Error("Unexpected exception while loading merged assets from [{0}]", ex, outputPath); } } return result; }
/// <summary> /// Merges the specified assets from <c>base</c> and <c>from2</c> into <c>from1</c>. /// </summary> /// <param name="assetDiff">A precomputed asset difference.</param> /// <param name="mergePolicy">The merge policy.</param> /// <param name="previewOnly">if set to <c>true</c> then the merge will not change the object.</param> /// <returns>MergePreviewResult.</returns> /// <exception cref="System.ArgumentNullException">assetDiff /// or /// mergePolicy</exception> public static MergeResult Merge(AssetDiff assetDiff, MergePolicyDelegate mergePolicy, bool previewOnly = false) { if (assetDiff == null) throw new ArgumentNullException("assetDiff"); if (mergePolicy == null) throw new ArgumentNullException("mergePolicy"); var allDiffs = assetDiff.Compute(); var diff3 = allDiffs.FindDifferences().ToList(); var result = new MergeResult(assetDiff.Asset1); // Try to merge foreach (var diff3Node in diff3) { Diff3ChangeType changeType; try { changeType = mergePolicy(diff3Node); if (changeType >= Diff3ChangeType.Conflict) { result.Error("Unresolved conflict [{0}] on node [{1}/{2}/{3}]", diff3Node.ChangeType, diff3Node.BaseNode, diff3Node.Asset1Node, diff3Node.Asset2Node); continue; } // If we are in preview only mode, just skip the update if (previewOnly) { continue; } object dataInstance; bool replaceValue; switch (changeType) { case Diff3ChangeType.MergeFromAsset2: // As we are merging into asset1, the only relevant changes can only come from asset2 dataInstance = diff3Node.Asset2Node != null ? diff3Node.Asset2Node.Instance : null; replaceValue = true; break; case Diff3ChangeType.Children: MergeContainer(diff3Node, out dataInstance); replaceValue = dataInstance != null; break; default: continue; } // Sets the value on the node if (replaceValue) diff3Node.ReplaceValue(dataInstance, node => node.Asset1Node); } catch (Exception ex) { result.Error("Unexpected error while merging [{0}] on node [{1}]", ex, diff3Node.ChangeType, diff3Node.InstanceType); break; } } if (!previewOnly) { foreach (var node in allDiffs.Asset1Node.Children(node => true)) { if (node.Instance is IDiffProxy) { ((IDiffProxy)node.Instance).ApplyChanges(); } } } return result; }