메인 콘텐츠로 건너뛰기
이 데이터셋에는 ClickHouse 리포지토리의 모든 커밋과 변경 사항이 포함되어 있습니다. 이 데이터셋은 ClickHouse와 함께 배포되는 네이티브 git-import 도구를 사용해 생성할 수 있습니다. 생성된 데이터는 다음 각 테이블(table)에 대한 tsv 파일을 제공합니다.
  • commits - 통계 정보가 포함된 커밋입니다.
  • file_changes - 각 커밋에서 변경된 파일과 해당 변경 내용 및 통계 정보입니다.
  • line_changes - 각 커밋에서 변경된 각 파일의 모든 변경된 줄과, 해당 줄의 전체 정보 및 이 줄의 이전 변경 정보입니다.
2022년 11월 8일 기준, 각 TSV의 대략적인 크기와 행 수는 다음과 같습니다.
  • commits - 7.8M - 266,051행
  • file_changes - 53M - 266,051행
  • line_changes - 2.7G - 7,535,157행

데이터 생성

이는 선택 사항입니다. 데이터는 무료로 제공되므로 데이터 다운로드 및 삽입을 참조하십시오.
ClickHouse 리포지토리에서는 이 작업을 완료하는 데 약 3분이 걸립니다(2022년 11월 8일 기준, MacBook Pro 2021). 사용 가능한 전체 옵션 목록은 도구의 네이티브 도움말에서 확인할 수 있습니다.
이 도움말에는 위 각 테이블의 DDL도 제공됩니다. 예:
이 쿼리는 어떤 리포지토리에서도 작동해야 합니다. 자유롭게 살펴보시고 확인한 결과를 알려 주십시오 실행 시간에 대한 몇 가지 기준(2022년 11월 기준):
  • Linux - ~/clickhouse git-import - 160분

데이터 다운로드 및 삽입

다음 데이터는 실행 가능한 환경을 재현하는 데 사용할 수 있습니다. 또는 이 데이터셋은 play.clickhouse.com에서도 사용할 수 있습니다. 자세한 내용은 쿼리를 참조하십시오. 다음 리포지토리에 대해 생성된 파일은 아래에서 확인할 수 있습니다. 이 데이터를 삽입하려면 다음 쿼리를 실행하여 데이터베이스를 준비하십시오.
INSERT INTO SELECTs3 함수를 사용하여 데이터를 삽입합니다. 예를 들어, 아래에서는 ClickHouse 파일을 각각 해당 테이블에 삽입합니다: commits
file_changes
line_changes

쿼리

이 도구는 도움말 출력에서 여러 쿼리를 제안합니다. 이 문서에서는 해당 쿼리들에 대한 답과 함께, 추가로 살펴볼 만한 몇 가지 보충 질문에 대한 답도 제공합니다. 이 쿼리들은 도구가 임의로 제시하는 순서와는 달리, 대체로 복잡도가 점차 높아지는 순서로 구성되어 있습니다. 이 데이터셋은 play.clickhouse.comgit_clickhouse 데이터베이스에서 사용할 수 있습니다. 모든 쿼리에 대해 필요에 따라 데이터베이스 이름을 조정한 이 환경의 링크를 제공합니다. 데이터 수집 시점의 차이로 인해 play의 결과는 여기 제시된 결과와 다를 수 있습니다.

단일 파일의 이력

가장 단순한 쿼리입니다. 여기서는 StorageReplicatedMergeTree.cpp의 모든 커밋 메시지를 살펴봅니다. 이런 메시지가 더 흥미로울 가능성이 높으므로 가장 최근 메시지가 먼저 표시되도록 정렬합니다. 실행
이름 변경은 제외하고 줄 단위 변경 사항도 검토할 수 있습니다. 즉, 파일이 다른 이름으로 존재했던 이름 변경 이벤트 이전의 변경 사항은 표시되지 않습니다: 실행
이름 변경까지 고려하여 파일의 줄별 커밋 이력을 찾는 더 복잡한 변형 쿼리도 있습니다.

현재 활성 파일 찾기

이는 이후 분석에서 리포지토리의 현재 파일만 대상으로 삼아야 하므로 중요합니다. 여기서는 이름이 변경되거나 삭제된 적이 없는 파일(이후 다시 추가되거나 다시 이름이 변경된 경우 제외)을 이 집합으로 간주합니다. dbms, libs, tests/testflows/ 디렉터리 아래 파일은 이름이 변경되는 과정에서 커밋 이력이 손상된 것으로 보입니다. 따라서 이들도 제외합니다. 실행
이 방식에서는 파일 이름을 변경한 후 다시 원래 이름으로 되돌리는 것도 가능합니다. 먼저 이름 변경으로 인해 삭제된 파일 목록에 대해 old_path를 집계합니다. 그런 다음 이를 각 path별 마지막 작업과 합칩니다. 마지막으로 최종 이벤트가 Delete가 아닌 항목만 남기도록 이 목록을 필터링합니다. 실행
가져오기 과정에서 몇몇 디렉터리는 임포트에서 제외했다는 점에 유의하십시오. 즉, --skip-paths 'generated\.cpp|^(contrib|docs?|website|libs/(libcityhash|liblz4|libdivide|libvectorclass|libdouble-conversion|libcpuid|libzstd|libfarmhash|libmetrohash|libpoco|libwidechar_width))/' 이 패턴을 git list-files에 적용하면 18155가 반환됩니다.
따라서 현재 사용 중인 방법은 현재 파일에 대한 추정치입니다 여기서 차이가 발생하는 이유는 몇 가지가 있습니다:
  • 이름 변경은 파일의 다른 수정과 함께 발생할 수 있습니다. 이런 변경은 file_changes에는 별도의 이벤트로 기록되지만 시간은 같습니다. argMax 함수는 이를 구분할 수 없어 첫 번째 값을 선택합니다. 올바른 순서를 알 수 있는 유일한 기준인 삽입 순서는 union 전체에서 유지되지 않으므로, 수정 이벤트가 선택될 수 있습니다. 예를 들어 아래에서는 src/Functions/geometryFromColumn.h 파일이 src/Functions/geometryConverters.h로 이름이 변경되기 전에 여러 차례 수정되었습니다. 현재 방식에서는 Modify 이벤트가 최신 변경으로 선택될 수 있어, 그 결과 src/Functions/geometryFromColumn.h가 그대로 남을 수 있습니다.
실행
  • 커밋 이력 손상 - 삭제 이벤트가 누락되어 있습니다. 어디서 비롯됐는지와 원인은 추후 확인 예정입니다.
이러한 차이가 분석에 유의미한 영향을 미치지는 않을 것입니다. 이 쿼리의 개선안은 언제든 환영합니다.

수정이 가장 많은 파일 목록

현재 파일만 대상으로 하면 수정 횟수는 삭제 수와 추가 수의 합으로 봅니다. 실행

커밋은 보통 무슨 요일에 이루어집니까?

실행
금요일에는 생산성이 다소 떨어지는 것도 자연스럽습니다. 주말에도 코드를 커밋하는 모습을 보니 정말 좋습니다! 기여해 주신 분들께 진심으로 감사드립니다!

하위 디렉터리/파일 이력 - 시간 경과에 따른 줄 수, 커밋 수, 기여자 수

필터링하지 않으면 쿼리 결과가 매우 커져 표시하거나 시각화하기에 현실적이지 않습니다. 따라서 다음 예시에서는 파일 또는 하위 디렉터리로 필터링할 수 있도록 했습니다. 여기서는 toStartOfWeek 함수를 사용해 주 단위로 그룹화합니다. 필요에 따라 조정하십시오. 실행
이 데이터는 시각화하기에 적합합니다. 아래에서는 Superset을 사용합니다. 추가 및 삭제된 라인의 경우: 커밋 및 작성자의 경우:

작성자 수가 가장 많은 파일 목록

현재 존재하는 파일만 대상으로 합니다. 실행

리포지토리의 가장 오래된 코드 줄

현재 파일만 대상으로 합니다. 실행

이력이 가장 긴 파일

현재 파일만 포함합니다. 실행
핵심 데이터 구조인 Merge Tree는 오랜 수정 이력을 거치며 지금도 계속 발전하고 있습니다!

한 달 동안 문서와 코드 관련 기여자 분포

데이터를 수집하는 과정에서 docs/ 폴더의 변경 사항은 커밋 이력이 지나치게 복잡해 제외되었습니다. 따라서 이 쿼리의 결과는 정확하지 않습니다. 월중 특정 시점, 예를 들어 릴리스 날짜 전후에 문서를 더 많이 작성할까요? countIf 함수를 사용해 단순한 비율을 계산하고, bar 함수를 사용해 결과를 시각화할 수 있습니다. 실행
월말로 갈수록 조금 더 많아질 수는 있지만, 전반적으로는 고르게 잘 분포되어 있습니다. 다시 말씀드리지만 데이터 삽입 시 docs 필터가 적용되므로 이는 신뢰할 수 없습니다.

가장 폭넓게 기여한 작성자

여기서 다양성은 작성자가 기여한 고유 파일 수를 의미합니다. 실행
최근 작업 기준으로 가장 다양한 커밋을 남긴 사람이 누구인지 살펴보겠습니다. 날짜로 제한하는 대신 작성자의 최근 N개 커밋만 대상으로 하겠습니다(여기서는 3개를 사용했지만, 필요에 따라 수정할 수 있습니다): 실행

특정 작성자가 선호하는 파일

여기서는 창립자인 Alexey Milovidov을 선택하고, 분석을 현재 파일로 한정합니다. 실행
Alexey가 Change log를 관리해 왔기 때문에 이는 타당합니다. 하지만 파일의 기본 이름을 사용해 Alexey의 주요 파일을 식별하면 어떨까요? 이렇게 하면 이름이 바뀐 경우도 반영할 수 있고, 코드 기여에 더 집중할 수 있습니다. 실행
이는 아마 그의 관심 분야를 더 잘 반영합니다.

작성자 수가 가장 적은 가장 큰 파일

이를 위해서는 먼저 가장 큰 파일을 식별해야 합니다. 모든 파일의 커밋 이력에서 전체 파일을 재구성해 크기를 추정하는 방식은 비용이 매우 많이 듭니다! 따라서 현재 파일로 범위를 제한한다고 가정하고, 추가된 줄 수를 합산한 뒤 삭제된 줄 수를 빼서 추정합니다. 그런 다음 길이와 작성자 수의 비율을 계산할 수 있습니다. 실행
텍스트 사전은 그다지 현실적이지 않을 수 있으므로, 파일 확장자 필터를 사용해 코드만 보도록 제한하겠습니다! 실행
여기에는 어느 정도 최근성 편향이 있습니다. 최신 파일일수록 커밋될 기회가 적기 때문입니다. 그렇다면 최소 1년 이상 된 파일로 범위를 제한하면 어떨까요? 실행

시간별, 요일별, 작성자별, 특정 하위 디렉터리별 커밋 및 코드 줄 수 분포

여기서는 이를 요일별로 추가 및 삭제된 코드 줄 수로 해석합니다. 이 경우 Functions 디렉터리에 초점을 맞춥니다 실행
시간대별로도, 실행
개발팀 대부분이 암스테르담에 있으므로 이런 분포는 자연스럽습니다. bar 함수는 이 분포를 시각화하는 데 도움이 됩니다. 실행

누가 다른 작성자의 코드를 재작성하는 경향이 있는지 보여주는 작성자 행렬

