static bool CompareSerializedPropertyObjectReference(SerializedProperty p1, SerializedProperty p2, GameObject root1, GameObject root2, ref Action apply, ref Action onGUI)
        {
            // If this is objectReference property, we want to try to find same object in our hierarchy and use it. Otherwise
            // property is just copied and links same object.

            onGUI = () =>
            {
                EditorGUILayout.LabelField(p2.serializedObject.targetObject.GetType().Name + "." + p2.propertyPath + ": reference changed");
                EditorGUILayout.ObjectField(p1.objectReferenceValue, typeof(UnityEngine.Object), false, GUILayout.Width(200));
            };

            if (p1.objectReferenceValue == null || EditorUtility.IsPersistent(p1.objectReferenceValue))
            {
                apply = () =>
                {
                    p2.objectReferenceValue = p1.objectReferenceValue;
                };
                return(p2.objectReferenceValue == p1.objectReferenceValue);
            }
            else
            {
                GameObject referencedGO = null;
                if (p1.objectReferenceValue is GameObject)
                {
                    referencedGO = p1.objectReferenceValue as GameObject;
                }
                else if (p1.objectReferenceValue is Component)
                {
                    referencedGO = (p1.objectReferenceValue as Component).gameObject;
                }
                else
                {
                    Debug.LogWarning(string.Format("Unknown object reference type {0}", p1.objectReferenceValue.GetType()), p1.serializedObject.targetObject);
                    return(true);
                }

                if (referencedGO == null)
                {
                    Debug.LogError(string.Format("Wrong object reference type {0}", p1.objectReferenceValue.GetType()), p1.serializedObject.targetObject);
                    return(true);
                }

                string path1 = EditorUtils.GetPathForObjectInHierarchy(referencedGO, root1);
                if (path1 == null)
                {
                    // It means it references to some of other assets, leave link as it is.
                    return(true);
                }

                //Debug.LogWarning("Path: {0} for: {1}", path, fromProp.objectReferenceValue.name);

                GameObject newGO = root2.GetChildByPath(path1);
                if (newGO == null)
                {
                    //Debug.LogWarning(string.Format("Can't find transform for path {0}", path1), p1.serializedObject.targetObject);
                    return(true);
                }

                if (p1.objectReferenceValue is GameObject)
                {
                    apply = () => { p2.objectReferenceValue = newGO.gameObject; };
                    return(path1 == EditorUtils.GetPathForObjectInHierarchy(p2.objectReferenceValue as GameObject, root2));
                }
                else if (p1.objectReferenceValue is Component)
                {
                    Component myComp = newGO.gameObject.GetComponent(p1.objectReferenceValue.GetType());
                    if (myComp == null)
                    {
                        // Can't apply this change since we can't find needed component in hierarchy
                        //Debug.LogError(string.Format("Can't find component on object {0}", path1), p1.serializedObject.targetObject);
                    }

                    apply = () => { p2.objectReferenceValue = myComp; };

                    var c2 = p2.objectReferenceValue as Component;
                    if (c2 != null)
                    {
                        return(path1 == EditorUtils.GetPathForObjectInHierarchy(c2.gameObject, root2));
                    }
                    else
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
        public static List <Diff> GetDiffs(GameObject instance, GameObject instanceRoot, GameObject prefab, GameObject prefabRoot)
        {
            List <Diff> diffs = new List <Diff>();

            if (prefab)
            {
                // compare existing components
                var prefabComponents  = prefab.GetComponents <Component>();
                var deletedComponents = new List <Component>();
                foreach (var prefabComponent in prefabComponents)
                {
                    var       instanceComponentsOfType = instance.GetComponents(prefabComponent.GetType());
                    Component instanceComponent        = instanceComponentsOfType.Length > 0 ? instanceComponentsOfType[0] : null;

                    // if we have more than one same component -> try to get the same by order of GetComponents()
                    if (instanceComponentsOfType.Length > 1)
                    {
                        int indexInPrefab = Array.IndexOf(prefab.GetComponents(prefabComponent.GetType()), prefabComponent);
                        instanceComponent = instanceComponentsOfType[indexInPrefab % instanceComponentsOfType.Length];
                    }

                    if (instanceComponent)
                    {
                        FillDiffsFromComponent(ref diffs, instanceComponent, prefabComponent, instanceRoot, prefabRoot);
                    }
                    else
                    {
                        deletedComponents.Add(prefabComponent);
                    }
                }

                // delete obsolete components
                foreach (var deletedComponent in deletedComponents)
                {
                    Component prefabDeletedComponent = deletedComponent;
                    Diff      deleteDiff             = new Diff(Diff.Operation.Delete, prefabDeletedComponent, null);
                    deleteDiff.Apply = () => { GameObject.DestroyImmediate(prefabDeletedComponent, true); };
                    deleteDiff.OnGUI = () =>
                    {
                        GUI.color = new Color(1, 0.7f, 0.7f);
                        EditorGUILayout.LabelField("Deleted component:", prefabDeletedComponent.GetType().Name);
                        GUI.color = Color.white;
                        EditorGUILayout.ObjectField(prefabDeletedComponent, typeof(Component), false);
                    };
                    diffs.Add(deleteDiff);
                }

                // add new components
                var instanceComponents = instance.GetComponents <Component>();
                foreach (var instanceComponent in instanceComponents)
                {
                    if (!System.Array.Find <Component>(prefabComponents, (x) => { return(x.GetType() == instanceComponent.GetType()); }))
                    {
                        Component instanceNewComponent = instanceComponent;
                        Diff      addDiff = new Diff(Diff.Operation.Add, instanceNewComponent, null);
                        addDiff.Apply = () => {
                            var prefabComponent = prefab.AddComponent(instanceNewComponent.GetType());
                            UnityEditor.EditorUtility.CopySerialized(instanceNewComponent, prefabComponent);
                        };
                        addDiff.OnGUI = () =>
                        {
                            GUI.color = new Color(0.7f, 1, 0.7f);
                            EditorGUILayout.LabelField("Added component:", instanceNewComponent.GetType().Name);
                            GUI.color = Color.white;
                            EditorGUILayout.ObjectField(instanceNewComponent, typeof(Component), false);
                        };
                        diffs.Add(addDiff);
                    }
                }
            }
            else
            {
                // probably new gameobject
                GameObjectDiff.Diff d = new GameObjectDiff.Diff(GameObjectDiff.Diff.Operation.New, null, instance.name);
                d.Apply = () =>
                {
                    GameObject prefabInstance = PrefabUtility.InstantiatePrefab(prefabRoot) as GameObject;
                    GameObject instanceCopy   = new GameObject(instance.name);
                    string     path           = EditorUtils.GetPathForObjectInHierarchy(instance, instanceRoot);
                    instanceCopy.transform.parent = prefabInstance.GetChildByPath(path).transform;
                    foreach (Component c in instance.GetComponents <Component>())
                    {
                        if (c is Transform)
                        {
                            continue;
                        }
                        EditorUtility.CopySerialized(c, instanceCopy.AddComponent(c.GetType()));
                    }
                    EditorUtility.CopySerialized(instance.transform, instanceCopy.transform);
                    PrefabUtility.ReplacePrefab(prefabInstance, prefabRoot);
                    GameObject.DestroyImmediate(prefabInstance);
                };
                d.OnGUI = () =>
                {
                    GUI.color = new Color(0, 1f, 0);
                    EditorGUILayout.LabelField("New GameObject:", instance.name);
                    GUI.color = Color.white;
                };
                diffs.Add(d);
            }

            return(diffs);
        }
        static bool CompareSerializedProperty(SerializedProperty p1, SerializedProperty p2, GameObject root1, GameObject root2, ref Action apply, ref Action onGUI)
        {
            if (p1.propertyType != p2.propertyType)
            {
                Debug.LogError("SerializedPropertys have different types!");
            }

            string SIMPLE_FORMAT = string.Format("{0}.{1}: {2}", p1.serializedObject.targetObject.GetType().Name, p1.propertyPath, "{1} -> {0}");

            switch (p1.propertyType)
            {
            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Integer:
            case SerializedPropertyType.LayerMask:
            case SerializedPropertyType.Character:
            case SerializedPropertyType.ArraySize:
                apply = () =>
                {
                    p2.intValue = p1.intValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.intValue, p2.intValue));
                };
                return(p2.intValue == p1.intValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Boolean:
                apply = () =>
                {
                    p2.boolValue = p1.boolValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.boolValue, p2.boolValue));
                };
                return(p2.boolValue == p1.boolValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Float:
                apply = () =>
                {
                    p2.floatValue = p1.floatValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.floatValue, p2.floatValue));
                };
                return(p2.floatValue == p1.floatValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.String:
                apply = () =>
                {
                    p2.stringValue = p1.stringValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.stringValue, p2.stringValue));
                };
                return(p2.stringValue == p1.stringValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Color:
                apply = () =>
                {
                    p2.colorValue = p1.colorValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.colorValue.ToString255(), p2.colorValue.ToString255()));
                    EditorGUILayout.ColorField(p2.colorValue, GUILayout.Width(45));
                    EditorGUILayout.LabelField(" -> ", GUILayout.Width(35));
                    EditorGUILayout.ColorField(p1.colorValue, GUILayout.Width(45));
                };
                return(p2.colorValue == p1.colorValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Enum:
                apply = () =>
                {
                    p2.enumValueIndex = p1.enumValueIndex;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.enumNames[p1.enumValueIndex], p2.enumNames[p2.enumValueIndex]));
                };
                return(p2.enumValueIndex == p1.enumValueIndex);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Vector2:
                apply = () =>
                {
                    p2.vector2Value = p1.vector2Value;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.vector2Value, p2.vector2Value));
                };
                return(p2.vector2Value == p1.vector2Value);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Vector3:
                apply = () =>
                {
                    p2.vector3Value = p1.vector3Value;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.vector3Value, p2.vector3Value));
                };
                return(p2.vector3Value == p1.vector3Value);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Quaternion:
                apply = () =>
                {
                    p2.quaternionValue = p1.quaternionValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.quaternionValue.eulerAngles, p2.quaternionValue.eulerAngles));
                };
                return(p2.quaternionValue == p1.quaternionValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Rect:
                apply = () =>
                {
                    p2.rectValue = p1.rectValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.rectValue, p2.rectValue));
                };
                return(p2.rectValue == p1.rectValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.AnimationCurve:

                apply = () =>
                {
                    p2.animationCurveValue = p1.animationCurveValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(p2.serializedObject.targetObject.GetType().Name + "." + p2.propertyPath + ": animation curve changed");
                };
                return(EditorUtils.IsAnimationCurvesEqual(p2.animationCurveValue, p1.animationCurveValue));

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Bounds:
                apply = () =>
                {
                    p2.boundsValue = p1.boundsValue;
                };
                onGUI = () =>
                {
                    EditorGUILayout.LabelField(string.Format(SIMPLE_FORMAT, p1.boundsValue, p2.boundsValue));
                };
                return(p2.boundsValue == p1.boundsValue);

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.ObjectReference:
                return(CompareSerializedPropertyObjectReference(p1, p2, root1, root2, ref apply, ref onGUI));

            /////////////////////////////////////////////////////////
            case SerializedPropertyType.Generic:
                // not implemented
                return(true);

            /////////////////////////////////////////////////////////
            /////////////////////////////////////////////////////////
            default:
                // not implemented
                //Debug.LogError("Implement: " + p1.propertyType + " " + p1.propertyPath);
                return(true);
            }
        }