Stow로 Dotfiles 관리

서문

Dotfiles를 관리하는 방법을 수동, Git 활용, 심볼릭 링크 활용, GNU stow 활용의 단계로 정리하였습니다.

Dotfiles

유닉스 관련 시스템(Unix, BSD, Linux 등)에서는 파일명 앞에 .을 붙이는 경우, 숨긴 파일이 되어 ls만으로는 파일을 보이지 않습니다. 이를 활용해, 각종 프로그램 설정이 담긴 일반 텍스트 파일을 .을 붙여 활용해왔습니다.

예를 들어, Hello 라는 프로그램을 제작하는 단계에서,

실행 시점에 덮어 씌울 각종 설정을 담은 텍스트 파일을 .hello로 요구하기로 하고, (설정 파일이 기본 명령어인 $ls에 까지 보일 필요가 없으므로, ‘. ’ 을 붙이기로 함)

혹시 해당 파일이 있는지 확인 할 경로로 ~(유저 홈 디렉터리)를 결정한 경우, (사용자가 특별히 요구하는 설정이 있으면 기본 옵션이 아니라 그것을 적용해 실행해야 하므로)

사용자들은 ~/.hello라는 파일을 생성하고 본인의 입맛에 맞는 설정값들을 해당 파일에 적어둘 수 있게 됩니다.

이렇게 설정 파일들이 대부분 dotfile(점을 붙인 파일)이기 때문에 설정 파일들을 모두 통틀어 dotfiles(점을 붙인 파일들)로 부르게 되었습니다.

홈 디렉터리

초기에는 설정 파일을 저장해야 하는 위치를 지정함에 있어서 규약이라고 할 것들이 없었습니다. 따라서, 대부분의 프로그램들이 홈 디렉터리에서 설정 파일을 찾았고, 사용자들도 홈 디렉터리 아래에 저장해왔습니다.

예를 들어, vim의 경우, 홈 디렉터리인 아래에 .vimrc라는 파일을 통해 기본 설정을 관리하고, bash의 경우, 마찬가지로 홈 디렉터리 아래에 .bashrc를 갖게 되는 것입니다.

rc?

설정 파일에 자주 붙는 접미어 ‘rc ‘는 ‘run command ‘에서 따왔다고 알려져있습니다. 이 ‘run command ‘는 1965년 MIT의 CTSS(Compatible Time-Sharing System)의 ‘runcom ‘에서 유래되었다고 합니다.

그러나, Eric S. Raymond는 그의 저서 ‘The Art of Unix Programming ‘에서 지속적으로 ‘run control ‘로 지칭하고 있습니다.

이 외에 소수설로, “resource control이다 “, “runtime configuration이다 " 하는 말들도 있습니다.

~/.config/ ?

그렇다면 리눅스 시스템에 조금 익숙하신 분들은 의아하실 겁니다. 대부분의 프로그램들은 각자의 설정 파일을 ‘홈 디렉터리 아래 .config 디렉터리(~/.config/) ‘에 저장하기 때문이죠.

특히, 이 디렉터리 내부에는 각 프로그램의 이름으로 디렉터리를 생성하여 모든 해당 프로그램 설정 파일을 그 디렉터리 내부에서 관리하고 있습니다.

예를 들어, 이메일 클라이언트인 mutt 혹은 neomutt를 사용하시는 분들이라면 홈 아래에 바로 설정파일을 생성하는 경우도 있지만, ~/.config/mutt를 만들어, 그 아래에 muttrc를 저장하시는 경우도 많을겁니다.

또는, i3 윈도우 매니저를 사용하신다면, ~/.config/i3디렉터리 아래에 config파일 하나로 i3의 외관과 동작을 컨트롤 하고 계십니다.

이 .config디렉터리는 XDG 재단에서 제정한 XDG Base Directory Specification -XDG 기본 디렉터리 제원에 따라 만들어집니다. 각 프로그램들이 실행시 읽어들이는 설정 파일의 위치를 제각각 다르게 요구하기 때문에 발생하는 복잡한 홈 디렉터리 상태를 깔끔하게 유지하고자 제원을 만든 것입니다.