sign = -1은 코드 삭제를 나타냅니다. 구두점과 빈 줄 삽입은 제외합니다. 실행
Sankey chart(SuperSet)를 사용하면 이를 효과적으로 시각화할 수 있습니다. 시각화의 다양성을 높이기 위해 각 작성자별로 코드 제거량이 많은 상위 3명을 가져오도록 LIMIT BY를 3으로 늘렸다는 점에 유의하십시오. Alexey는 다른 사람의 코드를 제거하는 경향이 뚜렷합니다. 코드 제거 양상을 더 균형 있게 보기 위해 Alexey는 제외하겠습니다.

요일별로 가장 높은 비중을 차지하는 기여자는 누구입니까?

커밋 수만 고려하면: 실행
좋습니다. 여기에는 가장 오랫동안 기여해 온 사람인 창립자 Alexey에게 다소 유리할 수 있는 몇 가지 요소가 있습니다. 분석 범위는 지난 1년으로 제한하겠습니다. 실행
이 방식은 여전히 다소 단순하며, 실제 작업량을 충분히 반영하지 못합니다. 더 나은 메트릭은 지난 1년간 수행된 전체 작업에서 차지하는 비율을 기준으로, 각 날짜별 최다 기여자가 누구인지 살펴보는 것일 수 있습니다. 코드 삭제와 추가는 동일하게 취급한다는 점에 유의하십시오. 실행

리포지토리 전체의 코드 연령 분포

분석은 현재 파일로 제한합니다. 간결성을 위해 결과는 깊이 2, 루트 폴더당 파일 5개로 제한합니다. 필요에 따라 조정하십시오. 실행

한 작성자의 코드 중 다른 작성자에 의해 삭제된 비율은 얼마입니까?

이 질문에는 작성자가 작성한 줄 수를 다른 기여자가 삭제한 해당 작성자의 전체 줄 수로 나누어 계산합니다. 실행

가장 많이 다시 작성된 파일은 무엇입니까?

이 질문에 답하는 가장 간단한 방법은 현재 파일만 대상으로 각 경로별 줄 수정 횟수를 집계해 가장 많은 순으로 보는 것입니다. 예를 들면 다음과 같습니다:
하지만 이는 어떤 커밋에서든 파일의 상당 부분이 변경되는 “재작성”이라는 개념은 포착하지 못합니다. 이를 위해서는 더 복잡한 쿼리가 필요합니다. 파일의 50%를 초과하여 삭제되고 50%가 추가되는 경우를 재작성으로 간주해 보겠습니다. 무엇을 재작성으로 볼지는 해석에 맞게 쿼리를 조정할 수 있습니다. 이 쿼리는 현재 존재하는 파일만 대상으로 제한됩니다. pathcommit_hash를 기준으로 그룹화하여 모든 파일 변경을 나열하고, 추가된 줄 수와 삭제된 줄 수를 반환합니다. 윈도 함수를 사용해 누적 합계를 계산하고, 파일 크기에 대한 각 변경의 영향을 lines added - lines removed로 추정하여 각 시점의 파일 총크기를 추정합니다. 이 통계를 사용하면 각 변경에서 파일의 몇 퍼센트가 추가되거나 삭제되었는지 계산할 수 있습니다. 마지막으로 파일별로 재작성에 해당하는 파일 변경 횟수, 즉 (percent_add >= 0.5) AND (percent_delete >= 0.5) AND current_size > 50를 계산합니다. 파일에 대한 초기 기여가 재작성으로 집계되는 것을 방지하기 위해 파일 길이가 50줄을 초과해야 한다는 조건을 둡니다. 또한 이렇게 하면 재작성될 가능성이 더 높은 매우 작은 파일에 대한 편향도 줄일 수 있습니다. 실행

코드가 리포지토리에 가장 오래 남는 경향이 있는 요일은 언제인가요?

