Configure a Git Shell Prompt Under Nix

Michael Lynch

Nix에서 Git 셸 프롬프트 설정하기

원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기

최근 Julia Evans의 git에 관한 최신 zine을 읽었는데, 그중 한 가지 팁은 터미널 셸 프롬프트에 git 상태를 표시하도록 설정하기였다.

Julia Evans의 터미널 프롬프트는 이렇게 생겼다:

~/work/homepage (main) $

main은 Julia Evans가 현재 작업 중인 git 브랜치다. bisect나 merge 같은 git 작업을 진행 중일 때는 터미널이 이렇게 바뀐다:

~/work/homepage (main|MERGING) $

셸 프롬프트를 커스텀할 생각은 한 번도 해본 적이 없었지만, 그 가치는 바로 알 수 있었다.

나는 내가 어떤 브랜치에 있는지 기억하려고 git status를 수시로 실행하고, git merge 도중에 그 사실을 자주 잊어버린다.

터미널에 git 프롬프트 설정하기

Julia Evans의 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 설정을 관리하는 시스템에서도 나와 비슷한 설정을 원하는 분들에게 도움이 되길 바란다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.

댓글