느린 Git 작업 문제 해결: 일반적인 함정과 해결책

상태, 복제, 가져오기, 푸시, 훅, 파일 시스템, 네트워크 및 저장소 크기 원인을 분리하여 느린 Git 명령을 진단합니다.

느린 Git 작업 문제 해결: 일반적인 함정과 해결책

느린 Git은 어떤 명령이 느린지에 따라 원인이 다릅니다. 느린 git status는 일반적으로 로컬 파일 시스템 또는 인덱스 작업 때문입니다. 느린 git fetch는 종종 네트워크, 원격 크기 또는 협상 때문입니다. 느린 git checkout은 파일 수, 바이러스 백신 검사, 스파스 체크아웃 문제 또는 생성된 파일 때문일 수 있습니다. 느린 git push는 큰 객체, 훅, 압축 또는 원격 서버 때문일 수 있습니다.

따라서 첫 번째 해결책은 git gc가 아닙니다. 첫 번째 해결책은 정확한 작업을 측정하는 것입니다.

macOS 또는 Linux에서:

time git status
time git fetch --prune
time git checkout main

PowerShell에서:

Measure-Command { git status }

명령을 두 번 실행하십시오. 첫 번째 실행은 OS 캐시가 차가워서 더 느릴 수 있습니다. 첫 번째 git status가 10초 걸리고 두 번째가 1초 걸리면 디스크 캐시 동작을 살펴볼 수 있습니다. 둘 다 느리면 계속 조사하십시오.

Git에는 시간이 어디에 소요되는지 보여주는 내장 추적 기능이 있습니다:

GIT_TRACE=1 git status
GIT_TRACE_PERFORMANCE=1 git status
GIT_TRACE_PACKET=1 GIT_TRACE=1 git fetch

GIT_TRACE_PACKET은 시끄럽지만, 프로토콜 협상 중에 가져오기 또는 푸시가 중단될 때 유용합니다. 개인 저장소 URL이나 토큰이 포함된 추적 출력을 공개 티켓에 붙여넣지 마십시오.

git status가 느린 경우

git status는 인덱스와 작업 트리를 확인합니다. 저장소에 파일이 엄청나게 많거나, 작업 트리가 느린 파일 시스템에 있거나, 파일 메타데이터를 읽는 데 비용이 많이 들거나, 다른 프로그램이 Git이 접촉하는 모든 파일을 검사할 때 느려집니다.

기본부터 시작하십시오:

git status --short
git config --show-origin --get core.fsmonitor
git config --show-origin --get core.untrackedCache
git config --show-origin --get core.preloadIndex

대규모 작업 트리의 경우 이러한 설정이 많은 시스템에서 도움이 될 수 있습니다:

git config core.untrackedCache true
git config core.preloadIndex true

먼저 로컬 구성을 사용하여 저장소별로 테스트할 수 있습니다. 도움이 되면 나중에 전역으로 설정하십시오.

Git의 내장 파일 시스템 모니터는 지원되는 플랫폼 및 Git 버전에서 전체 검사를 피하여 상태를 빠르게 할 수 있습니다:

git config core.fsmonitor true

활성화한 후 상태가 올바르지 않거나 이상해지면 끄고 Git을 업데이트한 후 다시 시도하십시오:

git config --unset core.fsmonitor

추적되지 않은 파일은 숨겨진 문제일 수 있습니다. 빌드 출력, 종속성 디렉터리, 생성된 보고서 및 로컬 로그는 일반적으로 무시해야 합니다. Git이 무엇을 검사하고 있는지 확인하십시오:

git status --untracked-files=all --short | head -100

node_modules/, dist/, .venv/, target/ 또는 이와 유사한 생성된 디렉터리가 보이면 .gitignore에 올바른 패턴을 추가하십시오. 상태를 빠르게 만들기 위해 소스 파일을 무시하지 마십시오. 버전 관리되어서는 안 되는 파일만 무시하십시오.

Windows에서 실시간 바이러스 백신 검사는 Git이 느리게 느껴지는 일반적인 이유입니다. Git은 .git 및 작업 트리 내부의 많은 작은 파일을 읽으며 보안 소프트웨어가 각 액세스를 검사할 수 있습니다. 조직에서 허용하는 경우 신뢰할 수 있는 개발 작업 영역을 실시간 검사에서 제외하십시오. 신뢰할 수 없는 코드를 실행하는 디렉터리는 제외하지 마십시오.