따라서, XDG 제원을 성실히 이식하는 소프트웨어의 경우, 기본적으로 .config 디렉터리 아래에 생성된 본인 소프트웨어 이름의 디렉터리에서도 설정 파일을 읽어들일 수 있어야만 합니다.

XDG

XDG 재단(현: freedesktop 재단 / 전: X Desktop Group)은 2000년 3월 레드햇(Red Hat)에서 일하던 그놈(Gnome) 개발자 하복 페닝턴(Havoc Pennington)에 의해 설립되었습니다.

XDG는 리눅스를 더불어 다른 유닉스 유사 시스템의 X 윈도우 시스템과 웨이랜드 시스템의 환경이 서로 호환가능해질 수 있도록 다양한 일을 수행하고 있습니다.

이런 방식을 취하는 경우, 상위에 숨겨진 디렉터리를 거치기 때문에 굳이, 점을 붙여 설정 파일 자체를 숨길 필요는 없어집니다. 그러나 여전히 ‘설정 파일 ‘임에는 변함이 없으므로, 설정 파일들을 의미하는 것으로 굳어진 dotfiles로 지칭하고 있습니다.

결과물

결과적으로 사용자의 홈 디렉터리는 아래와 같은 모양을 띄게 됩니다.

$ tree -a ~
.
├── .bashrc
└── .vimrc
└── .config
    └── mutt
        └── muttrc
    └── i3
        └── config

이식

컴퓨터를 단 한 대만 사용한다면, 이 글에 나오는 내용은 쓸모가 없습니다.

문제는 새로운 PC나 노트북에 내 개인 설정을 이식해야할 때 발생합니다. 가장 간단한 방법은, 위의 파일들을 각각 USB와 같은 이동식 저장 매체에 담아둔 이후, 새 기기에 붙여넣는 방법입니다. (물론, 이메일을 사용하는 사람도 있습니다;)

사실, 위와 같은 방법도 설정 파일을 옮기는 것 자체에는 조금의 번거로움을 제외하면 아무 문제가 없습니다. 그러나 설정 파일에 수정이나 변경을 할 때 또 다른 문제가 생깁니다. 예를 들어, .vimrc파일을 처음 생성했을 때는 vim 에디터를 잘 활용할 줄 몰라 매우 기초적인 설정만 적혀있었다고 합시다. 그러나, 시간이 흘러 vim 에디터의 각종 기능에 익숙해졌고, 그 과정에서 vim plugin을 사용하기 시작했습니다. 이 과정에서 매번 설정을 변경할 때마다 USB에 새 버전을 저장할 수도 없는 노릇입니다.

이런 문제는 이미 알려진 다른 문제 상황과 매우 유사합니다. 사실상 ‘버전 관리 문제 ‘인 셈이죠. 그리고 우리는 매우 획기적인 해결 방법을 가지고 있습니다.

Git

Git 레파지토리를 생성합니다. 그 안에 깔끔하게 정리해서 설정 파일을 저장합니다.

$ tree -a ~/Git/dotfiles
.
├── .git
├── .bashrc
└── .vimrc
└── .config
    └── i3
        └── config
    └── mutt
        └── muttrc

디렉터리 구조도 복사하기 편하도록, 향해야 하는 위치와 동일하게 만들었습니다. 바로 보이는 것들은 홈 디렉터리에 복사하면 되고, .config디렉터리에 있는 것들은 ~/.config아래에 복사하면 되니까 이식 문제는 깔끔하게 해결되었습니다.

버전 관리 문제는 어떨까요? 가끔씩 생각이 날 때마다, 지금 사용하고 있는 설정 파일과 깃 레파지토리에 있는 파일이 얼마나 다른지 $diff ~/.bashrc ~/Git/dotfiles/.bashrc등의 명령을 통해 확인하고, 달라진 것들은 깃에 적용하면 그만입니다. 버전 관리 문제도 해결되었습니다.

보통의 일반 사용자는 실제로 이 정도 설정을 갖추면 문제가 없습니다.

