program story

Git의 팩 파일은 스냅 샷이 아닌 델타입니까?

inputbox 2020. 11. 25. 07:56
반응형

Git의 팩 파일은 스냅 샷이 아닌 델타입니까?


Git과 대부분의 다른 버전 제어 시스템의 주요 차이점 중 하나는 다른 것들이 커밋을 일련의 델타 (한 커밋과 다음 커밋 간의 변경 집합)로 저장하는 경향이 있다는 것입니다. 이것은 커밋에 대해 저장할 수있는 정보의 양이 가장 적기 때문에 논리적으로 보입니다. 그러나 커밋 기록이 길어질수록 개정 범위를 비교하는 데 더 많은 계산이 필요합니다.

반대로 Git은 각 개정에 전체 프로젝트의 전체 스냅 샷을 저장합니다 . 이것이 커밋 할 때마다 리포지토리 크기가 크게 증가하지 않는 이유는 프로젝트의 각 파일이 해당 콘텐츠의 해시 이름으로 명명 된 Git 하위 디렉터리에 파일로 저장되기 때문입니다. 따라서 내용이 변경되지 않은 경우 해시가 변경되지 않았으며 커밋은 동일한 파일을 가리 킵니다. 그리고 다른 최적화도 있습니다.

내가 팩 파일에 대한 정보를 우연히 발견하기 전까지는이 모든 것이 나에게 의미가 있었다 .

그 공간을 절약하기 위해 Git은 packfile을 사용합니다. 이것은 Git이 비슷한 파일에 대한 포인터와 함께 두 번째 파일에서 변경된 부분 만 저장하는 형식입니다.

이것은 기본적으로 델타 저장으로 돌아 가지 않습니까? 그렇지 않다면 어떻게 다른가요? 이렇게하면 Git이 다른 버전 제어 시스템과 동일한 문제를 겪는 것을 어떻게 방지 할 수 있습니까?

예를 들어 Subversion은 델타를 사용하고 50 개 버전을 롤백한다는 것은 50 개의 diff를 실행 취소하는 것을 의미하지만 Git에서는 적절한 스냅 샷 만 가져올 수 있습니다. git이 packfiles에 50 개의 diff를 저장하지 않는 한 ... "적은 수의 델타 후에는 완전히 새로운 스냅 샷을 저장합니다"라는 메커니즘이있어 변경 세트가 너무 많이 쌓이지 않는가? Git은 델타의 단점을 어떻게 피할 수 있습니까?


요약 :
Git의 팩 파일은 디스크 캐시를 효과적으로 사용하고 일반적인 명령과 최근에 참조 된 객체를 읽기위한 "좋은"액세스 패턴을 제공하도록 신중하게 구성되었습니다.


망할 놈의 팩 파일 형식 (참조 매우 유연 문서 / 기술 / 팩 - FORMAT.TXT을 , 또는 Packfile힘내 커뮤니티 도서 ). 팩 파일은 두 가지 주요 방법으로 개체를 저장합니다. "비준수"(원시 개체 데이터를 가져와 압축 압축) 또는 "삭제"(다른 개체에 대해 델타를 형성 한 다음 결과 델타 데이터 압축을 풉니 다). 팩에 저장된 객체는 임의의 순서로 될 수 있으며 (반드시 객체 유형, 객체 이름 또는 기타 속성별로 정렬 할 필요가 없음) 동일한 유형의 다른 적합한 객체에 대해 델타 화 된 객체를 만들 수 있습니다.

Git의 pack-objects 명령은 몇 가지 휴리스틱사용 하여 공통 명령 에 대한 우수한 참조 위치 를 제공 합니다. 이러한 휴리스틱은 델 티화 된 개체에 대한 기본 개체 선택과 개체 순서를 모두 제어합니다. 각 메커니즘은 대부분 독립적이지만 몇 가지 목표를 공유합니다.

Git은 델타 압축 객체의 긴 체인을 형성하지만 휴리스틱은 "오래된"객체 만 긴 체인의 끝에 있도록합니다. 델타 기본 캐시 ( core.deltaBaseCacheLimit구성 변수에 의해 크기가 제어 됨 )는 자동으로 사용되며 많은 수의 객체를 읽어야하는 명령에 필요한 "재 구축"횟수를 크게 줄일 수 있습니다 (예 :) git log -p.

델타 압축 휴리스틱

일반적인 Git 저장소는 매우 많은 수의 개체를 저장하므로 가장 작은 델타 표현을 생성 할 쌍 (및 체인)을 찾기 위해 모두를 합리적으로 비교할 수 없습니다.

델타베이스 선택 휴리스틱은 파일 이름과 크기가 비슷한 객체에서 좋은 델타베이스를 찾을 수 있다는 생각을 기반으로합니다. 각 유형의 객체는 개별적으로 처리됩니다 (즉, 한 유형의 객체는 다른 유형의 객체에 대한 델타 기준으로 사용되지 않습니다).

델타 기본 선택을 위해 객체는 (주로) 파일 이름과 크기별로 정렬됩니다. 이 정렬 된 목록의 창을 사용하여 잠재적 인 델타 기준으로 간주되는 개체 수를 제한합니다. 창에있는 개체 중 개체에 대해 "충분히 좋은" 1 델타 표현이 없으면 개체가 델타 압축되지 않습니다.

