123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
-
- using System;
- namespace GitMerge
- {
- using UnityEngine;
- using UnityEditor;
- using System.Collections.Generic;
- using System.Text;
-
- /// <summary>
- /// One instance of this class represents one GameObject with relevance to the merge process.
- /// Holds all MergeActions that can be applied to the GameObject or its Components.
- /// Is considered as "merged" when all its MergeActions are "merged".
- /// </summary>
- public class GameObjectMergeActions
- {
- /// <summary>
- /// Reference to "our" version of the GameObject.
- /// </summary>
- public GameObject ours { private set; get; }
- /// <summary>
- /// Reference to "their" versoin of the GameObject.
- /// </summary>
- public GameObject theirs { private set; get; }
- public bool applied { protected set; get; }
- public string name { private set; get; }
- public bool merged { private set; get; }
- public bool hasActions
- {
- get { return actions.Count > 0; }
- }
- /// <summary>
- /// All actions available for solving specific conflicts on the GameObject.
- /// </summary>
- private List<MergeAction> actions;
- public GameObjectMergeActions(GameObject ours, GameObject theirs)
- {
- actions = new List<MergeAction>();
- this.ours = ours;
- this.theirs = theirs;
- GenerateName();
- if (theirs && !ours)
- {
- actions.Add(new MergeActionNewGameObject(ours, theirs));
- }
- else if (ours && !theirs)
- {
- actions.Add(new MergeActionDeleteGameObject(ours, theirs));
- }
- else if (ours && theirs)
- {
- FindPropertyDifferences();
- FindComponentDifferences();
- }
- // Some Actions have a default and are merged from the beginning.
- // If all the others did was to add GameObjects, we're done with merging from the start.
- CheckIfMerged();
- }
- /// <summary>
- /// Generate a title for this object
- /// </summary>
- private void GenerateName()
- {
- name = "";
- if (ours)
- {
- name = "Your[" + ours.GetPath() + "]";
- }
- if (theirs)
- {
- if (ours)
- {
- name += " vs. ";
- }
- name += "Their[" + theirs.GetPath() + "]";
- }
- }
- /// <summary>
- /// Finds the differences between properties of the two GameObjects.
- /// That means the name, layer, tag... everything that's not part of a Component. Also, the parent.
- /// </summary>
- private void FindPropertyDifferences()
- {
- CheckForDifferentParents();
- FindPropertyDifferences(ours, theirs);
- }
- /// <summary>
- /// Since parenting is quite special, here's some dedicated handling.
- /// </summary>
- private void CheckForDifferentParents()
- {
- var transform = ours.GetComponent<Transform>();
- var ourParent = transform.parent;
- var theirParent = theirs.GetComponent<Transform>().parent;
- if (!ObjectID.GetFor(ourParent).Equals(ObjectID.GetFor(theirParent)))
- {
- actions.Add(new MergeActionParenting(transform, ourParent, theirParent));
- }
- }
- /// <summary>
- /// Check for Components that one of the sides doesn't have, and/or for defferent values
- /// on Components.
- /// </summary>
- private void FindComponentDifferences()
- {
- var ourComponents = ours.GetComponents<Component>();
- var theirComponents = theirs.GetComponents<Component>();
- // Map "their" Components to their respective ids.
- var theirDict = new Dictionary<ObjectID, Component>();
- foreach (var theirComponent in theirComponents)
- {
- // Ignore null components.
- if (theirComponent != null)
- {
- theirDict.Add(ObjectID.GetFor(theirComponent), theirComponent);
- }
- }
- foreach (var ourComponent in ourComponents)
- {
- // Ignore null components.
- if (ourComponent == null) continue;
- // Try to find "their" equivalent to our Components.
- var id = ObjectID.GetFor(ourComponent);
- Component theirComponent;
- theirDict.TryGetValue(id, out theirComponent);
- if (theirComponent) // Both Components exist.
- {
- FindPropertyDifferences(ourComponent, theirComponent);
- // Remove "their" Component from the dict to only keep those new to us.
- theirDict.Remove(id);
- }
- else
- {
- // Component doesn't exist in their version, offer a deletion.
- actions.Add(new MergeActionDeleteComponent(ours, ourComponent));
- }
- }
- // Everything left in the dict is a...
- foreach (var theirComponent in theirDict.Values)
- {
- // ...new Component from them.
- actions.Add(new MergeActionNewComponent(ours, theirComponent));
- }
- }
- /// <summary>
- /// Find all the values different in "our" and "their" version of a component.
- /// </summary>
- private void FindPropertyDifferences(Object ourObject, Object theirObject)
- {
- var ourSerializedObject = new SerializedObject(ourObject);
- var theirSerializedObject = new SerializedObject(theirObject);
- var ourProperty = ourSerializedObject.GetIterator();
- if (ourProperty.Next(true))
- {
- var theirProperty = theirSerializedObject.GetIterator();
- theirProperty.Next(true);
- var shouldEnterChildren = ourProperty.hasVisibleChildren;
- while (ourProperty.NextVisible(shouldEnterChildren))
- {
- theirProperty.NextVisible(shouldEnterChildren);
- // If merging a prefab, ignore the gameobject name.
- if (ourObject is GameObject
- && MergeManagerBase.isMergingPrefab
- && ourProperty.GetPlainName() == "Name")
- {
- continue;
- }
- if (DifferentValues(ourProperty, theirProperty))
- {
- // We found a difference, accordingly add a MergeAction.
- actions.Add(new MergeActionChangeValues(ours, ourProperty.Copy(), theirProperty.Copy()));
- }
- }
- }
- }
- /// <summary>
- /// Returns true when the two properties have different values, false otherwise.
- /// </summary>
- private static bool DifferentValues(SerializedProperty ourProperty, SerializedProperty theirProperty)
- {
- if (!ourProperty.IsRealArray())
- {
- // Regular single-value property.
- return DifferentValuesFlat(ourProperty, theirProperty);
- }
- else
- {
- // Array property.
- if (ourProperty.arraySize != theirProperty.arraySize)
- {
- return true;
- }
- var op = ourProperty.Copy();
- var tp = theirProperty.Copy();
- op.Next(true);
- op.Next(true);
- tp.Next(true);
- tp.Next(true);
- for (int i = 0; i < ourProperty.arraySize; ++i)
- {
- op.Next(false);
- tp.Next(false);
- if (DifferentValuesFlat(op, tp))
- {
- return true;
- }
- }
- }
- return false;
- }
- private static bool DifferentValuesFlat(SerializedProperty ourProperty, SerializedProperty theirProperty)
- {
- var our = ourProperty.GetValue();
- var their = theirProperty.GetValue();
- if (ourProperty.propertyType == SerializedPropertyType.ObjectReference)
- {
- if (our != null && their != null)
- {
- our = ObjectID.GetFor(our as Object);
- their = ObjectID.GetFor(their as Object);
- }
- }
- return !object.Equals(our, their);
- }
- private void CheckIfMerged()
- {
- merged = actions.TrueForAll(action => action.merged);
- }
-
- private static void RefreshPrefabInstance()
- {
- if (MergeManagerBase.isMergingPrefab)
- {
- PrefabUtility.RevertObjectOverride(MergeManagerPrefab.ourPrefabInstance, InteractionMode.AutomatedAction);
- }
- }
- /// <summary>
- /// Use "our" version for all conflicts.
- /// This is used on all GameObjectMergeActions objects when the merge is aborted.
- /// </summary>
- public void UseOurs(bool forceApply = false)
- {
- foreach (var action in actions)
- {
- action.UseOurs();
- }
- merged = true;
- applied = forceApply;
- }
- /// <summary>
- /// Use "their" version for all conflicts.
- /// </summary>
- public void UseTheirs(bool forceApply = false)
- {
- foreach (var action in actions)
- {
- action.UseTheirs();
- }
- merged = true;
- applied = forceApply;
- }
- //If the foldout is open
- private bool open;
- public void OnGUI()
- {
- // Show only the selected ones
- var selection = Selection.activeGameObject;
- var objectToHighlight = MergeManagerBase.isMergingPrefab ? MergeManagerPrefab.ourPrefabInstance.GetChildWithEqualPath(ours) : ours;
- if (applied || selection == null || selection.gameObject != objectToHighlight)
- {
- return;
- }
-
- if (open)
- {
- GUI.backgroundColor = new Color(0, 0, 0, .8f);
- }
- else
- {
- GUI.backgroundColor = merged ? new Color(0, .5f, 0, .8f) : new Color(.5f, 0, 0, .8f);
- }
- GUILayout.BeginVertical(Resources.styles.mergeActions);
- GUI.backgroundColor = Color.white;
- GUILayout.BeginHorizontal();
- open = EditorGUILayout.Foldout(open, new GUIContent(name));
- // if (ours && GUILayout.Button("Focus", EditorStyles.miniButton, GUILayout.Width(100)))
- // {
- // // Highlight the instance of the prefab, not the prefab itself.
- // // Otherwise, "ours".
- // objectToHighlight.Highlight();
- // }
-
- // Only show when we resolve the conflict for all merge actions
- bool resolved = true;
- foreach (var action in actions)
- {
- if (action.merged == false)
- {
- resolved = false;
- }
- }
- if (resolved)
- {
- if (ours && GUILayout.Button("Apply", EditorStyles.miniButton, GUILayout.Width(100)))
- {
- foreach (var action in actions)
- {
- action.mergeAction?.Invoke();
- }
- applied = true;
- RefreshPrefabInstance();
- }
- }
- GUILayout.EndHorizontal();
- if (open)
- {
- // Display all merge actions.
- foreach (var action in actions)
- {
- if (action.OnGUIMerge())
- {
- CheckIfMerged();
- }
- }
- }
- else
- {
- GUILayout.BeginHorizontal();
- if (GUILayout.Button("Use ours >>>", EditorStyles.miniButton))
- {
- UseOurs();
- }
- if (GUILayout.Button("<<< Use theirs", EditorStyles.miniButton))
- {
- UseTheirs();
- }
- GUILayout.EndHorizontal();
- }
- // If "ours" is null, the GameObject doesn't exist in one of the versions.
- // Try to get a reference if the object exists in the current merging state.
- // If it exists, the new/gelete MergeAction will have a reference.
- if (!ours)
- {
- foreach (var action in actions)
- {
- ours = action.ours;
- }
- }
- GUILayout.EndVertical();
- GUI.backgroundColor = Color.white;
- }
- }
- }
|