GitUtil.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using Debug = UnityEngine.Debug;
  5. namespace SingularityGroup.HotReload.Editor {
  6. internal static class GitUtil {
  7. /// <remarks>
  8. /// Fallback is PatchServerInfo.UnknownCommitHash
  9. /// </remarks>
  10. public static string GetShortCommitHashOrFallback(int timeoutAfterMillis = 5000) {
  11. var shortCommitHash = PatchServerInfo.UnknownCommitHash;
  12. var commitHash = GetShortCommitHashSafe(timeoutAfterMillis);
  13. // On MacOS GetShortCommitHash() returns 7 characters, on Windows it returns 8 characters.
  14. // When git command produced an unexpected result, use a fallback string
  15. if (commitHash != null && commitHash.Length >= 6) {
  16. shortCommitHash = commitHash.Length < 8 ? commitHash : commitHash.Substring(0, 8);
  17. }
  18. return shortCommitHash;
  19. }
  20. // only log exception once per domain reload, to prevent spamming the console
  21. private static bool loggedExceptionInGetShortCommitHashSafe = false;
  22. /// <summary>
  23. /// Get the git commit hash, returning null if it takes too long.
  24. /// </summary>
  25. /// <param name="timeoutAfterMillis"></param>
  26. /// <returns></returns>
  27. /// <remarks>
  28. /// This method is 'better safe than sorry' because we must not break the user's build.<br/>
  29. /// It is better to not know the commit hash than to fail the build.
  30. /// </remarks>
  31. private static string GetShortCommitHashSafe(int timeoutAfterMillis) {
  32. Process process = null;
  33. // Note: don't use ReadToEndAsync because waiting on that task blocks forever.
  34. try {
  35. process = StartGitCommand("log", " -n 1 --pretty=format:%h");
  36. var stdout = process.StandardOutput;
  37. if (process.WaitForExit(timeoutAfterMillis)) {
  38. return stdout.ReadToEnd();
  39. } else {
  40. // In a git repo with git lfs, git log can be blocked by waiting for switch branches / download lfs objects
  41. // For that reason I disabled this warning log until a better solution is implemented (e.g. cache the commit and use cached if timeout).
  42. // Log.Warning(
  43. // $"[{CodePatcher.TAG}] Timed out trying to get the git commit hash, HotReload will not warn you about" +
  44. // " a build connecting to a server running on a different commit (which is not supported)");
  45. return null;
  46. }
  47. } catch (Win32Exception ex) {
  48. if (ex.NativeErrorCode == 2) {
  49. // git not found, ignore because user doesn't use git for version control
  50. return null;
  51. } else if (!loggedExceptionInGetShortCommitHashSafe) {
  52. loggedExceptionInGetShortCommitHashSafe = true;
  53. Debug.LogException(ex);
  54. }
  55. } catch (Exception ex) {
  56. if (!loggedExceptionInGetShortCommitHashSafe) {
  57. loggedExceptionInGetShortCommitHashSafe = true;
  58. Log.Exception(ex);
  59. }
  60. } finally {
  61. if (process != null) {
  62. process.Dispose();
  63. }
  64. }
  65. return null;
  66. }
  67. static Process StartGitCommand(string command, string arguments, Action<ProcessStartInfo> modifySettings = null) {
  68. var startInfo = new ProcessStartInfo("git", command + " " + arguments) {
  69. RedirectStandardOutput = true,
  70. RedirectStandardError = true,
  71. UseShellExecute = false,
  72. CreateNoWindow = true,
  73. };
  74. if (modifySettings != null) {
  75. modifySettings(startInfo);
  76. }
  77. return Process.Start(startInfo);
  78. }
  79. }
  80. }