Configure a Git Shell Prompt Under Nix

Michael Lynch

Nix 環境で Git のシェルプロンプトを設定する

原文は Michael Lynch により に公開されました。 このブログを購読する

最近、Julia Evans のgit についての最新の zineを読んだのですが、その中でターミナルのシェルプロンプトを設定して git のステータスを表示するというヒントが紹介されていました。

Julia のターミナルプロンプトはこんな感じです。

~/work/homepage (main) $

mainは Julia が現在いる git ブランチです。bisectmergeのような git 操作の途中にいるときは、ターミナルは次のように変わります。

~/work/homepage (main|MERGING) $

シェルプロンプトをカスタマイズするという発想はこれまでなかったのですが、すぐにその価値を理解しました。

私は自分がどのブランチにいるかを思い出すためにいつもgit statusを実行していますし、git mergeの途中であることをよく忘れてしまいます。

ターミナルで git プロンプトを設定する

Julia の zine に影響されて、ターミナルのシェルをカスタマイズして git のステータスを表示するようにしようと思ったのですが、方法はすぐにはわかりませんでした。どうやらgit-prompt.shというファイルが必要らしいのですが、Debian にインストールした git 2.30.2 には含まれていませんでした。

$ find / -name 'git-prompt.sh' -type f -print
# No results

もう一つの方法は、GitHub からgit-prompt.shの公式コピーをダウンロードすることでした。

# Download git-prompt.sh
curl https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh > ~/.git-prompt.sh

# Add it to my .bashrc
echo '. ~/.git-prompt.sh' >> ~/.bashrc

# Reload .bashrc
. ~/.bashrc

正しく動作しているか確認するために、テスト用の git リポジトリを作成しました。

mkdir examplerepo && \
  cd examplerepo && \
  git init

git-prompt.shが正しく動作していれば、__git_ps1コマンドで現在の git ブランチが表示されるはずです。

$ __git_ps1
(master)

よし、動いています。

ただ、毎回手動で__git_ps1を呼び出したいわけではありません。Julia Evans が紹介していたように、ブランチがターミナルプロンプトに自動で表示されるようにしたいのです。

ターミナルシェルで git のステータスを表示する最も簡単な方法は、bash セッションでPS1変数を上書きすることです。

$ export PS1='$(__git_ps1 "(%s)") \$ '
(master) $

これで git コマンドを実行すると、ターミナルがステータスを反映して変化するようになります。

(master) $ echo 'hi' > hello.txt && git add -A && git commit -m "Test commit"
[master (root-commit) 7b14d2a] Test commit
 1 file changed, 1 insertion(+)
 create mode 100644 hello.txt
(master) $ git checkout -b branchA && echo 'hey' > hello.txt && git add -A && git commit -m "test A"
Switched to a new branch 'branchA'
[branchA 65b3d63] test A
 1 file changed, 1 insertion(+), 1 deletion(-)
(branchA) $ git checkout master
Switched to branch 'master'
(master) $ git checkout -b branchB && echo 'hello' > hello.txt && git add -A && git commit -m "test B"
Switched to a new branch 'branchB'
[branchB 198c875] test B
 1 file changed, 1 insertion(+), 1 deletion(-)
(branchB) $ git merge branchA
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Automatic merge failed; fix conflicts and then commit the result.
(branchB|MERGING) $

いい感じです。うまく動いているようです。

ただ、シェルプロンプトで作業ディレクトリが見えなくなってしまったので、PS1を次のように変更しました。

$ export PS1='\w $(__git_ps1 "(%s)") \$ '
~/examplerepo (branchB|MERGING) $

きれいな色も欲しくなったので、色も追加しました。

export PS1='\[\033[01;34m\]\w\[\033[00m\]\[\033[01;32m\]$(__git_ps1 " (%s)")\[\033[00m\]\$ '

ここまでくるとPS1の編集はかなり複雑になるので、Bash Prompt Generatorのような無料ツールを使ってコマンドを生成し、色付けしてもらうのがおすすめです。

最終的なターミナルプロンプトはこんな感じになりました。

~/examplerepo (branchB|MERGING)$

