본문으로 건너뛰기
AZ Tools

.gitattributes 테스터

.gitattributes와 경로 목록을 붙여넣으면 각 경로에 최종 적용되는 모든 속성과, 그 값을 정한 줄 또는 매크로를 보여줍니다.

경로

9

속성 파일

2

규칙

9

지적

3

src/app.js

참조 순서(가까운 것부터): src/.gitattributes → .gitattributes

속성상태결정한 곳
diff값은 diff 드라이버 이름이고(헝크 헤더, textconv), -diff는 모든 diff에서 파일을 바이너리로 취급하게 합니다.javascriptsrc/.gitattributes:1 *.js
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.auto.gitattributes:1 *
src/vendor/jquery.min.js

참조 순서(가까운 것부터): src/.gitattributes → .gitattributes

속성상태결정한 곳
diff값은 diff 드라이버 이름이고(헝크 헤더, textconv), -diff는 모든 diff에서 파일을 바이너리로 취급하게 합니다.javascriptsrc/.gitattributes:1 *.js같은 속성을 쓴 다른 곳: .gitattributes:9 (minified)
linguist-generatedgit이 아니라 GitHub의 것입니다. 언어 통계와 diff 접기를 위해 Linguist만 읽습니다.set.gitattributes:9 *.min.js매크로 확장: minified
minified매크로입니다. 설정하면 [attr] 정의에 있는 내용이 모두 적용됩니다.set.gitattributes:9 *.min.js
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.auto.gitattributes:1 *
scripts/build.sh

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
eol작업 트리에 기록되는 줄 끝(lf 또는 crlf)입니다. text가 unset이면 아무 일도 하지 않습니다.lf.gitattributes:3 *.sh
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.set.gitattributes:3 *.sh같은 속성을 쓴 다른 곳: .gitattributes:1
scripts/build.bat

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
eol작업 트리에 기록되는 줄 끝(lf 또는 crlf)입니다. text가 unset이면 아무 일도 하지 않습니다.crlf.gitattributes:4 *.bat
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.set.gitattributes:4 *.bat같은 속성을 쓴 다른 곳: .gitattributes:1
assets/logo.png

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
binarygit이 읽는 속성이 아니라 -diff -merge -text를 뜻하는 내장 매크로입니다.set.gitattributes:5 *.png
diff값은 diff 드라이버 이름이고(헝크 헤더, textconv), -diff는 모든 diff에서 파일을 바이너리로 취급하게 합니다.unset.gitattributes:5 *.png매크로 확장: binary
merge충돌 시 사용할 병합 드라이버입니다. -merge는 병합을 거부하고 내 쪽을 그대로 둡니다.unset.gitattributes:5 *.png매크로 확장: binary
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.unset.gitattributes:5 *.png매크로 확장: binary같은 속성을 쓴 다른 곳: .gitattributes:1
art/cover.psd

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
diff값은 diff 드라이버 이름이고(헝크 헤더, textconv), -diff는 모든 diff에서 파일을 바이너리로 취급하게 합니다.lfs.gitattributes:6 *.psd
filter저장소와 작업 트리 사이에서 내용을 바꿔 쓰는 clean/smudge 쌍입니다. Git LFS가 이 방식으로 동작합니다.lfs.gitattributes:6 *.psd
merge충돌 시 사용할 병합 드라이버입니다. -merge는 병합을 거부하고 내 쪽을 그대로 둡니다.lfs.gitattributes:6 *.psd
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.unset.gitattributes:6 *.psd같은 속성을 쓴 다른 곳: .gitattributes:1
.github/workflows/ci.yml

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
export-ignoregit archive에서 제외되어 릴리스 압축 파일에는 빠지지만 저장소에는 남습니다.set.gitattributes:11 .github/**
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.auto.gitattributes:1 *
VERSION

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
export-substgit archive가 파일 안의 $Format:...$ 자리표시자를 치환합니다.set.gitattributes:12 VERSION
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.auto.gitattributes:1 *
README.md

참조 순서(가까운 것부터): .gitattributes

속성상태결정한 곳
text저장소 안에서 줄 끝을 정규화할지 정합니다. text=auto는 텍스트로 판별된 파일만 변환합니다.auto.gitattributes:1 *
살펴볼 곳

git이 오류로 보고하는 것은 하나도 없습니다. 보이는 것과 다르게 동작하기 쉬운 줄들입니다.

  • .gitattributes:1 — 매크로와 명시적인 줄이 이 속성을 두고 어긋나며, 나중 쪽만 반영됩니다: text · binary
  • .gitattributes:9 — 쓰였고 맞았지만 여기 모든 경로에서 덮였습니다: diff · minified
  • .gitattributes:9 — 매크로와 명시적인 줄이 이 속성을 두고 어긋나며, 나중 쪽만 반영됩니다: diff · minified

git이 하는 방식 그대로 브라우저에서 계산하며 아무것도 업로드하지 않습니다. 실제 체크아웃에서 git check-attr --all -- <경로>를 실행한 것과 같은 답입니다.

사용법

  1. .gitattributes를 디스크에 있는 그대로 첫 번째 상자에 붙여넣으세요. 주석, 빈 줄, 뒤쪽 공백, 공백이 들어간 따옴표 패턴, 이스케이프한 \!까지 git과 똑같이 읽습니다.
  2. 파일이 여러 개라면 각 파일 앞에 # path: src/.gitattributes 표시 줄을 두세요. 첫 표시 줄 앞의 내용은 루트 파일이며, 매크로는 그곳에만 둘 수 있습니다.
  3. 검사할 경로를 두 번째 상자에 한 줄에 하나씩, 저장소 루트 기준으로 적으세요. 끝에 슬래시는 디렉터리를 뜻할 때만 붙입니다.
  4. 경로별 카드를 읽으세요. 표에는 속성과 상태(set·unset·값·unspecified), 그리고 그것을 정한 파일·줄·패턴이 나오고, 매크로 확장으로 들어온 값에는 거쳐 온 매크로가 함께 표시됩니다.
  5. 아래 지적 패널에서 죽은 패턴, 항상 덮이는 상태, 명시적 설정과 충돌하는 매크로, 엉뚱한 파일에 있는 [attr] 정의를 확인하세요.

자세한 설명

펼치기

.gitattributes의 한 줄은 패턴과 속성 상태로 이루어지는데, "왜 git이 이 파일을 바이너리로 다루지?"의 답은 지금 보고 있는 줄에 있는 경우가 거의 없습니다. 우선순위가 거꾸로 흐르기 때문입니다. 한 파일 안에서는 마지막으로 일치한 줄이 이기고, 한 줄 안에서도 오른쪽에 쓴 상태가 이깁니다. 파일끼리는 트리 아래쪽에 있는 .gitattributes가 루트의 것을 누릅니다. 파일 하나 또는 깊이가 다른 여러 개와 검사할 경로를 붙여넣으면, 경로마다 최종 속성 집합과 그것을 정한 파일·줄 번호·패턴을 그대로 보여줍니다. 실제 체크아웃에서 git check-attr --all -- <경로>가 주는 답과 같으며, 계산은 브라우저 안에서 끝납니다.

나머지 절반의 혼란은 매크로입니다. binary는 git이 읽는 속성이 아니라 -diff -merge -text를 뜻하는 내장 매크로여서, 파일에서 diff를 찾아봐야 아무것도 나오지 않습니다. 직접 만든 [attr]이름 ... 매크로도 같으며, 최상위 파일에 정의했을 때만 인정됩니다. 하위 디렉터리의 정의는 git이 거부하고 그 이름을 평범한 속성으로 남겨 두기 때문에 겉보기에는 동작하는 것처럼 보입니다. 이 도구는 확장으로 들어온 상태마다 거쳐 온 매크로 사슬을 함께 표시합니다.

아래 지적 패널은 그럴듯해 보이지만 아무 일도 하지 않는 줄을 짚어 줍니다. 어떤 경로에도 맞지 않는 패턴, 쓰였지만 매번 덮여 버리는 속성, 서로 다른 답을 내는 두 파일의 text, binary 옆에 모순되는 diff=, git이 거부한 [attr] 정의, 줄 전체를 버리게 만드는 잘못된 속성 이름, 그리고 디렉터리에만 걸리는 끝의 슬래시입니다.

알 수 없는 것도 있습니다. 그 속성이 실제로 쓸모가 있는지는 알려주지 않습니다. diff=swift 드라이버는 설정에 있어야 하고, filter=lfs는 Git LFS가 설치돼 있어야 하며, linguist-*는 GitHub의 것입니다. .git/info/attributes와 core.attributesFile은 저장소 밖이라 여기서는 다루지 않고, text와 eol은 체크아웃이나 add 시점에만 적용되므로 규칙만 바꿔서는 이미 작업 트리에 있는 파일이 달라지지 않습니다.

자주 묻는 질문

-diff를 쓴 적이 없는데 왜 파일이 바이너리로 취급되나요?

binary가 그렇게 말하고 있기 때문입니다. binary는 git이 조회하는 속성이 아니라 -diff -merge -text로 펼쳐지는 내장 매크로여서, 한 줄의 한 단어가 diff와 병합과 줄 끝 정규화를 한꺼번에 끕니다. 파일에서 diff를 검색해도 찾을 수 없는 이유가 이것입니다. 확장은 매크로가 놓인 줄의 우선순위에서 일어나므로 *.png binary 다음에 *.png diff=exif를 쓰면 exif 드라이버가 이기고, 순서를 뒤집으면 diff는 unset이 됩니다. 이 도구는 매크로를 거쳐 들어온 상태마다 그 사슬을 표시하고, 매크로와 명시적인 줄이 같은 속성을 두고 어긋나면 따로 알려 줍니다.

두 패턴이 모두 맞으면 어느 쪽이 이기나요?

언제나 나중 쪽입니다. 첫 일치가 이기는 설정 형식에 익숙한 사람이 가장 많이 틀리는 지점입니다. 한 파일 안에서는 마지막으로 일치한 줄이 정하고, 한 줄이 같은 속성을 두 번 쓰면 더 오른쪽이 정하므로 -text text는 text를 set으로 남깁니다. 파일끼리는 대상 파일에 더 가까운 .gitattributes가 이기므로, 루트의 * -diff는 src/.gitattributes의 *.js diff=javascript 한 줄로 src 아래 전체에서 뒤집힙니다. git은 그 위로 .git/info/attributes를, 아래로 core.attributesFile을 더 읽지만 둘 다 저장소 안에 없어 여기서는 다루지 않습니다.

!attr은 -attr과 무엇이 다른가요?

-attr은 속성을 unset으로 정하는 분명한 답이며 text라면 "이 파일은 절대 정규화하지 않는다"는 뜻입니다. !attr은 성격이 다릅니다. 어떤 규칙도 그 속성을 언급하지 않은 것처럼 unspecified 상태로 되돌립니다. 중요한 점은 그러면서도 자리를 차지한다는 것입니다. 일치하는 !text를 한 번 읽고 나면 같은 파일의 앞선 줄도, 상위의 모든 .gitattributes도 더는 text를 채우지 못합니다. 저장소 전체 기본값에서 한 하위 트리만 도려낼 때 쓰는 도구인 셈입니다. git check-attr --all이 그 속성을 아예 보여주지 않는 이유이기도 합니다. --all은 값이 정해진 속성만 출력하므로 이름을 직접 물어봐야 보입니다.

패턴 문법은 .gitignore와 같나요?

거의 같습니다. 매처가 같아서 슬래시가 없는 패턴은 그 파일 아래 어느 깊이의 파일 이름에나 맞고, 슬래시가 하나라도 있으면 그 파일이 있는 디렉터리에 고정되며, *·?·[abc]는 구분자를 넘지 않고 **만 넘습니다. 다른 점은 둘입니다. 맨 앞의 !는 여기서 부정이 아니라 git이 경고를 내며 줄 전체를 버리는 오류이므로, 느낌표로 시작하는 실제 파일 이름에는 \!가 필요합니다. 그리고 .gitignore에서 규칙을 디렉터리로 제한하던 끝의 슬래시는 여기서 사실상 쓸모가 없습니다. 속성은 파일마다 조회되고 디렉터리에서 상속되지 않으므로, logs/ text는 git이 묻지 않을 대상에 속성을 주는 셈입니다.

text와 eol을 바꿨는데 아무 일도 일어나지 않습니다.

두 속성은 내용이 저장소와 작업 트리 사이를 오갈 때 적용되지, 파일이 그냥 놓여 있을 때 적용되지 않기 때문입니다. 이미 체크아웃된 파일은 지금의 줄 끝을 그대로 유지합니다. 규칙을 고친 뒤에는 git add --renormalize .을 실행하면 추적 중인 모든 파일에 변환을 다시 걸고 실제로 달라진 것만 스테이지합니다. 처음에는 크고 지루한 커밋이 나오는 것이 정상입니다. text=auto는 앞부분의 NUL 바이트를 보고 텍스트라고 판단한 파일만 변환하므로 UTF-16 소스는 바이너리로 취급되어 그대로 남습니다. 또 text가 unset인 동안 eol은 아무 일도 하지 않는데, 이것이 eol=lf 한 줄만 적어 두고 효과가 없는 흔한 이유입니다.

매크로는 어디에 정의해야 하나요?

작업 트리 루트의 최상위 .gitattributes, 또는 .git/info/attributes뿐입니다. 그 밖의 위치에서는 git이 [attr]이름 ... not allowed를 출력하고 정의를 버립니다. 그래도 파일은 정상적으로 파싱되고 그 이름을 쓴 줄은 평범한 사용자 속성으로 계속 동작하므로 겉으로는 아무 문제가 없어 보입니다. 두 가지가 더 걸림돌입니다. 매크로는 set일 때만 펼쳐지므로 -mybin이나 mybin=maybe는 이름 자체의 상태만 기록할 뿐 매크로를 되돌리지 않습니다. 그리고 정의는 최상위 파일 안이라면 사용하는 줄보다 뒤에 있어도 됩니다. 매크로는 경로를 해석하기 전에 모두 모아 두기 때문입니다.

관련 도구