또한 활성 저장소를 OneDrive, Dropbox 또는 iCloud Drive와 같은 클라우드 동기화 폴더에 두지 마십시오. 동기화 도구는 파일을 잠그고, 메타데이터를 다시 쓰고, Git의 자체 파일 작업과 경쟁할 수 있습니다.

복제 또는 가져오기가 느린 경우

느린 복제는 큰 기록, 많은 큰 blob, 느린 원격 또는 높은 지연 시간의 네트워크 경로를 의미할 수 있습니다. 복제 후 저장소 크기를 측정하십시오:

git count-objects -vH
du -sh .git 2>/dev/null

CI 작업 및 임시 환경의 경우 기록이 필요하지 않을 때 얕은 복제를 사용하십시오:

git clone --depth 1 <url>

브랜치 빌드의 경우:

git clone --depth 1 --branch main <url>

얕은 복제는 모든 워크플로에 이상적이지 않습니다. 기록, 태그, 병합 기준 또는 버전 계산이 필요한 명령은 실패하거나 불완전한 답변을 생성할 수 있습니다. CI에서는 종종 허용됩니다. 개발자 머신에서는 실망스러울 수 있습니다.

부분 복제는 저장소 기록이 필요하지만 파일 blob을 느리게 다운로드할 수 있을 때 유용합니다:

git clone --filter=blob:none <url>

이것은 부분 복제를 잘 지원하는 최신 Git 서버에서 가장 잘 작동합니다. 공식 팀 권장 사항으로 만들기 전에 호스트에서 테스트하십시오.

모노레포의 한 부분만 필요한 경우 일반 또는 부분 복제와 함께 스파스 체크아웃을 결합하십시오:

git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/api shared/lib

스파스 체크아웃은 작업 트리 크기를 줄입니다. 모든 Git 작업을 마술처럼 저렴하게 만들지는 않지만 파일 수가 주요 문제일 때 도움이 됩니다.

삭제된 원격 브랜치가 많은 가져오기의 경우 오래된 참조를 정리하십시오:

git fetch --prune

기본값으로 설정하려면:

git config --global fetch.prune true

푸시가 느린 경우

푸시 속도는 보내는 새 객체 데이터의 양, 로컬 패킹 비용, 훅 실행 여부 및 원격이 팩을 수락하는 속도에 따라 달라집니다.

실수로 큰 파일을 커밋했는지 확인하십시오:

git rev-list --objects --all | sort -k 2 | tail

해당 명령은 크기를 표시하지 않기 때문에 조잡합니다. 더 깊이 조사하려면 사용 가능한 경우 git-sizer 또는 git filter-repo 분석 명령과 같은 도구를 사용하십시오. 실용적인 요점은 간단합니다. 비디오, 데이터베이스 덤프, 아카이브 또는 빌드 아티팩트가 기록에 들어가면 기록을 다시 쓰거나 프로젝트가 더 나은 저장소 패턴으로 이동할 때까지 모든 복제가 비용을 지불할 수 있습니다.

Git LFS는 프로젝트에 속하지만 일반 Git blob으로 존재해서는 안 되는 큰 바이너리 자산에 대한 일반적인 답변입니다:

git lfs install
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes

Git LFS는 큰 파일이 기록에 들어가기 전에 채택될 때 가장 도움이 됩니다. 기존 기록을 마이그레이션하는 것은 가능하지만 커밋을 다시 쓰고 팀 조정이 필요합니다.

http.postBuffer를 높이라는 오래된 조언에 주의하십시오. 푸시 문제에 대해 자주 제안되지만 최신 Git의 일반적인 느림을 거의 해결하지 못합니다. 특정 HTTP 오류로 푸시가 실패하면 임의의 버퍼 설정을 적용하기 전에 정확한 오류, 프록시, 서버 제한 및 Git 버전을 확인하십시오.

저장소 유지 관리: git gc, 커밋 그래프 및 Repack