이를 위해 먼저 코드 한 줄을 고유하게 식별해야 합니다. 동일한 줄이 하나의 파일에 여러 번 나타날 수 있으므로, 경로와 줄 내용을 사용해 이를 추정합니다. 추가된 줄을 대상으로 쿼리한 다음, 이를 제거된 줄과 join하고 후자가 전자보다 더 나중에 발생한 경우만 필터링합니다. 이렇게 하면 삭제된 줄을 얻을 수 있으며, 이를 바탕으로 두 이벤트 사이의 시간을 계산할 수 있습니다. 마지막으로, 이 데이터셋 전체를 집계해 요일별로 각 줄이 리포지토리에 머무르는 평균 일수를 계산합니다. 실행

평균 코드 연령 기준으로 정렬된 파일

이 쿼리는 코드가 리포지토리에 남아 있을 가능성이 가장 높은 요일은 무엇인가요와 동일한 원리를 사용합니다. 즉, 경로와 줄 내용을 사용해 코드 줄을 고유하게 식별합니다. 이를 통해 각 줄이 추가된 시점부터 제거된 시점까지의 시간을 파악할 수 있습니다. 다만 여기서는 현재 파일과 현재 코드만 필터링하며, 각 파일의 값을 줄 단위로 평균 계산합니다. 실행

누가 테스트 / CPP 코드 / 주석을 더 많이 작성하는 경향이 있습니까?

이 질문에는 몇 가지 방법으로 답할 수 있습니다. 코드 대비 테스트 비율에 초점을 맞추면, 이 쿼리는 비교적 단순합니다. tests가 포함된 폴더에 대한 기여 수를 집계하고 전체 기여 수 대비 비율을 계산합니다. 여기서는 정기적으로 커밋하는 사용자를 중심으로 보고, 일회성 기여로 인한 편향을 피하기 위해 변경 횟수가 20회를 초과한 사용자로 쿼리를 제한합니다. 실행
이 분포는 히스토그램으로 나타낼 수 있습니다. 실행
예상대로 대부분의 기여자는 테스트보다 코드를 더 많이 작성합니다. 그렇다면 코드를 기여할 때 주석을 가장 많이 추가하는 사람은 누구일까요? 실행
코드 기여도를 기준으로 정렬했다는 점에 유의하십시오. 놀랍게도 주요 기여자들은 모두 그 비율이 매우 높으며, 이것이 우리 코드의 가독성을 높이는 요인 중 하나입니다.

작성자의 커밋은 코드/주석 비율에 따라 시간 경과에 따라 어떻게 변합니까?

이를 작성자별로 계산하는 것은 간단합니다,
하지만 이상적으로는 모든 작성자에 대해, 첫 커밋을 시작한 날부터 이런 변화가 전체적으로 어떻게 나타나는지 보고 싶습니다. 시간이 지날수록 작성하는 주석 수가 서서히 줄어들까요? 이를 계산하려면 먼저 시간에 따른 각 작성자의 주석 비율을 구합니다. 이는 누가 테스트 / CPP 코드 / 주석을 더 많이 작성하는 경향이 있습니까?와 유사합니다. 그런 다음 이를 각 작성자의 시작일과 결합하여 주차 오프셋별 주석 비율을 계산합니다. 모든 작성자에 대해 주차 오프셋별 평균을 계산한 뒤, 10주마다 하나씩 선택해 이 결과를 샘플링합니다. 실행
고무적인 점은 주석 비율(%)이 비교적 일정하게 유지되며, 작성자의 기여 기간이 길어져도 감소하지 않는다는 점입니다.

코드가 재작성되기까지 걸리는 평균 시간과 중앙값(코드 감쇠의 반감기)은 얼마입니까?

재작성을 식별하기 위해 가장 자주 재작성되었거나 가장 많은 작성자가 재작성한 파일 나열과 같은 원리를 사용할 수 있지만, 여기서는 모든 파일을 대상으로 합니다. 각 파일에서 재작성 사이의 시간 간격을 계산하기 위해 윈도 함수가 사용됩니다. 이를 바탕으로 전체 파일에 대한 평균과 중앙값을 계산할 수 있습니다. 실행

코드가 재작성될 가능성이 가장 높은, 즉 코드를 작성하기에 가장 좋지 않은 시간대는 언제입니까?

