Configure a Git Shell Prompt Under Nix

Michael Lynch

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

最近、Julia EvansのGitについての最新のzineを読んでいたところ、その中にGitの状態が表示されるようにターミナルのシェルプロンプトを設定するというヒントがありました。

Juliaのターミナルプロンプトは次のようになっています。

~/work/homepage (main) $

mainはJuliaが現在いるGitブランチです。bisectやmergeなど、Gitの操作中はターミナルが次のように変わります。

~/work/homepage (main|MERGING) $

シェルプロンプトをカスタマイズするという発想はこれまでありませんでしたが、その便利さはすぐに理解できました。

今いるブランチを思い出すために、私はいつもgit statusを実行しています。それに、Gitのmergeの途中だったことを忘れてしまうこともよくあります。

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

Juliaのzineを読んで、ターミナルのシェルにGitの状態を表示したくなりました。しかし、どう設定すればいいのかはすぐには分かりませんでした。どうやらgit-prompt.shというファイルが必要らしいのですが、Git 2.30.2を使っているDebianの環境にはありませんでした。

$ 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の設定はすべて少し複雑になります。.bashrcに単純に行を追加することはできません。次にHome Managerを実行すると、変更が上書きされてしまうからです。

幸い、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シェルで行っています。開発環境を読み込むために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 installerが、このプレフィックスを設定したのでしょう。

$ 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の設定を管理しているシステムでも、私と同じような設定をしたい人の役に立てば幸いです。

原文は Michael Lynch により に公開されました。

この記事は「gpt-5.6-terra」を使用して翻訳されました。