git-import, который поставляется вместе с ClickHouse.
Сгенерированные данные включают файл tsv для каждой из следующих таблиц:
commits- коммиты со статистикой.file_changes- файлы, изменённые в каждом коммите, с информацией об изменении и статистикой.line_changes- каждая изменённая строка в каждом изменённом файле и каждом коммите с полной информацией о строке и сведениями о её предыдущем изменении.
commits- 7.8M - 266,051 строкfile_changes- 53M - 266,051 строкline_changes- 2.7G - 7,535,157 строк
Генерация данных
- Linux -
~/clickhouse git-import- 160 мин
Загрузка и вставка данных
- ClickHouse (8 ноября 2022 г.)
- https://datasets-documentation.s3.amazonaws.com/github/commits/clickhouse/commits.tsv.xz - 2.5 MB
- https://datasets-documentation.s3.amazonaws.com/github/commits/clickhouse/file_changes.tsv.xz - 4.5MB
- https://datasets-documentation.s3.amazonaws.com/github/commits/clickhouse/line_changes.tsv.xz - 127.4 MB
- Linux (8 ноября 2022 г.)
INSERT INTO SELECT и функции s3. Например, ниже мы загружаем файлы ClickHouse в соответствующие таблицы:
commits
Запросы
git_clickhouse. Для всех запросов мы приводим ссылку на это окружение, при необходимости подставляя нужное имя базы данных. Обратите внимание, что результаты в play могут отличаться от приведённых здесь из-за разницы во времени сбора данных.
История одного файла
StorageReplicatedMergeTree.cpp. Поскольку наиболее интересны, скорее всего, последние изменения, мы сортируем сообщения от новых к старым.
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будет сохранён.
- Нарушенная история коммитов — отсутствуют события удаления. Источник и причина пока неизвестны.
Список файлов с наибольшим числом изменений
На какой день недели обычно приходятся коммиты?
История подкаталога/файла — количество строк, коммитов и участников во времени
toStartOfWeek — при необходимости адаптируйте её.
play
Самые старые строки кода в репозитории
Файлы с самой длинной историей
Распределение контрибьюторов по документации и коду в течение месяца
docs/ были отфильтрованы из-за очень «грязной» истории коммитов. Поэтому результаты этого запроса не являются точными.
Пишем ли мы больше документации в определённые периоды месяца, например, ближе к датам релизов? Мы можем использовать функцию countIf, чтобы вычислить простое соотношение и визуализировать результат с помощью функции bar.
play
bar помогают нам наглядно представить эти распределения:
play
sign = -1 указывает на удаление кода. Мы исключаем знаки пунктуации и вставку пустых строк.
play
LIMIT BY до 3, чтобы получить трёх основных удаляющих код для каждого автора и сделать визуализацию более разнообразной.
Алексею явно нравится удалять чужой код. Давайте исключим его, чтобы получить более сбалансированную картину удаления кода.
Кто вносит наибольший вклад в процентах по дням недели?
Распределение возраста кода по репозиторию
Какие файлы переписывались чаще всего?
path и commit_hash, и получаем количество добавленных и удалённых строк. С помощью оконной функции мы оцениваем общий размер файла в каждый момент времени, вычисляя накопительную сумму и оценивая влияние каждого изменения на размер файла как lines added - lines removed. Используя эту метрику, мы можем вычислить долю файла, которая была добавлена или удалена для каждого изменения. Наконец, мы подсчитываем для каждого файла количество изменений, которые считаются переписыванием, то есть (percent_add >= 0.5) AND (percent_delete >= 0.5) AND current_size > 50. Обратите внимание: мы учитываем только файлы длиннее 50 строк, чтобы начальные правки файла не считались переписыванием. Это также помогает избежать смещения в сторону очень маленьких файлов, которые с большей вероятностью могут быть переписаны.
play
В какой день недели код с наибольшей вероятностью остаётся в репозитории?
Файлы, отсортированные по среднему возрасту кода
Кто обычно пишет больше тестов / кода на C++ / комментариев?
tests, и вычислить их долю от общего числа изменений.
Здесь мы рассматриваем только пользователей, у которых больше 20 изменений, чтобы сосредоточиться на постоянных контрибьюторах и избежать перекоса из-за разовых вкладов.
play
Какой средний промежуток времени проходит до того, как код будет переписан, и какова медиана (период полураспада кода)?
В какое время хуже всего писать код — в том смысле, что вероятность его переписывания максимальна?
consecutive_day.
Затем функции для работы с массивами вычисляют для каждого автора самую длинную последовательность единиц подряд. Сначала функция groupArray собирает все значения consecutive_day для автора. Затем этот массив из 1 и 0 разбивается по значениям 0 на подмассивы. Наконец, вычисляется самый длинный подмассив.
play
Построчная история коммитов файла
path указывается новый путь к файлу, а old_path — его предыдущее расположение, например.
play
file_path_history('src/Storages/StorageReplicatedMergeTree.cpp') мы рекурсивно проходим по истории переименований: каждый вызов функции передаёт old_path на следующий уровень. Результаты объединяются с помощью arrayConcat.
Например,
path.
Нерешённые вопросы
Git blame
arrayFold или arrayReduce, которые позволяют сохранять состояние на каждой итерации.
Приблизительное решение, достаточное для анализа на верхнем уровне, может выглядеть примерно так: