Запись Запросов в ClickHouse с использованием Данных GitHub
Этот набор данных содержит все коммиты и изменения для репозитория 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 строк
Генерация данных
Это необязательно. Мы распространяем данные бесплатно - смотрите Скачивание и вставка данных.
Это займет около 3 минут (на 8 ноября 2022 года на MacBook Pro 2021) для завершения для репозитория ClickHouse.
Полный список доступных опций можно получить из встроенной справки инструмента.
Эта справка также предоставляет DDL для каждой из вышеупомянутых таблиц, например:
Эти запросы должны работать на любом репозитории. Не стесняйтесь исследовать и сообщать о своих находках. Несколько рекомендаций относительно времени выполнения (на ноябрь 2022 года):
- Linux -
~/clickhouse git-import
- 160 минут
Скачивание и вставка данных
Следующие данные могут быть использованы для воспроизведения рабочей среды. В качестве альтернативы этот набор данных доступен в play.clickhouse.com - смотрите Запросы для получения дополнительных сведений.
Сгенерированные файлы для следующих репозиториев можно найти ниже:
- 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
file_changes
line_changes
Запросы
Инструмент предлагает несколько запросов через свой вывод справки. Мы ответили на них в дополнение к некоторым дополнительным вспомогательным вопросам. Эти запросы имеют примерно возрастающую сложность по сравнению с произвольным порядком инструмента.
Этот набор данных доступен в play.clickhouse.com в базах данных git_clickhouse
. Мы предоставляем ссылку на эту среду для всех запросов, адаптируя имя базы данных по мере необходимости. Обратите внимание, что результаты игры могут отличаться от представленных здесь из-за различий во времени сбора данных.
История одного файла
Самые простые запросы. Здесь мы рассматриваем все сообщения коммитов для 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
не может их различать - она выбирает первое значение. Естественный порядок вставок (единственное средство для определения правильного порядка) не сохраняется через объединение, поэтому могут быть выбраны модифицированные события. Например, ниже файлsrc/Functions/geometryFromColumn.h
имел несколько модификаций до того, как был переименован вsrc/Functions/geometryConverters.h
. Наше текущее решение может выбрать событие Modify как последнее изменение, что приводит к удержаниюsrc/Functions/geometryFromColumn.h
.
- Неполная история коммитов - отсутствуют события удаления. Исходная причина должна быть установлена.
Эти различия не должны значительно повлиять на наш анализ. Мы приветствуем улучшенные версии этого запроса.
Список файлов с наибольшим количеством модификаций
Ограничиваясь текущими файлами, мы рассматриваем количество модификаций как сумму удалений и добавлений.
В какой день недели обычно происходят коммиты?
Это имеет смысл с некоторым снижением производительности по пятницам. Приятно видеть, что люди коммитят код в выходные! Большое спасибо нашим контрибьюторам!
История подкаталога/файла - количество строк, коммитов и участников за время
Это приведет к большому результату запроса, который будет нереалистичным для показа или визуализации без фильтрации. Поэтому мы позволяем фильтровать файл или подкаталог в следующем примере. Здесь мы группируем по неделям, используя функцию toStartOfWeek
- адаптируйте по необходимости.
Эти данные хорошо визуализируются. Ниже мы используем Superset.
Для добавленных и удаленных строк:

Для коммитов и авторов:

Список файлов с максимальным количеством авторов
Ограничено только актуальными файлами.
Самые старые строки кода в репозитории
Ограничено только актуальными файлами.
Файлы с самой длинной историей
Ограничено только актуальными файлами.
Наша основная структура данных, Merge Tree, очевидно, постоянно развивается и имеет длинную историю изменений!
Распределение участников по документации и коду за месяц
Во время захвата данных изменения в папке docs/
были отфильтрованы из-за нечистой истории коммитов. Результаты этого запроса поэтому не точны.
Пишем ли мы больше документации в определенные моменты месяца, например, вокруг дат релизов? Мы можем использовать функцию countIf
для вычисления простого соотношения, визуализируя результат с помощью функции bar
.
Может быть, немного больше в конце месяца, но в целом мы поддерживаем хорошее равномерное распределение. Опять же, это ненадежно из-за фильтрации папки документации во время вставки данных.
Авторы с самым разнообразным влиянием
Мы считаем разнообразием количество уникальных файлов, к которым автор внес свой вклад.
Давайте посмотрим, у кого самые разнообразные коммиты в недавней работе. Вместо ограничения по дате, мы ограничим последний N коммитов автора (в данном случае, мы использовали 3, но не стесняйтесь менять):
Любимые файлы для автора
Здесь мы выбираем нашего основателя Alexey Milovidov и ограничиваем наш анализ актуальными файлами.
Это имеет смысл, потому что Алексей отвечает за поддержание Change log. Но что если мы используем базовое имя файла, чтобы определить его популярные файлы - это позволяет учитывать переименования и должно сосредоточиться на вклад в код.
Это может более точно отразить его области интересов.
Самые большие файлы с наименьшим количеством авторов
Для этого нам сначала нужно определить самые большие файлы. Оценить это, реконструировав полный файл на основе истории коммитов, будет очень дорого!
Для оценки, предположим, что мы ограничены актуальными файлами, мы суммируем добавленные строки и вычитаем удаленные. Затем мы можем вычислить отношение длины к количеству авторов.
Словарные файлы, возможно, не являются реалистичными, поэтому давайте ограничимся только кодом с помощью фильтра по расширению файла!
В этом есть некоторый предвзятость к современности - новые файлы имеют меньше возможностей для коммитов. Что насчет ограничения файлов как минимум год назад?
Распределение коммитов и строк кода по времени; по дням недели, авторам; для конкретных подкаталогов
Мы интерпретируем это как количество добавленных и удалённых строк по дням недели. В данном случае мы фокусируемся на директории Functions.
И по времени суток,
Это распределение имеет смысл, учитывая, что большая часть нашей команды разработчиков находится в Амстердаме. Функции bar
помогают нам визуализировать эти распределения:
Матрица авторов, показывающая, какие авторы перерабатывают код других авторов
sign = -1
указывает на удаление кода. Мы исключаем пунктуацию и вставку пустых строк.
Санки-диаграмма (SuperSet) позволяет красиво визуализировать это. Обратите внимание, что мы увеличили наш LIMIT BY
до 3, чтобы получить 3 главных удалителей кода для каждого автора, улучшая разнообразие в визуализации.

