수정 사항 처리
이 페이지에는 수정 사항에 대한 개발자 문서가 포함되어 있습니다. 사용자 문서는 Merge Request의 수정 사항을 참조하십시오.
우리는 수정 사항을 표시하기 위해 다양한 소스를 활용합니다. 이에는 다음이 포함됩니다:
- Gitaly 서비스
- 데이터베이스 (
merge_request_diff_files
를 통해) - Redis (캐시된 강조 표시된 수정 사항)
심층 분석
2019년 1월, Oswaldo Ferreira가 GitLab Diffs 및 Diffs에 대한 코멘트 기능에 대한 심층 분석을 진행했습니다. (GitLab 팀 멤버 전용: https://gitlab.com/gitlab-org/create-stage/-/issues/1
) 우리는 이를 통해 향후 해당 코드베이스에서 작업하는 누구에게나 도메인 특화 지식을 공유하였습니다.
- YouTube 녹화
- Google 슬라이드(https://docs.google.com/presentation/d/1bGutFH2AT3bxOPZuLMGl1ANWHqFnrxwQwjiwAZkF-TU/edit)
- PDF 슬라이드
이 심층 분석에서 다룬 내용은 GitLab 11.7을 기준으로 정확했으며, 특정 세부 사항은 그 이후 변경되었을 수 있지만 여전히 좋은 소개 자료로 사용될 것입니다.
아키텍처 개요
Merge Request 수정 사항
Merge Request을 새로 고치는 경우(소스 브랜치로 푸시, 대상 브랜치로 강제 푸시하거나 MR에서 커밋이 이미 특정 브랜치에 포함된 경우)
우리는 Gitlab::Git::Compare
를 사용하여 base
및 head
데이터를 Gitaly를 통해 가져오고 그 사이의 차이를 Gitlab::Git::Diff.between
를 통해 가져옵니다. 수정 사항 가져오기 프로세스는 하나의 파일 수정 사이즈 및 전체 수정 사항 크기를 일련의 상수 값으로 제한합니다. 원시 수정 파일은 이후 merge_request_diff_files
테이블에 유지됩니다.
값 ApplicationSettings#diff_max_patch_bytes
의 10%보다 큰 수정 사항의 경우에도, 우리는 여전히 PostgreSQL에 유지합니다. 그러나 정의된 _안전한 한도_보다 큰 수정 사항은 데이터베이스에 _유지되지 않습니다.
Merge Request 수정 사항 페이지에서 수정 사항 정보를 제시하기 위해 우리는 다음을 수행합니다:
- 데이터베이스
merge_request_diff_files
에서 모든 수정 파일 가져오기 - 일괄적으로 이전 및 새로운 파일 블롭 가져오기:
- 이전 및 새로운 파일 내용 강조 표시
- 각 파일에 대한 뷰어를 알아내기(text, image, deleted 등)
- 파일 내용이 변경되었는지 알기
- 외부적으로 저장되었는지 알기
- 저장 오류가 있었는지 알기
- 수정 파일이 캐시 가능한 경우(텍스트 기반),
Gitlab::Diff::FileCollection::MergeRequestDiff
를 사용하여 Redis에 캐시합니다.
코멘트 수정 사항
수정 사항에 코멘트를 작성하는 경우(어떤 비교에서든), 우리는 NoteDiffFile
에 수정된 버전을 유지합니다(실제 DiffNote
와 연관). 따라서 파일의 수정 사항이 필요할 때마다 리포지터리에 접근하는 대신, 우리는 다음을 수행합니다:
- 유지된
NoteDiffFile#diff
가 있는지 확인하고 사용합니다. - 그렇지 않은 경우, 현재 MR 리비전인 경우, 유지된
MergeRequestDiffFile#diff
사용합니다. - 마지막 시나리오인 경우, 리포지터리로 이동하여 수정 사항 가져오기
수정 사항 제한
위에서 설명한 대로, 단일 수정 사항 및 전체 수정 사항의 크기를 제한합니다. 수정 사항이 축소되는 시나리오 및 전혀 표시되지 않는 수정 사항이 있는 시나리오가 있습니다. 사용자는 Blob 뷰를 볼 것입니다.
수정 사항 수집 제한
모든 수정 파일 수집에 영향을 주는 제한입니다. 파일 수, 라인 수 및 파일 크기가 고려됩니다.
Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_files] = 100
수정 파일이 이미 100개 렌더링되었다면 파일 수정 사항이 축소됩니다(그러나 확장 가능).
Gitlab::Git::DiffCollection.collection_limits[:safe_max_lines] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
수정 파일이 이미 5000줄 렌더링되었다면 파일 수정 사항이 축소됩니다(그러나 확장 가능).
Gitlab::Git::DiffCollection.collection_limits[:safe_max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] * 5.kilobytes = 500.kilobytes
수정 파일이 이미 500킬로바이트 렌더링되었다면 파일 수정 사항이 축소됩니다(그러나 확장 가능).
Gitlab::Git::DiffCollection.collection_limits[:max_files] = Commit::DIFF_HARD_LIMIT_FILES = 1000
이미 1000개 파일이 렌더링되었다면 더 이상 파일을 전혀 렌더링하지 않습니다.
Gitlab::Git::DiffCollection.collection_limits[:max_lines] = Commit::DIFF_HARD_LIMIT_LINES = 50000
이미 50,000줄이 렌더링되었다면 더 이상 파일을 전혀 렌더링하지 않습니다.
Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:max_files] * 5.kilobytes = 5000.kilobytes
이미 5MB가 렌더링되었다면 더 이상 파일을 전혀 렌더링하지 않습니다.
모든 수집 한도 매개변수가 Gitaly로 전송되어 적용됩니다. 즉, 한도가 초과된 후에는 Gitaly가 merge_request_diff_files
에 유지할 안전한 양의 데이터만 반환합니다.
개별 수정 파일 제한
수집의 각 수정 파일에 영향을 주는 제한입니다. 파일 수, 라인 수, 파일 크기가 고려됩니다.
확장 가능한 패치 (축소됨)
수정 패치의 크기가 ApplicationSettings#diff_max_patch_bytes
에서 설정된 값의 10%를 초과하는 경우 수정 패치가 축소됩니다. 즉, 최대 허용 값이 100kb인 경우 10kb와 동등합니다. 수정 패치 크기가 ApplicationSettings#diff_max_patch_bytes
를 초과하지 않으면 수정 패치는 유지됩니다.
이 명칭인 _축소_는 Gitaly에서도 사용되지만, 이 제한은 GitLab에서만 사용됩니다(하드코딩 - Gitaly로 전송되지 않음). Gitaly는 수정 사항 수집 제한을 초과할 때만 Diff.Collapsed
(RPC)를 반환합니다.
확장 불가능한 패치 (너무 큼)
ApplicationSettings#diff_max_patch_bytes
보다 큰 경우 패치가 렌더링되지 않습니다.
사용자는 변경 사항이 표시하기에 너무 큽니다.
메시지와 해당 커밋의 해당 파일만 보기 위한 버튼이 표시됩니다.
Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
파일 차이가 5000줄 이상인 경우 파일 차이가 억제됩니다 (기술적으로 축소된 것과 다르지만 동일하게 작동하며 확장 가능함).
이 제한은 하드코딩되어 있으며 GitLab에서만 적용됩니다.
뷰어
models/diff_viewer/*
에서 찾을 수 있는 Diff Viewer는 각 유형의 Diff 파일에 대한 메타데이터를 매핑하는 데 사용되는 클래스입니다. 이 클래스에는 렌더링하는 데 사용해야 할 부분이 이진인지 여부, 이 클래스가 처리하는 파일 확장자 등의 정보가 들어 있습니다.
DiffViewer::Base
는 변형 (이전 및 새 버전), 확장 및 파일 유형을 유효성 검사하여 렌더링할 수 있는지를 확인합니다.
대상 브랜치의 HEAD
에 대한 Merge Request 차이
과거에는 Merge Request 차이가 git diff target...source
에 의해 계산되어 왔는데, 이 명령은 대상 브랜치의 HEAD
와 Merge 베이스(또는 대상 브랜치 및 소스의 공통 조상)를 비교합니다.
이 솔루션은 대상 브랜치가 소스 브랜치에서 도입된 일부 변경 내용을 포함하기 시작할 때까지 잘 작동합니다: 다음 경우를 고려해 보십시오. 소스 브랜치가 feature_a
이고 대상이 main
인 경우:
-
main
에서feature_a
브랜치를 확인하고 그 안에서file_a
와file_b
를 제거합니다. -
main
에file_a
를 제거하는 커밋을 추가합니다.
실제 차이는 main
의 HEAD
에서 file_b
만 제거되었을지라도 Merge Request 차이에는 여전히 file_a
제거가 포함되어 있습니다. 이러한 중복 변경 내용이 포함된 차이를 검토하기 어려워집니다.
GitLab 12.9에서는 이러한 문제점을 해결하기 위해 대상 브랜치의 HEAD
에 대한 Merge Request 차이가 소개되었습니다: 대상 브랜치가 소스 브랜치에 인위적으로 Merge되고, 그 결과로 나타난 Merge 참조가 소스 브랜치와 비교되어 정확한 차이가 계산됩니다.
“Merge 참조를 차이에 사용하기” 및 “차이에서 Merge 충돌해결”이라는 에픽들을 완료할 때까지 두 가지 옵션 main (base)
및 main (HEAD)
이 Merge Request에 표시될 수 있습니다.
main (HEAD)
옵션은 향후 main (base)
를 대체하기 위해 의도되었습니다.
또한 두 옵션에 대한 코멘트를 지원하기 위해 Merge 충돌들의 위치는 main (base)
및 main (HEAD)
버전에 모두 저장됩니다. (GitLab 12.10에서 소개됨). main (base)
버전의 위치는 Note#position
및 Note#original_position
열에 저장되며, main (HEAD)
버전의 위치에는 DiffNotePosition
이 도입되었습니다.
Merge 참조 차이를 처리할 때 중요한 도전 중 하나는 Merge 충돌입니다. 대상 및 소스 브랜치에 Merge 충돌이 있는 경우, 브랜치를 자동으로 Merge할 수 없습니다. 유튜브 녹화영상 은 이 문제와 “Merge 참조에 대한 에픽”에 대한 동기에 대해 간략히 소개합니다.
13.5에서는 수정된 Merge 충돌을 위한 솔루션이 소개되었습니다. 그러나 앞으로 처리해야 할 Merge 충돌의 더 많은 클래스가 있습니다.