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);
        }
        public List <PrefabCandidate> GetPrefabCandidatesForSceneObject(GameObject instance)
        {
            List <PrefabCandidate> candidates = new List <PrefabCandidate>();

            RefreshIndex();

            GameObject instanceRoot = instance;

            while (instanceRoot != null)
            {
                string candidateName = EditorUtils.WithoutClonePostfix(instanceRoot.name);

                List <string> paths;
                if (_index.TryGetValue(candidateName, out paths))
                {
                    foreach (var path in paths)
                    {
                        GameObject prefabRoot = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject;
                        if (!prefabRoot)
                        {
                            continue;
                        }

                        var        prefabPath = EditorUtils.GetPathForObjectInHierarchy(instance, instanceRoot);
                        GameObject prefab     = prefabRoot.GetChildByPath(prefabPath);

                        if (prefab == null)
                        {
                            // ATTENTION:
                            // new gameobjects unsupported yet, comment "continue" to enable
                            continue;

                            // try find prefab for parent
                            prefabPath = EditorUtils.GetParentPath(prefabPath);
                            GameObject prefabParent = prefabRoot.GetChildByPath(prefabPath);
                            if (prefabParent == null)
                            {
                                continue;
                            }
                        }

                        PrefabCandidate candidate;
                        candidate.instanceRoot = instanceRoot;
                        candidate.prefabRoot   = prefabRoot;
                        candidate.prefab       = prefab;
                        candidate.prefabPath   = prefabPath;

                        candidates.Add(candidate);
                    }
                }

                instanceRoot = instanceRoot.GetParent();
            }

            // longer path is preferable
            if (candidates.Count > 0)
            {
                candidates.Sort((c1, c2) => c2.prefabPath.Length.CompareTo(c1.prefabPath.Length));
            }

            return(candidates);
        }
        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);
        }