Перейти к основному содержанию
Этот набор данных содержит все коммиты и изменения из репозитория ClickHouse. Его можно сгенерировать с помощью нативного инструмента git-import, который поставляется вместе с ClickHouse. Сгенерированные данные включают файл tsv для каждой из следующих таблиц:
  • commits - коммиты со статистикой.
  • file_changes - файлы, изменённые в каждом коммите, с информацией об изменении и статистикой.
  • line_changes - каждая изменённая строка в каждом изменённом файле и каждом коммите с полной информацией о строке и сведениями о её предыдущем изменении.
По состоянию на 8 ноября 2022 года каждый TSV имеет примерно следующий размер и количество строк:
  • commits - 7.8M - 266,051 строк
  • file_changes - 53M - 266,051 строк
  • line_changes - 2.7G - 7,535,157 строк

Генерация данных

Это необязательно. Данные доступны бесплатно — см. Загрузка и вставка данных.
Для репозитория ClickHouse это займет около 3 минут (по состоянию на 8 ноября 2022 года на MacBook Pro 2021). Полный список доступных параметров можно получить во встроенной справке инструмента.
В этой справке также приведён DDL для каждой из указанных выше таблиц, например.
Эти запросы должны работать в любом репозитории. Смело изучайте и делитесь своими результатами Некоторые ориентиры по времени выполнения (по состоянию на ноябрь 2022 года):
  • Linux - ~/clickhouse git-import - 160 мин

Загрузка и вставка данных

Следующие данные можно использовать, чтобы воссоздать рабочее окружение. Кроме того, этот набор данных доступен на play.clickhouse.com — подробнее см. в разделе Запросы. Ниже приведены сгенерированные файлы для следующих репозиториев: Чтобы выполнить вставку этих данных, подготовьте базу данных, выполнив следующие запросы:
Вставьте данные с помощью INSERT INTO SELECT и функции s3. Например, ниже мы загружаем файлы ClickHouse в соответствующие таблицы: commits
file_changes
line_changes

Запросы

Инструмент предлагает несколько запросов в выводе справки. Ниже мы разобрали их, а также несколько дополнительных вопросов, которые могут представлять интерес. Запросы расположены примерно по возрастанию сложности, а не в произвольном порядке, как у инструмента. Этот набор данных доступен на play.clickhouse.com в базе данных git_clickhouse. Для всех запросов мы приводим ссылку на это окружение, при необходимости подставляя нужное имя базы данных. Обратите внимание, что результаты в play могут отличаться от приведённых здесь из-за разницы во времени сбора данных.

История одного файла

Самый простой запрос. Здесь мы смотрим все сообщения коммитов для StorageReplicatedMergeTree.cpp. Поскольку наиболее интересны, скорее всего, последние изменения, мы сортируем сообщения от новых к старым. play
Мы также можем просмотреть изменения по строкам без учета переименований, то есть изменения до события переименования не будут показаны, если файл существовал под другим именем: play
Обратите внимание: существует и более сложный вариант этого запроса, в котором определяется построчная история коммитов файла с учётом переименований.

Найдите текущие активные файлы

Это важно для дальнейшего анализа, когда нужно учитывать только текущие файлы в репозитории. Мы считаем, что в это множество входят файлы, которые не были переименованы или удалены (а затем повторно добавлены/переименованы). Обратите внимание: судя по всему, история коммитов для файлов в каталогах dbms, libs, tests/testflows/ была нарушена во время их переименования. Поэтому мы тоже исключаем их. play
Обратите внимание, что это позволяет файлам быть переименованными, а затем снова переименованными в исходные имена. Сначала мы агрегируем old_path, чтобы получить список файлов, удалённых в результате переименования. Затем объединяем его с последней операцией для каждого path. Наконец, отфильтровываем этот список, оставляя только те случаи, где финальное событие — не Delete. play
Обратите внимание: при импорте мы пропустили несколько каталогов, а именно: --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 не может их различить — она выбирает первое значение. Естественный порядок вставок (единственный способ определить правильную последовательность) при объединении не сохраняется, поэтому вместо переименования могут выбираться события изменения. Например, ниже файл src/Functions/geometryFromColumn.h несколько раз изменяется, прежде чем его переименовывают в src/Functions/geometryConverters.h. Наше текущее решение может выбрать событие Modify как последнее изменение, из-за чего src/Functions/geometryFromColumn.h будет сохранён.
play
  • Нарушенная история коммитов — отсутствуют события удаления. Источник и причина пока неизвестны.
