Community연구 & 데이터 분석github.com

dragon-fish/config-file-versioning

로컬 설정 파일을 git + OS 파일 감시자를 통해 자동 버전 관리합니다. AI 코딩 에이전트(Claude Code, Codex, Gemini CLI 등)를 위한 드롭인 스킬입니다.

config-file-versioning란 무엇인가요?

이 스킬은 '작고 중요하며 덮어쓰여도 눈치채기 어려운' 로컬 설정 파일들을 자동으로 버전 관리합니다. OS 레벨 파일 감시자(launchd, systemd)가 파일 변경을 감지하면 10초 이내에 자동 git commit을 생성하여 별도의 분리된 git 디렉토리(separate-git-dir)에 보관합니다. 사용자는 언제든지 과거 버전으로 쉽게 롤백할 수 있습니다. Claude Code, Codex, Gemini CLI 등 주요 AI 코딩 에이전트와 함께 사용하도록 설계되었으며, macOS와 Linux를 기본 지원합니다. 각 설정 도메인(예: ssh, zsh, vscode)마다 독립적인 git 저장소를 사용하여 히스토리를 깨끗하게 유지하고, 화이트리스트 .gitignore를 통해 불필요한 파일이 함께 커밋되는 것을 방지합니다. CPU와 디스크 사용을 최소화하기 위해 이벤트 기반 감시와 쓰로틀링을 적용했으며, 설정 디렉토리 전체를 다른 머신으로 쉽게 이전할 수 있도록 설계되었습니다.

지원 대상✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add dragon-fish/config-file-versioning

Installed? Explore more 연구 & 데이터 분석 skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Skill: Config File Versioning

'작고 + 중요하며 + 조용히 덮어쓰여도 모르는' 로컬 설정 파일들을 자동 버전 백업합니다.

감시 중인 파일의 실제 내용이 변경될 때마다 10초 이내에 자동으로 git commit을 생성하여 로컬 separate-git-dir 저장소에 보관합니다. 원클릭으로 원하는 과거 버전으로 롤백할 수 있습니다.


언제 사용하는가

사용자에게 다음과 같은 트리거 신호가 나타나면 이 스킬을 사용합니다:

  • "<특정 파일>에 자동 버전/백업/덮어쓰기 방지를 추가해줘"
  • "어느 프로그램이 또 내 을 덮어쓰지 않았으면 좋겠어"
  • "<설정>이 변경되면 자동 commit 해줘"
  • "<기존 백업 디렉토리>를 참고해서 <다른 파일>도 백업해줘"
  • 사용자가 주요 설정 파일이 어떤 도구나 실수로 덮어쓰여서 손실된 적이 있고, 앞으로 어떻게 예방할지 묻는 경우

적용되지 않는 경우:

  • 대규모 코드 저장소 (일반 git workflow 사용)
  • 머신 간 동기화 (이 스킬은 로컬 버전 히스토리만 제공; 동기화하려면 syncthing 추가 또는 수동 git push to private remote)
  • 빈번하고 빠르게 변하는 파일 (.bash_history, history.jsonl 등 — 엄청난 수의 commit이 생성됨)

아키텍처 개요

   <보호 대상 파일>           (실제 위치, 예: ~/.ssh/config)
        │ 수정
        ▼
   OS 수준 파일 감시
   ┌──────────────────────────────┬──────────────────────┬─────────┐
   │ macOS: launchd WatchPaths    │ Linux: systemd .path │ 기타    │
   └──────────────────────────────┴──────────────────────┴─────────┘
        │ 트리거 (10초 쓰로틀링 후)
        ▼
   auto-commit.sh
        │ git add -A → staged diff가 비어있지 않을 때만 commit
        ▼
   <BACKUP_ROOT>/<DOMAIN>-config.git/   ← 실제 git repo (separate-git-dir)
        ▲
        │ worktree 루트의 ".git"(한 줄 `gitdir:` 텍스트)으로 연결

주요 설계 포인트:

설계이유
각 설정 도메인별 독립 저장소 (하나의 큰 저장소로 통합하지 않음)히스토리 깔끔; 개별 활성화/비활성화/마이그레이션 가능
separate-git-dirworktree 전체가 어떤 프로그램에 의해 삭제되어도 <BACKUP_ROOT>에 히스토리가 남아 있음
화이트리스트 .gitignore (* + !<보호 대상 파일>)같은 디렉토리의 런타임 데이터/캐시/임시 파일을 실수로 포함하지 않음
이벤트 기반 감시 + 쓰로틀링 (기본 10초)평시에 상주 프로세스 없음; 한 번의 쓰기로 여러 commit이 생성되는 것을 방지
commit 스크립트 내 git diff --cached --quiet 검사내용이 실제로 변경되지 않았을 때(mtime만 변경) 조용히 종료

구현 단계

단계 0: 사용자와 4가지 변수 합의

