GIT / FIELD NOTES

gitignore에 적었는데 왜 계속 커밋될까

이미 커밋한 파일은 .gitignore에 적어도 계속 추적됩니다. git 2.54.0 실행 결과로 무시되지 않는 이유를 두 명령으로 가려내고 rm --cached 뒤 팀원의 파일이 지워지는 일과 남는 기록까지 정리합니다.

개발용 설정 파일 settings.local을 실수로 한 번 커밋했습니다. 뒤늦게 .gitignore에 파일 이름을 적었는데, 파일을 고칠 때마다 git status에 여전히 “수정됨”으로 뜨고 다음 커밋에도 딸려 들어갑니다. 규칙을 잘못 쓴 걸까요? 그렇지 않습니다. .gitignore는 처음부터 이런 파일에는 적용되지 않습니다. 이 글을 읽고 나면 파일이 무시되지 않는 이유를 두 명령으로 가려낼 수 있고, 추적을 끊을 때 팀원의 파일이 지워지는 일과 이미 올라간 비밀값 문제까지 미리 대비할 수 있습니다.

예제의 settings.local, DEBUG 값, logs 폴더와 저장소는 모두 이 글을 위해 만든 가상 예제입니다. 2026년 9월 30일 macOS 27.0의 git 2.54.0(Apple Git-157)으로 빈 저장소를 만들어 실행했고, 규칙은 git-scm.com의 공식 문서(gitignore, git-rm, git-check-ignore, git-update-index)를 기준으로 했습니다.

.gitignore는 아직 추적하지 않는 파일에만 적용됩니다

실험은 이렇게 했습니다. settings.local에 DEBUG=true를 넣어 첫 커밋을 하고, 그다음 .gitignore에 settings.local 한 줄을 추가한 뒤 파일 내용을 DEBUG=false로 바꿨습니다. git status --short는 “ M settings.local”, 즉 추적 중인 파일이 수정되었다고 알려 줍니다. 무시 규칙이 전혀 효과가 없는 것처럼 보입니다.

공식 문서의 첫머리에 답이 있습니다. gitignore 문서는 이미 Git이 추적하고 있는 파일은 영향을 받지 않는다고 적고, 무시 규칙의 목적을 “추적하지 않는 파일이 계속 추적되지 않게 하는 것”으로 설명합니다. Git은 한 번 인덱스(다음 커밋에 들어갈 목록)에 올라간 파일을 계속 비교합니다. .gitignore는 git add .처럼 새 파일을 고를 때 걸러 내는 체일 뿐, 이미 목록에 있는 파일을 빼 주지는 않습니다.

그래서 “왜 무시가 안 되지?”라는 질문에는 먼저 두 가지를 확인하면 됩니다. git ls-files settings.local이 파일 이름을 출력하면 그 파일은 추적 중입니다. 그리고 git check-ignore -v는 어떤 규칙이 그 경로에 걸렸는지 파일 이름과 줄 번호로 보여 줍니다. 다만 check-ignore는 기본적으로 추적 중인 파일을 아예 보여 주지 않습니다. 실험에서도 아무것도 출력하지 않고 종료 코드 1(무시되지 않음)로 끝났습니다. --no-index를 붙여야 인덱스를 보지 않고 규칙만 따져서 “.gitignore:1:settings.local”이라고 답합니다. 규칙은 맞게 썼고, 파일이 이미 추적 중이라서 적용되지 않았다는 뜻입니다.

git 2.54.0 실행 결과. .gitignore에 settings.local을 추가해도 status는 M settings.local. check-ignore -v는 출력 없이 exit=1. ls-files는 settings.local을 출력. check-ignore -v --no-index는 .gitignore:1:settings.local. git rm --cached 뒤 status는 D settings.local. 커밋 후 다시 고쳐도 status는 비어 있고 ls는 settings.local을 보여 준다.
이미 커밋한 settings.local을 .gitignore에 적은 뒤의 실제 출력입니다. 규칙은 맞았지만 추적 중이라 적용되지 않았고, rm --cached 뒤에야 조용해졌습니다.
세 단계. 1 git ls-files settings.local이 이름을 출력하면 추적 중이고 .gitignore가 적용되지 않는다. 2 git check-ignore -v --no-index로 규칙만 따지며, --no-index가 없으면 추적 파일은 보이지 않는다. 3 추적 중이면 rm --cached를 검토하고, 규칙이 안 나오면 규칙을 고친다.
.gitignore가 안 먹을 때의 확인 순서입니다. 추적 중인 파일이면 규칙이 맞아도 적용되지 않습니다.