この変更を永続化するために、export PS1の行を~/.bashrcファイルに追加しました。これで新しい bash シェルを起動するたびにカスタムしたプロンプトが表示されます。

Nix で bash のシェルプロンプトに git を統合する

上記の手順の問題点は、私がNixHome Managerを使っていることです。便利ではあるのですが、bash の設定に関することすべてが複雑になってしまいます。次に Home Manager を実行したときに変更が消されてしまうため、単純に.bashrcに一行追加するわけにはいきませんでした。

幸い、Jeff Kreeftmeijer による投稿のおかげで、git-prompt.shを使った方法を Nix に適用する方法がわかりました。

Home Manager 用のhome.nixファイルを次のように修正しました。

  programs.bash = {
    enable = true;
    enableCompletion = true;
    initExtra = ''
      # Load __git_ps1 bash command.
      . ~/.nix-profile/share/git/contrib/completion/git-prompt.sh
      # Also load git command completions for bash.
      . ~/.nix-profile/share/git/contrib/completion/git-completion.bash

      # Show git branch status in terminal shell.
      export PS1='\[\033[01;34m\]\w\[\033[00m\]\[\033[01;32m\]$(__git_ps1 " (%s)")\[\033[00m\]\$ '
    '';
  };

ファイルを更新したらhome-manager switchを実行しました。すると.bashrcが更新され、すべて期待通りに動作しました。

開発環境から Nix ターミナルの余計な表示をなくす

それでも、カスタムした bash ターミナルプロンプトにはまだ一つ引っかかる点がありました。

私はすべての開発をプロジェクトごとの Nix shellで行っています。nix developで開発環境を読み込むと、Nix がターミナルに余計なプレフィックスを追加していることに気づきました。

~/examplerepo (branchB|MERGING) $ nix develop
...
(nix:nix-shell-env) ~/examplerepo (branchB|MERGING) $
^^^^^^^^^^^^^^^^^^^ why?

そのプレフィックスがかなりのスペースを占めていました。ターミナルプロンプトのほぼ半分が、単に Nix 環境にいることを伝えるだけの表示になっていたのです。

特定のプロジェクトでは、Nix のシェルプロンプトのプレフィックスはさらに長くなります。PicoShareでは次のように表示されました。

~/examplerepo (branchB|MERGING) $ nix develop
...
(nix:nix-shell-x86_64-unknown-linux-musl-env) ~/picoshare (master)$
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ too much!

このシェルプレフィックスのせいで、画面のスペースを大量に失っていました。どうすれば消せるのでしょうか。

調べてみると、nix developには--bash-prompt-prefixフラグがあり、次のようにターミナルシェルのプレフィックスをカスタマイズできることがわかりました。

$ nix develop --bash-prompt-prefix 'nix:'
nix:~/picoshare (master)$

まったく表示したくない場合は、空文字列に設定することもできます。

$ nix develop --bash-prompt-prefix ''
~/picoshare (master)$

ただ、もちろんnix developを実行するたびにこの追加のフラグを付けたくはありません。

この余計な表示は、実は私のnix.confファイルに由来していることに気づきました。私の Debian システムでは/etc/nix/nix.confにあります。おそらくDeterminate Systems Nix インストーラーがこのプレフィックスを設定したのでしょう。

$ grep 'bash-prompt-prefix' /etc/nix/nix.conf
bash-prompt-prefix = (nix:$name)

その行を、より簡潔なnix:というプレフィックスに変更しました。

bash-prompt-prefix = nix:

そして、変更を適用するために Nix を再起動しました。

sudo systemctl restart nix-daemon

これで Nix の開発環境にいるときは、次のような短くて分かりやすいプレフィックスが表示されるようになりました。

nix:~/picoshare (sqlite-performance)$

まとめ

ソフトウェア開発をするときは、シェルプロンプトごとにディレクトリの git ステータスが見えるようにターミナルシェルをカスタマイズしておくと便利です。

この投稿が、通常の bash 環境でも、Nix や Home Manager で bash の設定を管理している環境でも、私と同じような設定をしたいと考えている方々の役に立てば嬉しいです。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント