Unity

[Unity]現在のGitブランチ名を拾う(Windows限定)

More than 1 year has passed since last update.


経緯

現在のワークスペース以下のブランチ名が欲しいぞウオァァァァァという衝動で作りました。

コマンドたたいてるだけですが思いのほか汎用性が高かったので公開


内容

using System;

using System.Collections.Generic;
using System.Diagnostics;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Hoge
{
/// <summary>
/// 外部プロセスに関する汎用クラス
/// </summary>
public static partial class ProcessUtility
{
#if UNITY_EDITOR
/// <summary>
/// テスト用処理
/// </summary>
[MenuItem("Tools/ExecProcess/git_blanches_name")]
static void ExecBlanchesNameTest()
{
ExecBlanchesName( ( ret ) => UnityEngine.Debug.Log( ret ) );
}
#endif
/// <summary>
/// プロセスの実行
/// </summary>
public static void ExecBlanchesName( Action<string> onCompelted )
{
#if !UNITY_EDITOR
onCompelted( string.Empty );
return;
#endif
// usingステートメントを使用するとスコープから抜けたタイミングでDisposeされて
// 完了通知が来る前にイベントを破棄してしまうのでこの書き方はダメ
//using ( var process = new OneProcess() )
//{
// process.Exec( "@echo off /k git branch --contains", ( str ) =>
// {
// onCompelted( str );
// });
//}
var process = new OneProcess();
process.Exec( "@echo off /k git branch --contains", ( str ) =>
{
process.Dispose();
onCompelted( str );
});
}

/// <summary>
/// 1プロセス単位
/// </summary>
private class OneProcess : IDisposable
{
//=============================
//! メンバー変数
//=============================
private Process m_process = null;
private List<string> m_list = new List<string>();
private Action<string> m_onCompleted = null;

/// <summary>
/// 破棄処理
/// </summary>
public void Dispose()
{
m_onCompleted = null;

m_list.Clear();
m_list = null;

if ( m_process == null )
{
return;
}
m_process.OutputDataReceived -= Process_OutputDataReceived;
m_process.Exited -= Process_Exited;

m_process.Close();

m_process = null;
}

/// <summary>
/// 実行
/// </summary>
public void Exec( string cmd, Action<string> onCompleted = null )
{
m_process = new Process();

m_process.StartInfo.FileName = Environment.GetEnvironmentVariable( "ComSpec" ); // 実行するファイル名(ComSpecがコマンドプロンプト
m_process.StartInfo.Arguments = cmd; // 引数
m_process.StartInfo.UseShellExecute = false; // プロセス起動時にシェルを実行するか
m_process.StartInfo.RedirectStandardOutput = true; // 標準出力読み取り
m_process.OutputDataReceived += Process_OutputDataReceived; // 出力時のコールバック
m_process.StartInfo.CreateNoWindow = true; // 新しいwindowを作らない
m_process.EnableRaisingEvents = true; // プロセス終了時にExitedイベント発生するかどうか
m_process.Exited += Process_Exited; // プロセス終了時イベント

m_onCompleted = onCompleted;

// プロセスの起動
m_process.Start();

// プロセス標準出力
m_process.BeginOutputReadLine();
}

/// <summary>
/// 標準出力を拾う
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Process_OutputDataReceived( object sender, DataReceivedEventArgs e )
{
m_list.Add( e.Data );
}

/// <summary>
/// 完了処理を拾う
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Process_Exited( object sender, EventArgs e )
{
if ( m_onCompleted == null )
{
return;
}
m_onCompleted( m_list[0] );
}
}
}
}

OneProcessで保持する値はList<string>じゃなくてStringBuilderの方がいいかなと思いつつ、一旦はこの形に。

Macの開発環境は未対応

たぶん実行するファイル名とコマンドの内容を変えたらいけると思います。(未検証

あと コールバックから一連の処理でUnityのAPIに触れると例外出ます (別プロセスなので)


UniRx使用した場合の対応方法は下記

var diposable = Observable.Create<string>( observer =>

{
ProcessUtility.ExecBlanchesName( ( str ) => observer.OnNext( str ) );
return null;
} )
.SubscribeOn( Scheduler.ThreadPool ) // Subscribeをスレッドプール上で実行する
.ObserveOnMainThread() // UnityAPIを使用するためにメインスレッドに切り替える
.Subscribe( str => Debug.Log( str ) ); // 戻り値がブランチ名

// 破棄処理忘れずに
// AddTo( Component );が出来るならそれでも可
// diposable.Dispose();
// diposable = null;


まとめ

ユーザ名は Environment.UserNameで取得

エラーログは Application.logMessageReceived にイベント登録して取得

エラー時に↑をまとめてをSlackに投げるようにしたらとてもGood

毎回してる事は積極的にプログラムに任せていきましょう