Эти различия не должны существенно повлиять на наш анализ. Мы приветствуем улучшенные версии этого запроса.

Список файлов с наибольшим числом изменений

Если учитывать только текущие файлы, количеством изменений считается сумма удалений и добавлений. play

На какой день недели обычно приходятся коммиты?

play
Это вполне ожидаемо, учитывая некоторое снижение продуктивности по пятницам. Здорово видеть, что люди коммитят код по выходным! Огромное спасибо нашим контрибьюторам!

История подкаталога/файла — количество строк, коммитов и участников во времени

В результате получится большой набор данных, который без фильтрации непрактично ни показывать, ни визуализировать. Поэтому в следующем примере можно отфильтровать файл или подкаталог. Здесь используется группировка по неделям с помощью функции toStartOfWeek — при необходимости адаптируйте её. play
Эти данные хорошо подходят для визуализации. Ниже мы используем Superset. Для добавленных и удалённых строк: Для коммитов и авторов:

Список файлов с наибольшим количеством авторов

Ограничьтесь только текущими файлами. play

Самые старые строки кода в репозитории

Только в текущих файлах. play

Файлы с самой длинной историей

Только для текущих файлов. play
Наша основная структура данных — Merge Tree —, очевидно, постоянно развивается и имеет длинную историю правок!

Распределение контрибьюторов по документации и коду в течение месяца

При сборе данных изменения в папке docs/ были отфильтрованы из-за очень «грязной» истории коммитов. Поэтому результаты этого запроса не являются точными. Пишем ли мы больше документации в определённые периоды месяца, например, ближе к датам релизов? Мы можем использовать функцию countIf, чтобы вычислить простое соотношение и визуализировать результат с помощью функции bar. play
Возможно, ближе к концу месяца данных немного больше, но в целом распределение остается достаточно равномерным. Опять же, это ненадежный вывод, поскольку при вставке данных применяется фильтр docs.

Авторы с самым разносторонним вкладом

Под разнообразием здесь понимается количество уникальных файлов, в которые внёс вклад автор. play
Давайте посмотрим, у кого в последних коммитах самая разнообразная история изменений. Вместо ограничения по дате возьмём последние N коммитов каждого автора (в этом примере — 3, но вы можете изменить это значение): play

Любимые файлы автора

Здесь мы берем нашего основателя Alexey Milovidov и ограничиваем анализ только текущими файлами. play
Это логично, потому что Alexey отвечал за ведение Change log. Но что, если использовать базовое имя файла, чтобы определить его самые популярные файлы? Это позволит учесть переименования и сосредоточиться именно на вкладе в код. play
Это, возможно, лучше отражает круг его интересов.

Самые большие файлы с наименьшим числом авторов

Для этого сначала нужно определить самые большие файлы. Оценка через полную реконструкцию каждого файла по истории коммитов будет очень затратной! Для приблизительной оценки, если ограничиться текущими файлами, можно сложить количество добавленных строк и вычесть количество удалённых. Затем можно вычислить отношение длины файла к числу авторов. play
Текстовые словари, вероятно, не слишком показательны, поэтому ограничимся только кодом, отфильтровав его по расширению файла! play
Здесь есть некоторая предвзятость в пользу недавних данных: у более новых файлов было меньше возможностей для коммитов. А что, если ограничиться файлами возрастом не менее 1 года? play

Распределение коммитов и строк кода по времени: по дням недели, по авторам; для конкретных подкаталогов

Здесь под этим понимается количество добавленных и удалённых строк в разбивке по дням недели. В данном случае мы сосредоточимся на каталоге Functions play
И по времени суток, play
Такое распределение выглядит логично, учитывая, что большая часть нашей команды разработки находится в Амстердаме. Функции bar помогают нам наглядно представить эти распределения: play

Матрица авторов, показывающая, какие авторы склонны переписывать код друг друга

sign = -1 указывает на удаление кода. Мы исключаем знаки пунктуации и вставку пустых строк. play
Диаграмма Санки (SuperSet) позволяет наглядно это показать. Обратите внимание: мы увеличиваем LIMIT BY до 3, чтобы получить трёх основных удаляющих код для каждого автора и сделать визуализацию более разнообразной. Алексею явно нравится удалять чужой код. Давайте исключим его, чтобы получить более сбалансированную картину удаления кода.