그런데, 굉장히 많은 기기를 관리해야 하거나 아니면 저같은 배포판 널뛰기(distro hopper)를 하는 사람들은 새로운 기기를 설정해야할 때마다 파일들을 복사 붙여넣기 하는 것조차 귀찮습니다.

리눅스에는 ‘hard link ‘와 ‘symbolic link ‘라는 것이 있습니다. 관심 있는 것은 ‘symbolic link ‘, 줄여서 ‘symlink ‘입니다. ‘symlink ‘는 C언어에서 포인터와 매우 유사하게 작동합니다. 파일처럼 보이는 심볼릭 링크를 생성해둔 이후, 사용자가 그 심볼릭 링크에 (파일인 줄로 알고) 접근하는 경우, 해당 링크가 가리키는 실제 파일로 향하게 해줍니다.

따라서, 사용자는 요구했던 파일에 접근한 것으로 느끼지만, 실제로는 그 자리를 차지하는 이정표를 따라 참조하고 있는 파일을 수정한 것이 됩니다.

심링크(symlink)는 아래와 같은 문법으로 적용할 수 있습니다.

홈 디렉터리의 A.rc라는 파일이 사실은 깃 디렉터리의 B.rc라는 파일로 링크되기를 희망할 때,

$ ln -s ~/Git/B.rc ~/A.rc

이를 활용해 깃에 올려둔 각종 설정 파일을, 원래 있어야 할 위치에 링크 시키는 방법이 있습니다.

그런데, 아직도 해결되지 않은 문제가 하나 있습니다. 설정 파일을 활용하는 프로그램이 10개, 20개가 넘어가기 시작하면 그 많은 파일의 링크를 언제 다 생성하고 관리할까요?

귀찮음이 이 정도 수준에 이르면 우리는 다른 해결책을 찾아 나섭니다. 자동으로 심볼릭 링크를 생성할 수 있어야겠습니다.

인자로 주어지는 파일의 순서가 항상 헷갈립니다. 그럴 때, 외우기 매우 쉬운 힌트로 리눅스 명령어는 자주 ‘이미 있는 것 ‘, ‘없는 것 ’ 순서로 쓰인다는 것을 알면 유용합니다.

예시 →$mv existing_file not_existing_yet,$cp existing_file not_existing_yet,$ln existing_file not_existing_yet

그 대안은 2가지 입니다.

  1. Git Bare Repository
  2. GNU Stow Software

2가지를 천천히 살펴보시고 더 입맛에 맞는 방법을 찾으시면 됩니다.

Git Bare Repository

git init은 현재 디렉터리를 깃 관리 디렉터리로 만듬과 동시에 .git이라는 디렉터리도 생성합니다. 이 .git에는 각종 커밋 정보와 파일 해쉬값들이 들어가는 git명령어의 두뇌에 해당합니다. 따라서, 일반적인 상황에서는 현재 디렉터리를 실제 작업하는 환경으로 활용하기 위해 git init을 사용합니다.

여기서 적고 있는 설정 파일(dotfiles) 관리는 사실 위와 같은 전체 구조가 필요한 것은 아닙니다. 단순히 어떤 파일들을 트랙킹(계속 주시하며 변경사항을 관리)해야 하는지가 중요할 뿐이죠. 그 말은, 현재 이 디렉터리 아래에서 어떤 새로운 기능을 추가하거나 하는 등이 작업이 이루어지지 않아도 된다는 뜻입니다.

그런데 깃은 이런 스타일의 디렉터리를 관리하기 위한 기능이 이미 있습니다. 바로 bare repository입니다.

bare repository(이하: 배어 레파지토리)는 통상적으로 다양한 사람들의 작업 환경을 원격 레파지토리에서 관리하기 위해서 사용합니다. 따라서, 추가된 파일들이나 삭제된 파일을 트랙킹할 뿐, 이 레파지토리 내부에서 어떤 작업을 하지 않습니다.

이 배어 레파지토리를 활용한 닷 파일 관리는 아래와 같습니다.

