빌드노트
.gitignore에 예외를 넣어도 새 파일이 안 보이는 이유
.gitignore의 파일 예외는 제외된 부모 폴더를 통과하지 못한다. check-ignore -v로 적용된 줄을 찾고, 폴더 자체 대신 내용물을 제외해 필요한 파일만 새 파일 목록에 남기는 방법을 실제 출력으로 비교한다.
.gitignore에 !/generated/keep.txt를 넣었는데도 새 파일이 git status에 나타나지 않는다면, generated/ 폴더 자체가 제외됐는지 확인한다. 부모 디렉터리가 제외된 상태에서는 내부 파일만 예외로 되살릴 수 없다. 부모는 탐색할 수 있게 남기고, 그 안의 내용물을 제외한 뒤 필요한 파일에 예외를 둬야 한다. Git의 예외 패턴 설명 (새 탭에서 열림)
파일과 부모에 적용된 규칙을 같이 확인한다
Git이 설치된 터미널에서 해당 저장소의 루트로 이동한다. 아래 경로는 루트의 generated 폴더 안에 아직 추적하지 않은 keep.txt와 discard.txt가 있는 경우다. 자기 프로젝트에서는 보관하려는 파일과 같은 폴더의 제외할 파일로 경로를 바꾼다. 첫 명령으로 부모와 두 파일에 적용된 규칙을 보고, 두 번째 명령으로 실제로 표시되는 새 파일을 확인한다.
git check-ignore -v -- generated generated/keep.txt generated/discard.txt
git status --short --untracked-files=all -- generated
--untracked-files=all은 새 폴더를 한 줄로 묶는 대신 그 안의 개별 파일까지 표시한다. 뒤의 -- generated는 이번에 확인할 경로를 좁힌다. status의 새 파일 표시 옵션 (새 탭에서 열림)
별도로 만든 빈 저장소에서 합성 파일로 확인했다. Windows 11, Git 2.45.1.windows.1, PowerShell 7.6.6을 사용했고 파일을 스테이징하거나 커밋하지 않았다. 루트 .gitignore가 다음 두 줄일 때의 결과다.
/generated/
!/generated/keep.txt
check-ignore -v는 다음 세 줄을 출력했다. 두 번째 명령인 status는 아무것도 출력하지 않았다.
.gitignore:1:/generated/ generated
.gitignore:1:/generated/ generated/keep.txt
.gitignore:1:/generated/ generated/discard.txt
출력은 규칙 파일, 줄 번호, 패턴, 조회한 경로 순서다. keep.txt를 조회했는데도 두 번째 줄의 예외가 아니라 첫 번째 줄의 /generated/가 나온다. 부모부터 제외됐다는 단서다. 자신의 출력이 다른 .gitignore나 별도 제외 파일을 가리키면 그 파일의 해당 줄을 확인한다. check-ignore 출력 형식 (새 탭에서 열림)
Git 공식 문서는 제외된 디렉터리를 내부 탐색 대상에서 빼기 때문에 자식 예외로 복구할 수 없다고 설명한다. 파일 예외를 마지막 줄로 옮겨도 부모가 계속 제외돼 있으면 해결되지 않는다. 위 실험에서도 예외는 이미 마지막 줄이었다.
폴더 제외를 내용물 제외로 바꾼다
루트의 generated/keep.txt 하나만 남기려면 앞의 두 줄을 다음처럼 바꾼다. 기존 .gitignore 전체를 덮어쓰지 않고 해당 규칙을 수정한다. 앞의 /는 이 .gitignore가 놓인 위치를 기준으로 경로를 잡는다.
/generated/*
!/generated/keep.txt
/generated/*는 generated 자체가 아니라 바로 아래의 파일과 디렉터리에 맞는다. 그래서 Git이 폴더 안으로 들어갈 수 있고, 뒤의 예외로 keep.txt를 남길 수 있다. 같은 합성 파일을 넣은 별도 새 저장소에 이 규칙을 적용하고 앞의 두 명령을 실행했다. check-ignore -v 출력은 다음과 같았다.
.gitignore:2:!/generated/keep.txt generated/keep.txt
.gitignore:1:/generated/* generated/discard.txt
여기서는 generated 자체에 해당하는 출력이 없어졌다. -v 출력에 파일이 나왔다고 모두 무시되는 것은 아니다. 패턴이 !로 시작하면 예외에 맞았다는 뜻이다. 이 동작은 check-ignore의 verbose 옵션 (새 탭에서 열림)에도 명시돼 있다.
status에는 다음 한 줄이 나왔다. ??는 아직 추적하지 않은 파일이라는 표시다. 예외는 파일을 자동으로 추적하거나 스테이징하지 않는다. 이제 새 파일 목록에서 선택할 수 있게 된 것이다. discard.txt는 계속 제외됐다.
?? generated/keep.txt
한 단계 더 안쪽 파일이면 중간 폴더도 열어 둔다
같은 방식으로 generated/templates/keep.txt만 남기고 싶다고 하자. /generated/* 뒤에 !/generated/templates/keep.txt만 넣으면 여전히 실패한다. templates 디렉터리 자체가 첫 규칙에 맞기 때문이다.
두 합성 파일 generated/templates/keep.txt와 generated/templates/discard.txt를 둔 별도 저장소에서 이를 비교했다. 중간 폴더가 제외됐는지 보려면 조회 경로도 다음처럼 한 단계 안으로 옮긴다.
git check-ignore -v -- generated/templates generated/templates/keep.txt generated/templates/discard.txt
git status --short --untracked-files=all -- generated
실패한 규칙에서는 파일 조회에도 .gitignore:1:/generated/*가 나왔고 status는 빈 출력이었다. 원하는 파일까지 도달하도록 다음 네 줄로 바꾼 경우를 따로 실행했다. 이 규칙은 앞의 두 줄짜리 예시를 대체하며, 이번에는 templates/keep.txt만 남긴다.
/generated/*
!/generated/templates/
/generated/templates/*
!/generated/templates/keep.txt
두 번째 줄은 templates를 탐색할 수 있게 연다. 세 번째 줄은 그 안의 내용물을 다시 제외하고, 네 번째 줄은 필요한 파일 하나를 남긴다. 폴더만 예외로 열고 내용물 제외를 빼면 다른 파일까지 노출될 수 있으므로 두 역할을 나눈다. 수정한 조건에서 같은 조회 명령은 다음을 출력했다.
.gitignore:2:!/generated/templates/ generated/templates
.gitignore:4:!/generated/templates/keep.txt generated/templates/keep.txt
.gitignore:3:/generated/templates/* generated/templates/discard.txt
status에는 ?? generated/templates/keep.txt만 나왔다. 바로 아래의 generated/keep.txt와 두 discard.txt는 나오지 않았다. 예외로 열어 둔 부모를 따라 필요한 파일까지 도달하고, 나머지는 제외한 결과다.
출력이 다르면 추적 여부와 실제 규칙을 구분한다
check-ignore -v가 아무것도 출력하지 않으면 곧바로 이 부모 제외 문제라고 단정하지 않는다. 파일이 실제로 존재하는지, 저장소 루트에서 맞는 경로를 조회했는지 확인한다. git ls-files -- generated/keep.txt generated/templates/keep.txt로 경로가 나오면 이미 인덱스, 즉 Git의 추적 목록에 등록된 파일이다. check-ignore는 기본적으로 추적 중인 파일을 표시하지 않는다. ls-files의 기본 조회 범위 (새 탭에서 열림), check-ignore의 추적 파일 처리 (새 탭에서 열림)
이번 실험은 아직 추적하지 않은 일반 텍스트 파일과 루트 .gitignore 한 개를 대상으로 했다. 사용자 전역 제외 규칙은 실험 자식 프로세스에서 배제했다. 여러 단계의 .gitignore가 서로 덮어쓰는 구성, 심볼릭 링크, 다른 OS에서는 직접 시험하지 않았다. 실제 프로젝트에서 결과가 다르면 check-ignore -v가 가리키는 출처와 줄 번호를 다시 읽고, 수정 뒤에는 원하는 파일과 계속 제외할 파일을 함께 확인한다.