Кто вносит наибольший вклад в процентах по дням недели?

Если учитывать только количество коммитов: play
Хорошо, у самого давнего участника — нашего основателя Алексея — здесь могут быть некоторые преимущества. Давайте ограничим анализ последним годом. play
Это всё ещё слишком упрощённо и не отражает реальную работу людей. Более удачной метрикой может быть доля ведущего участника за каждый день в общем объёме работы, выполненной за последний год. Обратите внимание, что удаление и добавление кода мы считаем равноценными. play

Распределение возраста кода по репозиторию

Мы ограничиваем анализ только текущими файлами. Для краткости ограничим результаты глубиной 2 и пятью файлами на каждую корневую папку. При необходимости измените эти параметры. play

Какой процент кода автора был удалён другими авторами?

Чтобы ответить на этот вопрос, нужно разделить количество строк, написанных автором, на общее количество его строк, удалённых другими участниками. play

Какие файлы переписывались чаще всего?

Самый простой способ ответить на этот вопрос — просто подсчитать максимальное число изменений строк для каждого пути (ограничившись текущими файлами), например:
Это, однако, не учитывает понятие «переписывания», когда в одном commit меняется значительная часть файла. Для этого требуется более сложный запрос. Если считать переписыванием случай, когда удаляется более 50% файла и добавляется 50%, запрос можно скорректировать в соответствии с вашим представлением о том, что следует считать переписыванием. Запрос ограничен только текущими файлами. Мы перечисляем все изменения файлов, группируя их по path и commit_hash, и получаем количество добавленных и удалённых строк. С помощью оконной функции мы оцениваем общий размер файла в каждый момент времени, вычисляя накопительную сумму и оценивая влияние каждого изменения на размер файла как lines added - lines removed. Используя эту метрику, мы можем вычислить долю файла, которая была добавлена или удалена для каждого изменения. Наконец, мы подсчитываем для каждого файла количество изменений, которые считаются переписыванием, то есть (percent_add >= 0.5) AND (percent_delete >= 0.5) AND current_size > 50. Обратите внимание: мы учитываем только файлы длиннее 50 строк, чтобы начальные правки файла не считались переписыванием. Это также помогает избежать смещения в сторону очень маленьких файлов, которые с большей вероятностью могут быть переписаны. play

В какой день недели код с наибольшей вероятностью остаётся в репозитории?

Для этого нам нужно однозначно идентифицировать строку кода. Мы делаем это по пути и содержимому строки, поскольку одна и та же строка может встречаться в файле несколько раз. Мы выполняем запрос по добавленным строкам, объединяя его со строками удаления, и отбираем случаи, когда удаление произошло позже добавления. Это позволяет получить удалённые строки, для которых можно вычислить время между этими двумя событиями. Наконец, мы агрегируем данные по этому набору, чтобы вычислить среднее число дней, в течение которых строки остаются в репозитории, в разбивке по дням недели. play

Файлы, отсортированные по среднему возрасту кода

В этом запросе используется тот же принцип, что и в В какой день недели у кода самый высокий шанс остаться в репозитории: строка кода однозначно определяется по пути и её содержимому. Это позволяет определить, сколько времени проходит между добавлением строки и её удалением. Однако здесь учитываются только текущие файлы и только код, а затем для каждого файла вычисляется среднее значение по всем строкам. play

Кто обычно пишет больше тестов / кода на C++ / комментариев?

На этот вопрос можно ответить несколькими способами. Если смотреть на соотношение тестов и кода, этот запрос довольно прост: нужно подсчитать количество изменений в каталогах, содержащих tests, и вычислить их долю от общего числа изменений. Здесь мы рассматриваем только пользователей, у которых больше 20 изменений, чтобы сосредоточиться на постоянных контрибьюторах и избежать перекоса из-за разовых вкладов. play
Это распределение можно представить в виде гистограммы. play
Большинство участников, как и следовало ожидать, пишут больше кода, чем тестов. А кто при написании кода добавляет больше всего комментариев? play
Обратите внимание: мы сортируем по вкладу в код. Удивительно, насколько высок этот процент у всех наших крупнейших участников, и отчасти именно поэтому наш код так легко читать.

Как со временем меняется соотношение кода и комментариев в коммитах автора?

