はてなエンジニア - Qiita Advent Calendar 2025 - Qiita の12/23の記事です。
magitはEmacsで動くgitクライアントです。
キーを押すとメニューが開き、メニュー内の表示に従ってオプションやサブコマンドを指定することでgitのコマンドを組み立てて実行するのが特徴です。
言葉で説明するのは難しいですが、ブランチ作成→編集→add→コミット→プッシュはこんな感じで行えます:

例えば「ブランチを作成してチェックアウト」は、
bを押してブランチ関連のメニューを開く
cを押してCheckoutグループのnew branchを選択
- ベースブランチの名前を入力(このときカーソル上のブランチがデフォルトで使用される)
- 作成するブランチ名を入力
という手順で行っています。
このようなキー操作を抽象化したのが magit/transientです。今回はこのtransientを使ってGitHub CLIの小さいラッパーを作ってみます。プルリクエストを一覧してマージできるまでを目標とします。
まずはステータスバッファを作成してプルリクエスト一覧を表示します。
ここは普通にキーマップとメジャーモードを作り、gh pr list --jsonをパースします。

次に作成したメジャーモードのキーマップでtransientを発動させます。
(transient-define-prefix magh-pr-transient ()
["Pull Request"
("o" "Open in browser" magh-pr-view)
("m" "Merge" magh-pr-merge-transient)])
(define-key map "P" 'magh-pr-transient)
これで、Pキーを押すとプルリクエストのメニューが出てくるようになります。
magh-pr-viewはコマンドで、PR番号を指定してブラウザで開きます。
(defun magh-pr-view ()
(interactive)
(let ((pr-number (read-number "PR number: " (magh-pr-number-at-point))))
(magh-git-call "pr" "view" "--web" (number-to-string pr-number))))
magh-pr-number-at-pointはとりあえずre-search-forwardを使う感じで、
magh-git-callはだいたい(apply #'process-file (find-executable "gh") nil t nil args)って感じです。
これでgh pr view --webコマンドを実行してPRをブラウザで開けるようになります。

次は ("m" "Merge" magh-pr-merge-transient) です。先程の magh-pr-view はコマンドでしたが magh-pr-merge-transient は別のtransient prefixです。
(transient-define-prefix magh-pr-merge-transient ()
["Arguments"
("-d" "Delete branch" "--delete-branch")]
["Actions"
("m" "Merge commit" magh-pr-merge-commit)
("r" "Rebase and merge" magh-pr-merge-rebase)
("s" "Squash and merge" magh-pr-merge-squash)])
Argumentsではトグル可能なオプションを定義しています。
3つ目のロングオプションの部分を"--option="とすることで値をインタラクティブに聞くこともできます。
このオプションは (transient-args 'magh-pr-merge-transient) でリストとして取り出すことができます。
あとは"gh pr merge"を実行する関数を作ってやれば動きます。

(マージしたあとリストから消えて欲しいけど、すぐリフレッシュしても消えていないのでちょっと難しい…)
以上でだいたいやりたいことができました。
感想
transientを使ってみたのは初めてでしたが、非常に簡単にmagit風のツールを作ることができました。
個人的にこのghラッパーは少しずつ発展させていきたいと思っています。
楽観的には、man ghがあればあとはだいたい生成AIに任せられそうですね。
(ところで、わざわざghのラッパーを作らなくてもmagitにはリモートの操作を抽象化したforgeという拡張があるじゃん、と思われた方もいるかもしれません。が、forgeはGitHub以外のリモートも含めて汎用のAPIを使う前提であり機能に制限があること、認証情報の保存がEmacsの仕組みに乗っていてgh auth loginほど簡単でないことから見送っています。)
みなさんもtransientを使って便利な道具を作ってみてください。その際はtransient-showcaseが参考になると思います。
はてなアドベントカレンダー、明日12/24は id:SlashNephy さんの記事の予定です。お楽しみに!!!