$ git init --bare $HOME/.dotfiles
$ echo "alias config='/usr/bin/git \
        --git-dir=$HOME/.dotfiles  \
        --work-tree=$HOME'" >> $HOME/.bashrc
$ source $HOME/.bashrc
본인의 쉘이 다르면 .bashrc 말고 각자 쉘의 환경 설정 파일에 추가하면 됩니다.

$ config config --local status.showUntrackedFiles no

예를 들어, .vimrc 를 관리한다고 하면
$ config add .vimrc
$ config commit -m 'added .vimrc'

$ config push
당연하지만 원격 레파지토리(github, gitlab 등)이 설정되고나서 진행합니다.

즉, 기존 상황에서 git명령어를 입력할 상황을 config명령어로 대체 설정해둔 다음, config명령어를 통해 깃 레파지토리를 관리할 수 있습니다.

배어 레파티토리이므로, 깃 디렉터리 자체는 여기에 있으나 실제 작업 환경은 저쪽에 있다는 설정을 수행합니다.(alias config)

이렇게 관리하고 있는 설정파일을 실제 새로운 컴퓨터에 설치하기 위해서는 아래의 명령들을 수행합니다.

$ echo ".dotfiles" >> .gitignore
$ git clone --bare <REPOSITORY-URL> $HOME/.dotfiles
$ echo "alias config='/usr/bin/git \
            --git-dir=$HOME/.dotfiles/ \
            --work-tree=$HOME'" >> $HOME/.bashrc
$ source $HOME/.bashrc
본인의 쉘이 다르면 .bashrc 말고 각자 쉘의 환경 설정 파일에 추가하면 됩니다.

$ config config --local status.showUntrackedFiles no
$ config checkout

이미 눈치 채셨겠지만, git checkout기능을 적극 활용하는 방식입니다. 새로운 PC 환경은 새로운 브랜치와 다름 없으므로, 원격 레파지토리를 이 PC의 작업 환경으로 지정한 $HOME으로 체크아웃 하여 작업하는 것입니다.

Stow

기본 개념

stow는 원래 심볼릭 링크 관리자로 만들어진 프로그램 입니다. GNU stow 프로젝트에서 소개하고 있는 예시를 그대로 가져오자면, /usr/local/bin아래에 /usr/local/stow/emacs/bin/usr/local/stow/perl/bin을 향하는 심볼릭 링크를 생성할 수 있게 해줍니다.

stow의 기본 행동은 현재 있는 디렉터리 아래의 구조와 파일을 파악한 후, 한 단계 위의 디렉터리로 가서 그대로 심볼릭 링크들을 설정하는 것 입니다.

추가 개념

추가적으로 꼭 알아야 할 점이 있습니다.

  1. 디렉터리 폴딩

stow는 디렉터리 폴딩이라는 개념을 사용합니다. 적용하고자 하는 디렉터리 구조가, /usr/lib/test/A/A.rc로 존재한다고 가정했을 때, /usr/lib/디렉토리가 test디렉터리를 제외하고 아직 그 어떤 파일이나 디렉터리를 포함하지 않은 경우, /usr/lib/test/으로 이동해서 $stow를 실행하면 /usr/lib/A자체가 /usr/lib/test/A 를 가리키는 심볼릭 링크로 생성됩니다.

상위 디렉토리와 지시한 디렉토리 구조를 맨 상위부터 비교해가며 가장 빠르게 구분할 수 있는 차이가 발생하는 시점에 그냥 심볼릭 링크를 생성하고 멈춥니다. 즉, 디렉터리 구조 맨 아래에 있는 파일까지 똑깥이 복사되고 파일만 심볼릭 링크가 생성되는 것이 아닙니다.

아래는 그것을 보여주는 예시입니다.

$ tree -a .
.
└── test
    └── A
        └── .config
            └── A.rc
인 상황에서

$ cd test
이후,

$ stow A
를 하고

$ cd ..
다시 나와서 확인했을 때, 아래와 같이 적용됩니다.

$ tree -a .
.
├── .config -> test/A/.config
└── test
    └── A
        └── .config
            └── A.rc

