Git Commands From Unity

For most of my projects I use git for version control, sometimes I use custom scripts to version my builds so they include the current commit hash. To do this in Unity I use the .Net Process class in an editor script to launch the git command.

The function below allows me to launch any git command that doesn’t require feedback.

public static string RunGitCommand(string gitCommand) {
	// Strings that will catch the output from our process.
	string output = "no-git";
	string errorOutput = "no-git";

	// Set up our processInfo to run the git command and log to output and errorOutput.
	ProcessStartInfo processInfo = new ProcessStartInfo("git", @gitCommand) {
		CreateNoWindow = true,          // We want no visible pop-ups
		UseShellExecute = false,        // Allows us to redirect input, output and error streams
		RedirectStandardOutput = true,  // Allows us to read the output stream
		RedirectStandardError = true    // Allows us to read the error stream
	};

	// Set up the Process
	Process process = new Process {
		StartInfo = processInfo
	};

	try {
		process.Start();  // Try to start it, catching any exceptions if it fails
	} catch (Exception e) {
		// For now just assume its failed cause it can't find git.
		Debug.LogError("Git is not set-up correctly, required to be on PATH, and to be a git project.");
		throw e;
	}

	// Read the results back from the process so we can get the output and check for errors
	output = process.StandardOutput.ReadToEnd();
	errorOutput = process.StandardError.ReadToEnd();

	process.WaitForExit();  // Make sure we wait till the process has fully finished.
	process.Close();        // Close the process ensuring it frees it resources.

	// Check for failure due to no git setup in the project itself or other fatal errors from git.
	if (output.Contains("fatal") || output == "no-git" || output == "") {
		throw new Exception("Command: git " + @gitCommand + " Failed\n" + output + errorOutput);
	}
	// Log any errors.
	if (errorOutput != "") {
		Debug.LogError("Git Error: " + errorOutput);
	}

	return output;  // Return the output from git.
}

This function sets up the process, launches the git command and returns the result, throwing exceptions if anything goes wrong. This allows it to be easily extended to handle multiple git commands for example to get the git commit hash.

public static string RetrieveCurrentCommitShorthash() {
	string result = RunGitCommand("rev-parse --short --verify HEAD");
	// Clean up whitespace around hash. (seems to just be the way this command returns :/ )
	result = string.Join("", result.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
	Debug.Log("Current Commit: " + result);
	return result;
}

With this information we can update the bundle version of the current build, allowing for them to always be associated to a specific commit.

public static void UpdateBundleVersion() {
	string currentVersion, commit;
	int major, minor, patch;

	try {
		// Cut up the version into Major.Minor.Patch.Commit.
		currentVersion = PlayerSettings.bundleVersion;
		major = Convert.ToInt32(currentVersion.Split('.')[0]);
		minor = Convert.ToInt32(currentVersion.Split('.')[1]);
		patch = Convert.ToInt32(currentVersion.Split('.')[2]);
	} catch (Exception e) {
		// Most likely failed to get major/minor/patch in correct format.
		Debug.LogErrorFormat("Unexpected bundleVersion Format: {0}\nExpected format: *.*.*.* ", PlayerSettings.bundleVersion);
		throw e;
	}

	// Run the Git Command to get the commit short hash.
	// Throws exceptions if it fails, we want a build to fail if this happens.
	commit = RetrieveCurrentCommitShorthash();

	// Update to the new version
	PlayerSettings.bundleVersion = string.Format("{0}.{1}.{2}.{3}", major, minor, patch, commit);

	Debug.LogFormat("Updated Game Version: {0}.{1}.{2}.{3}", major, minor, patch, commit);
	// This is to ensure that it is saved properly, if you were to build afterwards it would reset to library setting.
	AssetDatabase.SaveAssets();
	// This Ensures that it will be used if we build directly after calling the function.
	AssetDatabase.Refresh();
}

Having commits associated to your builds makes bug tracking much easier however, using IPreProcessBuild & IPostProcessBuild can ensure that the current build is always up to date.

The full CommonGitCommands file I use is available here StewMcC/LittleLotLibrary

Related

comments powered by Disqus