// Copyright (c) 2025 TerraByte Inc. // // This script creates a custom Unity Editor window called "Arbitrator" to compare // local Git changes with the tracked remote branch using the LibGit2Sharp library. // // HOW TO USE: // 1. Ensure you have manually installed the LibGit2Sharp v0.27.0 package. // 2. Create an "Editor" folder in your Assets directory if you don't have one. // 3. Save this script as "Arbitrator.cs" inside the "Editor" folder. // 4. In Unity, open the window from the top menu: Terra > Arbitrator. // 5. Click the "Compare with Cloud" button. Results will appear in the console. using System; using System.IO; using UnityEngine; using UnityEditor; using LibGit2Sharp; namespace Terra.Arbitrator { public class Arbitrator : EditorWindow { // Creates a menu item in the Unity Editor to open this window. [MenuItem("Terra/Arbitrator")] public static void ShowWindow() { // Get an existing open window or if none, make a new one. GetWindow("Arbitrator"); } // This method is called to draw the contents of the editor window. private void OnGUI() { // Add some descriptive text to the window. EditorGUILayout.LabelField("Compare local changes against the cloud.", EditorStyles.boldLabel); EditorGUILayout.HelpBox("This tool will fetch the latest state from the remote repository and show you which files have been added, modified, or deleted locally.", MessageType.Info); // Render a button. If the user clicks it, call our main logic function. if (GUILayout.Button("Compare with Cloud", GUILayout.Height(40))) { CompareLocalToRemote(); } } /// /// The core function that performs the fetch and diff operations. /// private static void CompareLocalToRemote() { // The project root is one level above the "Assets" directory. var projectRoot = Directory.GetParent(Application.dataPath)?.FullName; try { // Use a 'using' statement to ensure the repository object is properly disposed of. // This opens the repository at the root of your Unity project. using var repo = new Repository(projectRoot); Debug.Log("Repository opened successfully."); // --- Step 1: Fetch the latest state from the remote --- // Get the primary remote, which is typically named "origin". var remote = repo.Network.Remotes["origin"]; if (remote == null) { Debug.LogError("No remote named 'origin' was found. Please configure a remote for your repository."); return; } // IMPORTANT: For private repositories, you'll need to provide credentials. // This is a more advanced topic involving Personal Access Tokens (PATs). var fetchOptions = new FetchOptions(); Debug.Log($"Fetching latest changes from '{remote.Name}' at {remote.Url}..."); Commands.Fetch(repo, remote.Name, Array.Empty(), fetchOptions, null); Debug.Log("Fetch complete."); // --- Step 2: Identify branches to compare --- var currentBranch = repo.Head; var remoteBranch = currentBranch.TrackedBranch; if (remoteBranch == null || !remoteBranch.IsRemote) { Debug.LogError($"The current branch '{currentBranch.FriendlyName}' is not tracking a remote branch. Set the upstream branch using 'git branch --set-upstream-to=origin/{currentBranch.FriendlyName}'."); return; } Debug.Log($"Comparing local '{currentBranch.FriendlyName}' against remote '{remoteBranch.FriendlyName}'."); // --- Step 3: Perform the Diff --- // We compare the tree from the latest commit on the remote branch // against the files currently in your working directory. var remoteTree = remoteBranch.Tip.Tree; var changes = repo.Diff.Compare(remoteTree, DiffTargets.Index | DiffTargets.WorkingDirectory); // --- Step 4: Log the Results --- if (changes.Count == 0) { Debug.Log("--- STATUS: You are up to date! No differences found with the remote branch. ---"); } else { Debug.Log($"--- STATUS: Found {changes.Count} changes. ---"); foreach (var change in changes) { // Log the status (Added, Deleted, Modified) and the file path. // The color-coding helps to quickly see the status. switch (change.Status) { case ChangeKind.Added: Debug.Log($"ADDED: {change.Path}"); break; case ChangeKind.Deleted: Debug.Log($"DELETED: {change.Path}"); break; case ChangeKind.Modified: Debug.Log($"MODIFIED: {change.Path}"); break; case ChangeKind.Unmodified: case ChangeKind.Renamed: case ChangeKind.Copied: case ChangeKind.Ignored: case ChangeKind.Untracked: case ChangeKind.TypeChanged: case ChangeKind.Unreadable: case ChangeKind.Conflicted: default: Debug.Log($"{change.Status.ToString().ToUpper()}: {change.Path}"); break; } } Debug.Log("--- End of Report ---"); } } catch (RepositoryNotFoundException) { Debug.LogError("Error: This project is not a Git repository or the .git folder is missing. Please initialize a repository first."); } catch (Exception ex) { // Catch any other exceptions that might occur (e.g., network issues during fetch). Debug.LogError($"An unexpected error occurred: {ex.Message}"); Debug.LogException(ex); } } } }