在 Nix 下配置 Git Shell 提示符
我最近读了 Julia Evans(朱莉娅·埃文斯)的最新一期关于 git 的 zine,其中一条技巧是配置终端 shell 提示符以显示 git 状态。
Julia 的终端提示符是这样的:
main 是 Julia 当前的 git 分支。当她正处于 bisect 或 merge 之类的 git 操作中间时,终端会变成这样:
我从未想过要自定义我的 shell 提示符,但我立刻意识到了它的价值。
我经常运行 git status 来记住自己当前在哪个分支上,而且常常忘记自己正处在一次 git merge 的过程中。
在我的终端中设置 git 提示符
Julia 的 zine 说服我自定义终端 shell 以包含 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 展示的那样自动出现在我的终端提示符中。
在我的终端 shell 中显示 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 这样的免费工具来生成命令并帮你添加颜色。
以下是我最终的终端提示符的样子:
为了让这个更改永久生效,我把我的 export PS1 那一行加到了 ~/.bashrc 文件中,现在每次启动新的 bash shell 时都能看到我的自定义终端。
在 Nix 上把 git 集成到 bash shell 提示符中
上面所有步骤的问题在于,我使用的是 Nix 和 Home 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 shell 中进行。当我运行 nix develop 加载开发环境时,我发现 Nix 在我的终端上多加了一个前缀:
~/examplerepo (branchB|MERGING) $ nix develop
...
(nix:nix-shell-env) ~/examplerepo (branchB|MERGING) $
^^^^^^^^^^^^^^^^^^^ why?这个前缀占用了大量空间。终端提示符几乎有一半只是在告诉我:我在一个 Nix 环境里。
在某些项目中,Nix 的 shell 提示符前缀甚至更长。这是我在 PicoShare 中看到的:
~/examplerepo (branchB|MERGING) $ nix develop
...
(nix:nix-shell-x86_64-unknown-linux-musl-env) ~/picoshare (master)$
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ too much!现在这个 shell 前缀正在吞噬我大量的屏幕空间。怎么才能去掉它?
原来 nix develop 有一个 --bash-prompt-prefix 标志,可以让我这样自定义终端 shell 前缀:
$ 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 开发环境中时,会看到一个简短而有用的前缀,就像这样:
总结
做软件开发工作时,自定义终端 shell 很有帮助,这样我就能在每个 shell 提示符上看到自己目录的 git 状态。
我希望这篇文章能帮到那些想要类似配置的人,无论是在普通的 bash 系统上,还是在由 Nix 和 Home Manager 管理 bash 设置的系统上。
随机一篇博客