Git은 객체를 팩 파일에 저장합니다. 시간이 지남에 따라 로컬 저장소는 느슨한 객체와 비효율적인 팩을 축적할 수 있습니다. Git은 많은 워크플로에서 자동으로 유지 관리를 실행하지만 수동 유지 관리는 오래되었거나 사용량이 많은 저장소에 여전히 도움이 될 수 있습니다.

안전한 유지 관리 명령으로 시작하십시오:

git maintenance run

또는 이전 명령:

git gc

git gc --prune=now를 습관적인 첫 번째 조치로 사용하지 마십시오. 즉시 정리하면 일정 기간 동안 복구할 수 있는 도달할 수 없는 객체가 제거됩니다. 자신이 무엇을 하고 있는지 알 때는 괜찮지만 무해한 속도 버튼은 아닙니다.

기록이 큰 저장소의 경우 커밋 그래프는 로그, merge-base 및 가져오기 협상과 같은 명령에서 사용하는 기록 탐색을 개선할 수 있습니다:

git commit-graph write --reachable

최신 Git 유지 관리는 이를 자동으로 처리할 수 있습니다. 버전을 확인하십시오:

git --version

Git을 최신 상태로 유지하는 것은 가장 극적이지 않은 성능 수정 중 하나입니다. 최신 버전은 정기적으로 스파스 체크아웃, 부분 복제, 파일 시스템 모니터링 및 유지 관리 동작을 개선합니다.

대규모 저장소 및 모노레포

저장소가 진정으로 크기 때문에 느린 경우 로컬 조정만으로는 한계가 있습니다. 워크플로 변경이 필요합니다.

바이너리가 많은 저장소의 경우 큰 자산을 Git LFS 또는 아티팩트 저장소로 이동하십시오. 생성된 파일의 경우 다시 빌드할 수 있는 출력을 커밋하지 마십시오. 모노레포의 경우 스파스 체크아웃과 프로젝트 경계를 이해하는 빌드 도구를 사용하십시오. CI의 경우 작업에 전체 기록이 필요하지 않으면 전체 깊이 복제를 피하십시오.

하나의 서비스에서 작업하는 개발자를 위한 유용한 모노레포 설정은 다음과 같습니다:

git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/billing packages/common

간단한 테스트 작업을 위한 유용한 CI 설정은 다음과 같습니다:

git fetch --depth 50 origin main

올바른 깊이는 작업에 따라 다릅니다. 버전 관리 도구가 몇 달 전의 태그를 사용하는 경우 깊이 1이 중단됩니다.

훅 및 외부 도구

Git이 느린 부분이 아닐 수 있습니다. pre-commit 훅은 포맷터, 린터, 테스트, 비밀 검사 또는 종속성 검사를 실행할 수 있습니다. post-checkout 훅은 파일을 다시 빌드할 수 있습니다. 자격 증명 도우미는 키체인 잠금을 해제하려고 시도하는 동안 일시 중지될 수 있습니다.

훅 확인:

git config --get core.hooksPath
ls -l .git/hooks .githooks 2>/dev/null

위험을 이해하는 경우에만 훅을 비활성화한 상태로 일시적으로 비교하십시오:

git commit --no-verify

커밋이 아닌 명령의 경우 기본 체크아웃에서 팀 훅을 삭제하는 대신 저장소의 테스트 복사본에서 훅을 이동하거나 비활성화하십시오.

IDE가 Git을 느리게 만들지만 터미널이 빠른 경우 IDE Git 통합을 검사하십시오. 일부 도구는 git status를 반복적으로 실행하고, 추적되지 않은 파일을 검사하거나, 백그라운드에서 브랜치 상태를 새로 고칩니다.

네트워크 및 원격 확인

원격 작업의 경우 Git을 네트워크 경로와 분리하십시오. 시도:

GIT_TRACE_PERFORMANCE=1 git ls-remote <url>
GIT_TRACE_PERFORMANCE=1 git fetch

git ls-remote가 느리면 많은 저장소 데이터가 전송되기 전에 지연이 발생합니다. DNS, 프록시, VPN, SSH 인증, 원격 가용성 또는 자격 증명 프롬프트를 생각하십시오. ls-remote는 빠르지만 가져오기가 느리면 저장소 데이터 크기 및 협상 가능성이 더 높습니다.

SSH 원격의 경우 SSH를 직접 테스트하십시오:

ssh -T [email protected]

