技術部のyuckieee(ゆっきー)です。
色々なプロジェクトで開発を行っていて、ちょいちょい発生する作業で面倒だなって思っていることがありました。それは納品物やリリース対象物の準備です。
何かというと、運用保守中に追加開発などが発生した場合に、プログラムの差分ファイルのみを納品物やリリース対象として準備する必要があります。
これ、中々に面倒くさいんですよね。間違えたら大変だし、毎回ドキドキしちゃいます(笑)
そこで、出来るだけ間違いが起こらないように自動化出来ないか探してみた結果、私がいつも使っているGit管理ツール「Sourcetree」のカスタムアクションを試してみたら良さげだったので、ご紹介しようと思います。
■作成したカスタムアクションの概要
まずはイメージ共有のため、今回作成したカスタムアクションの概要を説明します。
ざっくりした動作仕様としては「Sourcetreeで現在選択されているリポジトリ-ブランチに存在するコミット間の差分ファイルを取得する。」です。
利用イメージ(方法)は以下のとおり。
[利用イメージ(方法)]
Sourcetreeの該当ブランチの履歴から差分ファイルをとりたいコミットを選択のうえ、カスタムアクションを実行します。
差分を取るためにコミットは2つ選択。ただし、1つだけ選択した場合でも、選択したコミットから最新コミットまでを対象と実行します。
実行時は、経過が分かるようにダイアログにログが表示されます。
実行後、ログ記載の出力先に差分ファイル(zip)、差分ファイル一覧、実行結果ログが格納されます。
以上です!
■カスタムアクションの実装方法
実装完了までの大まかな流れは以下のとおりです。
[実装の流れ]
① 呼び出しスクリプト作成
② カスタムアクション登録
③ 動作確認
それではサクッと詳細の説明に入ります。
① 呼び出しスクリプト作成
最初にカスタムアクションで呼び出されるスクリプトを作成します。
今回はシェルスクリプト(sh)で作成し、実際に作成したコードは以下のとおりです。(スクリプトの引数は②で説明しますが、
$1
にリポジトリ名、
$2
と
$3
にコミットIDが指定されています)
#!/bin/sh
#########################################
# ファイル名: export_diff.sh
# 処理内容 : 該当コミット間の差分ファイル出力
#########################################
#########################################
# 変数定義
#########################################
repo_name=$(basename "$(pwd)")
branch_name=$(git rev-parse --abbrev-ref @)
file_name="$(basename "$(pwd)")_diff.zip"
list_name="$(basename "$(pwd)")_diff_list.log"
today="$(date +"%Y%m%d")"
dir_name="export_diff/$repo_name/$branch_name/$today"
log_file=export_diff_result.log
#########################################
# 出力処理
#########################################
export_diff() {
# リモートブランチと同期を実行した上で、出力対象をダイアログ表示
echo ""
echo "出力を開始します($(date))"
echo "-------------------"
echo "■リモートブランチと同期"
echo "-------------------"
git pull
echo ""
echo "-------------------"
echo "■出力対象"
echo "-------------------"
echo "レポジトリ:${1}"
echo "ブランチ:${branch_name}"
echo ""
echo "-------------------"
# 差分ファイル出力処理
if [ "$3" != "" ]; then
# 2コミット間の差分ファイルを出力
git archive --worktree-attributes -o "$file_name" "$2" $(git diff --name-only --diff-filter=du "$3" "$2")
echo "■差分対象に含まれるコミット"
echo "-------------------"
git log "$3"..."$2" --pretty=format:"%h : %s"
echo ""
elif [ "$2" != "" ]; then
# 選択されたコミットから最新コミットまでを出力
git archive --worktree-attributes -o "$file_name" HEAD $(git diff --name-only --diff-filter=d "$2" HEAD)
echo "■差分対象に含まれるコミット"
echo "-------------------"
git log "$2"...HEAD --pretty=format:"%h : %s"
echo ""
else
# 上記以外の場合は正しく出力できないためエラーとする
echo "【ERROR】指定できるコミット数は1又は2です。"
echo "-------------------"
exit
fi
# 出力された差分ファイルの一覧をリスト出力(ディレクトリのみの表示は除く)
zipinfo -1 "$file_name" | grep -v /$ > "$list_name"
echo ""
echo "-------------------"
echo "■出力ファイル一覧"
echo "-------------------"
cat "$list_name"
echo ""
# 出力されたファイル移動(混乱しないように)
mv "$file_name" ~/"$dir_name"
mv "$list_name" ~/"$dir_name"
echo "-------------------"
echo "■ファイル出力先"
echo "-------------------"
echo "${HOME}/$dir_name"
echo "-------------------"
echo ""
echo "出力が完了しました($(date))"
}
#########################################
# メイン処理
#########################################
if [ $# -gt 3 ]; then
# 選択したコミットが4つ以上の場合は正しく出力できないためエラーとする
echo "【ERROR】指定できるコミット数は1又は2です。"
echo "-------------------"
exit
fi
# 出力ディレクトリを作成
mkdir -p ~/"$dir_name"
# 出力処理を呼び出し
export_diff "$1" "$2" "$3" 2>&1 | tee "${HOME}/$dir_name/$log_file"
...長い!!!(笑)
ダイアログ表示やファイル移動等の余分なコードが含まれていますが、重要なのは以下に抜粋したコマンドです。
git archive --format=zip -o release.zip --worktree-attributes "$2" $(git diff --name-only --diff-filter=d "$2" "$3")
上記は、Gitが提供している「Git管理対象のみを圧縮形式で出力してくれる」コマンドです。
出力対象の指定がない場合は、指定したブランチ又は、コミット時点のGit管理対象の全ファイルが出力されますが、ファイル名及び、パスを指定することで出力対象ファイルを絞り込むことが出来ます。
今回は、このファイル名・パス指定に
git diff
コマンドを使用することで差分ファイルの抽出を実現しました。
以降、
git diff
と
git archive
、それぞれのコマンドについて紹介します。
●git diff
コマンド
差分ファイルを抽出するために使用したコマンドです。
オプションを省いた場合のシンプルな構成は以下のとおりです。
git diff <変更前のコミットID> <変更後のコミットID>
例) git diff 1668413042b61f16f0f353fd66b53cd4c0dbc5d5 2d13b702a783bb2c8252ba07f5410619ee3a0186
なお、コミットIDの指定順序が逆転してしまうと「削除したファイル」と「新規作成されたファイル」の識別が逆転してしまう等、その後の出力に影響があるので注意してください。
以降に私が指定が必要そうだと考えたオプションについて補足します。
[コマンドオプション]
--name-only (必須)
差分比較結果としてファイル名(パス含む)のみを返す指定です。
本オプションを指定しない場合、ズラズラとコミット間の比較内容が表示されるのですが、それが
git archive
に引数として渡ってしまうとエラーになりますので指定必須です。
--diff-filter=<出力対象の指定> (必須)
例)--diff-filter=ACMR
指定することで、差分取得対象を絞り込むことが出来ます。
このオプションは、大文字で記述すると「出力対象」を、小文字で記述すると「出力除外対象」を指定することが出来ます。それぞれのオプション文字列の説明を下表にまとめました。
指定可能なオプション文字列
オプション 文字列
説明
A/a
追加されたファイル
C/c
コピーされたファイル
D/d
削除されたファイル
M/m
ファイル内容又は、ファイル属性が変更されたファイル
R/r
ファイル名が変更されたファイル
T/t
ファイルタイプ(通常ファイル、シンボリックファイル、サブモジュール等)が変更されたファイル
U/u
マージされていないファイル(コンフリクトファイル確認時に使用されるようです)
B/b
ペアリングが壊れているファイル
X/x
不明な変更タイプ(通常は有り得ないようです)
今回は差分に「削除されたファイルのパス」が返ってしまうと
git archive
時に対象無しのエラーになってしまうので「明確に出力対象としたいファイルのみ」を指定しました。
この指定方法以外に
--diff-filter=d
とすることで、削除されたファイルを「出力対象外」にすることも可能です。
●git archive
コマンド
指定したコミット時点のGit管理対象ファイルを圧縮出力してくれるコマンドです。(ブランチ指定も可能ですが、Sourcetreeから引数として受け取れないので説明割愛します)
指定したコミットID時点のファイルが出力対象となりますので、当たり前ではありますが、必ず変更後のコミットIDを指定するようにしてください。
git archive <変更後コミットID>
例) git archive 1668413042b61f16f0f353fd66b53cd4c0dbc5d5
[コマンドオプション]
--format=<圧縮形式> (任意)
例) --format=zip
出力される差分ファイルの圧縮形式として、zip形式又はtar形式が指定可能です。
このオプションが指定されていない場合は、ファイル名に付与された拡張子から推測され、推測ができない場合はtar形式となるようです。
今回はzip形式で出力したいため、念のため、フォーマットも指定しておきました。
-o <ファイル名> (必須)
※--output=<ファイル名>としても同じ
例) -o export_diff.zip
本オプションを指定しない場合は、標準出力(コンソール)に結果が出力されます。
結果をファイルに出力したい場合は、本オプションを付与した上で、 出力ファイル名称を指定する必要があります。
--worktree-attributes(任意)
Git管理対象となっているファイルでも、今回の出力対象として含めたくない!というファイルがある場合は、本オプションを使用します。
例えば、本番環境へのリリース対象としてReadMeや環境変数のサンプルファイルなどは不要で、これらを除外したい、と言う時に使います。
なお、除外対象は、リポジトリ直下にある
.gitattributesファイル
に記述することで認識されます。
以下のように
対象ファイル export-ignore
と書き込めばOKです。
README.md export-ignore
*.example export-ignore
試してみた感じでは、今回の実装内容であれば、本ファイルをリモートリポジトリにマージしていなくても適用されるようでした。
--prefix=<プレフィックス文字列/> (任意)
例)--prefix=prod/
今回は使用しませんでしたが、出力されるファイルを特定のディレクトリでラップしたい場合に指定します。 例えば
--prefix=prod/
と入力しておくと、出力されたzipを解凍した際にprodディレクトリの配下に差分ファイルが格納されることになります。
試してみたところ
--prefix=prod/app1/
など、ディレクトリを多階層で指定することもできるようでした。
ザックリ説明はここまでとなりますが、各コマンドには他にもオプションがありますので、公式ドキュメントを参考にしながら試してみるのも面白いと思います。
git-diffコマンド (Documentation - Reference)
git-archiveコマンド (Documentation - Reference)
なお、スクリプトについては、私のやりたように書いていますが、自分仕様にカスタマイズいただければと思います♪(見やすさ重視で
echo
の乱れ咲きさせてたり...笑)
② カスタムアクションの登録
①で作ったスクリプトを実際にSourceTreeに登録します。
Sourcetreeを開き、左上の[Sourcetree]>[環境設定...]を押下し、設定画面を開きます。そのうえで、右上にある[>>]>[カスタムアクション]を選択してカスタムアクションの定義画面を開き、左下の[追加]ボタンを押下してください。
[注意事項]
Sourcetree日本語版では、不具合(確認したところ、4.1及び、4.2.0は駄目でした)により上記の流れで画面を開いた場合、カスタムアクションが非活性となっており選択できないようです!(笑)
選択できない場合は、対処法として2パターン有り、1つ目が言語を英語に切り替えてSourcetreeの再立ち上げを行う方法、2つ目がトップ画面で任意のリポジトリ右クリックメニューから[カスタムアクション]>[編集...]を選択する方法です。どちらかお好きな方をお試しください。
登録画面が開きますので、必要事項を入力し[OK]を押下して、登録を完了させます。
【登録内容】
メニューキャプション:差分出力(zip) ※1
別のウィンドウで開く:チェックオフorオン どちらでも可 ※2
フル出力を表示:チェックオン ※3
実行するスクリプト:①で作成したスクリプトを選択
パラメータ:$REPO $SHA ※4
※1:メニューに表示される名称なので、お好きなものを指定してください。
※2:選択したファイルをVSCodeなどの別ウィンドウで表示したい場合は、このチェックをオンにします。なお、このチェックをオンにすることでダイアログも別ウィンドウとなるのでダイアログの拡大縮小が可能になります。
※3:チェックがオフの場合は、エラーのみがダイアログ表示されます。今回は全て表示したいのでチェックをオンにします。
※4:$SHAについては、複数コミットを選択した場合、コミットタイミングが新しいコミットIDから順に、選択した全てのコミットIDがスクリプトの引数として渡されます。(順番の選択順には依らないようです)
③ 動作確認
最後に動作確認を行います。
今回登録したカスタムアクションは、できるだけ汎用的に作っているので、どのリポジトリ、ブランチでも対応可能(な、はず)ですので、皆さんも動作確認してみてください。
実行方法は、「■作成したカスタムアクションの概要」と同様の流れになるため、説明は割愛しますが、コミットの選択方法について、少しだけ補足します。
[実行時の補足事項]
Sourcetreeのデフォルトでは、履歴に「すべてのブランチ」のコミットが表示されています。
この場合、親ブランチの差分を取得しようとした際に、間違って未マージの子ブランチのコミットを選択してしまっても、それらが差分出力対象となっているようでした。(これは
--diff-filter=u
としても結果は変わりませんでした)
そのため、履歴の表示フィルターを使用して「現在のブランチ」のみの表示にしておくと、ミスがなくて良いのかなと思います。
ここまでで、カスタムアクションの実装は完了となります!
これでサクッと差分ファイルの抽出ができるようになったので、これからのリリース準備作業も捗りそうです♪
ただし、自動化しても、間違いがないかのダブルチェックは怠らず!!
■まとめ
Sourcetreeやgitには、沢山の便利機能やコマンドがあるのに活用できていないなぁ、と、改めて感じました。
特に今回使ったSourcetreeのカスタムアクションは、利用の幅が広く、もっと色々な応用がききそうだなと感じたため、継続して色々と試してみたいと思います。