작업 트리
고친 사람 github-actions[bot]
작업 트리는 Git 이 보관해 둔 코드 가운데 한 판을 보통 파일로 펼쳐 두어 내가 편집기로 고칠 수 있게 해 줍니다. 평소 프로젝트 폴더라고 부르는 곳이 대개 이것입니다. 여기서 고친 내용은 커밋하기 전까지 기록으로 남지 않습니다.
쉽고 빠른 이해
무슨 일을 하나 — Git 이 보관해 둔 코드 가운데 한 판을 폴더에 파일로 꺼내 둡니다. git clone 으로 프로젝트를 받으면 생기는 폴더에서 .git 폴더를 뺀 나머지가 작업 트리입니다.
왜 따로 두나 — Git 은 이력을 제 방식으로 압축해 보관합니다. 그 상태로는 편집기로 열어 고칠 수 없습니다. 고칠 판 하나를 보통 파일로 꺼내 둘 곳이 필요합니다.
어떻게 도나
- 판을 하나 고르면 그 판의 파일이 작업 트리에 펼쳐집니다
- 작업 트리에서 파일을 고칩니다. 이력은 아직 안 바뀝니다
- 고친 것을 골라 커밋하면 비로소 새 판으로 이력에 남습니다
대가 — git add 도 안 한 변경은 작업 트리에만 있습니다. 실수로 덮거나 지우면 Git 으로 되살릴 수 없습니다.
안 두는 곳 — 아무도 파일을 고치지 않는 서버 쪽 저장소는 작업 트리 없이 둡니다.
상세
작업 트리는 저장소와 나뉘어 있습니다. 그리고 Git 이 파일 내용을 나눠 두는 세 곳 가운데 하나입니다. 이 절은 그 셋을 그림 하나와 git status 출력으로 봅니다.
서고의 원고와 책상 위의 사본
출판사 서고에 원고가 판마다 차례로 보관돼 있다고 해 봅시다. 작가는 최신 판을 한 부 복사해 자기 책상에 펼칩니다. 그리고 빨간 펜으로 고칩니다. 책상 위에서 아무리 고쳐도 서고에 있는 판들은 바뀌지 않습니다.
고친 원고는 새 판으로 서고에 넣어야 비로소 보관됩니다. 넣기 전에 책상 위 사본에 커피를 쏟으면 고친 내용도 함께 사라집니다.
저장소와 작업 트리
Git 은 파일이 바뀌어 온 과정을 판으로 끊어 기록합니다. 판 하나를 커밋이라고 부릅니다. 커밋에는 그때의 파일 전체 모습과 누가 왜 고쳤는지가 담깁니다.
커밋을 쌓아 두는 곳이 저장소입니다. Git 은 저장소를 프로젝트 폴더 안의 .git 이라는 숨은 폴더에 둡니다. 저장소 안의 커밋은 Git 이 제 방식으로 압축해 보관합니다. 그래서 편집기로 열어 고칠 수 없습니다.
고치려면 커밋 하나를 보통 파일로 꺼내 둬야 합니다. 꺼낸 파일들이 놓인 폴더가 작업 트리입니다. 폴더 안에 폴더가 가지처럼 매달린 모양이라 트리라는 이름이 붙었습니다.
flowchart TD
subgraph P ["프로젝트 폴더"]
subgraph G [".git · 저장소"]
C1["커밋 1"] --> C2["커밋 2"] --> C3["커밋 3"]
end
subgraph W ["작업 트리"]
F1["app.py"]
F2["util.py"]
end
end
C3 -. "펼친다" .-> W
그림에서 저장소와 작업 트리는 한 폴더 안에 나란히 있습니다. 저장소는 커밋을 여럿 들고 있습니다. 작업 트리는 그 가운데 하나만 펼쳐 둡니다. 프로젝트 폴더에서 .git 을 뺀 나머지가 작업 트리라고 보면 됩니다.
git clone 으로 프로젝트를 처음 받으면 둘이 한꺼번에 생깁니다. .git 에는 저장소가 전부 복사됩니다. 작업 트리에는 커밋 하나가 펼쳐집니다.
어느 커밋을 펼칠지는 흔히 브랜치로 고릅니다. 브랜치는 커밋이 이어지는 한 줄기에 붙인 이름입니다. 처음 받은 뒤에는 main 같은 기본 브랜치의 마지막 커밋이 펼쳐집니다.
저장소와 나눠 두는 까닭
나눠 두면 이력을 건드리지 않고 마음껏 고쳐 볼 수 있습니다. 고치다 망쳐도 저장소의 커밋은 멀쩡합니다. 마지막 커밋으로 돌아가면 처음부터 다시 할 수 있습니다.
한 폴더로 여러 판을 번갈아 볼 수도 있습니다. 판을 고를 때는 흔히 브랜치를 댑니다.
다른 브랜치로 옮겨 가면 Git 이 작업 트리의 파일을 그 브랜치의 모습으로 바꿔 끼웁니다. 이 옮겨 가기를 체크아웃이라고 부릅니다. 폴더 경로는 안 바뀌고 안의 내용만 바뀝니다.
파일 내용이 거치는 세 곳
Git 은 파일 내용을 세 곳에 나눠 둡니다. 작업 트리는 그 가운데 하나입니다. 나머지 둘을 알아야 작업 트리가 하는 일이 또렷해집니다.
| 곳 | 담긴 것 | 사람이 편집기로 고치나 |
|---|---|---|
| 마지막 커밋 | 이력에 확정된 판 | ✗ |
| 인덱스 | 다음 커밋에 넣으려고 골라 둔 내용 | ✗ |
| 작업 트리 | 지금 편집기로 여는 파일 | ✓ |
지금 작업 트리에 펼쳐 둔 판이 어느 커밋인지는 HEAD 라는 표지가 가리킵니다. 표에서 「마지막 커밋」이라 한 것이 HEAD 가 가리키는 커밋입니다.
인덱스는 스테이징 영역이라고도 부릅니다. 고친 파일 가운데 일부만 골라 커밋하고 싶을 때가 있습니다. 인덱스는 그렇게 고른 것을 커밋 전에 모아 두는 중간 단계입니다. 인덱스에 올리는 일을 흔히 스테이징한다고 말합니다.
고친 내용은 작업 트리에서 인덱스를 거쳐 커밋으로 올라갑니다. 체크아웃은 반대로 커밋의 내용을 작업 트리로 내려보냅니다.
flowchart TD
W["작업 트리"] -->|"git add"| I["인덱스"]
I -->|"git commit"| C["마지막 커밋"]
C -->|"체크아웃"| W
작업 트리에서 고친 내용은 git add 로 인덱스에 올립니다. 인덱스에 모인 것은 git commit 이 새 커밋으로 확정합니다. 그 커밋이 새 마지막 커밋이 됩니다.
체크아웃은 커밋의 내용을 작업 트리로 다시 꺼내 옵니다. 이때 인덱스도 그 커밋에 맞춰집니다.
작업 트리 안 파일의 상태
작업 트리의 파일은 Git 이 보기에 몇 가지 상태 가운데 하나에 있습니다. 상태는 파일을 앞의 세 곳과 견줘서 정해집니다.
먼저 Git 이 아는 파일과 모르는 파일로 갈립니다. 한 번이라도 커밋이나 인덱스에 들어간 파일은 Git 이 지켜봅니다. 이런 파일을 추적하는 파일이라고 부릅니다. 새로 만들어 한 번도 올리지 않은 파일은 추적하지 않는 파일입니다.
git status 는 이 상태를 보여 주는 명령입니다. --short 를 붙이면 파일마다 두 칸짜리 표시가 앞에 붙습니다. 앞 칸은 인덱스를 마지막 커밋과 견준 결과입니다. 뒤 칸은 작업 트리를 인덱스와 견준 결과입니다.
| 상태 | 무엇과 견주나 | git status --short 표시 |
|---|---|---|
| 추적 안 함 | 작업 트리에만 있고 인덱스와 마지막 커밋에는 없다 | ?? |
| 고침 | 뒤 칸 — 작업 트리가 인덱스와 다르다 | M |
| 인덱스에 올림 | 앞 칸 — 인덱스가 마지막 커밋과 다르다 | M |
| 변경 없음 | 세 곳이 모두 같다 | 안 나온다 |
아래는 세 파일이 저마다 다른 상태에 있을 때의 출력입니다.
$ git status --short
M app.py # 고쳤고 아직 안 올렸다
M util.py # 고쳐서 인덱스에 올렸다
?? notes.txt # Git 이 모르는 새 파일
app.py 는 고치기만 했으니 지금 커밋하면 안 들어갑니다. 다음 커밋에 들어가는 것은 인덱스에 올린 util.py 뿐입니다. notes.txt 는 올리기 전까지 Git 이 모릅니다.
작업 트리에는 Git 이 아예 모른 척해야 할 파일도 생깁니다. build/ 폴더에 쌓이는 빌드 결과물, *.log 로그 파일, 편집기가 만드는 설정 폴더 같은 것입니다. 이런 파일의 이름 규칙을 .gitignore 파일에 적어 두면 git status 에 안 나옵니다. 실수로 커밋되는 일도 막아 줍니다.
세 곳의 차이 보기
git diff 는 세 곳 가운데 두 곳을 견줘 무엇이 다른지 줄 단위로 보여 줍니다. 무엇과 무엇을 견주는지는 옵션이 정합니다.
git diff # 작업 트리 ↔ 인덱스
git diff --staged # 인덱스 ↔ 마지막 커밋
옵션 없는 git diff 는 인덱스에 올린 변경을 안 보여 줍니다. 고친 것을 전부 git add 한 뒤라면 빈 화면이 나옵니다. 올린 변경까지 보려면 --staged 를 붙입니다.
풀과 병합이 작업 트리를 건드릴 때
병합은 다른 브랜치의 커밋을 내 브랜치에 합치는 일입니다. 합친 결과는 저장소의 이력뿐 아니라 작업 트리의 파일에도 반영됩니다.
풀은 서버에 둔 저장소에서 남이 올린 커밋을 받아 오는 명령입니다. 받아 온 뒤에는 이어서 병합까지 합니다. 그래서 풀을 해도 작업 트리의 파일이 바뀝니다.
고치던 파일이 있으면 문제가 생깁니다. 합치면서 바뀔 파일을 내가 고치고 있었다면 Git 은 덮어쓰지 않고 멈춥니다. 인덱스에도 올리지 않은 변경은 작업 트리에만 있습니다. 덮이면 되살릴 길이 없습니다.
그래서 합치기 전에는 작업 트리를 깨끗하게 해 둡니다. 깨끗하다는 것은 고친 파일도 올린 파일도 없다는 뜻입니다. 작업 트리와 인덱스가 마지막 커밋과 같은 상태입니다.
깨끗하게 만드는 길은 둘입니다. 고치던 것을 커밋하거나 스태시로 잠시 치워 둡니다. 스태시는 고치던 내용을 따로 보관했다가 나중에 다시 꺼내 붙이는 기능입니다.
두 쪽이 같은 줄을 다르게 고쳤으면 병합이 멈추고 병합 충돌이 납니다. 이때 Git 은 두 쪽 내용을 표시 줄과 함께 작업 트리의 파일에 적어 둡니다.
<<<<<<< HEAD
timeout = 30
=======
timeout = 60
>>>>>>> feature
위는 내 브랜치가 30, 합치려는 feature 브랜치가 60 으로 고친 모습입니다. 사람이 이 파일을 열어 남길 값을 고르고 표시 줄을 지웁니다. 그다음 git add 로 올려 커밋하면 병합이 끝납니다.
되살릴 수 없는 변경
git add 로 인덱스에 올린 내용은 .git 폴더 안에 적힙니다. 위험한 것은 그 전 단계입니다. 작업 트리에서만 고친 변경은 Git 어디에도 기록되지 않습니다. 그래서 작업 트리를 되돌리는 명령은 조심해서 씁니다.
git restore 에 파일 이름을 대면 작업 트리의 그 파일을 인덱스의 내용으로 덮어씁니다. git clean 은 추적하지 않는 파일을 작업 트리에서 지웁니다. 둘 다 버린 내용을 따로 남겨 두지 않습니다. 인덱스에 올린 적이 없는 내용이라 Git 으로 되찾을 수 없습니다.
작업 트리가 없거나 여럿인 저장소
서버에 두고 여럿이 커밋을 올리는 원격 저장소는 대개 작업 트리 없이 만듭니다. 이런 저장소를 베어 저장소라고 합니다. 거기서는 아무도 파일을 고치지 않고 커밋을 주고받기만 합니다. 펼쳐 둘 까닭이 없습니다.
반대로 한 저장소에 작업 트리를 여럿 붙일 수도 있습니다. git worktree 명령이 다른 폴더에 작업 트리를 하나 더 만들어 줍니다. 그러면 고치던 작업을 치우지 않고도 옆 폴더에 다른 브랜치를 펼쳐 급한 버그를 고칠 수 있습니다.
이름이 비슷한 이웃
작업 트리와 이름이 겹치거나 비슷한 말이 몇 있습니다. 가리키는 것이 서로 다릅니다.
| 이름 | 무엇인가 |
|---|---|
| working directory · 작업 디렉터리 | 영어 문서에서 작업 트리를 이렇게 부르기도 한다. 같은 것이다 |
| 현재 작업 디렉터리 | 셸이나 프로그램이 지금 머무는 폴더. Git 과 상관없는 운영체제의 말이다 |
| 작업 사본 | Subversion 같은 중앙 서버 도구에서 작업 트리에 해당하는 폴더 |
| worktree | git worktree 로 붙인 작업 트리 하나하나. 폴더에 저만의 HEAD 와 인덱스가 딸린 묶음이다 |
| 트리 객체 | 커밋 안에서 폴더 하나의 내용을 적어 둔 Git 기록. 작업 트리와 이름만 겹친다 |
대화에서 「작업 디렉터리」가 나오면 Git 이야기인지 셸 이야기인지부터 봅니다. Git 이야기라면 작업 트리를 가리킵니다.
관련 항목
작업 트리와 함께 Git 을 이루는 구성 요소
저장소 · 로컬 저장소 · 커밋 · 스테이징 영역 · HEAD (Git) · 브랜치
작업 트리의 파일을 옮기거나 되돌리는 Git 명령
체크아웃 · git switch · git restore · git add · git status · git diff · git clean · 스태시 · 리셋
작업 트리에 결과를 적는 합치기 작업
풀 · 병합 · 리베이스 · 체리픽 · 병합 충돌 · 충돌 해소
작업 트리의 파일을 가려내는 규칙과 상태
.gitignore · 추적하지 않는 파일 · 스테이징 · 희소 체크아웃
작업 트리 없이 두거나 여럿 붙이는 저장소 구성
베어 저장소 · git worktree · 원격 저장소 · 클론
작업 트리와 이름이 헷갈리는 이웃
작업 사본 · 현재 작업 디렉터리 · 트리 객체 · 트리
작업 트리를 쓰는 버전관리 방식
버전관리 · Git · 분산 버전관리 · 중앙집중식 버전관리 · Subversion
다른 이름: working tree · 워킹 트리