Алексей явно любит удалять код других людей. Исключим его для более сбалансированного взгляда на удаление кода.

Кто является автором с наибольшим процентом вкладов по дням недели?
Если рассматривать только по количеству коммитов:
Хорошо, здесь есть некоторые возможные преимущества у самого продолжительного автора - нашего основателя Алексея. Ограничим наш анализ последним годом.
Это все еще немного просто и не отражает работу людей.
Лучшей метрикой может быть, кто является ведущим автором каждый день в процентном отношении от общего объема выполненной работы за последний год. Обратите внимание, что мы рассматриваем удаление и добавление кода одинаково.
Распределение возраста кода по репозиторию
Мы ограничиваем анализ текущими файлами. Для краткости мы ограничиваем результаты глубиной 2 и 5 файлов на корневую папку. Настройте по мере необходимости.
Какой процент кода для автора был удалён другими авторами?
Для этого вопроса нам нужно количество строк, написанных автором, делённое на общее количество строк, которые были удалены другим участником.
Список файлов, которые были переписаны наибольшее количество раз
Самый простой подход к этому вопросу заключается в том, чтобы просто посчитать наибольшее количество изменений строк по пути (с ограничением на текущие файлы), например:
Это не учитывает понятие "переписывания", когда большая часть файла изменяется в любом коммите. Это требует более сложного запроса. Если мы считаем переписыванием, когда более 50% файла удаляется и 50% добавляется. Вы можете настроить запрос в соответствии с вашим собственным пониманием того, что составляет это.
Запрос ограничен только текущими файлами. Мы перечисляем все изменения файла, группируя по path
и commit_hash
, возвращая количество добавленных и удалённых строк. Используя оконную функцию, мы оцениваем общий размер файла в любой момент времени, выполняя накопительную сумму и оценивая влияние любого изменения на размер файла как lines added - lines removed
. Используя эту статистику, мы можем подсчитать процент файла, который был добавлен или удалён для каждого изменения. Наконец, мы считаем количество изменений файла, которые составляют переписывание, т.е. (percent_add >= 0.5) AND (percent_delete >= 0.5) AND current_size > 50
. Обратите внимание, что мы требуем, чтобы файлы содержали более 50 строк, чтобы избежать считывания ранних взносов в файл, которые могут быть засчитаны в качестве переписывания. Это также избегает предвзятости к очень маленьким файлам, которые могут быть более склонны к переписыванию.
Какой день недели имеет наибольшие шансы остаться в репозитории?
Для этого мы должны идентифицировать строку кода уникально. Мы оцениваем это (поскольку одна и та же строка может несколько раз появляться в файле) по пути и содержимому строки.
Мы запрашиваем добавленные строки, присоединяя их к удалённым строкам - фильтруя случаи, когда последние происходят более недавно, чем первые. Это даёт нам удалённые строки, из которых мы можем вычислить время между этими двумя событиями.
Наконец, мы агрегируем по этому набору данных, чтобы вычислить среднее количество дней, которые строки остаются в репозитории по дням недели.
Файлы отсортированные по среднему возрасти кода
Этот запрос использует тот же принцип, что и В какой день недели код имеет наибольшую вероятность остаться в репозитории - с целью уникально идентифицировать строку кода, используя путь и содержимое строки. Это позволяет нам определить время между добавлением и удалением строки. Мы фильтруем только текущие файлы и коды и усредняем время для каждого файла по строкам.
Кто чаще всего пишет больше тестов / CPP кода / комментариев?
Существует несколько способов решить этот вопрос. Сосредоточившись на соотношении кода к тестам, этот запрос относительно прост - посчитаем количество вкладов в папки, содержащие tests
, и вычислим соотношение к общему количеству вкладов.
Обратите внимание, что мы ограничиваем пользователей более чем 20 изменениями, чтобы сосредоточиться на регулярных участниках и избежать предвзятости к разовым вкладам.
Мы можем изобразить это распределение в виде гистограммы.
Большинство участников пишут больше кода, чем тестов, как и следовало ожидать.
Что насчет тех, кто добавляет больше всего комментариев при внесении кода?
Обратите внимание, что мы сортируем по вкладкам кода. Удивительно высокий % для всех наших крупнейших участников и часть того, что делает наш код таким читаемым.
Как меняются коммиты автора со временем в отношении % кода/комментариев?
Чтобы вычислить это по авторам, ничего сложного,
В идеале, однако, мы хотим увидеть, как это меняется в совокупности по всем авторам с первого дня, когда они начинают коммитить. Снижают ли они постепенно количество комментариев, которые пишут?
Чтобы вычислить это, мы вначале определяем отношение комментариев каждого автора с течением времени - аналогично Кто чаще всего пишет больше тестов / CPP кода / комментариев?. Это соединяется с датой начала каждого автора, позволяя нам вычислить соотношение комментариев по смещению недели.
После вычисления среднего по смещению недель по всем авторам, мы отбираем эти результаты, выбирая каждую 10-ю неделю.
Обнадеживающе, наш % комментариев достаточно стабилен и не ухудшается по мере внесения вклада автора.
Каково среднее время, прежде чем код будет переписан, и медиана (период полураспада кода)?
Мы можем использовать тот же принцип, что и Список файлов, которые переписывались наибольшее количество раз или наибольшее количество авторов для идентификации переписываний, но учитывая все файлы. Оконная функция используется для вычисления времени между переписываниями для каждого файла. С этого мы можем рассчитать среднее и медиану по всем файлам.
Какое худшее время для написания кода с точки зрения высокой вероятности переписывания?
Похоже на Каково среднее время, прежде чем код будет переписан, и медиана (период полураспада кода)? и Список файлов, которые переписывались наибольшее количество раз или наибольшее количество авторов, за исключением того, что мы агрегируем по дню недели. Настройте по мере необходимости, например, по месяцу года.
Код каких авторов является самым «липким»?
Мы определяем "липкость" как то, как долго код автора остается до его переписывания. Аналогично предыдущему вопросу Каково среднее время, прежде чем код будет переписан, и медиана (период полураспада кода)? - используя тот же показатель для переписываний, т.е. 50% добавлений и 50% удалений из файла. Мы вычисляем среднее время переписывания для каждого автора и учитываем только участников с более чем двумя файлами.
Наибольшее количество последовательных дней коммитов автором
Этот запрос сначала требует от нас вычислить дни, когда автор делал коммиты. Используя оконную функцию, разбивая по авторам, мы можем вычислить дни между их коммитами. Для каждого коммита, если время с последнего коммита составило 1 день, мы отмечаем это как последовательное (1), иначе - 0, сохраняя этот результат в consecutive_day
.
Наши последующие массивные функции вычисляют самую длинную последовательность последовательных единиц для каждого автора. Сначала используется функция groupArray
, чтобы собрать все значения consecutive_day
для автора. Этот массив из 1s и 0s затем разбивается на подмассивы по значениям 0. Наконец, мы вычисляем самый длинный подмассив.
История коммитов файла построчно
Файлы могут быть переименованы. Когда это происходит, мы получаем событие переименования, где колонка path
устанавливается в новый путь файла, а old_path
представляет собой предыдущее местоположение, например:
Это затрудняет просмотр полной истории файла, поскольку у нас нет единого значения, связывающего все изменения линий или файлов.
Чтобы решить эту проблему, мы можем использовать Пользовательские функции (UDF). В настоящее время они не могут быть рекурсивными, поэтому, чтобы определить историю файла, мы должны определить ряд UDF, которые вызывают друг друга явно.
Это означает, что мы можем отслеживать переименования на максимум до 5 уровней глубины - приведенный ниже пример имеет глубину 5. Маловероятно, что файл будет переименован больше раз, чем это, так что на данный момент этого достаточно.
Вызывая file_path_history('src/Storages/StorageReplicatedMergeTree.cpp')
, мы рекурсируем через историю переименований, где каждая функция вызывает следующий уровень с old_path
. Результаты объединяются с помощью arrayConcat
.
Например,
Мы можем использовать эту возможность, чтобы собрать коммиты для всей истории файла. В этом примере мы показываем один коммит для каждого из значений path
.
Нерешенные вопросы
Git blame
Это особенно сложно получить точный результат из-за невозможности в настоящее время сохранять состояние в массивных функциях. Это станет возможным с использованием arrayFold
или arrayReduce
, которые позволяют удерживать состояние на каждой итерации.
Приблизительное решение, достаточное для высокоуровневого анализа, может выглядеть примерно так:
Мы приветствуем точные и улучшенные решения здесь.