코드가 재작성되기까지 걸리는 평균 시간과 중앙값(코드 감쇠의 반감기)은 얼마입니까?가장 많이 재작성되었거나 가장 많은 작성자가 재작성한 파일 목록과 유사하지만, 여기서는 요일별로 집계합니다. 필요에 따라 연중 월별 등으로 조정하세요. 실행

어떤 작성자의 코드가 가장 오래 남습니까?

여기서 “sticky”는 작성자의 코드가 재작성되기 전까지 얼마나 오래 남아 있는지를 의미합니다. 이전 질문인 코드가 재작성되기까지의 평균 시간과 중앙값(코드 감쇠의 반감기)은 얼마입니까?과 마찬가지로, 재작성에는 동일한 메트릭을 사용합니다. 즉, 파일 기준으로 추가 50%, 삭제 50%를 재작성으로 봅니다. 작성자별 평균 재작성 시간을 계산하고, 파일이 2개를 초과하는 contributors만 고려합니다. 실행

작성자별 최장 연속 커밋 일수

이 쿼리에서는 먼저 작성자가 커밋한 날짜를 계산해야 합니다. 작성자별로 파티셔닝한 윈도 함수를 사용하면 각 커밋 사이의 일수를 계산할 수 있습니다. 각 커밋에서 이전 커밋 이후 경과 시간이 1일이면 연속(1)으로 표시하고, 그렇지 않으면 0으로 표시해 이 결과를 consecutive_day에 저장합니다. 이어서 배열 함수로 각 작성자의 가장 긴 연속 1 시퀀스를 계산합니다. 먼저 groupArray 함수를 사용해 작성자의 모든 consecutive_day 값을 모읍니다. 그런 다음 1과 0으로 이루어진 이 배열을 0을 기준으로 하위 배열로 나눕니다. 마지막으로 가장 긴 하위 배열을 계산합니다. 실행

파일의 줄 단위 커밋 이력

파일은 이름이 변경될 수 있습니다. 이 경우 이름 변경 이벤트가 발생하며, 이때 path 컬럼은 파일의 새 경로로 설정되고 old_path는 이전 위치를 나타냅니다. 예시는 다음과 같습니다. 실행
이 때문에 모든 라인 변경이나 파일 변경을 하나로 연결하는 단일 값이 없어, 파일의 전체 이력을 확인하기가 어렵습니다. 이를 해결하기 위해 사용자 정의 함수(UDF)를 사용할 수 있습니다. 현재 UDF는 재귀적으로 사용할 수 없으므로, 파일의 이력을 식별하려면 서로를 명시적으로 호출하는 일련의 UDF를 정의해야 합니다. 즉, 이름 변경은 최대 깊이까지만 추적할 수 있습니다. 아래 예시는 깊이 5까지 추적합니다. 파일 이름이 이보다 더 많이 변경될 가능성은 낮으므로, 현재로서는 이것으로 충분합니다.
file_path_history('src/Storages/StorageReplicatedMergeTree.cpp')를 호출하면 이름 변경 이력을 재귀적으로 따라가고, 각 함수는 old_path를 사용해 다음 단계의 함수를 호출합니다. 결과는 arrayConcat으로 합쳐집니다. 예를 들어,
이 기능을 사용하면 이제 파일의 전체 이력에 대한 커밋을 구성할 수 있습니다. 이 예시에서는 각 path 값마다 커밋 하나를 보여줍니다.

남은 질문

Git blame

현재 배열 함수에서 상태를 유지할 수 없기 때문에 정확한 결과를 얻기가 특히 어렵습니다. 이는 각 반복에서 상태를 유지할 수 있는 arrayFold 또는 arrayReduce로 가능해질 것입니다. 개략적인 해법으로, 상위 수준의 분석에는 다음과 같은 방식이면 충분할 수 있습니다:
여기에는 정확한 해결책이나 더 나은 해결책을 제안해 주시면 환영합니다.
마지막 수정일 2026년 7월 2일