창의 크기는의 --window=옵션 git pack-objects또는 pack.window구성 변수에 의해 제어됩니다 . 델타 체인의 최대 깊이는의 --depth=옵션 git pack-objects또는 pack.depth구성 변수 로 제어됩니다 . --aggressive의 옵션을 git gc크게 창의 크기와 최대 깊이 모두가 작은 팩 파일을 만들려고하는 확대합니다.

파일 이름 정렬은 동일한 이름 (또는 적어도 비슷한 끝 (예 :) .c) 을 가진 항목의 개체를 함께 묶습니다 . 크기 정렬은 데이터를 제거하는 델타가 데이터를 추가하는 델타보다 선호되도록 (제거 델타는 더 짧은 표현을 갖기 때문에) 가장 큰 것부터 가장 작은 것까지 정렬되어 이전의 더 큰 객체 (일반적으로 더 최신)가 일반 압축으로 표현되는 경향이 있습니다.

1 "충분히 좋음"으로 인정되는 것은 해당 객체의 크기와 잠재적 인 델타 기준 및 결과 델타 체인의 깊이에 따라 다릅니다.

객체 순서 휴리스틱

개체는 "가장 최근에 참조 된"순서로 팩 파일에 저장됩니다. 가장 최근의 역사를 재구성하는 데 필요한 개체는 팩의 앞부분에 배치되고 서로 가깝게 배치됩니다. 이것은 일반적으로 OS 디스크 캐시에서 잘 작동합니다.

모든 커밋 객체는 커밋 날짜 (가장 최근 것부터)별로 정렬되고 함께 저장됩니다. 이 배치 및 순서는 히스토리 그래프를 살펴보고 기본 커밋 정보 (예 :)를 추출하는 데 필요한 디스크 액세스를 최적화합니다 git log.

트리 및 Blob 개체는 첫 번째 저장된 (가장 최근) 커밋의 트리부터 저장됩니다. 각 트리는 깊이 우선 방식으로 처리되어 아직 저장되지 않은 모든 객체를 저장합니다. 이렇게하면 가장 최근 커밋을 재구성하는 데 필요한 모든 트리와 블롭이 한 곳에 모입니다. 아직 저장되지 않았지만 이후 커밋에 필요한 모든 트리 및 Blob은 정렬 된 커밋 순서로 다음에 저장됩니다.

최종 객체 순서는 델타 표현을 위해 객체가 선택되고 해당 기본 객체가 아직 저장되지 않은 경우 기본 객체가 델타 화 된 객체 자체 바로 앞에 저장된다는 점에서 델타 기본 선택의 영향을 약간받습니다. 이렇게하면 나중에 팩 파일에 "자연스럽게"저장되었을 기본 개체를 읽는 데 필요한 비선형 액세스로 인한 디스크 캐시 누락 가능성을 방지 할 수 있습니다.


팩 파일에서 델타 스토리지를 사용하는 것은 구현 세부 사항 일뿐입니다. 해당 수준에서 Git은 변경된 내용이 한 개정판에서 다음 개정판으로 변경된 이유 또는 방법을 알지 못합니다. 오히려 Blob B가 이러한 변경 사항 C를 제외하고 Blob A와 매우 유사하다는 것을 알고 있습니다. 따라서 Blob A를 저장하고 C 만 변경합니다. (그렇게 선택하면 blob A와 blob B를 저장하도록 선택할 수도 있습니다).

팩 파일에서 개체를 검색 할 때 델타 저장소는 호출자에게 노출되지 않습니다. 호출자는 여전히 완전한 Blob을 볼 수 있습니다. 따라서 Git은 델타 스토리지 최적화없이 항상 동일한 방식으로 작동합니다.


" Git의 얇은 팩은 무엇입니까? " 에서 언급했듯이

Git은 packfiles에서만 deltification을 수행합니다.

팩 파일에 사용되는 델타 인코딩은 " Is the git binary diff algorithm (delta storage) standardized? "에 자세히 설명되어 있습니다.
" 언제, 어떻게 git이 저장을 위해 델타를 사용합니까? "를 참조하십시오.

core.deltaBaseCacheLimit팩 파일의 기본 크기를 제어 하는 구성은 곧 Git 2.0.x / 2.1 (2014 년 3 분기)의 경우 16MB에서 96MB로 증가 할 것입니다.

David Kastrup의 commit 4874f54 참조 (2014 년 5 월) :

core.deltaBaseCacheLimit를 96m로 범프

기본값 인 16m는 큰 파일과 결합 된 큰 델타 체인에 심각한 스 래싱을 유발합니다.

다음은 몇 가지 벤치 마크 (의 pu 변형 git blame)입니다.

time git blame -C src/xdisp.c >/dev/null

git gc --aggressiveSSD 드라이브에있는 (v1.9, 결과적으로 창 크기가 250)로 재 포장 된 Emacs 저장 소용.
문제의 파일에는 약 30000 줄, 1Mb 크기 및 약 2500 커밋 기록이 있습니다.

16m (previous default):
  real  3m33.936s
  user  2m15.396s
  sys   1m17.352s

96m:
  real  2m5.668s
  user  1m50.784s
  sys   0m14.288s

참고 URL : https://stackoverflow.com/questions/5176225/are-gits-pack-files-deltas-rather-than-snapshots

반응형