Вычислить это для автора несложно,
Однако в идеале мы хотим увидеть, как этот показатель в целом меняется у всех авторов, начиная с первого дня, когда они начинают делать коммиты. Постепенно ли они уменьшают количество комментариев, которые пишут? Чтобы это вычислить, сначала определим, как со временем меняется доля комментариев у каждого автора — аналогично Кто обычно пишет больше тестов / CPP-кода / комментариев?. Затем объединим эти данные с датой начала работы каждого автора, чтобы рассчитать долю комментариев по смещению в неделях. После вычисления среднего значения по недельному смещению для всех авторов мы делаем выборку из этих результатов, оставляя каждую 10-ю неделю. play
Что обнадёживает, доля комментариев у нас остаётся довольно стабильной и не снижается, даже если авторы вносят вклад на протяжении долгого времени.

Какой средний промежуток времени проходит до того, как код будет переписан, и какова медиана (период полураспада кода)?

Мы можем использовать тот же принцип, что и в разделе список файлов, которые переписывались наибольшее число раз или наибольшим числом авторов, чтобы определить переписывания, но уже с учетом всех файлов. Оконная функция используется для вычисления времени между переписываниями для каждого файла. На основе этого можно рассчитать среднее значение и медиану по всем файлам. play

В какое время хуже всего писать код — в том смысле, что вероятность его переписывания максимальна?

Аналогично Какой средний промежуток времени проходит до того, как код будет переписан, и какова медиана (период полураспада кода)? и список файлов, которые переписывались наибольшее число раз или наибольшим числом авторов, но здесь агрегация выполняется по дням недели. При необходимости скорректируйте, например, на месяц года. play

Код каких авторов остаётся неизменным дольше всего?

Под «стойкостью» мы понимаем, как долго код автора сохраняется, прежде чем будет переписан. Как и в предыдущем вопросе Какой средний промежуток времени проходит до того, как код будет переписан, и какова медиана (период полураспада кода)?, здесь используется та же метрика переписывания, то есть 50 % добавлений и 50 % удалений в файле. Мы вычисляем среднее время переписывания для каждого автора и учитываем только тех участников, у которых больше двух файлов. play

Наибольшее число последовательных дней с коммитами у автора

В этом запросе сначала нужно определить дни, в которые автор делал коммиты. Используя оконную функцию с разбиением по автору, можно вычислить число дней между его коммитами. Для каждого коммита, если с момента предыдущего коммита прошёл 1 день, мы помечаем его как последовательный (1), иначе — 0, сохраняя результат в consecutive_day. Затем функции для работы с массивами вычисляют для каждого автора самую длинную последовательность единиц подряд. Сначала функция groupArray собирает все значения consecutive_day для автора. Затем этот массив из 1 и 0 разбивается по значениям 0 на подмассивы. Наконец, вычисляется самый длинный подмассив. play

Построчная история коммитов файла

Файлы можно переименовывать. Когда это происходит, мы получаем событие переименования, в котором в столбце path указывается новый путь к файлу, а old_path — его предыдущее расположение, например. play
Это затрудняет просмотр полной истории файла, поскольку у нас нет единого значения, связывающего все изменения строк или файла. Чтобы решить эту проблему, мы можем использовать Пользовательские функции (UDF). В настоящее время они не могут быть рекурсивными, поэтому, чтобы определить историю файла, нам нужно определить серию UDF, которые явно вызывают друг друга. Это означает, что мы можем отслеживать переименования только до некоторой максимальной глубины — в приведённом ниже примере это 5 уровней. Маловероятно, что файл будет переименован большее число раз, поэтому пока этого достаточно.
При вызове file_path_history('src/Storages/StorageReplicatedMergeTree.cpp') мы рекурсивно проходим по истории переименований: каждый вызов функции передаёт old_path на следующий уровень. Результаты объединяются с помощью arrayConcat. Например,
Теперь с помощью этой возможности можно собрать коммиты за всю историю файла. В этом примере показано по одному коммиту для каждого значения path.

Нерешённые вопросы

Git blame

Получить здесь точный результат особенно сложно, поскольку сейчас в функциях для работы с массивами нельзя сохранять состояние. Это станет возможным с помощью arrayFold или arrayReduce, которые позволяют сохранять состояние на каждой итерации. Приблизительное решение, достаточное для анализа на верхнем уровне, может выглядеть примерно так:
Здесь приветствуются более точные и удачные решения.
Последнее изменение 25 июня 2026 г.