실제 Git 호스트를 사용하십시오. HTTPS 원격의 경우 자격 증명 관리자 프롬프트가 GUI 창 뒤에 숨겨질 수 있습니다. 중단된 가져오기는 인증을 기다리고 있을 수 있습니다.

짧은 의사 결정 트리

git status가 느리면 추적되지 않은 파일, 생성된 디렉터리, 바이러스 백신, 클라우드 동기화 폴더, 파일 시스템 모니터 및 인덱스 설정을 검사하십시오.

복제가 느리면 얕은 복제, 부분 복제, 스파스 체크아웃, Git LFS 및 저장소 기록에 큰 blob이 포함되어 있는지 고려하십시오.

가져오기가 느리면 오래된 참조를 정리하고, Git을 업데이트하고, 네트워크 추적을 검사하고, 원격에 많은 브랜치 또는 태그가 있는지 확인하십시오.

푸시가 느리면 큰 새 객체, 느린 훅, 서버 측 검사 및 네트워크 또는 프록시 문제를 찾으십시오.

모든 Git 명령이 느리면 디스크 상태, 여유 공간, 보안 소프트웨어, Git 버전 및 저장소가 네트워크 마운트에 있는지 확인하십시오.

가장 좋은 수정은 측정된 병목 현상과 일치하는 것입니다. 모든 제안이 한 번에 적용되면 Git 성능 작업이 지저분해집니다. 한 번에 하나씩 변경하고 다시 측정하고 실제로 도움이 되는 경우에만 변경을 유지하십시오.

팀 수준 수정이 개인 조정보다 낫습니다.

한 명의 개발자만 Git이 느리면 로컬 설정과 머신 상태가 시작하기 좋은 곳입니다. 모든 사람이 Git이 느리면 저장소에 주의가 필요합니다. 개인 조정은 잠시 동안 고통을 숨기겠지만 새 개발자와 CI 작업은 계속 비용을 지불할 것입니다.

커밋되어서는 안 되는 큰 객체, .gitignore에 속하는 생성된 디렉터리 및 불필요한 기록을 유지하는 오래된 브랜치를 찾으십시오. 기록을 다시 쓰기 전에 팀과 상의하십시오. 기록 재작성은 모든 복제와 모든 열린 브랜치에 영향을 미칩니다. 가치가 있을 수 있지만 조정이 필요합니다.

합법적인 대규모 자산이 있는 저장소의 경우 기억에 의존하는 대신 정책을 정의하십시오. 예: Git의 소스 코드, Git LFS의 디자인 내보내기, 아티팩트 저장소의 빌드 아티팩트, 제어된 저장소의 데이터베이스 덤프 및 무시된 로컬 스크래치 파일. 이러한 규칙을 .gitattributes.gitignore에 넣어 Git이 저장소의 형태를 적용할 수 있도록 하십시오.

CI는 자체 검토가 필요합니다. 많은 파이프라인이 몇 년 전에 복사된 기본값이었기 때문에 전체 기록을 복제합니다. 작업이 단위 테스트만 실행하는 경우 모든 태그와 모든 브랜치가 필요하지 않을 수 있습니다. 작업이 릴리스를 빌드하는 경우 태그가 필요할 수 있지만 모노레포의 모든 blob이 필요하지는 않습니다. 저장소 비용을 확인할 수 있도록 복제 시간을 빌드 시간과 별도로 측정하십시오.

간단한 CI 감사는 다음과 같이 질문합니다:

이 작업에 전체 기록이 필요합니까?
태그가 필요합니까?
모든 서브모듈이 필요합니까?
모노레포의 모든 디렉터리가 필요합니까?
읽지 않는 LFS 파일을 가져옵니까?

정직하게 대답하면 모호한 Git 옵션을 조정하는 것보다 더 많은 시간을 절약하는 경우가 많습니다.

마지막으로 프로젝트에 권장되는 복제 명령을 문서화하십시오. 새 개발자가 부분 복제 및 스파스 체크아웃을 사용해야 하는 경우 README에 명시하십시오. 체크아웃 전에 Git LFS가 필요한 경우에도 명시하십시오. 한 명의 시니어 개발자의 셸 기록에만 존재하는 성능 지침은 다음 사람에게 도움이 되지 않습니다.