위에서 확인하실 수 있듯이, .config/A.rc -> test/A/.config/A.rc가 아니라 .config자체를 링크해버렸습니다.

  1. 패키지 개념

패키지 개념은 어렵지 않습니다. 위에서 이미 보셨겠지만 $stow A와 같이 현재 디렉터리 내부의 특정한 디렉터리를 명령 대상으로 삼을 수 있습니다. 실제 stow 프로그램이 개발될 당시에는 라이브러리와 명령어, 매뉴얼 페이지를 포함하는 각종 perl 패키지를 설치하기 용이하게 하는데에 목적이 있었습니다. 즉, A라는 디렉터리 아래에 lib, bin, man 등의 디렉터리가 또 존재하고, 각각의 디렉터리 내부에 실제 파일이 존재하였던 것입니다. 이 때, 구조를 모두 포함하는 최종 디렉터리인 A를 패키지로 삼을 수 있습니다.

stow 매뉴얼에서의 정의에 따르면 “패키지란, 하나의 단위로 관리하고 싶은 파일이나 디렉터리의 집합 “입니다.

그렇기 때문에, 위 예시에서 $stow A옵션으로 실행이 가능한 것입니다.

옵션

하지만 이것은 그냥 사실상 심볼릭 링크와 다를 바가 없습니다. stow의 강점은 옵션에 있습니다. stow의 옵션들은 가장 대표적으로 아래와 같습니다.

S DIR, (-stow=DIR, 소스 디렉터리/패키지를 지정)

stow를 실행했다는 것은 어쨌든 무언가를 집어넣으려고 한 것이기 때문에 이 옵션 플래그 자체는 생략 가능합니다. 다시 말해서, 그냥 소스 디렉터리/패키지를 쭉 나열만 해도 -S가 켜진 것과 동일하게 이해합니다. 그러나 무엇을 집어 넣으려고 하는 건지 그 소스 디렉터리/패키지 자체는 항상 명시해야 합니다. .*일지라도 명시 -D-R등과 혼합 사용할 시에는 반드시 지정해서 켜야 함

t DIR(-target=DIR)

옵션을 지정하지 않은 경우, 타겟 디렉터리의 기본값은 바로 한 단계 상위 디렉터리 입니다.

소스 디렉터리는 현재 위치이고, 타겟 디렉터리는 홈 디렉터리 일 때, 위 옵션들을 활용해 $stow *혹은 $stow -t ~ *로 사용할 수 있습니다. -t옵션이 사용되는 경우, 먼저 나오는 경로가 타겟 디렉터리, 뒤에 나오는 경로(들)는 소스 디렉터리 입니다.

하나의 설정 파일만 연결하면 되더라도, 해당 파일을 필요로하는 프로그램의 이름으로된 디렉터리를 생성하여 그 아래에 저장하시면 문제가 없습니다.

추가적으로 매우 유용한 옵션은 아래와 같습니다.

  • 결과를 상세히 출력하도록 하는 v(-verbose=N)옵션을 함께 활용하면 어떤 변화가 생겼는지 출력을 통해 다시 확인할 수 있습니다.
  • n-no-simulate) 옵션을 붙여, 가상으로 실행해보고 나서, n옵션을 제거한 명령어로 실제 수행을 하는 방법도 있습니다.

$stow .와 $stow *의 차이점

.은 현재 디렉터리 자체를 패키지로 인식시킵니다. 즉, 현재 디렉터리 내부의 A/A.rc는 상위 디렉터리에 A -> ./test/A로, B.rc는 상위 디렉터리에 B.rc -> ./test/B.rc로 연결됩니다.

*은 현재 디렉터리 내부의 디렉터리들을 패키지로 인식시킵니다. 따라서, 파일도 함께 포함되어 있는 디렉터리에서 그 디렉터리 자체를 인자로 수행하는 경우 패키지로 인식하지 못해 다음과 같은 에러를 뿜습니다. stow: ERROR: The stow directory stow does not contain package B.rc

결론적으로, $stow -nvt ~ *로 결과가 예상한 것과 같은지 확인한 뒤, $stow -vt ~ *롤 하게 됩니다.