변수의미권장 기본값
<DOMAIN>설정 도메인 약어 (소문자, 숫자, 하이픈만 사용; 디렉토리명/Label 접미사로 사용)사용자와 논의; 예: ssh / zsh / vscode / claude
<WORKTREE>보호 대상 파일이 있는 디렉토리의 절대 경로 (git worktree 루트)일반적으로 감시 대상 파일이 있는 디렉토리
<FILES>감시할 절대 경로 목록 (1개 이상)사용자가 지정
<BACKUP_ROOT>저장소 + 스크립트 + 서비스 템플릿을 저장할 루트 디렉토리반드시 질문: 기본값은 ~/.local/share/config-versioning/<DOMAIN>/ 제안 (XDG 데이터 디렉토리 규약); 사용자가 이미 전용 백업/동기화 디렉토리(예: ~/Documents/backups/, ~/Sync/..., 자신의 샌드박스 디렉토리)를 가지고 있다면 사용자가 결정

단계 1: 데이터 디렉토리 생성 + git 저장소 초기화

mkdir -p "<BACKUP_ROOT>"

mkdir -p "<WORKTREE>"
cd "<WORKTREE>" && git init -q -b main

단계 2: 화이트리스트 .gitignore 작성

templates/gitignore-whitelist를 <WORKTREE>/.gitignore로 복사한 다음, <FILES>의 각 항목에 대해 <WORKTREE> 기준 상대 경로를 !<rel-path> 줄로 추가합니다.

예: worktree=~/.ssh, ~/.ssh/config와 ~/.ssh/known_hosts를 보호하려면 다음을 추가:

!config
!known_hosts

단계 3: 초기 commit

cd "<WORKTREE>"
git add -A
git commit -q -m "chore: initial snapshot of <DOMAIN> config"

-c user.name/-c user.email로 강제로 덮어쓰지 마세요. git이 사용자 글로벌 설정을 사용하도록 두세요. 저장소는 순수 로컬이며, push하지 않으므로 커밋 작성자는 중요하지 않습니다.

단계 4: .git 디렉토리를 <BACKUP_ROOT>로 이동하고 gitdir 포인터 남기기

GITDIR="<BACKUP_ROOT>/<DOMAIN>-config.git"
mv "<WORKTREE>/.git" "$GITDIR"
echo "gitdir: $GITDIR" > "<WORKTREE>/.git"
git -C "$GITDIR" config core.worktree "<WORKTREE>"

단계 5: auto-commit 스크립트 복사 + 수정

templates/auto-commit.sh를 <BACKUP_ROOT>/auto-commit.sh로 복사하고, 2개의 플레이스홀더를 다음과 같이 바꿉니다:

플레이스홀더바꿀 내용
__WORKTREE__<WORKTREE> 절대 경로
__LOG__로그 경로, macOS는 ~/Library/Logs/config-file-versioning.log, Linux는 ~/.local/state/config-file-versioning.log 권장

chmod +x를 실행합니다.

단계 6: 감시 계층 설치 (운영체제별 분기)

6a. macOS (launchd LaunchAgent)

templates/launchagent.plist를 복사하고 플레이스홀더를 바꿉니다:

플레이스홀더바꿀 내용
__LABEL__local.<USER>.<DOMAIN>-config-watch (local. 접두사는 충돌 방지; <USER>는 $USER 사용)
__SCRIPT_PATH__<BACKUP_ROOT>/auto-commit.sh 절대 경로
__WATCH_PATHS__<FILES>의 각 파일을 <string>...</string> 쌍으로 감싸고 전체를 <array> 안에 배치
__LOG__단계 5와 동일

두 곳에 작성 (디렉토리 이전 가능하도록):

  1. <BACKUP_ROOT>/<LABEL>.plist (백업 템플릿)
  2. ~/Library/LaunchAgents/<LABEL>.plist (실제 적용)

로드:

launchctl unload "$HOME/Library/LaunchAgents/<LABEL>.plist" 2>/dev/null
launchctl load   "$HOME/Library/LaunchAgents/<LABEL>.plist"
launchctl list | grep "<LABEL>"   # 등록 확인

6b. Linux (systemd path unit)

templates/systemd.path와 templates/systemd.service를 ~/.config/systemd/user/로 복사하고, 이름을 <DOMAIN>-config-watch.path 및 <DOMAIN>-config-watch.service로 변경한 후, 파일 내 플레이스홀더를 바꿉니다. 그런 다음:

systemctl --user daemon-reload
systemctl --user enable --now <DOMAIN>-config-watch.path

6c. 기타 / 폴백

'watch + run command'가 가능한 모든 도구를 사용할 수 있습니다:

  • gitwatch (크로스 플랫폼 셸): brew install gitwatch 또는 apt install gitwatch
  • fswatch + 자체 관리 데몬: fswatch <FILES> | xargs -n1 <BACKUP_ROOT>/auto-commit.sh
  • inotifywait (Linux) + 셸 루프