추적만 끊는 명령: git rm --cached

파일은 내 컴퓨터에 남기고 Git의 목록에서만 빼려면 git rm --cached settings.local을 씁니다. --cached는 인덱스에서만 지우고 작업 폴더의 파일은 수정 여부와 관계없이 그대로 둡니다. --cached 없이 git rm을 쓰면 디스크의 파일까지 지워지므로 이 옵션이 핵심입니다.

실행 직후 git status는 “D settings.local”을 보여 줍니다. 파일을 삭제하는 변경이 다음 커밋에 올라갔다는 뜻이지만 ls로 보면 파일은 그대로 있습니다. .gitignore와 함께 커밋한 뒤 settings.local을 다시 DEBUG=maybe로 고쳐 보았더니 git status는 아무것도 출력하지 않았습니다. 이제야 .gitignore가 제 역할을 합니다. 공식 문서가 권하는 순서도 이와 같습니다. 인덱스에서 빼고, 이름을 .gitignore에 적어 다음 커밋부터 다시 들어오지 않게 합니다.

끊은 뒤에 생기는 두 가지 일

첫째, 과거 커밋에는 파일이 그대로 남아 있습니다. git log --format=%s -- settings.local은 “first commit”과 “stop tracking settings.local” 두 커밋을 보여 주고, git show HEAD~1:settings.local은 처음 올렸던 DEBUG=true를 그대로 출력합니다. rm --cached는 앞으로의 커밋에서 파일을 빼는 것이지 기록을 지우는 것이 아닙니다.

둘째, 다른 사람의 복제본에서는 파일이 사라질 수 있습니다. 로컬 원격 저장소 하나에 “나”와 “팀원” 두 복제본을 만들어 확인했습니다. 팀원은 처음 복제할 때 settings.local을 받았습니다. 내가 추적을 끊는 커밋을 올린 뒤 팀원이 git pull을 하자, 팀원의 폴더에서 settings.local이 삭제되고 .gitignore만 남았습니다. 그 커밋에는 “이 파일을 삭제한다”는 변경이 들어 있고, 팀원의 파일은 아직 추적 중이었으므로 Git이 그대로 적용한 것입니다. 파일 내용은 과거 커밋에 있으니 되살릴 수는 있지만, 팀원은 이유를 모른 채 설정을 잃습니다.

이런 설정 파일은 처음부터 다른 방식으로 두는 편이 낫습니다. git-update-index 문서는 추적 중인 파일의 변경을 무시하는 방법은 Git에 없다고 하면서, 저장소에는 예시 파일을 두고 각자 그것을 무시되는 이름으로 복사해 고쳐 쓰는 방식을 권합니다. 예를 들어 settings.local.example을 커밋하고 README에 “settings.local로 복사해서 쓰세요”라고 적어 두는 식입니다. 추적을 끊는 커밋을 올릴 때는 팀원에게 pull 전에 파일을 백업하라고 먼저 알려 주세요. 흔히 쓰는 assume-unchanged나 skip-worktree 표시는 Git이 여전히 파일을 비교할 수 있어 기대대로 동작하지 않는다고 같은 문서가 설명합니다.

파일에 비밀번호나 API 토큰이 들어 있었다면 순서가 달라집니다. GitHub 문서는 비밀값이 커밋되었다면 가장 먼저 그 값을 폐기하거나 교체하라고 합니다. rm --cached로는 과거 커밋이 그대로이고, 기록에서 지우려면 git-filter-repo 같은 도구로 히스토리를 다시 써야 하는데, 이 작업은 모든 팀원이 저장소를 새로 받아야 하는 큰 조율이 필요합니다. 이 글의 실험에서는 히스토리 재작성을 실행하지 않았습니다.

