파일 속성
.gitattributes는 git에서 가치가 가장 높으면서도 거의 아무도 쓰지 않는
파일이에요. 저장소가 자기 내용물에 대해 git에게 가르치는 방법이거든요. 어떤
파일이 바이너리인지, 어떤 파일이 충돌하는 대신 이어 붙여야 하는지, 어떤 파일이
아카이브에는 절대 들어가지 않는지, 모두가 어떤 줄바꿈 문자를 받게 되는지.
중요한 점: 이 파일은 커밋돼요. 여러분이 추가한 규칙 하나가 클론하는 모든 사람에게, 모든 OS에서, 영원히 문제를 해결해 줘요. 자기 설정에 넣은 항목이 자기에게만 문제를 풀어 주고 동료들은 알아서 고생하며 발견하게 두는 것과는 다르죠.
⌘K → 파일 속성.

각 규칙이 하는 일
| 속성 | 해결하는 문제 |
|---|---|
text=auto eol=lf |
누가 체크아웃했느냐에 따라 뒤바뀌는 줄바꿈 문자 |
binary |
git이 PSD, DOCX, 컴파일된 자산의 차이를 내거나 3-way 머지하려 드는 것 |
merge=union |
모두가 덧붙이고, 모두가 충돌하는 변경 기록 |
-merge |
3-way 머지가 말이 안 되는 결과를 내는 파일 — 잠금 파일, 생성된 코드 |
export-ignore |
릴리스 tarball 안에 실려 나가는 CI 설정과 픽스처 |
diff=<driver> |
변환기만 있으면 읽을 수 있는 형식인데도 읽을 수 없는 차이 |
filter=lfs |
LFS로 저장되는 큰 파일 |
linguist-vendored |
언어 통계에서 여러분 것으로 계산되는 벤더링된 코드 |
binary는 -diff -merge -text의 줄임말이에요. "이 파일에 대해서는 추측을
그만하라"는 세 가지 대답을 한 단어에 담은 것이죠.
편집하기
프리셋은 패턴과 그 속성을 채워 넣어요. 추가하기 전에 패턴을 손보세요 —
CHANGELOG.md는 제안이지, 여러분 프로젝트에 대한 규칙이 아니에요.
편집은 외과적이에요. 이미 규칙이 있는 패턴에 규칙을 추가하면, 나중에 나온다는 이유로 이기는 두 번째 규칙을 덧붙이는 대신 그 줄을 제자리에서 다시 써요. 파일 안의 주석은 손대지 않고 살아남아요. 규칙 옆에 적힌 "왜"가 보통 규칙 자체보다 값어치가 크기 때문이에요.
저장할 때마다 평범한 Gitcito 동작이 일어나요. 토스트가 뜨고, 되돌리기가 파일을 원래 상태 그대로 복원해요.
저장소에는 속성 파일이 여러 개 있을 수 있어요. 루트에 하나, 어느
하위 디렉터리에나 하나씩, 그리고 절대 커밋되지 않고 여러분 컴퓨터에서만
적용되는 비공개 .git/info/attributes — 프로젝트가 아니라 여러분에 관한 규칙을
두기에 알맞은 자리예요. Gitcito는 그것들을 모두 나열하고 어느 것이 어느
것인지 알려 줘요.
어떤 경로에 무엇이 적용되나요?
규칙은 여러 파일에서 오고, 더 구체적인 쪽이 이기며, 그것들을 읽어서 답을 알아내는
일은 짐작에 가까워요. **어떤 경로에 무엇이 적용되나요?**는 git check-attr을
실행해서 git 자신이 내린 결론을 보여 줘요. 유일하게 의미 있는 답이죠.
차이 드라이버: Word 문서를 읽을 수 있게 만들기
.docx는 zip이에요. .pdf는 압축된 객체 그래프예요. git은 그것들을 있는
그대로 — 잡음으로 — 비교하므로, 문서 자체는 읽을 수 있는데도 문서의 히스토리는
읽을 수 없어요.
차이 드라이버는 textconv로 이 문제를 해결해요. 차이를 낼 때만 파일을
텍스트로 바꿔 주는 명령이죠. 작업 트리에 있는 파일은 건드리지 않아요. git은
변환된 텍스트를 비교할 뿐이에요.
두 부분이 있고, 둘 다 필요해요.
- git 설정의
diff.<name>.textconv— 변환 명령. .gitattributes의*.docx diff=<name>— 어떤 파일에 적용할지.
여기 있는 버튼들이 둘을 한 번에 해 줘요. Word, Excel, JSON, .strings은 Gitcito가
변환기를 직접 동봉해요 — 미리보기가 쓰는 것과 같은 문서 파싱을 앱 안의
작은 gitcito-textconv 명령으로 내놓은 것이라, 이 넷은 아무것도 설치하지
않아도 동작해요. 나머지는 여전히 PATH에 진짜 도구가 필요해요. Gitcito는 이를
확인해서, 첫 번째 차이 비교에서 실패할 드라이버를 적는 대신 없는 것을 흐리게
표시해요.
| 드라이버 | 필요한 것 | 얻는 것 |
|---|---|---|
word |
없음 — Gitcito에 동봉 | .docx의 문장 단위 차이 |
excel |
없음 — Gitcito에 동봉 | .xlsx/.xls의 행 단위 차이 (시트별 CSV) |
json |
없음 — Gitcito에 동봉 | 키 순서가 정렬된, 안정적인 JSON 차이 |
strings |
없음 — Gitcito에 포함 | git이 바이너리라고 부르는 UTF-16 .strings의 줄 단위 diff |
pdf |
pdftotext (poppler) |
.pdf의 텍스트 차이 |
exif |
exiftool |
픽셀이 불투명할 때, 이미지에 대해 무엇이 바뀌었는지 |
iOS 프로젝트를 실제로 무는 것
Localizable.strings는 Xcode 역사의 대부분 동안 UTF-16이고, UTF-16은 NUL
바이트투성이라 git이 바이너리로 취급해 아무것도 보여주지 않습니다.
diff --git a/Localizable.strings b/Localizable.strings
Binary files a/Localizable.strings and b/Localizable.strings differ
누가 어떤 문자열을 옮겼는지 보는 게 가장 중요한 파일이 바로 이것인데요.
strings 드라이버는 diff를 위해서만 디코딩합니다 — 바이트 순서 표시를
가정하지 않고 읽으므로, 최신 UTF-8 .strings는 깨지지 않고 그대로 지나갑니다.
String Catalog(.xcstrings, Xcode 15 이상)는 JSON이라 json 드라이버가
처리합니다. 키를 정렬하므로 맨 위에 추가한 번역이 diff에서 파일 전체를 다시
쓴 것처럼 보이지 않습니다.
동봉된 변환기의 한계를 솔직히 말하면: .doc(옛 이진 Word 형식)은 이해하지
못하고 .docx만 돼요. PDF는 다루지 않아요 — Gitcito는 PDF를 브라우저의
뷰어로 미리 보여 주기 때문에 재사용할 텍스트 추출기가 없어요. 그리고 문서의
차이를 볼 때마다 변환기의 짧은 시작 비용을 치러요.
git config diff.<name>.cachetextconv true를 설정하면 git이 출력을 blob마다
캐시해요.
변환기 쪽은 저장소가 아니라 여러분의 설정에 살아요. git은 클론이 건네주는
명령을 실행하지 않으니까요. 지켜 둘 만한 보안 특성이에요. 동봉된 드라이버들도
여러분의 Gitcito 설치 경로를 가리키기 때문에, 클론한 동료는 diff=word
규칙은 받지만, 자기 변환기(Gitcito든 다른 것이든)를 연결하기 전까지는 예전의
읽을 수 없는 차이를 보게 돼요. README에 그 사실을 적어 두세요.
clean/smudge 필터 — 먼저 드라이런부터
필터는 저장소에 드나드는 내용을 그 길목에서 다시 써요. clean은
스테이징할 때(작업 트리 → 저장소), smudge는 체크아웃할 때(저장소 → 작업
트리) 실행돼요. git-lfs가 이 방식으로 동작하고, 팀이 커밋되는 내용에서 자격
증명이나 생성된 잡음을 걷어내는 방법이기도 해요.
동시에 .gitattributes가 가리킬 수 있는 것 중 가장 위험한 것이기도 해요.
필터는 일치하는 모든 파일의 모든 체크아웃마다 실행되고, 잘못된 필터는
작업 트리를 조용히 망가뜨려요. 그래서 Gitcito는 여기서 그냥 텍스트 상자가
되기를 거부해요. 필터 설정은 저장소 안에서 실제로 일치하는 파일들을 상대로 한
드라이런을 거쳐요:
clean명령은 일치하는 각 파일(최대 다섯 개)의 복사본 위에서 실행돼요 — 저장소에도 그 설정에도 아무것도 손대지 않아요.smudge명령이 주어졌다면clean을 거친 출력 위에서 실행되고, 그 결과를 원본과 바이트 단위로 비교해요 — 왕복 검사예요. 왕복이 되지 않는 필터는, 체크아웃해도 원래 있던 것이 복원되지 않는다는 뜻이에요.- 저장 버튼은 정확히 지금 저장하려는 그 값으로 드라이런을 마친 뒤에야 활성화돼요. 실패한 드라이런 — 명령 오류, 일치 없음, 왕복 결과 불일치 — 상태로도 저장할 수는 있지만, 무엇을 잃을 수 있는지 말해 주는 명시적인 경고를 거쳐야만 해요.
저장하면 filter.<name>.clean/smudge가 로컬 git 설정에, filter=<name>
규칙이 속성 파일에 적히고, 설정이 이전에 담고 있던 내용을 복원하는 되돌리기
항목이 남아요. required 토글은 filter.<name>.required를 설정해서, 필터가
고장 났을 때 git이 파일을 조용히 통과시키는 대신 작업을 실패하게 만들어요.
한계를 솔직히 말하면: 드라이런은 일치하는 파일을 최대 다섯 개, 각각 5 MB
까지만 골라 보고, 명령마다 10초의 제한 시간이 있어요 — 표본에서는 얌전하던
필터가 표본이 놓친 파일에서는 딴짓을 할 수 있어요. 명령은 여러분의 설정에
살기 때문에, 클론한 동료는 filter=<name> 규칙은 받지만 명령은 받지 못해요.
명령이 없으면(그리고 required가 없으면) 그 파일들은 바뀌지 않은 채 그대로
통과해요.
알아 둘 만한 한계
text=auto는 무엇이 커밋되는지를 바꿔요. 들어오는 길에 줄바꿈 문자를 정규화하니까요. 이미 존재하는 저장소라면, 추가한 다음 의도적으로git add --renormalize .를 실행하고 그것만을 위한 커밋 하나로 남기세요.- 속성은 소급 적용되지 않아요. 오늘 파일을
binary로 표시한다고 해서 그 파일의 과거 차이가 저장된 방식이 바뀌지는 않아요. 지금부터 git이 그 파일을 어떻게 다루는지가 바뀔 뿐이에요. - 규칙은 그 파일이 보이는 곳에서만 효력이 있어요.
design/.gitattributes의 규칙은src/에 대해서는 아무 말도 하지 않아요. - Gitcito는 파일을 통째로 쓰므로 손으로 서식을 맞춘 파일은 그 서식 그대로
돌아와요. 다만 Gitcito가 다시 쓴 규칙은 git의 표준 형태인
pattern attr attr간격으로 재정렬돼요.
함께 보기: LFS와 스파스 체크아웃 · 번들과 아카이브 · 머지 옵션 · 훅