watcher 프로세스를 해당 플랫폼의 서비스 관리자(OpenRC, runit, launchd, systemd)에 위임합니다.

단계 7: 종단 간 테스트 (필수, 건너뛰지 마세요)

감시 중인 파일 하나를 수정하고, 쓰로틀 간격보다 2초 더 기다린 후 로그와 git log에 각각 하나씩 추가되었는지 확인합니다. 먼저 임시 필드를 추가했다가 다시 제거하여 2개의 commit이 생성될 것으로 예상합니다:

# 예: JSON 파일에 임시 키 _autoCommitTest 추가
# sleep 12 (쓰로틀=10초 가정)
tail -2 "<LOG>"
git -C "<WORKTREE>" log --oneline | head -3

단계 8: README 작성

각 <BACKUP_ROOT>에 README.md를 포함시켜 나중에 이전하기 쉽게 합니다. 최소한 다음 내용을 포함합니다:

  • 목적 (이 시스템이 왜 필요한지)
  • 파일 목록 표
  • 일상 명령어 (히스토리 보기, 롤백, watcher 상태 확인, 로그 보기)
  • 유지보수 (새 감시 파일 추가, 쓰로틀 간격 변경)
  • 새 머신에서 재구축 단계
  • 완전 제거 단계

안티 패턴 (명시적으로 피하기)

❌ 잘못된 방법✅ 올바른 방법
여러 설정 도메인을 같은 git 저장소에 넣기도메인당 하나의 저장소
.git을 worktree 내부에 유지<BACKUP_ROOT>로 이동하고 gitdir 포인터 사용
블랙리스트 .gitignore 사용화이트리스트 사용
종단 간 테스트를 건너뛰고 전달반드시 수행, commit이 실제로 생성되는지 확인
plist/service 유닛을 시스템 디렉토리에만 배치하고 <BACKUP_ROOT>에 템플릿을 남기지 않음두 곳 모두 배치하여 디렉토리가 자체 포함되어 마이그레이션 가능하도록 보장
commit할 때 공개 원격으로 push금지 — 설정에는 토큰/민감 정보가 포함될 수 있으므로 순수 로컬 저장소
auto-commit.sh에 git user.name/email 하드코딩git 글로벌 설정에 맡기기; 스크립트에 개인 정보를 포함하지 않음
fswatch를 KeepAlive=true로 상주 실행이벤트 기반(WatchPaths / .path unit) 우선 사용, 상주 프로세스 없음

경계 케이스 처리

  • worktree 디렉토리가 존재하지 않는 경우: 먼저 mkdir -p를 실행합니다. 처음이라면 감시 대상 파일 자체도 존재하지 않을 수 있습니다 — 먼저 빈 파일을 생성할지 확인합니다.
  • 감시 대상 파일이 심볼릭 링크인 경우: watcher에서 링크 경로 와 실제 대상 경로를 동시에 감시하여, 일부 쓰기 방식이 둘 중 하나만 트리거하는 경우를 방지합니다.
  • 감시 대상 파일이 바이너리인 경우: 정상 작동하지만(git이 blob 저장), diff로 내용을 볼 수 없습니다.
  • 동일한 Label/서비스가 이미 존재하는 경우: 새 것을 로드하기 전에 먼저 기존 것을 unload/disable합니다.
  • 쓰로틀 기간 내에 여러 번 쓰기가 발생하는 경우: 쓰로틀 기간 내의 여러 변경 사항은 단일 commit으로 병합됩니다 (의도된 동작입니다).
  • 원자적 쓰기(write-temp + rename): launchd WatchPaths는 일반적으로 올바르게 트리거합니다. 특정 프로그램의 쓰기 방식으로 인해 commit이 누락되는 경우, 사용자에게 KeepAlive + gitwatch / fswatch 메인 루프 사용을 권장합니다.

템플릿 파일


설계 철학

  1. 가시성 + 가독성 + 수동 편집 가능: 버전 히스토리는 텍스트 git입니다. git을 아는 사람이라면 누구나 위치를 찾고 롤백할 수 있으며, GUI에 의존하지 않습니다.
  2. 이벤트 기반 ≫ 폴링: 유휴 CPU/디스크 소모를 줄입니다.
  3. 실패 안전: commit 스크립트는 부작용이 없습니다 — cd 실패, diff가 비어도 조용히 종료되며 worktree를 손상시키지 않습니다.
  4. 디렉토리 자체 포함: <BACKUP_ROOT>에는 데이터 + 스크립트 + plist/unit 템플릿 + README가 모두 포함되어 있어, 디렉토리 전체를 복사하여 새 머신에서 재구축할 수 있습니다.
  5. push 금지: 민감한 설정이 원격에 업로드되는 것을 방지합니다. 원격 백업이 필요하면 파일 수준 솔루션(Time Machine / Backblaze / 암호화된 syncthing)을 사용하세요.

관련 스킬