git 2.54.0 실행 결과. mate 복제본의 ls는 settings.local. git pull 뒤 ls -A는 .gitignore만 보여 준다. git show HEAD~1:settings.local은 DEBUG=true를 출력한다.
다른 복제본(mate)에서 추적을 끊는 커밋을 받은 결과입니다. 팀원의 settings.local은 지워졌고, 내용은 과거 커밋에만 남았습니다.
네 칸. 인덱스에서는 빠져 다음 커밋에 삭제로 기록되고 이후 수정은 status에 뜨지 않는다. 내 작업 폴더의 파일은 남는다. 과거 커밋에는 처음 내용이 남으므로 비밀값이면 먼저 폐기하거나 교체한다. 팀원 복제본에서는 pull할 때 파일이 삭제된다.
rm --cached는 인덱스에서만 파일을 뺍니다. 과거 커밋에는 남고, pull한 팀원의 폴더에서는 지워집니다.

폴더를 통째로 무시하면 !로 되살릴 수 없습니다

추적 중인 파일이 아닌데도 규칙이 뜻대로 안 되는 흔한 경우가 하나 더 있습니다. 빈 logs 폴더를 저장소에 남기려고 .gitkeep만 예외로 두는 규칙입니다. .gitignore에 logs/와 !logs/.gitkeep 두 줄을 적고 git status --short --untracked-files=all을 보면 .gitignore만 나오고 logs/.gitkeep은 보이지 않습니다. git check-ignore -v logs/.gitkeep은 “.gitignore:1:logs/”, 즉 첫 줄의 폴더 규칙이 걸렸다고 알려 줍니다.

gitignore 문서는 부모 폴더가 제외되면 그 안의 파일은 다시 포함할 수 없다고 적습니다. Git은 성능 때문에 제외된 폴더 안을 아예 들여다보지 않으므로, 안쪽 파일에 대한 ! 규칙은 어디에 적든 효과가 없습니다. 첫 줄을 logs/*로 바꿔 폴더가 아니라 폴더의 내용을 무시하자 logs/.gitkeep이 추적 대상 후보로 나타났습니다. 한 단계 더 깊은 logs/2026/app.log도 check-ignore는 logs/* 규칙에 걸린다고 답했습니다. *는 슬래시를 넘지 않지만 하위 폴더 logs/2026 자체가 logs/*에 걸려 그 안이 통째로 빠지기 때문입니다.

두 칸 비교. 왼쪽은 logs/와 !logs/.gitkeep로, Git이 폴더 안을 보지 않아 .gitkeep이 보이지 않고 check-ignore는 .gitignore 1행 logs/를 가리킨다. 오른쪽은 logs/*와 !logs/.gitkeep로, .gitkeep만 추적 후보로 나타난다.
같은 예외 규칙이라도 첫 줄이 폴더를 막으면 효과가 없고, 폴더의 내용을 막으면 동작합니다.

규칙을 어디에 적을지도 고르세요

모든 개발자가 무시해야 하는 파일(빌드 결과, 의존성 폴더)은 저장소에 커밋하는 .gitignore에 적습니다. 나만의 작업 파일처럼 이 저장소에서만 무시하고 팀과 나눌 필요가 없는 것은 .git/info/exclude에 적습니다. 편집기 백업 파일처럼 어느 저장소에서나 무시할 것은 사용자 전역 파일에 둡니다. core.excludesFile로 지정하며, 따로 정하지 않으면 $XDG_CONFIG_HOME/git/ignore, 그 변수가 없으면 ~/.config/git/ignore입니다. 어느 쪽에 적든 이미 추적 중인 파일에는 효과가 없다는 점은 같습니다.

다음에 .gitignore가 안 먹는다고 느끼면 규칙을 고치기 전에 git ls-files <경로>와 git check-ignore -v --no-index <경로>를 먼저 실행해 보세요. 앞의 것이 이름을 출력하면 추적을 끊을지부터 정하고, 뒤의 것이 아무 규칙도 보여 주지 않으면 그때 규칙을 고치면 됩니다.

END OF NOTE목록으로
COMMENTS BOX

이 기록에 대화를 더해 주세요.

궁금한 점, 다른 접근, 직접 해 본 결과를 나눠 주세요.

최신순

로그인 상태 확인 중…

댓글을 불러오는 중…