뿐만 아니라, 특정 디렉터리들만 생성하고 싶을 때는, $stow -vt ~ this_dir that_dir also_dir을 통해, 나머지 디렉터리는 무시하고 this_dir, that_dir, also_dir 아래의 설정 파일만을 심볼릭 링크로 생성할 수 있습니다.

즉, 본인 입맛에 따라 아무대나 깃 레파지토리를 클론해둔 다음, 설정 파일을 적용/관리할 수 있는 셈입니다.

예시

아래와 같은 test디렉터리를 기준으로 다양한 명령어를 수행한 결과들을 보여드리겠습니다.

$ tree -a .
.
└── test
    ├── A
    │   └── .config
    │       └── A.rc
    ├── B
    │   └── .config
    │       └── B.rc
    └── C
        └── C.rc

각각의 결과 항목은

  • test디렉토리 내부로 이동하여
  • 각 항목 제목에 있는 명령을 수행한 이후,
  • 실행 결과를 복사하였습니다.
  • 이후, 다음 예시를 위해, 생성된 링크를 모두 삭제하고 진행했습니다.

각 항목 명령어를 보고 결과를 예상해보시는 것을 추천드립니다.

$ stow C

$ tree -a .
.
├── C.rc -> test/C/C.rc
└── test
    ├── A
    │   └── .config
    │       └── A.rc
    ├── B
    │   └── .config
    │       └── B.rc
    └── C
        └── C.rc

$ stow B

$ tree -a .
.
├── .config -> test/B/.config
└── test
    ├── A
    │   └── .config
    │       └── A.rc
    ├── B
    │   └── .config
    │       └── B.rc
    └── C
        └── C.rc

기존에 상위 디렉토리에서 .config를 가지고 있지 않다는 이유로, B.rc가 아닌 .config자체를 링크한 것을 확인할 수 있습니다.

$ stow A

$ tree -a .
.
├── .config -> test/A/.config
└── test
    ├── A
    │   └── .config
    │       └── A.rc
    ├── B
    │   └── .config
    │       └── B.rc
    └── C
        └── C.rc

A만 실행해도 마찬가지입니다.

$ stow -nv A

$ stow -nv A
LINK: .config => test/A/.config
WARNING: in simulation mode so not modifying filesystem.

n옵션, v옵션을 활용해 결과 미리 확인

$ stow -nvS A

$ stow -nvS A
LINK: .config => test/A/.config
WARNING: in simulation mode so not modifying filesystem.

S를 켜도 암묵적으로 실행중이었으므로 변화가 없음

$ stow -nvt A B

$ stow -nvt A B
LINK: .config/B.rc => ../../B/.config/B.rc
WARNING: in simulation mode so not modifying filesystem.

t옵션을 활용해, 반드시 바로 위 디렉터리가 아니라 타겟 디렉터리를 자유자재로 설정 가능. 여기서는 목적지가 A로 설정되었습니다.

인자들의 적용 순서는 타겟_디렉터리 소스_디렉터리 입니다. 이러한 적용 순서 덕분에, 실행하고자 하는 소스 디렉터리를 여러개 나열할 수 있게 됩니다.

ex) $stow -vt ~ Dir1 Dir2 Dir3

타겟 디렉터리를 A로 지목하였으므로, 여기서 표시하는 .config는 A/.config입니다! 따라서 아래 예시와 같은 결과가 생깁니다.

$ stow -vt A B

$ tree -a .
.
└── test
    ├── A
    │   └── .config
    │       ├── A.rc
    │       └── B.rc -> ../../B/.config/B.rc
    ├── B
    │   └── .config
    │       └── B.rc
    └── C
        └── C.rc

n옵션을 제거하여 실제로 수행할 수 있습니다.

위에서 언급한 것처럼 타겟을 A로 지정하였으므로, A디렉터리 내부에 도달하여 이미 .config디렉터리가 있는 것을 확인, B.rc의 링크만 새로 생성한 것을 확인할 수 있습니다.

$ stow -nvt .. *

