namespace GitMerge { using UnityEngine; using UnityEditor; using UnityEngine.SceneManagement; using UnityEditor.SceneManagement; using System.Collections.Generic; using Object = UnityEngine.Object; public class MergeManagerScene : MergeManagerBase { private const int NUMBER_OF_INITIALIZATION_STEPS = 3; private Scene theirScene; public MergeManagerScene(GitMergeWindow window, VCS vcs) : base(window, vcs) { } public bool TryInitializeMerge(string path = null) { var activeScene = EditorSceneManager.GetActiveScene(); if (activeScene.isDirty) { window.ShowNotification(new GUIContent("Please make sure there are no unsaved changes before attempting to merge.")); return false; } DisplayProgressBar(0, "Checking out scene..."); isMergingScene = true; var scenePath = path ?? activeScene.path; // Overwrite the current scene to prevent the reload/ignore dialog that pops up after the upcoming changes to the file. // Pressing "reload" on it would invalidate the GameObject references we're about to collect. EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); Lightmapping.ForceStop(); vcs.CheckoutOurs(scenePath); CheckoutTheirVersionOf(scenePath); AssetDatabase.Refresh(); DisplayProgressBar(1, "Opening scene..."); activeScene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); MergeAction.inMergePhase = false; ObjectDictionaries.Clear(); List ourObjects; try { DisplayProgressBar(2, "Collecting differences..."); // Find all of "our" objects ourObjects = GetAllSceneObjects(); ObjectDictionaries.AddToOurObjects(ourObjects); // Add "their" objects theirScene = EditorSceneManager.OpenScene(theirFilename, OpenSceneMode.Additive); var addedObjects = GetAllNewSceneObjects(ourObjects); BuildAllMergeActions(ourObjects, addedObjects); ObjectDictionaries.AddToTheirObjects(addedObjects); MoveGameObjectsToScene(theirScene.GetRootGameObjects(), activeScene); } finally { EditorSceneManager.UnloadSceneAsync(theirScene); AssetDatabase.DeleteAsset(theirFilename); EditorUtility.ClearProgressBar(); } if (allMergeActions.Count == 0) { window.ShowNotification(new GUIContent("No conflict found for this scene.")); return false; } MergeAction.inMergePhase = true; return true; } private static void DisplayProgressBar(int step, string text) { var progress = step / (float)NUMBER_OF_INITIALIZATION_STEPS; EditorUtility.DisplayProgressBar("GitMerge for Unity", text, progress); } private static void MoveGameObjectsToScene(IEnumerable addedObjects, Scene scene) { foreach (var obj in addedObjects) { EditorSceneManager.MoveGameObjectToScene(obj, scene); } } private static List GetAllSceneObjects() { var objects = (GameObject[])Object.FindObjectsOfType(typeof(GameObject)); return new List(objects); } /// /// Finds all GameObjects in the scene, minus the ones passed. /// private static List GetAllNewSceneObjects(List oldObjects) { var all = GetAllSceneObjects(); foreach (var obj in oldObjects) { all.Remove(obj); } return all; } /// /// Completes the merge process after solving all conflicts. /// Cleans up the scene by deleting "their" GameObjects, clears merge related data structures, /// executes git add scene_name. /// public override void CompleteMerge() { MergeAction.inMergePhase = false; ObjectDictionaries.DestroyTheirObjects(); ObjectDictionaries.Clear(); EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene()); allMergeActions = null; vcs.MarkAsMerged(fileName); // Directly committing here might not be that smart, since there might be more conflicts window.ShowNotification(new GUIContent("Scene successfully merged.")); } /// /// Aborts merge by using "our" version in all conflicts. /// Cleans up merge related data. /// public override void AbortMerge(bool showNotification = true) { base.AbortMerge(showNotification); EditorSceneManager.CloseScene(theirScene, true); EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene()); } } }