> ## Documentation Index
> Fetch the complete documentation index at: https://blog.nvim.me/llms.txt
> Use this file to discover all available pages before exploring further.

# .gitignore에 적었는데 왜 계속 보일까요?

> 개발 중 생긴 로그와 이미 기록한 메모를 비교하며 추적 상태와 제외 규칙을 구분해요. 작업 파일을 보존하면서 추적을 해제하고 과거 기록도 확인해봐요.

코드를 고치다가 `debug.log`와 개인 메모인 `local-notes.txt`를 만들었어요. 둘 다 커밋에 담고 싶지 않아 `.gitignore`에 적었는데, 로그는 사라지고 메모는 여전히 변경 목록에 보여요. 같은 규칙을 넣었는데 왜 다를까요?

[앞편](https://blog.nvim.me/git/learn/read-history-and-diff.md)에서는 이미 남긴 기록을 읽었어요. 이번에는 **어떤 파일을 다음 기록에 포함할지** 살펴봐요. 핵심은 제외 규칙보다 먼저 **이미 추적하는 파일인지** 확인하는 거예요.

## .gitignore는 이미 추적하는 파일을 제외하지 않아요

새로 만든 파일과 이미 `git add`한 파일에 같은 제외 규칙을 적용해볼 거예요. 먼저 구분할 것은 추적 여부예요.

| 상태와 설정 | 뜻 |
| - | - |
| 추적 중인 파일(tracked) | Git이 관리하는 파일이에요. 새 파일은 `git add`로 인덱스에 추가하면 첫 커밋 전부터 추적해요 |
| 추적하지 않는 파일(untracked) | 작업 트리에 있지만 Git이 아직 관리하지 않는 파일이에요 |
| `.gitignore` | 미추적 파일 중 규칙에 맞는 파일을 일반적인 status와 add 대상에서 제외해요 |

**파일이 디스크에 있는지, 인덱스에 있는지, 과거 커밋에 있는지는 별개**예요. 제외된 파일(ignored)은 커밋 다음에 도달하는 단계가 아니에요. [인덱스 설명](https://blog.nvim.me/git/learn/working-tree-index-and-commit.md)과 연결해서 보면, 제외 규칙이 이미 인덱스에 있는 파일을 제거하지 않는다는 점이 핵심이에요.

추적과 미추적의 구분은 [Pro Git의 변경 기록 설명](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository), 제외 규칙의 적용 범위는 [gitignore 공식 문서](https://git-scm.com/docs/gitignore)를 기준으로 해요.

## 시작 상태를 맞춰요

명령은 **Bash** 기준이며 저장소 루트에서 실행해요. 코드를 실행하지 않으므로 Node.js는 필요하지 않아요. 앞편을 마친 연습 저장소를 이어 써요.

| 항목 | 시작 상태 |
| - | - |
| 브랜치와 이력 | `main`, 초기 기록 → hello git 변경 → hello world 변경, 총 세 커밋 |
| `index.js` | `console.log("hello world");` 한 줄 |
| `README.md` | `# Git practice` 한 줄 |
| 미커밋 변경과 원격 | 둘 다 없음 |
| 이번에 만들 파일 | `local-notes.txt`, `debug.log`, `.gitignore`는 아직 없음 |

다른 작업이나 제외 설정이 섞였다면 기존 파일을 지우지 말고 별도 연습 저장소를 준비해요. 실습은 별도 hook이나 커밋 서명 설정이 없고, 전역 제외 규칙이 예제 파일명에 영향을 주지 않는 환경을 기준으로 해요.

> [accordion] 이 글부터 시작한다면: 같은 세 커밋 준비하기
>
> Git 설치는 [첫 커밋 실습](https://blog.nvim.me/git/learn/setup-and-first-commit.md)을 참고해요. `git init -b main`에는 Git 2.28 이상이 필요해요.
>
> 기존 Git 저장소 밖에서 실행해요. 같은 이름의 폴더가 있어 `mkdir`가 실패하면 멈추고 새 이름을 골라요.
>
> ```bash theme={null}
> mkdir git-practice-ignore && cd git-practice-ignore && git init -b main
> ```
>
> 아래 `>`는 파일을 덮어써요. 방금 만든 빈 연습 폴더에서만 실행해요. 작성자 설정은 이 저장소에만 적용하고, 원격은 연결하지 않아요.
>
> ```bash theme={null}
> git config --local user.name "Git Learner"
> git config --local user.email "learner@example.com"
> printf 'console.log("hello");\n' > index.js
> printf '# Git practice\n' > README.md
> git add index.js README.md
> git commit -m "Add initial script and README"
>
> printf 'console.log("hello git");\n' > index.js
> git add index.js
> git commit -m "Change greeting to hello git"
>
> printf 'console.log("hello world");\n' > index.js
> git add index.js
> git commit -m "Change greeting to hello world"
> ```

같은 터미널에서 시작 상태를 확인해요.

```bash theme={null}
git status
git log --oneline
cat index.js README.md
git remote -v
```

`main`과 `working tree clean`, 세 커밋이 보여야 해요. 파일은 차례로 `console.log("hello world");`, `# Git practice`이고 원격 출력은 없어야 해요. 출력이 별도 보기 화면으로 열리면 `q`로 나올 수 있어요.

## 같은 규칙인데 결과가 다른 이유를 관찰해요

**1. 메모만 먼저 기록하기**

개인 메모를 실수로 기록한 상황을 만들어요. 실제 개인정보나 비밀값 대신 연습 문장을 써요. 아래 명령은 새 연습 파일 두 개를 만들어요. 같은 이름의 파일이 있다면 덮어쓰지 말고 시작 상태부터 확인해요.

```bash theme={null}
printf 'Practice note: inspect greeting\n' > local-notes.txt
printf 'Debug: application started\n' > debug.log
git status --short
git add local-notes.txt
git status --short
git commit -m "Record practice local note"
git ls-files -- local-notes.txt debug.log
```

첫 `status`에는 두 파일 모두 `??`로 보여요. `add` 뒤에는 메모만 `A `로 바뀌고 로그는 `??`로 남아요. `A`는 추가가 스테이징됐다는 뜻이에요.

커밋 뒤 `ls-files`에는 `local-notes.txt`만 나와요. 이 명령은 기본적으로 인덱스의 파일을 나열해요. 폴더의 모든 파일이나 과거 전체 이력을 나열하는 명령은 아니에요. [git ls-files 공식 문서](https://git-scm.com/docs/git-ls-files)를 참고해요.

**2. 두 파일에 제외 규칙 적용하기**

아래 명령은 새 `.gitignore`를 만들어요. 실제 프로젝트의 기존 `.gitignore`에 그대로 실행하면 규칙을 덮어쓰므로, 이번 연습 저장소에서만 실행해요.

```bash theme={null}
printf '*.log\n/local-notes.txt\n' > .gitignore
git status --short
git status --short --ignored
git check-ignore -v -- debug.log
```

일반 `status`에는 `?? .gitignore`만 남아요. 로그는 제외됐고, 추적 중인 메모는 커밋 이후 바뀌지 않아 표시되지 않아요. 메모가 제외됐다고 판단하기에는 아직 일러요.

`--ignored`를 붙이면 `!! debug.log`도 보여요. `!!`는 제외된 미추적 파일을 뜻해요. `check-ignore -v`에서는 `.gitignore`의 1번 줄 `*.log`가 이 파일과 일치한다는 정보를 읽을 수 있어요. 표시 방식은 [git status 공식 문서](https://git-scm.com/docs/git-status)를 참고해요.

**3. 추적 중인 메모를 다시 고치기**

`>>`로 연습 메모에 한 줄을 덧붙여요. 실행 전에 메모가 변경 목록에 나올지 예상해보세요.

```bash theme={null}
printf 'Practice note: inspect diff\n' >> local-notes.txt
git status --short
git diff -- local-notes.txt
git ls-files -- local-notes.txt
git check-ignore --no-index -v -- local-notes.txt
```

`status`에는 ` M local-notes.txt`와 `?? .gitignore`가 나와요. `diff`에는 `+Practice note: inspect diff`가 보여요. `ls-files`에도 메모가 남아 있어요. **규칙에 이름이 있어도 이미 추적 중인 파일의 변경은 계속 비교해요.**

일반 `check-ignore`는 추적 중인 파일을 보고하지 않아요. 여기서는 `--no-index`로 추적 여부를 검사에서 제외하고, 어떤 규칙과 일치하는지만 물었어요. 결과는 `.gitignore`의 2번 줄 `/local-notes.txt`예요. 이 출력이 추적 해제를 뜻하지는 않아요. [git check-ignore 공식 문서](https://git-scm.com/docs/git-check-ignore)에 두 동작의 차이가 설명되어 있어요.

## 파일은 남겨두고 다음 기록에서 빼봐요

현재 메모는 작업 트리에 두 줄, 인덱스와 마지막 커밋에는 한 줄이 있어요. 이번에는 **이 연습 파일 하나만 인덱스에서 제거**해요. 제외 규칙도 함께 커밋하면 이후에는 보통의 추가 대상에서 빠져요.

> [!WARNING]
>
> `git rm --cached`는 현재 작업 파일을 보존하지만, 다음 커밋에는 해당 경로의 삭제를 기록해요. `--cached`를 생략한 `git rm`은 작업 파일도 삭제 대상으로 삼아요. 과거 커밋의 내용은 어느 쪽도 지우지 않아요.
>
> 파일 보존은 이 명령을 실행한 현재 작업 트리에 대한 설명이에요. 삭제 커밋을 공유하면 동료의 추적 중인 파일도 업데이트 과정에서 삭제될 수 있어요. 실제 팀 설정 파일이라면 백업과 대체 파일 준비를 먼저 조율해요. 이번 실습에는 원격이 없어요.

```bash theme={null}
git rm --cached -- local-notes.txt
cat local-notes.txt
git status --short
git diff --cached -- local-notes.txt
git ls-files -- local-notes.txt
git check-ignore -v -- local-notes.txt
```

`cat`에는 메모 두 줄이 그대로 나와요. `status`의 `D`는 **인덱스에 삭제가 스테이징됐다**는 뜻이에요. 디스크의 파일까지 사라졌다는 뜻은 아니에요. 함께 보이는 `?? .gitignore`는 아직 추가하지 않은 제외 규칙이에요.

스테이징된 삭제 패치에는 마지막 커밋에 있던 `Practice note: inspect greeting` 한 줄이 나와요. 새로 덧붙인 줄은 한 번도 스테이징하지 않았으므로 삭제 패치에도 없어요. `ls-files` 출력은 비고, 이제 일반 `check-ignore`로도 메모의 제외 규칙을 찾을 수 있어요.

이 예제에서는 인덱스의 메모가 `HEAD`와 같아서 `rm --cached`가 실행돼요. 인덱스 내용이 `HEAD`와 작업 파일 양쪽 모두와 다르면 거절될 수 있어요. 그때는 `-f`를 붙이기 전에 두 diff를 확인하고 필요한 내용을 별도로 보존해요. 실행 조건은 [git rm 공식 문서](https://git-scm.com/docs/git-rm)를 참고해요.

이제 제외 규칙을 스테이징하고 **규칙 추가와 메모 삭제만** 들어가는지 확인한 뒤 커밋해요.

```bash theme={null}
git add .gitignore
git diff --cached
git commit -m "Ignore logs and stop tracking local note"
```

이번에 `git add`한 경로는 `.gitignore`뿐이에요. 메모 삭제는 앞의 `rm --cached`에서 이미 스테이징했어요.

## 제외 규칙은 어디까지 적용될까요?

아래 표는 저장소 루트에 둔 `.gitignore`를 기준으로 해요. 앞의 두 규칙을 실습에 사용했고, 뒤의 두 규칙은 문법을 비교하는 예예요.

| 규칙 | 의미 |
| - | - |
| `*.log` | 이 디렉터리와 하위에서 이름이 `.log`로 끝나는 파일·디렉터리에 일치해요 |
| `/local-notes.txt` | 이 `.gitignore`가 있는 디렉터리 바로 아래의 해당 이름에만 일치해요 |
| `cache/` | 이 디렉터리와 하위의 `cache` 디렉터리를 제외해요. 끝의 `/`는 디렉터리만 뜻해요 |
| `!keep.log` | 앞선 규칙에서 제외한 이름을 다시 포함하는 예외예요. 상위 디렉터리 자체가 제외돼 있으면 파일 규칙만으로 다시 포함할 수 없어요 |

프로젝트 전체에서 공유할 규칙은 `.gitignore`에 넣고 커밋해요. 이 저장소에서 나만 쓸 규칙은 일반적인 단일 작업 트리 저장소의 `.git/info/exclude`, 여러 저장소에 적용할 개인 규칙은 `core.excludesFile` 설정으로 관리할 수 있어요. 규칙이 예상과 다르면 `check-ignore -v`로 실제 출처부터 확인해요. 자세한 패턴과 우선순위는 [gitignore 공식 문서](https://git-scm.com/docs/gitignore)를 참고해요.

제외는 접근 통제나 비밀값 보호 기능이 아니에요. 명시적인 `git add -f`로 제외된 파일도 추가할 수 있어요. 이미 비밀값을 커밋했다면 `.gitignore` 추가나 추적 해제만으로 해결되지 않아요. 실제 자격 증명은 폐기·재발급하고, 이력 정리는 별도의 범위로 판단해야 해요. 이번 예제에는 실제 비밀값을 사용하지 않았어요.

## 끝난 상태와 과거 기록을 함께 확인해요

```bash theme={null}
git status
git status --short --ignored
git ls-files
cat index.js README.md local-notes.txt debug.log
git diff
git diff --cached
git log --oneline
git show HEAD~1:local-notes.txt
git remote -v
```

* 브랜치는 `main`이고 커밋은 총 다섯 개예요. index.js와 README.md는 시작할 때와 같아요.

* 일반 `status`는 `working tree clean`이지만, `--ignored`에는 `!! local-notes.txt`와 `!! debug.log`가 보여요.

* `ls-files`에는 `.gitignore`, `README.md`, `index.js`만 있어요. 두 미커밋 diff는 비어 있어요.

* 디스크에는 메모 두 줄과 로그가 그대로 있어요. 원격 저장소는 없어요.

* `git show HEAD~1:local-notes.txt`에는 첫 메모 한 줄이 나와요. 추적 해제가 과거 기록까지 지우지는 않았어요.

“상태가 깨끗하다”는 말이 폴더의 모든 파일을 커밋했다는 뜻은 아니에요. 이번에는 메모와 로그가 제외된 채 남아 있어요. **파일의 존재, 추적 여부, 규칙의 일치, 과거 기록**을 나누어 확인하면 `.gitignore`를 수정할지, 이미 추적 중인 파일을 다룰지 판단할 수 있어요.

## 예상과 다른 결과가 나왔다면

| 상황 | 확인과 대응 |
| - | - |
| 처음부터 예제 파일이 `??`로 안 보여요 | `git status --short --untracked-files=all`로 표시 설정을 확인해요. 이어 `git check-ignore -v -- 파일경로`로 전역 규칙이나 `.git/info/exclude`의 영향을 살펴봐요 |
| 규칙에 적었는데 `M`으로 보여요 | `git ls-files -- 파일경로`로 추적 중인지 먼저 확인해요. 계속 기록할 파일이면 추적을 해제할 필요가 없어요 |
| `check-ignore`에 출력이 없어요 | 추적 중인 파일이거나 일치하는 규칙이 없을 수 있어요. 이때 종료 코드는 1일 수 있어요. `ls-files`와 `--no-index -v`로 질문을 나눠 확인해요 |
| `rm --cached`가 거절돼요 | `git diff -- 파일경로`와 `git diff --cached -- 파일경로`를 확인해요. 필요한 내용을 보존한 뒤 시작 상태를 맞추고, 범위를 넓힌 삭제나 강제 옵션으로 우회하지 않아요 |
| 준비 단계에서 커밋이 실패했어요 | [첫 커밋 실습의 오류 대응](https://blog.nvim.me/git/learn/setup-and-first-commit.md)을 참고해요. 실패한 커밋을 완료한 뒤 다음 단계로 진행해요 |

## 브랜치로 작업 위치를 나눠봐요

> [card] [브랜치를 만들면 폴더가 복사될까요?](https://blog.nvim.me/git/learn/branches-head-and-switch.md)
>
> 이 실습의 다섯 커밋을 이어서 사용해요. 새 브랜치와 HEAD, switch 전후의 파일을 비교하고 미커밋 변경의 이동 조건을 확인해봐요.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.