본문 바로가기
TYLER SONGBlog
← Blog index
CS학습5 min read

Working Tree: 커밋 전 파일을 직접 편집하는 작업 디렉터리

워킹 트리(Working Tree)는 Git 저장소에서 개발자가 실제로 파일을 열어 수정하는 작업 디렉터리다. 커밋된 스냅샷을 체크아웃한 결과물이며, 스테이징 영역(Index), 저장소(Repository)와 구분되는 세 번째 영역이다. 워킹 트리의 상태를 읽고 되돌리는 명령(`status`, `diff`, `restore`, `stash`, `clean`

By 송민성

1. 개념

워킹 트리(Working Tree)는 Git이 관리하는 프로젝트 디렉터리에서 .git 디렉터리를 제외한, 개발자가 눈으로 보고 편집기로 수정하는 실제 파일 집합이다. 작업 디렉터리(Working Directory)라고도 부른다.

Git은 파일의 상태를 저장하는 영역을 세 곳으로 나눈다.

영역위치역할
워킹 트리(Working Tree)프로젝트 폴더의 일반 파일실제로 편집하는 파일
스테이징 영역(Index, Staging Area).git/index다음 커밋에 포함할 내용을 모아 둔 목록
저장소(Repository).git/objects커밋된 이력(스냅샷)

워킹 트리의 각 파일은 크게 두 상태로 나뉜다.

  • 추적됨(tracked): 마지막 커밋이나 인덱스에 존재하는 파일. 수정되지 않음(unmodified) 또는 수정됨(modified) 상태가 된다.
  • 추적되지 않음(untracked): Git이 아직 알지 못하는 새 파일(무시 규칙에 걸린 파일 제외).

2. 왜 사용하는가

워킹 트리가 저장소와 분리되어 있기 때문에 다음이 가능하다.

  • 커밋 이력을 건드리지 않고 자유롭게 실험하고 수정할 수 있다.
  • 수정한 내용 중 일부만 골라 커밋할 수 있다(워킹 트리 → 인덱스 → 커밋의 단계 구조).
  • 마음에 들지 않으면 마지막 커밋 상태로 되돌릴 수 있다.
  • git worktree로 하나의 저장소에서 여러 워킹 트리를 동시에 열어 브랜치별로 병행 작업할 수 있다.

3. 동작 원리

데이터의 흐름은 다음과 같다.

text
워킹 트리 --git add--> 인덱스 --git commit--> 저장소
워킹 트리 <--git restore-- 인덱스
워킹 트리 <--git switch/checkout-- 저장소
  • git switch로 브랜치를 바꾸거나 git checkout을 하면, Git은 대상 커밋의 스냅샷을 워킹 트리에 풀어 놓는다.
  • git status는 두 가지 비교를 수행한다. 첫째 HEAD와 인덱스의 차이(커밋할 변경 사항), 둘째 인덱스와 워킹 트리의 차이(아직 스테이징하지 않은 변경 사항). 여기에 추적되지 않는 파일 목록이 더해진다.
  • 인덱스는 파일 경로, 파일 모드, 객체 해시(SHA), 파일 메타데이터(수정 시각, 크기 등)를 기록한다. Git은 이 메타데이터로 변경 여부를 빠르게 판단하고, 필요할 때만 내용을 비교한다.
  • 워킹 트리의 수정 내용은 git add 전까지 Git 객체로 저장되지 않는다. 따라서 이 상태에서 git restore로 덮어쓰면 복구할 방법이 없다.

4. 코드 예제

아래 스크립트는 임시 디렉터리에서 워킹 트리의 상태 변화를 단계별로 확인한다.

shell
#!/bin/zsh set -e dir=$(mktemp -d) cd "$dir" git init -q git config user.name "demo" git config user.email "demo@example.com" echo "hello" > a.txt git add a.txt git commit -qm "init" # 1) 워킹 트리 수정 echo "world" >> a.txt echo "new file" > b.txt echo "--- status (수정 + untracked)" git status --short # M a.txt # ?? b.txt # 2) 워킹 트리 vs 인덱스 비교 echo "--- diff (unstaged)" git diff # 3) 스테이징 후 비교 git add a.txt echo "--- status (staged)" git status --short # M a.txt # ?? b.txt echo "--- diff --staged" git diff --staged # 4) 워킹 트리 변경 되돌리기 echo "extra" >> a.txt git restore a.txt # 인덱스 상태로 워킹 트리 복원 cat a.txt # hello, world (스테이징된 내용) # 5) untracked 파일 정리 (-n: 미리보기) git clean -n git clean -f ls

임시로 치워 두는 stash 예제는 다음과 같다.

shell
echo "wip" >> a.txt git stash # 워킹 트리를 HEAD 상태로 되돌리고 변경 내용을 보관 git status --short # 깨끗함 git stash pop # 보관한 변경을 워킹 트리에 다시 적용

5. 시간 복잡도 또는 성능 특성

엄밀한 시간복잡도 표기보다 비용이 어디서 발생하는지를 아는 것이 실용적이다.

  • git status는 추적 중인 파일 수에 비례해 워킹 트리를 검사한다. 인덱스에 저장된 파일 메타데이터가 같으면 내용을 다시 읽지 않아 대부분의 경우 빠르다.
  • 파일이 매우 많은 대형 저장소에서는 git status가 느려질 수 있다. 이때 파일 시스템 모니터(core.fsmonitor)나 core.untrackedCache 같은 설정으로 개선할 수 있다.
  • 브랜치 전환은 두 커밋 사이에서 달라진 파일만 워킹 트리에서 갱신하므로, 전체 파일을 다시 쓰지 않는다.

6. 실무 사용 사례

  • 커밋 전 점검: git status와 git diff로 의도하지 않은 변경이 섞였는지 확인한다.
  • 부분 커밋: git add -p로 한 파일 안에서도 원하는 부분만 스테이징한다.
  • 긴급 작업 전환: 작업 중 급한 버그 수정 요청이 오면 git stash로 워킹 트리를 정리하고 다른 브랜치로 이동한다.
  • 병행 작업: git worktree add ../hotfix main으로 별도 디렉터리에 다른 브랜치의 워킹 트리를 만들어, 현재 작업을 건드리지 않고 핫픽스를 진행한다.
  • 빌드 산출물 제외: .gitignore에 node_modules/, dist/ 등을 등록해 워킹 트리에 있어도 추적 대상에서 제외한다.

7. 주의할 점

  • git restore <파일>(또는 구버전의 git checkout -- <파일>)과 git reset --hard는 커밋하지 않은 워킹 트리 변경을 영구히 지운다. 실행 전에 git diff로 확인한다.
  • git clean -f는 untracked 파일을 삭제한다. 반드시 git clean -n으로 대상을 먼저 확인한다. 무시된 파일까지 지우려면 -x 옵션이 필요하므로 더욱 주의한다.
  • git stash는 기본적으로 untracked 파일을 보관하지 않는다. 포함하려면 git stash -u를 사용한다.
  • .gitignore는 아직 추적되지 않은 파일에만 효과가 있다. 이미 커밋된 파일은 git rm --cached <파일>로 추적을 해제해야 한다.
  • 브랜치를 전환할 때 충돌하는 수정 사항이 있으면 Git이 전환을 거부한다. 먼저 커밋하거나 stash한다.
  • 워킹 트리가 깨끗하다(clean)는 것은 추적 중인 파일에 변경이 없다는 뜻이며, untracked 파일은 별도로 확인해야 한다.

8. 핵심 정리

  • 워킹 트리는 .git을 제외한, 실제로 편집하는 파일 집합이다.
  • Git의 세 영역은 워킹 트리, 인덱스(스테이징 영역), 저장소이며 변경은 add와 commit을 거쳐 이동한다.
  • git status는 HEAD↔인덱스, 인덱스↔워킹 트리의 차이와 untracked 파일을 보여 준다.
  • 커밋되지 않은 워킹 트리 변경은 Git 객체로 저장되지 않으므로 restore, reset --hard, clean으로 지우면 복구하기 어렵다.
  • stash와 git worktree를 쓰면 작업 중인 상태를 보존한 채 다른 일을 처리할 수 있다.