$ stow -nvt .. *
LINK: .config => test/A/.config
UNLINK: .config (reverts previous action)
MKDIR: .config
LINK: .config/A.rc => ../test/A/.config/A.rc
LINK: .config/B.rc => ../test/B/.config/B.rc
LINK: C.rc => test/C/C.rc
WARNING: in simulation mode so not modifying filesystem.

아웃풋을 눈여겨보시면 첫 줄에 A 패키지 순서에 상위 디렉터리엔 .config자체가 없음을 알고 귀찮아서 .config를 링크했다가, B 패키지 순서에 해당 디렉터리를 또 필요로 하는 것을 알고 화들짝 놀라, UNLINK하는 모습을 볼 수 있습니다. 그리고나서, .config디렉터리를 생성해 각각의 rc파일을 링크합니다.

$ stow -vt .. *

$ tree -a .
.
├── .config
│   ├── A.rc -> ../test/A/.config/A.rc
│   └── B.rc -> ../test/B/.config/B.rc
├── C.rc -> test/C/C.rc
└── test
    ├── A
    │   └── .config
    │       └── A.rc
    ├── B
    │   └── .config
    │       └── B.rc
    └── C
        └── C.rc

이 명령어는 결과적으로 test디렉터리 내부에서 실행한 $stow *와 동일합니다.

최종 정리

최종 정리를 하자면, 깃 아래에

$tree -a ~/Git/dotfiles
.
└── Stow
    ├── Bash
    │   └── dot-bashrc
    ├── i3
    │   └── .config
    │       ├── i3
    │       │   └── config
    └── Neomutt
        └── .config
            └── neomutt
                └── neomuttrc

와 같이 정리해둔 경우, $cd ~/Git/dotfiles로 해당 깃 디렉터리에 이동한 다음, $stow vt ~ *를 실행하면,

$ tree -a ~
.
├── .bashrc -> ~/Git/dotfiles/Stow/Bash/.bashrc
└── .config
    └── i3 -> ~/Git/dotfiles/Stow/i3/.config/i3
    └── neomutt -> ~/Git/dotfiles/Stow/Neomutt/.config/neomutt/neomuttrc

이렇게 적용할 수 있습니다.

결론적으로, stow를 활용해 깃에 저장된 설정 파일로 향하는 심볼릭 링크를 만들게되어, 깃에 있는 설정 파일 변경만으로 시스템에 적용되고 있는 설정까지 한번에 변경되며, (이미 깃에 있는 파일을 수정하는 것이므로) 버전 관리가 용이해집니다.

이미 생성된 dotfile 레파지토리도 없었고, stow도 이제서야 사용해보기로 하시는 분들은, --adopt옵션을 살펴보시기 바랍니다. ($stow --adopt -nvt ~ *)

수동으로 복사/이동할 필요 없이, stow가 현재 디렉터리로 문서를 옮기는 것을 도와줄지도 모릅니다!

이미 생성해버린 심볼릭 링크를 제거하거나, 여러가지 다른 이유로 심볼릭 링크를 제거하고 싶으신 분들은, $stow -D옵션을 활용하시면 됩니다! 생성 때와 마찬가지로 -n옵션을 유용하게 활용하시기 바랍니다. 아마도 결론적으로 사용하고자 하시는 명령어는, 설정 파일이 담긴 깃 디렉터리로 이동 후, $stow -D ${TARGET_PACKAGES}일 것으로 예상됩니다! 적용했을 파일들을 알아서 다시 연산한 후에, 그것들을 찾아다니면서 링크를 제거해줍니다.

Others’ Repository

다른 사람들 설정 파일 레파지토리 구경하기

NixOS

여기서 그치지 않고, 더 하드코어한 방식을 찾다 보면, Nix 패키지 매니저 혹은 아예 NixOS를 마주하게 됩니다.

Nix는 설정 파일에 그치지 않고, 내 시스템 전반에서 사용할 프로그램, 내 사용자가 사용할 프로그램 등 시스템 관련 설정을 *.nix파일로 저장해두고, 새 PC를 설치할 때, *.nix파일을 불러와 그대로 적용할 수 있습니다.