Skip to main content
Edit this page

GitHubデータを使用したClickHouseでのクエリ記述

このデータセットには、ClickHouseリポジトリのすべてのコミットと変更が含まれています。これは、ClickHouseに付属しているネイティブのgit-importツールを使用して生成できます。

生成されたデータは、以下の各テーブルに対して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行

目次

データの生成

これは任意です。データは自由に配布しています - データのダウンロードと挿入を参照してください。

git clone git@github.com:ClickHouse/ClickHouse.git
cd ClickHouse
clickhouse git-import --skip-paths 'generated\.cpp|^(contrib|docs?|website|libs/(libcityhash|liblz4|libdivide|libvectorclass|libdouble-conversion|libcpuid|libzstd|libfarmhash|libmetrohash|libpoco|libwidechar_width))/' --skip-commits-with-messages '^Merge branch '

この操作は、ClickHouseリポジトリに対して約3分(2022年11月8日時点のMacBook Pro 2021で)かかります。

利用可能なオプションの完全なリストは、ツールのネイティブヘルプから取得できます。

clickhouse git-import -h

このヘルプでは、上記の各テーブルのDDLも提供されます。例えば:

CREATE TABLE git.commits
(
hash String,
author LowCardinality(String),
time DateTime,
message String,
files_added UInt32,
files_deleted UInt32,
files_renamed UInt32,
files_modified UInt32,
lines_added UInt32,
lines_deleted UInt32,
hunks_added UInt32,
hunks_removed UInt32,
hunks_changed UInt32
) ENGINE = MergeTree ORDER BY time;

これらのクエリは、どのリポジトリでも機能するはずです。自由に探索して、結果を報告してください 実行時間に関するいくつかのガイドライン(2022年11月時点):

  • Linux - ~/clickhouse git-import - 160分

データのダウンロードと挿入

以下のデータを使用して、作業環境を再現できます。あるいは、このデータセットはplay.clickhouse.comで入手可能です - 詳細はクエリを参照してください。

以下のリポジトリのために生成されたファイルが見つかります:

このデータを挿入するには、以下のクエリを実行してデータベースを準備します:

DROP DATABASE IF EXISTS git;
CREATE DATABASE git;

CREATE TABLE git.commits
(
hash String,
author LowCardinality(String),
time DateTime,
message String,
files_added UInt32,
files_deleted UInt32,
files_renamed UInt32,
files_modified UInt32,
lines_added UInt32,
lines_deleted UInt32,
hunks_added UInt32,
hunks_removed UInt32,
hunks_changed UInt32
) ENGINE = MergeTree ORDER BY time;

CREATE TABLE git.file_changes
(
change_type Enum('Add' = 1, 'Delete' = 2, 'Modify' = 3, 'Rename' = 4, 'Copy' = 5, 'Type' = 6),
path LowCardinality(String),
old_path LowCardinality(String),
file_extension LowCardinality(String),
lines_added UInt32,
lines_deleted UInt32,
hunks_added UInt32,
hunks_removed UInt32,
hunks_changed UInt32,

commit_hash String,
author LowCardinality(String),
time DateTime,
commit_message String,
commit_files_added UInt32,
commit_files_deleted UInt32,
commit_files_renamed UInt32,
commit_files_modified UInt32,
commit_lines_added UInt32,
commit_lines_deleted UInt32,
commit_hunks_added UInt32,
commit_hunks_removed UInt32,
commit_hunks_changed UInt32
) ENGINE = MergeTree ORDER BY time;

CREATE TABLE git.line_changes
(
sign Int8,
line_number_old UInt32,
line_number_new UInt32,
hunk_num UInt32,
hunk_start_line_number_old UInt32,
hunk_start_line_number_new UInt32,
hunk_lines_added UInt32,
hunk_lines_deleted UInt32,
hunk_context LowCardinality(String),
line LowCardinality(String),
indent UInt8,
line_type Enum('Empty' = 0, 'Comment' = 1, 'Punct' = 2, 'Code' = 3),

prev_commit_hash String,
prev_author LowCardinality(String),
prev_time DateTime,

file_change_type Enum('Add' = 1, 'Delete' = 2, 'Modify' = 3, 'Rename' = 4, 'Copy' = 5, 'Type' = 6),
path LowCardinality(String),
old_path LowCardinality(String),
file_extension LowCardinality(String),
file_lines_added UInt32,
file_lines_deleted UInt32,
file_hunks_added UInt32,
file_hunks_removed UInt32,
file_hunks_changed UInt32,

commit_hash String,
author LowCardinality(String),
time DateTime,
commit_message String,
commit_files_added UInt32,
commit_files_deleted UInt32,
commit_files_renamed UInt32,
commit_files_modified UInt32,
commit_lines_added UInt32,
commit_lines_deleted UInt32,
commit_hunks_added UInt32,
commit_hunks_removed UInt32,
commit_hunks_changed UInt32
) ENGINE = MergeTree ORDER BY time;

データを挿入するには、INSERT INTO SELECTs3機能を使用します。例えば、以下のようにClickHouseファイルをそれぞれのテーブルに挿入します:

commits

INSERT INTO git.commits SELECT *
FROM s3('https://datasets-documentation.s3.amazonaws.com/github/commits/clickhouse/commits.tsv.xz', 'TSV', 'hash String,author LowCardinality(String), time DateTime, message String, files_added UInt32, files_deleted UInt32, files_renamed UInt32, files_modified UInt32, lines_added UInt32, lines_deleted UInt32, hunks_added UInt32, hunks_removed UInt32, hunks_changed UInt32')

0 rows in set. Elapsed: 1.826 sec. Processed 62.78 thousand rows, 8.50 MB (34.39 thousand rows/s., 4.66 MB/s.)

file_changes

INSERT INTO git.file_changes SELECT *
FROM s3('https://datasets-documentation.s3.amazonaws.com/github/commits/clickhouse/file_changes.tsv.xz', 'TSV', 'change_type Enum(\'Add\' = 1, \'Delete\' = 2, \'Modify\' = 3, \'Rename\' = 4, \'Copy\' = 5, \'Type\' = 6), path LowCardinality(String), old_path LowCardinality(String), file_extension LowCardinality(String), lines_added UInt32, lines_deleted UInt32, hunks_added UInt32, hunks_removed UInt32, hunks_changed UInt32, commit_hash String, author LowCardinality(String), time DateTime, commit_message String, commit_files_added UInt32, commit_files_deleted UInt32, commit_files_renamed UInt32, commit_files_modified UInt32, commit_lines_added UInt32, commit_lines_deleted UInt32, commit_hunks_added UInt32, commit_hunks_removed UInt32, commit_hunks_changed UInt32')

0 rows in set. Elapsed: 2.688 sec. Processed 266.05 thousand rows, 48.30 MB (98.97 thousand rows/s., 17.97 MB/s.)

line_changes

INSERT INTO git.line_changes SELECT *
FROM s3('https://datasets-documentation.s3.amazonaws.com/github/commits/clickhouse/line_changes.tsv.xz', 'TSV', ' sign Int8, line_number_old UInt32, line_number_new UInt32, hunk_num UInt32, hunk_start_line_number_old UInt32, hunk_start_line_number_new UInt32, hunk_lines_added UInt32,\n hunk_lines_deleted UInt32, hunk_context LowCardinality(String), line LowCardinality(String), indent UInt8, line_type Enum(\'Empty\' = 0, \'Comment\' = 1, \'Punct\' = 2, \'Code\' = 3), prev_commit_hash String, prev_author LowCardinality(String), prev_time DateTime, file_change_type Enum(\'Add\' = 1, \'Delete\' = 2, \'Modify\' = 3, \'Rename\' = 4, \'Copy\' = 5, \'Type\' = 6),\n path LowCardinality(String), old_path LowCardinality(String), file_extension LowCardinality(String), file_lines_added UInt32, file_lines_deleted UInt32, file_hunks_added UInt32, file_hunks_removed UInt32, file_hunks_changed UInt32, commit_hash String,\n author LowCardinality(String), time DateTime, commit_message String, commit_files_added UInt32, commit_files_deleted UInt32, commit_files_renamed UInt32, commit_files_modified UInt32, commit_lines_added UInt32, commit_lines_deleted UInt32, commit_hunks_added UInt32, commit_hunks_removed UInt32, commit_hunks_changed UInt32')

0 rows in set. Elapsed: 50.535 sec. Processed 7.54 million rows, 2.09 GB (149.11 thousand rows/s., 41.40 MB/s.)

クエリ

ツールはヘルプ出力に基づいて、いくつかのクエリを提案しています。私たちは、これらに加えて、興味深い追加のクエリにも回答しています。これらのクエリは、ツールの任意の順序に対しておおよそ増加する複雑さを持っています。

このデータセットは、git_clickhouseデータベースでplay.clickhouse.comで入手可能です。すべてのクエリのために、この環境へのリンクを提供しますが、データベース名は必要に応じて調整します。データ収集の時期によって、playの結果はここに表示されているものと異なる場合があります。

単一ファイルの履歴

最もシンプルなクエリです。ここでは、StorageReplicatedMergeTree.cppのすべてのコミットメッセージを表示します。これらはより興味深いものが多いので、最新のメッセージを最初に表示するようにソートします。

play

SELECT
time,
substring(commit_hash, 1, 11) AS commit,
change_type,
author,
path,
old_path,
lines_added,
lines_deleted,
commit_message
FROM git.file_changes
WHERE path = 'src/Storages/StorageReplicatedMergeTree.cpp'
ORDER BY time DESC
LIMIT 10

┌────────────────time─┬─commit──────┬─change_type─┬─author─────────────┬─path────────────────────────────────────────┬─old_path─┬─lines_added─┬─lines_deleted─┬─commit_message───────────────────────────────────┐
2022-10-30 16:30:51 │ c68ab231f91 │ Modify │ Alexander Tokmakov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 1310 │ partを削除状態でアクセスする修正 │
2022-10-23 16:24:20 │ b40d9200d20 │ Modify │ Anton Popov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 2830 │ DataPartStorageの定数性のセマンティクス向上 │
2022-10-23 01:23:1556e5daba0c9 │ Modify │ Anton Popov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 2844 │ DataPartStorageBuilderを削除 │
2022-10-21 13:35:37851f556d65a │ Modify │ Igor Nikonov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 32 │ 使用されていないパラメータを削除 │
2022-10-21 13:02:5213d31eefbc3 │ Modify │ Igor Nikonov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 44 │ Replicated merge treeの改良 │
2022-10-21 12:25:194e76629aafc │ Modify │ Azat Khuzhin │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 32-Wshorten-64-to-32への修正 │
2022-10-19 13:59:2805e6b94b541 │ Modify │ Antonio Andelic │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 40 │ 改良 │
2022-10-19 13:34:20 │ e5408aac991 │ Modify │ Antonio Andelic │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 353 │ 論理を簡素化 │
2022-10-18 15:36:117befe2825c9 │ Modify │ Alexey Milovidov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 22 │ StorageReplicatedMergeTree.cppの更新 │
2022-10-18 15:35:440623ad4e374 │ Modify │ Alexey Milovidov │ src/Storages/StorageReplicatedMergeTree.cpp │ │ 11 │ StorageReplicatedMergeTree.cppの更新 │
└─────────────────────┴─────────────┴─────────────┴────────────────────┴─────────────────────────────────────────────┴──────────┴─────────────┴───────────────┴──────────────────────────────────────────────────┘

10 rows in set. Elapsed: 0.006 sec. Processed 12.10 thousand rows, 1.60 MB (1.93 million rows/s., 255.40 MB/s.)

リネームイベントの前に存在していた変更は表示しないため、リネームを除外し、行変更を確認することもできます:

play

SELECT
time,
substring(commit_hash, 1, 11) AS commit,
sign,
line_number_old,
line_number_new,
author,
line
FROM git.line_changes
WHERE path = 'src/Storages/StorageReplicatedMergeTree.cpp'
ORDER BY line_number_new ASC
LIMIT 10

┌────────────────time─┬─commit──────┬─sign─┬─line_number_old─┬─line_number_new─┬─author───────────┬─line──────────────────────────────────────────────────┐
2020-04-16 02:06:10 │ cdeda4ab915 │ -111 │ Alexey Milovidov │ #include <Disks/DiskSpaceMonitor.h> │
2020-04-16 02:06:10 │ cdeda4ab915 │ 121 │ Alexey Milovidov │ #include <Core/Defines.h> │
2020-04-16 02:06:10 │ cdeda4ab915 │ 122 │ Alexey Milovidov │ │
2021-05-03 23:46:5102ce9cc7254 │ -132 │ Alexey Milovidov │ #include <Common/FieldVisitors.h> │
2021-05-27 22:21:02 │ e2f29b9df02 │ -132 │ s-kat │ #include <Common/FieldVisitors.h> │
2022-10-03 22:30:50210882b9c4d │ 123 │ alesapin │ #include <ranges> │
2022-10-23 16:24:20 │ b40d9200d20 │ 123 │ Anton Popov │ #include <cstddef> │
2021-06-20 09:24:434c391f8e994 │ 123 │ Mike Kot │ #include "Common/hex.h" │
2021-12-29 09:18:568112a712336 │ -165 │ avogar │ #include <Common/ThreadPool.h> │
2022-04-21 20:19:139133e398b8c │ 11112 │ Nikolai Kochetov │ #include <Storages/MergeTree/DataPartStorageOnDisk.h> │
└─────────────────────┴─────────────┴──────┴─────────────────┴─────────────────┴──────────────────┴───────────────────────────────────────────────────────┘

10 rows in set. Elapsed: 0.258 sec. Processed 7.54 million rows, 654.92 MB (29.24 million rows/s., 2.54 GB/s.)

リネームを考慮したファイルの行ごとのコミット履歴を見つけるよりも複雑なクエリもあります。

現在のアクティブファイルを見つける

これは、リポジトリ内の現在のファイルのみを考慮したい後の分析にとって重要です。このセットは、リネームまたは削除(および再追加/リネーム)されていないファイルとして推定します。

dbms、libs、tests/testflows/ディレクトリ内のファイルに関して壊れたコミット履歴があったようです。したがって、これらも除外します。

play

SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)') ORDER BY path
LIMIT 10

┌─path────────────────────────────────────────────────────────────┐
│ tests/queries/0_stateless/01054_random_printable_ascii_ubsan.sh │
│ tests/queries/0_stateless/02247_read_bools_as_numbers_json.sh │
│ tests/performance/file_table_function.xml │
│ tests/queries/0_stateless/01902_self_aliases_in_columns.sql
│ tests/queries/0_stateless/01070_h3_get_base_cell.reference │
│ src/Functions/ztest.cpp │
│ src/Interpreters/InterpreterShowTablesQuery.h │
│ src/Parsers/Kusto/ParserKQLStatement.h │
│ tests/queries/0_stateless/00938_dataset_test.sql
│ src/Dictionaries/Embedded/GeodataProviders/Types.h │
└─────────────────────────────────────────────────────────────────┘

10 rows in set. Elapsed: 0.085 sec. Processed 532.10 thousand rows, 8.68 MB (6.30 million rows/s., 102.64 MB/s.)

これにより、ファイルがリネームされた後に元の名前に再リネームされることが可能になります。まず、リネームの結果として削除されたファイルのold_pathのリストを集約します。これを最後の操作と統合します。最後に、最終イベントがDeleteではないものにフィルタリングします。

play

SELECT uniq(path)
FROM
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)') ORDER BY path
)

┌─uniq(path)─┐
18559
└────────────┘
1 row in set. Elapsed: 0.089 sec. Processed 532.10 thousand rows, 8.68 MB (6.01 million rows/s., 97.99 MB/s.)

このパターンをgit list-filesに適用すると、18155件が報告されます。

git ls-files | grep -v -E 'generated\.cpp|^(contrib|docs?|website|libs/(libcityhash|liblz4|libdivide|libvectorclass|libdouble-conversion|libcpuid|libzstd|libfarmhash|libmetrohash|libpoco|libwidechar_width))/' | wc -l
18155

したがって、現行のソリューションは現在のファイルの推定値です

ここでの違いは、いくつかの要因に起因しています:

  • リネームはファイルの他の変更と同時に発生する可能性があります。これらはfile_changes内で別のイベントとしてリストされますが、同じ時間で発生します。argMax関数はこれを区別する方法がなく、最初の値を選択します。挿入の自然な順序(正しい順序を知る唯一の手段)は、ユニオン全体で維持されないため、変更されたイベントが選択される可能性があります。例えば、src/Functions/geometryFromColumn.hファイルは、src/Functions/geometryConverters.hにリネームされる前に複数の変更があります。現在のソリューションでは、最後の変更イベントがModifyとして選ばれる可能性があるため、src/Functions/geometryFromColumn.hが保持されます。

play

SELECT
change_type,
path,
old_path,
time,
commit_hash
FROM git.file_changes
WHERE (path = 'src/Functions/geometryFromColumn.h') OR (old_path = 'src/Functions/geometryFromColumn.h')

┌─change_type─┬─path───────────────────────────────┬─old_path───────────────────────────┬────────────────time─┬─commit_hash──────────────────────────────┐
Add │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:169376b676e9a9bb8911b872e1887da85a45f7479d │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:166d59be5ea4768034f6526f7f9813062e0c369f7b │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:1633acc2aa5dc091a7cb948f78c558529789b2bad8 │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:1678e0db268ceadc42f82bc63a77ee1a4da6002463 │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:1614a891057d292a164c4179bfddaef45a74eaf83a │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:16 │ d0d6e6953c2a2af9fb2300921ff96b9362f22edb │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:16 │ fe8382521139a58c0ba277eb848e88894658db66 │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:163be3d5cde8788165bc0558f1e2a22568311c3103 │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:16 │ afad9bf4d0a55ed52a3f55483bc0973456e10a56 │
Modify │ src/Functions/geometryFromColumn.h │ │ 2021-03-11 12:08:16 │ e3290ecc78ca3ea82b49ebcda22b5d3a4df154e6 │
Rename │ src/Functions/geometryConverters.h │ src/Functions/geometryFromColumn.h │ 2021-03-11 12:08:16125945769586baf6ffd15919b29565b1b2a63218 │
└─────────────┴────────────────────────────────────┴────────────────────────────────────┴─────────────────────┴──────────────────────────────────────────┘
11 rows in set. Elapsed: 0.030 sec. Processed 266.05 thousand rows, 6.61 MB (8.89 million rows/s., 220.82 MB/s.)
  • 壊れたコミット履歴 - 削除イベントの欠落。ソースと原因は未定です。

これらの違いは、私たちの分析に有意に影響を及ぼすべきではありません。このクエリの改善版を歓迎します

最も多く変更されたファイルをリストする

現在のファイルに制限し、変更数を削除と追加の合計として考えます。

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
path,
sum(lines_added) + sum(lines_deleted) AS modifications
FROM git.file_changes
WHERE (path IN (current_files)) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY path
ORDER BY modifications DESC
LIMIT 10

┌─path───────────────────────────────────────────────────┬─modifications─┐
│ src/Storages/StorageReplicatedMergeTree.cpp │ 21871
│ src/Storages/MergeTree/MergeTreeData.cpp │ 17709
│ programs/client/Client.cpp │ 15882
│ src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp │ 14249
│ src/Interpreters/InterpreterSelectQuery.cpp │ 12636
│ src/Parsers/ExpressionListParsers.cpp │ 11794
│ src/Analyzer/QueryAnalysisPass.cpp │ 11760
│ src/Coordination/KeeperStorage.cpp │ 10225
│ src/Functions/FunctionsConversion.h │ 9247
│ src/Parsers/ExpressionElementParsers.cpp │ 8197
└────────────────────────────────────────────────────────┴───────────────┘

10 rows in set. Elapsed: 0.134 sec. Processed 798.15 thousand rows, 16.46 MB (5.95 million rows/s., 122.62 MB/s.)

コミットは通常、週のどの日に発生するか?

play

SELECT
day_of_week,
count() AS c
FROM git.commits
GROUP BY dayOfWeek(time) AS day_of_week

┌─day_of_week─┬─────c─┐
110575
210645
310748
410944
510090
64617
75166
└─────────────┴───────┘
7 rows in set. Elapsed: 0.262 sec. Processed 62.78 thousand rows, 251.14 KB (239.73 thousand rows/s., 958.93 KB/s.)

金曜日に生産性の低下が見られるのは理にかなっています。週末にコードをコミットしている人々を見るのは素晴らしいです!貢献者の皆さんに大感謝!

サブディレクトリ/ファイルの履歴 - 行数、コミット数、寄稿者数の推移

フィルタリングされない場合、これは非常に大きなクエリ結果を生成します。したがって、以下の例ではファイルまたはサブディレクトリでフィルタリングできるようにします。ここでは、toStartOfWeek関数を使用して週ごとにグループ化します - 必要に応じて適応してください。

play

SELECT
week,
sum(lines_added) AS lines_added,
sum(lines_deleted) AS lines_deleted,
uniq(commit_hash) AS num_commits,
uniq(author) AS authors
FROM git.file_changes
WHERE path LIKE 'src/Storages%'
GROUP BY toStartOfWeek(time) AS week
ORDER BY week ASC
LIMIT 10

┌───────week─┬─lines_added─┬─lines_deleted─┬─num_commits─┬─authors─┐
2020-03-29493543
2020-04-059406015514
2020-04-1214726073211
2020-04-199178413912
2020-04-2610676263610
2020-05-035144352710
2020-05-1025525374812
2020-05-1735851913839
2020-05-24285118127418
2020-05-31277120777716
└────────────┴─────────────┴───────────────┴─────────────┴─────────┘
10 rows in set. Elapsed: 0.043 sec. Processed 266.05 thousand rows, 15.85 MB (6.12 million rows/s., 364.61 MB/s.)

このデータは視覚化に適しています。以下では Superset を使用します。

追加された行と削除された行に関して:

コミットと著者に関して:

最大著者数のファイルリスト

現在のファイルのみに制限します。

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
path,
uniq(author) AS num_authors
FROM git.file_changes
WHERE path IN (current_files)
GROUP BY path
ORDER BY num_authors DESC
LIMIT 10

┌─path────────────────────────────────────────┬─num_authors─┐
│ src/Core/Settings.h │ 127
│ CMakeLists.txt │ 96
.gitmodules │ 85
│ src/Storages/MergeTree/MergeTreeData.cpp │ 72
│ src/CMakeLists.txt │ 71
│ programs/server/Server.cpp │ 70
│ src/Interpreters/Context.cpp │ 64
│ src/Storages/StorageReplicatedMergeTree.cpp │ 63
│ src/Common/ErrorCodes.cpp │ 61
│ src/Interpreters/InterpreterSelectQuery.cpp │ 59
└─────────────────────────────────────────────┴─────────────┘

10 rows in set. Elapsed: 0.239 sec. Processed 798.15 thousand rows, 14.13 MB (3.35 million rows/s., 59.22 MB/s.)

リポジトリ内の最古のコード行

現在のファイルのみに制限します。

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
any(path) AS file_path,
line,
max(time) AS latest_change,
any(file_change_type)
FROM git.line_changes
WHERE path IN (current_files)
GROUP BY line
ORDER BY latest_change ASC
LIMIT 10

┌─file_path───────────────────────────────────┬─line────────────────────────────────────────────────────────┬───────latest_change─┬─any(file_change_type)─┐
│ utils/compressor/test.sh │ ./compressor -d < compressor.snp > compressor2 │ 2011-06-17 22:19:39Modify
│ utils/compressor/test.sh │ ./compressor < compressor > compressor.snp │ 2011-06-17 22:19:39Modify
│ utils/compressor/test.sh │ ./compressor -d < compressor.qlz > compressor2 │ 2014-02-24 03:14:30Add
│ utils/compressor/test.sh │ ./compressor < compressor > compressor.qlz │ 2014-02-24 03:14:30Add
│ utils/config-processor/config-processor.cpp │ if (argc != 2)2014-02-26 19:10:00Add
│ utils/config-processor/config-processor.cpp │ std::cerr << "std::exception: " << e.what() << std::endl;2014-02-26 19:10:00Add
│ utils/config-processor/config-processor.cpp │ std::cerr << "Exception: " << e.displayText() << std::endl;2014-02-26 19:10:00Add
│ utils/config-processor/config-processor.cpp │ Poco::XML::DOMWriter().writeNode(std::cout, document);2014-02-26 19:10:00Add
│ utils/config-processor/config-processor.cpp │ std::cerr << "Some exception" << std::endl;2014-02-26 19:10:00Add
│ utils/config-processor/config-processor.cpp │ std::cerr << "usage: " << argv[0] << " path" << std::endl;2014-02-26 19:10:00Add
└─────────────────────────────────────────────┴─────────────────────────────────────────────────────────────┴─────────────────────┴───────────────────────┘

10 rows in set. Elapsed: 1.101 sec. Processed 8.07 million rows, 905.86 MB (7.33 million rows/s., 823.13 MB/s.)

最も古い履歴を持つファイル

現在のファイルのみに制限します。

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
count() AS c,
path,
max(time) AS latest_change
FROM git.file_changes
WHERE path IN (current_files)
GROUP BY path
ORDER BY c DESC
LIMIT 10

┌───c─┬─path────────────────────────────────────────┬───────latest_change─┐
790 │ src/Storages/StorageReplicatedMergeTree.cpp │ 2022-10-30 16:30:51
788 │ src/Storages/MergeTree/MergeTreeData.cpp │ 2022-11-04 09:26:44
752 │ src/Core/Settings.h │ 2022-10-25 11:35:25
749 │ CMakeLists.txt │ 2022-10-05 21:00:49
575 │ src/Interpreters/InterpreterSelectQuery.cpp │ 2022-11-01 10:20:10
563 │ CHANGELOG.md │ 2022-10-27 08:19:50
491 │ src/Interpreters/Context.cpp │ 2022-10-25 12:26:29
437 │ programs/server/Server.cpp │ 2022-10-21 12:25:19
375 │ programs/client/Client.cpp │ 2022-11-03 03:16:55
350 │ src/CMakeLists.txt │ 2022-10-24 09:22:37
└─────┴─────────────────────────────────────────────┴─────────────────────┘

10 rows in set. Elapsed: 0.124 sec. Processed 798.15 thousand rows, 14.71 MB (6.44 million rows/s., 118.61 MB/s.)

私たちのコアデータ構造である Merge Tree は、当然のことながら常に進化しており、長い編集の歴史があります!

ドキュメントとコードに関する寄稿者の分布

データ収集中に、docs/ フォルダ内の変更は非常に汚れたコミット履歴のためにフィルタリングされました。そのため、このクエリの結果は正確ではありません。

特定の時期、例えばリリース日周辺に、私たちはより多くのドキュメントを記述していますか?countIf 関数を使用して単純な比率を計算し、bar 関数を使用して結果を視覚化できます。

play

SELECT
day,
bar(docs_ratio * 1000, 0, 100, 100) AS bar
FROM
(
SELECT
day,
countIf(file_extension IN ('h', 'cpp', 'sql')) AS code,
countIf(file_extension = 'md') AS docs,
docs / (code + docs) AS docs_ratio
FROM git.line_changes
WHERE (sign = 1) AND (file_extension IN ('h', 'cpp', 'sql', 'md'))
GROUP BY dayOfMonth(time) AS day
)

┌─day─┬─bar─────────────────────────────────────────────────────────────┐
1 │ ███████████████████████████████████▍ │
2 │ ███████████████████████▋ │
3 │ ████████████████████████████████▋ │
4 │ █████████████ │
5 │ █████████████████████▎ │
6 │ ████████ │
7 │ ███▋ │
8 │ ████████▌ │
9 │ ██████████████▎ │
10 │ █████████████████▏ │
11 │ █████████████▎ │
12 │ ███████████████████████████████████▋ │
13 │ █████████████████████████████▎ │
14 │ ██████▋ │
15 │ █████████████████████████████████████████▊ │
16 │ ██████████▎ │
17 │ ██████████████████████████████████████▋ │
18 │ █████████████████████████████████▌ │
19 │ ███████████ │
20 │ █████████████████████████████████▊ │
21 │ █████ │
22 │ ███████████████████████▋ │
23 │ ███████████████████████████▌ │
24 │ ███████▌ │
25 │ ██████████████████████████████████▎ │
26 │ ███████████▏ │
27 │ ███████████████████████████████████████████████████████████████ │
28 │ ████████████████████████████████████████████████████▏ │
29 │ ███▌ │
30 │ ████████████████████████████████████████▎ │
31 │ █████████████████████████████████▏ │
└─────┴─────────────────────────────────────────────────────────────────┘

31 rows in set. Elapsed: 0.043 sec. Processed 7.54 million rows, 40.53 MB (176.71 million rows/s., 950.40 MB/s.)

月末近くに少し多いかもしれませんが、全体的には良好な均等分布を維持しています。再度、これはデータ挿入中のドキュメントフィルタリングによる信頼性が低いです。

最も多様な影響を持つ著者

ここでの多様性は、著者が貢献したユニークなファイルの数とします。

play

SELECT
author,
uniq(path) AS num_files
FROM git.file_changes
WHERE (change_type IN ('Add', 'Modify')) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY author
ORDER BY num_files DESC
LIMIT 10

┌─author─────────────┬─num_files─┐
│ Alexey Milovidov │ 8433
│ Nikolai Kochetov │ 3257
│ Vitaly Baranov │ 2316
│ Maksim Kita │ 2172
│ Azat Khuzhin │ 1988
│ alesapin │ 1818
│ Alexander Tokmakov │ 1751
│ Amos Bird │ 1641
│ Ivan │ 1629
│ alexey-milovidov │ 1581
└────────────────────┴───────────┘

10 rows in set. Elapsed: 0.041 sec. Processed 266.05 thousand rows, 4.92 MB (6.56 million rows/s., 121.21 MB/s.)

最近の作業において、最も多様なコミットを持つ人物を見てみましょう。日付で制限するのではなく、著者の最後の N 回のコミットに制限します(この場合、私たちは 3 を使用しましたが、自由に変更してください):

play

SELECT
author,
sum(num_files_commit) AS num_files
FROM
(
SELECT
author,
commit_hash,
uniq(path) AS num_files_commit,
max(time) AS commit_time
FROM git.file_changes
WHERE (change_type IN ('Add', 'Modify')) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY
author,
commit_hash
ORDER BY
author ASC,
commit_time DESC
LIMIT 3 BY author
)
GROUP BY author
ORDER BY num_files DESC
LIMIT 10

┌─author───────────────┬─num_files─┐
│ Mikhail │ 782
│ Li Yin │ 553
│ Roman Peshkurov │ 119
│ Vladimir Smirnov │ 88
│ f1yegor │ 65
│ maiha │ 54
│ Vitaliy Lyudvichenko │ 53
│ Pradeep Chhetri │ 40
│ Orivej Desh │ 38
│ liyang │ 36
└──────────────────────┴───────────┘

10 rows in set. Elapsed: 0.106 sec. Processed 266.05 thousand rows, 21.04 MB (2.52 million rows/s., 198.93 MB/s.)

著者のお気に入りファイル

ここでは、創設者である Alexey Milovidov を選択し、分析を現在のファイルに制限します。

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
path,
count() AS c
FROM git.file_changes
WHERE (author = 'Alexey Milovidov') AND (path IN (current_files))
GROUP BY path
ORDER BY c DESC
LIMIT 10

┌─path────────────────────────────────────────┬───c─┐
│ CMakeLists.txt │ 165
│ CHANGELOG.md │ 126
│ programs/server/Server.cpp │ 73
│ src/Storages/MergeTree/MergeTreeData.cpp │ 71
│ src/Storages/StorageReplicatedMergeTree.cpp │ 68
│ src/Core/Settings.h │ 65
│ programs/client/Client.cpp │ 57
│ programs/server/play.html │ 48
.gitmodules │ 47
│ programs/install/Install.cpp │ 37
└─────────────────────────────────────────────┴─────┘

10 rows in set. Elapsed: 0.106 sec. Processed 798.15 thousand rows, 13.97 MB (7.51 million rows/s., 131.41 MB/s.)

これは理にかなっています。なぜなら、Alexey は変更ログの維持を担当しているからです。しかし、ファイルの基本名を使用してお気に入りのファイルを特定すると、リネームを考慮に入れ、コードの貢献に焦点を当てることができます。

play

SELECT
base,
count() AS c
FROM git.file_changes
WHERE (author = 'Alexey Milovidov') AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY basename(path) AS base
ORDER BY c DESC
LIMIT 10

┌─base───────────────────────────┬───c─┐
│ StorageReplicatedMergeTree.cpp │ 393
│ InterpreterSelectQuery.cpp │ 299
│ Aggregator.cpp │ 297
│ Client.cpp │ 280
│ MergeTreeData.cpp │ 274
│ Server.cpp │ 264
│ ExpressionAnalyzer.cpp │ 259
│ StorageMergeTree.cpp │ 239
│ Settings.h │ 225
│ TCPHandler.cpp │ 205
└────────────────────────────────┴─────┘
10 rows in set. Elapsed: 0.032 sec. Processed 266.05 thousand rows, 5.68 MB (8.22 million rows/s., 175.50 MB/s.)

これは彼の興味分野をより反映しているかもしれません。

著者数が最も少ない最大ファイル

これを行うには、まず最大ファイルを特定する必要があります。すべてのファイルを、履歴からのコミットによる完全なファイル再構成により推定することは非常に負担がかかります!

推定として、現在のファイルに制限し、行の追加を合計し、削除を差し引きます。次に、著者数に対する長さの比率を計算することができます。

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
path,
sum(lines_added) - sum(lines_deleted) AS num_lines,
uniqExact(author) AS num_authors,
num_lines / num_authors AS lines_author_ratio
FROM git.file_changes
WHERE path IN (current_files)
GROUP BY path
ORDER BY lines_author_ratio DESC
LIMIT 10

┌─path──────────────────────────────────────────────────────────────────┬─num_lines─┬─num_authors─┬─lines_author_ratio─┐
│ src/Common/ClassificationDictionaries/emotional_dictionary_rus.txt │ 1485901148590
│ src/Functions/ClassificationDictionaries/emotional_dictionary_rus.txt │ 55533155533
│ src/Functions/ClassificationDictionaries/charset_freq.txt │ 35722135722
│ src/Common/ClassificationDictionaries/charset_freq.txt │ 35722135722
│ tests/integration/test_storage_meilisearch/movies.json │ 19549119549
│ tests/queries/0_stateless/02364_multiSearch_function_family.reference │ 12874112874
│ src/Functions/ClassificationDictionaries/programming_freq.txt │ 943419434
│ src/Common/ClassificationDictionaries/programming_freq.txt │ 943419434
│ tests/performance/explain_ast.xml │ 591115911
│ src/Analyzer/QueryAnalysisPass.cpp │ 568615686
└───────────────────────────────────────────────────────────────────────┴───────────┴─────────────┴────────────────────┘

10 rows in set. Elapsed: 0.138 sec. Processed 798.15 thousand rows, 16.57 MB (5.79 million rows/s., 120.11 MB/s.)

テキスト辞書はあまり現実的ではないかもしれないので、ファイル拡張子フィルターを介してコードのみに制限しましょう!

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
path,
sum(lines_added) - sum(lines_deleted) AS num_lines,
uniqExact(author) AS num_authors,
num_lines / num_authors AS lines_author_ratio
FROM git.file_changes
WHERE (path IN (current_files)) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY path
ORDER BY lines_author_ratio DESC
LIMIT 10

┌─path──────────────────────────────────┬─num_lines─┬─num_authors─┬─lines_author_ratio─┐
│ src/Analyzer/QueryAnalysisPass.cpp │ 568615686
│ src/Analyzer/QueryTreeBuilder.cpp │ 8801880
│ src/Planner/Planner.cpp │ 8731873
│ src/Backups/RestorerFromBackup.cpp │ 8691869
│ utils/memcpy-bench/FastMemcpy.h │ 7701770
│ src/Planner/PlannerActionsVisitor.cpp │ 7651765
│ src/Functions/sphinxstemen.cpp │ 7281728
│ src/Planner/PlannerJoinTree.cpp │ 7081708
│ src/Planner/PlannerJoins.cpp │ 6951695
│ src/Analyzer/QueryNode.h │ 6071607
└───────────────────────────────────────┴───────────┴─────────────┴────────────────────┘
10 rows in set. Elapsed: 0.140 sec. Processed 798.15 thousand rows, 16.84 MB (5.70 million rows/s., 120.32 MB/s.)

若干の新しいバイアスがあるため、これは、最近のファイルにはコミットの機会が少ないです。1 年以上経過したファイルに制限したらどうなるのでしょうか?

play

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
min(time) AS min_date,
path,
sum(lines_added) - sum(lines_deleted) AS num_lines,
uniqExact(author) AS num_authors,
num_lines / num_authors AS lines_author_ratio
FROM git.file_changes
WHERE (path IN (current_files)) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY path
HAVING min_date <= (now() - toIntervalYear(1))
ORDER BY lines_author_ratio DESC
LIMIT 10

┌────────────min_date─┬─path───────────────────────────────────────────────────────────┬─num_lines─┬─num_authors─┬─lines_author_ratio─┐
2021-03-08 07:00:54 │ utils/memcpy-bench/FastMemcpy.h │ 7701770
2021-05-04 13:47:34 │ src/Functions/sphinxstemen.cpp │ 7281728
2021-03-14 16:52:51 │ utils/memcpy-bench/glibc/dwarf2.h │ 5921592
2021-03-08 09:04:52 │ utils/memcpy-bench/FastMemcpy_Avx.h │ 4961496
2020-10-19 01:10:50 │ tests/queries/0_stateless/01518_nullable_aggregate_states2.sql4111411
2020-11-24 14:53:34 │ programs/server/GRPCHandler.cpp │ 3991399
2021-03-09 14:10:28 │ src/DataTypes/Serializations/SerializationSparse.cpp │ 3631363
2021-08-20 15:06:57 │ src/Functions/vectorFunctions.cpp │ 13274331.75
2020-08-04 03:26:23 │ src/Interpreters/MySQL/CreateQueryConvertVisitor.cpp │ 3111311
2020-11-06 15:45:13 │ src/Storages/Rocksdb/StorageEmbeddedRocksdb.cpp │ 6112305.5
└─────────────────────┴────────────────────────────────────────────────────────────────┴───────────┴─────────────┴────────────────────┘

10 rows in set. Elapsed: 0.143 sec. Processed 798.15 thousand rows, 18.00 MB (5.58 million rows/s., 125.87 MB/s.)

コミットとコード行の時間別分布; 曜日別、著者別; 特定のサブディレクトリ用

これは、週の曜日ごとの追加された行と削除された行の数として解釈します。この場合、Functions ディレクトリ に注目します。

play

SELECT
dayOfWeek,
uniq(commit_hash) AS commits,
sum(lines_added) AS lines_added,
sum(lines_deleted) AS lines_deleted
FROM git.file_changes
WHERE path LIKE 'src/Functions%'
GROUP BY toDayOfWeek(time) AS dayOfWeek

┌─dayOfWeek─┬─commits─┬─lines_added─┬─lines_deleted─┐
14762461915782
2434180989938
34962656220883
45876567418862
55048591714518
63141360410144
7294119386451
└───────────┴─────────┴─────────────┴───────────────┘

7 rows in set. Elapsed: 0.034 sec. Processed 266.05 thousand rows, 14.66 MB (7.73 million rows/s., 425.56 MB/s.)

および、時間帯別に。

play

SELECT
hourOfDay,
uniq(commit_hash) AS commits,
sum(lines_added) AS lines_added,
sum(lines_deleted) AS lines_deleted
FROM git.file_changes
WHERE path LIKE 'src/Functions%'
GROUP BY toHour(time) AS hourOfDay

┌─hourOfDay─┬─commits─┬─lines_added─┬─lines_deleted─┐
07141693404
19021741927
26523431515
3762552493
46214801304
5381644253
610444342979
711741711678
810646044673
9135605502678
1014961333482
1118280403833
122092942815040
13187102045491
1420490286060
152311517910077
1619695685925
1713849413849
1812341933036
1916588176646
2014037492379
21132415854182
228540943955
2310033321719
└───────────┴─────────┴─────────────┴───────────────┘

24 rows in set. Elapsed: 0.039 sec. Processed 266.05 thousand rows, 14.66 MB (6.77 million rows/s., 372.89 MB/s.)

この分布は、私たちの開発チームの大部分がアムステルダムにいることを考慮すると理にかなっています。bar 関数は、これらの分布を視覚化するのに役立ちます。

play

SELECT
hourOfDay,
bar(commits, 0, 400, 50) AS commits,
bar(lines_added, 0, 30000, 50) AS lines_added,
bar(lines_deleted, 0, 15000, 50) AS lines_deleted
FROM
(
SELECT
hourOfDay,
uniq(commit_hash) AS commits,
sum(lines_added) AS lines_added,
sum(lines_deleted) AS lines_deleted
FROM git.file_changes
WHERE path LIKE 'src/Functions%'
GROUP BY toHour(time) AS hourOfDay
)

┌─hourOfDay─┬─commits───────────────────────┬─lines_added────────────────────────────────────────┬─lines_deleted──────────────────────────────────────┐
0 │ ████████▊ │ ██████▊ │ ███████████▎ │
1 │ ███████████▎ │ ███▌ │ ██████▍ │
2 │ ████████ │ ███▊ │ █████ │
3 │ █████████▌ │ ████▎ │ █▋ │
4 │ ███████▋ │ ██▍ │ ████▎ │
5 │ ████▋ │ ██▋ │ ▋ │
6 │ █████████████ │ ███████▍ │ █████████▊ │
7 │ ██████████████▋ │ ██████▊ │ █████▌ │
8 │ █████████████▎ │ ███████▋ │ ███████████████▌ │
9 │ ████████████████▊ │ ██████████████████████████████████████████████████ │ ████████▊ │
10 │ ██████████████████▋ │ ██████████▏ │ ███████████▌ │
11 │ ██████████████████████▋ │ █████████████▍ │ ████████████▋ │
12 │ ██████████████████████████ │ █████████████████████████████████████████████████ │ ██████████████████████████████████████████████████ │
13 │ ███████████████████████▍ │ █████████████████ │ ██████████████████▎ │
14 │ █████████████████████████▌ │ ███████████████ │ ████████████████████▏ │
15 │ ████████████████████████████▊ │ █████████████████████████▎ │ █████████████████████████████████▌ │
16 │ ████████████████████████▌ │ ███████████████▊ │ ███████████████████▋ │
17 │ █████████████████▎ │ ████████▏ │ ████████████▋ │
18 │ ███████████████▍ │ ██████▊ │ ██████████ │
19 │ ████████████████████▋ │ ██████████████▋ │ ██████████████████████▏ │
20 │ █████████████████▌ │ ██████▏ │ ███████▊ │
21 │ ████████████████▌ │ ██████████████████████████████████████████████████ │ █████████████▊ │
22 │ ██████████▋ │ ██████▋ │ █████████████▏ │
23 │ ████████████▌ │ █████▌ │ █████▋ │
└───────────┴───────────────────────────────┴────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘

24 rows in set. Elapsed: 0.038 sec. Processed 266.05 thousand rows, 14.66 MB (7.09 million rows/s., 390.69 MB/s.)

著者の行列:他の著者のコードを書き直す傾向を示す

sign = -1 はコード削除を示しています。句読点や空行の挿入は除外します。

プレイ

SELECT
prev_author || '(a)' as add_author,
author || '(d)' as delete_author,
count() AS c
FROM git.line_changes
WHERE (sign = -1) AND (file_extension IN ('h', 'cpp')) AND (line_type NOT IN ('Punct', 'Empty')) AND (author != prev_author) AND (prev_author != '')
GROUP BY
prev_author,
author
ORDER BY c DESC
LIMIT 1 BY prev_author
LIMIT 100

┌─prev_author──────────┬─author───────────┬─────c─┐
│ Ivan │ Alexey Milovidov │ 18554
│ Alexey Arno │ Alexey Milovidov │ 18475
│ Michael Kolupaev │ Alexey Milovidov │ 14135
│ Alexey Milovidov │ Nikolai Kochetov │ 13435
│ Andrey Mironov │ Alexey Milovidov │ 10418
│ proller │ Alexey Milovidov │ 7280
│ Nikolai Kochetov │ Alexey Milovidov │ 6806
│ alexey-milovidov │ Alexey Milovidov │ 5027
│ Vitaliy Lyudvichenko │ Alexey Milovidov │ 4390
│ Amos Bird │ Ivan Lezhankin │ 3125
│ f1yegor │ Alexey Milovidov │ 3119
│ Pavel Kartavyy │ Alexey Milovidov │ 3087
│ Alexey Zatelepin │ Alexey Milovidov │ 2978
│ alesapin │ Alexey Milovidov │ 2949
│ Sergey Fedorov │ Alexey Milovidov │ 2727
│ Ivan Lezhankin │ Alexey Milovidov │ 2618
│ Vasily Nemkov │ Alexey Milovidov │ 2547
│ Alexander Tokmakov │ Alexey Milovidov │ 2493
│ Nikita Vasilev │ Maksim Kita │ 2420
│ Anton Popov │ Amos Bird │ 2127
└──────────────────────┴──────────────────┴───────┘

20 rows in set. Elapsed: 0.098 sec. Processed 7.54 million rows, 42.16 MB (76.67 million rows/s., 428.99 MB/s.)

Sankeyチャート(SuperSet)は、これをうまく視覚化できます。視覚的多様性を高めるために、各著者の上位3つのコード削除者を取得するためにLIMIT BYを3に増やします。

Alexeyは明らかに他の人のコードを削除するのが好きです。よりバランスの取れたコード削除のビューを得るために、彼を除外しましょう。

曜日ごとの最高貢献者は誰ですか?

コミット数を単純に考えると:

プレイ

SELECT
day_of_week,
author,
count() AS c
FROM git.commits
GROUP BY
dayOfWeek(time) AS day_of_week,
author
ORDER BY
day_of_week ASC,
c DESC
LIMIT 1 BY day_of_week

┌─day_of_week─┬─author───────────┬────c─┐
1 │ Alexey Milovidov │ 2204
2 │ Alexey Milovidov │ 1588
3 │ Alexey Milovidov │ 1725
4 │ Alexey Milovidov │ 1915
5 │ Alexey Milovidov │ 1940
6 │ Alexey Milovidov │ 1851
7 │ Alexey Milovidov │ 2400
└─────────────┴──────────────────┴──────┘

7 rows in set. Elapsed: 0.012 sec. Processed 62.78 thousand rows, 395.47 KB (5.44 million rows/s., 34.27 MB/s.)

ここでは、最も長い貢献者である創業者のAlexeyにいくつかの利点があります。過去1年に分析を制限しましょう。

プレイ

SELECT
day_of_week,
author,
count() AS c
FROM git.commits
WHERE time > (now() - toIntervalYear(1))
GROUP BY
dayOfWeek(time) AS day_of_week,
author
ORDER BY
day_of_week ASC,
c DESC
LIMIT 1 BY day_of_week

┌─day_of_week─┬─author───────────┬───c─┐
1 │ Alexey Milovidov │ 198
2 │ alesapin │ 162
3 │ alesapin │ 163
4 │ Azat Khuzhin │ 166
5 │ alesapin │ 191
6 │ Alexey Milovidov │ 179
7 │ Alexey Milovidov │ 243
└─────────────┴──────────────────┴─────┘

7 rows in set. Elapsed: 0.004 sec. Processed 21.82 thousand rows, 140.02 KB (4.88 million rows/s., 31.29 MB/s.)

これはまだ少し単純で、人々の作業を反映していません。

より良い指標は、過去1年の総作業に対する各日の最高貢献者の割合かもしれません。削除と追加のコードを同等に扱います。

プレイ

SELECT
top_author.day_of_week,
top_author.author,
top_author.author_work / all_work.total_work AS top_author_percent
FROM
(
SELECT
day_of_week,
author,
sum(lines_added) + sum(lines_deleted) AS author_work
FROM git.file_changes
WHERE time > (now() - toIntervalYear(1))
GROUP BY
author,
dayOfWeek(time) AS day_of_week
ORDER BY
day_of_week ASC,
author_work DESC
LIMIT 1 BY day_of_week
) AS top_author
INNER JOIN
(
SELECT
day_of_week,
sum(lines_added) + sum(lines_deleted) AS total_work
FROM git.file_changes
WHERE time > (now() - toIntervalYear(1))
GROUP BY dayOfWeek(time) AS day_of_week
) AS all_work USING (day_of_week)

┌─day_of_week─┬─author──────────────┬──top_author_percent─┐
1 │ Alexey Milovidov │ 0.3168282877768332
2 │ Mikhail f. Shiryaev │ 0.3523434231193969
3 │ vdimir │ 0.11859742484577324
4 │ Nikolay Degterinsky │ 0.34577318920318467
5 │ Alexey Milovidov │ 0.13208704423684223
6 │ Alexey Milovidov │ 0.18895257783624633
7 │ Robert Schulze │ 0.3617405888930302
└─────────────┴─────────────────────┴─────────────────────┘

7 rows in set. Elapsed: 0.014 sec. Processed 106.12 thousand rows, 1.38 MB (7.61 million rows/s., 98.65 MB/s.)

リポジトリ全体のコード年齢の分布

分析を現在のファイルに制限します。簡潔さのために、結果を深さ2のルートフォルダーごとに5ファイルに制限します。必要に応じて調整してください。

プレイ

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
concat(root, '/', sub_folder) AS folder,
round(avg(days_present)) AS avg_age_of_files,
min(days_present) AS min_age_files,
max(days_present) AS max_age_files,
count() AS c
FROM
(
SELECT
path,
dateDiff('day', min(time), toDate('2022-11-03')) AS days_present
FROM git.file_changes
WHERE (path IN (current_files)) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY path
)
GROUP BY
splitByChar('/', path)[1] AS root,
splitByChar('/', path)[2] AS sub_folder
ORDER BY
root ASC,
c DESC
LIMIT 5 BY root

┌─folder───────────────────────────┬─avg_age_of_files─┬─min_age_files─┬─max_age_files─┬────c─┐
│ base/base │ 38720139784
│ base/glibc-compatibility │ 8875999319
│ base/consistent-hashing │ 9939939935
│ base/widechar_width │ 9939939932
│ base/consistent-hashing-sumbur │ 9939939932
│ docker/test │ 1043104310431
│ programs/odbc-bridge │ 8359194525
│ programs/copier │ 5871494522
│ programs/library-bridge │ 1554760821
│ programs/disks │ 1446215014
│ programs/server │ 87470994510
│ rust/BLAKE3 │ 5252521
│ src/Functions │ 7520944809
│ src/Storages │ 7008944736
│ src/Interpreters │ 6843944490
│ src/Processors │ 70344944482
│ src/Common │ 6737944473
│ tests/queries │ 674-59453777
│ tests/integration │ 6561329454
│ utils/memcpy-bench │ 60159960510
│ utils/keeper-bench │ 5705695707
│ utils/durability-test │ 7937937934
│ utils/self-extracting-executable │ 1431431433
│ utils/self-extr-exec2242242242
└──────────────────────────────────┴──────────────────┴───────────────┴───────────────┴──────┘

24 rows in set. Elapsed: 0.129 sec. Processed 798.15 thousand rows, 15.11 MB (6.19 million rows/s., 117.08 MB/s.)

各著者のコードの何パーセントが他の著者によって削除されたか?

この質問を解決するには、著者によって書かれた行数を他の貢献者によって削除された総行数で割る必要があります。

プレイ

SELECT
k,
written_code.c,
removed_code.c,
removed_code.c / written_code.c AS remove_ratio
FROM
(
SELECT
author AS k,
count() AS c
FROM git.line_changes
WHERE (sign = 1) AND (file_extension IN ('h', 'cpp')) AND (line_type NOT IN ('Punct', 'Empty'))
GROUP BY k
) AS written_code
INNER JOIN
(
SELECT
prev_author AS k,
count() AS c
FROM git.line_changes
WHERE (sign = -1) AND (file_extension IN ('h', 'cpp')) AND (line_type NOT IN ('Punct', 'Empty')) AND (author != prev_author)
GROUP BY k
) AS removed_code USING (k)
WHERE written_code.c > 1000
ORDER BY remove_ratio DESC
LIMIT 10

┌─k──────────────────┬─────c─┬─removed_code.c─┬───────remove_ratio─┐
│ Marek Vavruša │ 145813180.9039780521262003
│ Ivan │ 32715275000.8405930001528351
│ artpaul │ 345028400.8231884057971014
│ Silviu Caragea │ 154212090.7840466926070039
│ Ruslan │ 10278020.7809152872444012
│ Tsarkova Anastasia │ 175513640.7772079772079772
│ Vyacheslav Alipov │ 352627270.7733976176971072
│ Marek Vavruša │ 146711240.7661895023858214
│ f1yegor │ 719452130.7246316374756742
│ kreuzerkrieg │ 340624680.724603640634175
└────────────────────┴───────┴────────────────┴────────────────────┘

10 rows in set. Elapsed: 0.126 sec. Processed 15.07 million rows, 73.51 MB (119.97 million rows/s., 585.16 MB/s.)

最も多く書き直されたファイルのリスト?

この質問に対する最も単純なアプローチは、パスごとの行修正数を単純にカウントすることかもしれません(現行ファイルに制限):

WITH current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
)
SELECT
path,
count() AS c
FROM git.line_changes
WHERE (file_extension IN ('h', 'cpp', 'sql')) AND (path IN (current_files))
GROUP BY path
ORDER BY c DESC
LIMIT 10

┌─path───────────────────────────────────────────────────┬─────c─┐
│ src/Storages/StorageReplicatedMergeTree.cpp │ 21871
│ src/Storages/MergeTree/MergeTreeData.cpp │ 17709
│ programs/client/Client.cpp │ 15882
│ src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp │ 14249
│ src/Interpreters/InterpreterSelectQuery.cpp │ 12636
│ src/Parsers/ExpressionListParsers.cpp │ 11794
│ src/Analyzer/QueryAnalysisPass.cpp │ 11760
│ src/Coordination/KeeperStorage.cpp │ 10225
│ src/Functions/FunctionsConversion.h │ 9247
│ src/Parsers/ExpressionElementParsers.cpp │ 8197
└────────────────────────────────────────────────────────┴───────┘

10 rows in set. Elapsed: 0.160 sec. Processed 8.07 million rows, 98.99 MB (50.49 million rows/s., 619.49 MB/s.)

これは「書き直し」という概念を捉えていませんが、コミット内の大部分のファイルが変更された場合に求めます。これは、より複雑なクエリを必要とします。50%以上が削除され、50%以上が追加された場合を書き直しと見なすとしましょう。このクエリは現在のファイルのみに制限されています。我々は、pathcommit_hashをグループ化することによってすべてのファイル変更をリストし、追加された行と削除された行の数を返します。ウィンドウ関数を使用して、ある瞬間のファイルの累積サイズを推定し、変更がファイルサイズに与える影響を追加行 - 削除行として推定します。この統計を使用して、各変更の追加されたまたは削除されたファイルの割合を計算できます。最後に、書き直しを構成するファイル変更の数をカウントします。すなわち、(percent_add >= 0.5) AND (percent_delete >= 0.5) AND current_size > 50。ファイルが90行未満の場合はそれを除外し、早期の寄稿が書き直しとしてカウントされるのを避けます。これにより非常に小さなファイルへのバイアスを避けることができます。

プレイ

WITH
current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
),
changes AS
(
SELECT
path,
max(time) AS max_time,
commit_hash,
any(lines_added) AS num_added,
any(lines_deleted) AS num_deleted,
any(change_type) AS type
FROM git.file_changes
WHERE (change_type IN ('Add', 'Modify')) AND (path IN (current_files)) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY
path,
commit_hash
ORDER BY
path ASC,
max_time ASC
),
rewrites AS
(
SELECT
path,
commit_hash,
max_time,
type,
num_added,
num_deleted,
sum(num_added - num_deleted) OVER (PARTITION BY path ORDER BY max_time ASC) AS current_size,
if(current_size > 0, num_added / current_size, 0) AS percent_add,
if(current_size > 0, num_deleted / current_size, 0) AS percent_delete
FROM changes
)
SELECT
path,
count() AS num_rewrites
FROM rewrites
WHERE (type = 'Modify') AND (percent_add >= 0.5) AND (percent_delete >= 0.5) AND (current_size > 50)
GROUP BY path
ORDER BY num_rewrites DESC
LIMIT 10

┌─path──────────────────────────────────────────────────┬─num_rewrites─┐
│ src/Storages/WindowView/StorageWindowView.cpp │ 8
│ src/Functions/array/arrayIndex.h │ 7
│ src/Dictionaries/CacheDictionary.cpp │ 6
│ src/Dictionaries/RangeHashedDictionary.cpp │ 5
│ programs/client/Client.cpp │ 4
│ src/Functions/polygonPerimeter.cpp │ 4
│ src/Functions/polygonsEquals.cpp │ 4
│ src/Functions/polygonsWithin.cpp │ 4
│ src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp │ 4
│ src/Functions/polygonsSymDifference.cpp │ 4
└───────────────────────────────────────────────────────┴──────────────┘

10 rows in set. Elapsed: 0.299 sec. Processed 798.15 thousand rows, 31.52 MB (2.67 million rows/s., 105.29 MB/s.)

コードがリポジトリに留まる可能性が最も高い曜日は?

このためには、コードの行を一意に特定する必要があります。同じ行がファイル内に複数回表示される可能性があるため、パスと行の内容を使用して推定します。

追加された行に関するクエリを作成し、削除された行と結合し、後者が前者よりも最近発生したケースをフィルタリングします。これにより、削除された行を取得し、これらの二つのイベント間の時間を計算できます。

最後に、このデータセットを集約して、曜日ごとのリポジトリの平均保持日数を計算します。

プレイ

SELECT
day_of_week_added,
count() AS num,
avg(days_present) AS avg_days_present
FROM
(
SELECT
added_code.line,
added_code.time AS added_day,
dateDiff('day', added_code.time, removed_code.time) AS days_present
FROM
(
SELECT
path,
line,
max(time) AS time
FROM git.line_changes
WHERE (sign = 1) AND (line_type NOT IN ('Punct', 'Empty'))
GROUP BY
path,
line
) AS added_code
INNER JOIN
(
SELECT
path,
line,
max(time) AS time
FROM git.line_changes
WHERE (sign = -1) AND (line_type NOT IN ('Punct', 'Empty'))
GROUP BY
path,
line
) AS removed_code USING (path, line)
WHERE removed_code.time > added_code.time
)
GROUP BY dayOfWeek(added_day) AS day_of_week_added

┌─day_of_week_added─┬────num─┬───avg_days_present─┐
1171879193.81759260875384
2141448153.0931013517335
3161230137.61553681076722
4255728121.14149799787273
5203907141.60181847606998
662305202.43449161383518
770904220.0266134491707
└───────────────────┴────────┴────────────────────┘

7 rows in set. Elapsed: 3.965 sec. Processed 15.07 million rows, 1.92 GB (3.80 million rows/s., 483.50 MB/s.)

平均コード年齢でソートされたファイル

このクエリは、コードがリポジトリに留まる可能性が最も高い曜日は? と同じ原則を使用します - パスと行の内容を使用して、行のコードを一意に特定します。これにより、ラインが追加されてから削除されるまでの時間を特定できます。ただし、現在のファイルとコードのみにフィルタリングし、各ファイルの行にわたって平均化します。

プレイ

WITH
current_files AS
(
SELECT path
FROM
(
SELECT
old_path AS path,
max(time) AS last_time,
2 AS change_type
FROM git.file_changes
GROUP BY old_path
UNION ALL
SELECT
path,
max(time) AS last_time,
argMax(change_type, time) AS change_type
FROM git.file_changes
GROUP BY path
)
GROUP BY path
HAVING (argMax(change_type, last_time) != 2) AND (NOT match(path, '(^dbms/)|(^libs/)|(^tests/testflows/)|(^programs/server/store/)'))
ORDER BY path ASC
),
lines_removed AS
(
SELECT
added_code.path AS path,
added_code.line,
added_code.time AS added_day,
dateDiff('day', added_code.time, removed_code.time) AS days_present
FROM
(
SELECT
path,
line,
max(time) AS time,
any(file_extension) AS file_extension
FROM git.line_changes
WHERE (sign = 1) AND (line_type NOT IN ('Punct', 'Empty'))
GROUP BY
path,
line
) AS added_code
INNER JOIN
(
SELECT
path,
line,
max(time) AS time
FROM git.line_changes
WHERE (sign = -1) AND (line_type NOT IN ('Punct', 'Empty'))
GROUP BY
path,
line
) AS removed_code USING (path, line)
WHERE (removed_code.time > added_code.time) AND (path IN (current_files)) AND (file_extension IN ('h', 'cpp', 'sql'))
)
SELECT
path,
avg(days_present) AS avg_code_age
FROM lines_removed
GROUP BY path
ORDER BY avg_code_age DESC
LIMIT 10

┌─path────────────────────────────────────────────────────────────┬──────avg_code_age─┐
│ utils/corrector_utf8/corrector_utf8.cpp │ 1353.888888888889
│ tests/queries/0_stateless/01288_shard_max_network_bandwidth.sql881
│ src/Functions/replaceRegexpOne.cpp │ 861
│ src/Functions/replaceRegexpAll.cpp │ 861
│ src/Functions/replaceOne.cpp │ 861
│ utils/zookeeper-remove-by-list/main.cpp │ 838.25
│ tests/queries/0_stateless/01356_state_resample.sql819
│ tests/queries/0_stateless/01293_create_role.sql819
│ src/Functions/ReplaceStringImpl.h │ 810
│ src/Interpreters/createBlockSelector.cpp │ 795
└─────────────────────────────────────────────────────────────────┴───────────────────┘

10 rows in set. Elapsed: 3.134 sec. Processed 16.13 million rows, 1.83 GB (5.15 million rows/s., 582.99 MB/s.)

誰がより多くのテスト/CPPコード/コメントを書く傾向があるのか?

この質問にアプローチする方法はいくつかあります。テスト対コードの比率に焦点を当て、このクエリは比較的簡単です - testsを含むフォルダへの寄与をカウントし、総寄与に対する比率を計算します。

ただし、20回以上の変更を行ったユーザーに制限して、定期的なコミッターに焦点を当て、一回限りの寄与へのバイアスを避けます。

プレイ

SELECT
author,
countIf((file_extension IN ('h', 'cpp', 'sql', 'sh', 'py', 'expect')) AND (path LIKE '%tests%')) AS test,
countIf((file_extension IN ('h', 'cpp', 'sql')) AND (NOT (path LIKE '%tests%'))) AS code,
code / (code + test) AS ratio_code
FROM git.file_changes
GROUP BY author
HAVING code > 20
ORDER BY code DESC
LIMIT 20

┌─author───────────────┬─test─┬──code─┬─────────ratio_code─┐
│ Alexey Milovidov │ 6617417990.8633303040317251
│ Nikolai Kochetov │ 916133610.9358408629263851
│ alesapin │ 240887960.785076758300607
│ kssenii │ 86967690.8862267609321812
│ Maksim Kita │ 79958620.8800480408347096
│ Alexander Tokmakov │ 147257270.7955271565495208
│ Vitaly Baranov │ 176455210.7578586135895676
│ Ivan Lezhankin │ 84346980.8478613968597726
│ Anton Popov │ 59943460.8788675429726996
│ Ivan │ 263042690.6187853312074214
│ Azat Khuzhin │ 166436970.689610147360567
│ Amos Bird │ 40029010.8788245986064829
│ proller │ 120723770.6632254464285714
│ chertus │ 45323590.8389046941678521
│ alexey-milovidov │ 30323210.8845274390243902
│ Alexey Arno │ 16923100.9318273497377975
│ Vitaliy Lyudvichenko │ 33422830.8723729461215132
│ Robert Schulze │ 18221960.9234650967199327
│ CurtizJ │ 46021580.8242933537051184
│ Alexander Kuzmenkov │ 29820920.8753138075313808
└──────────────────────┴──────┴───────┴────────────────────┘

20 rows in set. Elapsed: 0.034 sec. Processed 266.05 thousand rows, 4.65 MB (7.93 million rows/s., 138.76 MB/s.)

この分布をヒストグラムとしてプロットできます。

プレイ

WITH (
SELECT histogram(10)(ratio_code) AS hist
FROM
(
SELECT
author,
countIf((file_extension IN ('h', 'cpp', 'sql', 'sh', 'py', 'expect')) AND (path LIKE '%tests%')) AS test,
countIf((file_extension IN ('h', 'cpp', 'sql')) AND (NOT (path LIKE '%tests%'))) AS code,
code / (code + test) AS ratio_code
FROM git.file_changes
GROUP BY author
HAVING code > 20
ORDER BY code DESC
LIMIT 20
)
) AS hist
SELECT
arrayJoin(hist).1 AS lower,
arrayJoin(hist).2 AS upper,
bar(arrayJoin(hist).3, 0, 100, 500) AS bar

┌──────────────lower─┬──────────────upper─┬─bar───────────────────────────┐
0.61878533120742140.6410053888179964 │ █████ │
0.64100538881799640.6764177968945693 │ █████ │
0.67641779689456930.7237343804750673 │ █████ │
0.72373438047506730.7740802855073157 │ █████▋ │
0.77408028550731570.807297655565091 │ ████████▋ │
0.8072976555650910.8338381996094653 │ ██████▎ │
0.83383819960946530.8533566747727687 │ ████████▋ │
0.85335667477276870.871392376017531 │ █████████▍ │
0.8713923760175310.904916108899021 │ ████████████████████████████▋ │
0.9049161088990210.9358408629263851 │ █████████████████▌ │
└────────────────────┴────────────────────┴───────────────────────────────┘
10 rows in set. Elapsed: 0.051 sec. Processed 266.05 thousand rows, 4.65 MB (5.24 million rows/s., 91.64 MB/s.)

ほとんどの寄稿者は、予想通り、テストよりもコードを多く書きます。

コードを寄稿する際に、誰が最も多くのコメントを追加するのでしょうか?

プレイ

SELECT
author,
avg(ratio_comments) AS avg_ratio_comments,
sum(code) AS code
FROM
(
SELECT
author,
commit_hash,
countIf(line_type = 'Comment') AS comments,
countIf(line_type = 'Code') AS code,
if(comments > 0, comments / (comments + code), 0) AS ratio_comments
FROM git.line_changes
GROUP BY
author,
commit_hash
)
GROUP BY author
ORDER BY code DESC
LIMIT 10
┌─author─────────────┬──avg_ratio_comments─┬────code─┐
│ Alexey Milovidov │ 0.10349154083099021147196
│ s-kat │ 0.1361718900215362614224
│ Nikolai Kochetov │ 0.08722993407690126218328
│ alesapin │ 0.1040477684726504198082
│ Vitaly Baranov │ 0.06446875712939285161801
│ Maksim Kita │ 0.06863376297549255156381
│ Alexey Arno │ 0.11252677608033655146642
│ Vitaliy Zakaznikov │ 0.06199215397180561138530
│ kssenii │ 0.07455322590796751131143
│ Artur │ 0.12383737231074826121484
└────────────────────┴─────────────────────┴─────────┘
10 rows in set. Elapsed: 0.290 sec. Processed 7.54 million rows, 394.57 MB (26.00 million rows/s., 1.36 GB/s.)

コードの寄与によってソートしていることに注意してください。すべての大きな寄稿者に対して驚くほど高い%であり、これがコードを非常に読みやすくする一因です。

著者のコミットが時間経過とともにコード/コメントの割合に関してどのように変化するのか?

著者ごとの計算は簡単です。

プレイ

SELECT
author,
countIf(line_type = 'Code') AS code_lines,
countIf((line_type = 'Comment') OR (line_type = 'Punct')) AS comments,
code_lines / (comments + code_lines) AS ratio_code,
toStartOfWeek(time) AS week
FROM git.line_changes
GROUP BY
time,
author
ORDER BY
author ASC,
time ASC
LIMIT 10

┌─author──────────────────────┬─code_lines─┬─comments─┬─────────ratio_code─┬───────week─┐
1lann │ 8012022-03-06
200187122012020-09-13
243f6a8885a308d313198a2e037 │ 0202020-12-06
243f6a8885a308d313198a2e037 │ 011202020-12-06
243f6a8885a308d313198a2e037 │ 01402020-12-06
3ldar-nasyrov │ 2012021-03-14
821008736@qq.com2720.93103448275862072019-04-21
│ ANDREI STAROVEROV │ 182600.75206611570247942021-05-09
│ ANDREI STAROVEROV │ 7012021-05-09
│ ANDREI STAROVEROV │ 32120.72727272727272732021-05-09
└─────────────────────────────┴────────────┴──────────┴────────────────────┴────────────┘

10 rows in set. Elapsed: 0.145 sec. Processed 7.54 million rows, 51.09 MB (51.83 million rows/s., 351.44 MB/s.)

理想的には、すべての著者の最初のコミットからの集約でこの変化を見たいと思います。著者は徐々に書くコメントの数を減らしているのでしょうか?

これを計算するために、まず各著者の時間に伴うコメント比率を計算します - 誰がより多くのテスト/ CPP コード/ コメントを書きがちか?と同様です。これは各著者の開始日と結合され、コメント比率を週オフセットで計算できます。

すべての著者の平均を計算した後、毎10週目を選択してこれらの結果をサンプリングします。

プレイ

WITH author_ratios_by_offset AS
(
SELECT
author,
dateDiff('week', start_dates.start_date, contributions.week) AS week_offset,
ratio_code
FROM
(
SELECT
author,
toStartOfWeek(min(time)) AS start_date
FROM git.line_changes
WHERE file_extension IN ('h', 'cpp', 'sql')
GROUP BY author AS start_dates
) AS start_dates
INNER JOIN
(
SELECT
author,
countIf(line_type = 'Code') AS code,
countIf((line_type = 'Comment') OR (line_type = 'Punct')) AS comments,
comments / (comments + code) AS ratio_code,
toStartOfWeek(time) AS week
FROM git.line_changes
WHERE (file_extension IN ('h', 'cpp', 'sql')) AND (sign = 1)
GROUP BY
time,
author
HAVING code > 20
ORDER BY
author ASC,
time ASC
) AS contributions USING (author)
)
SELECT
week_offset,
avg(ratio_code) AS avg_code_ratio
FROM author_ratios_by_offset
GROUP BY week_offset
HAVING (week_offset % 10) = 0
ORDER BY week_offset ASC
LIMIT 20

┌─week_offset─┬──────avg_code_ratio─┐
00.21626798253005078
100.18299433892099454
200.22847255749045017
300.2037816688365288
400.1987063517030308
500.17341406302829748
600.1808884776496144
700.18711773536450496
800.18905573684766458
900.2505147771581594
1000.2427673990917429
1100.19088569009169926
1200.14218574654598348
1300.20894252550489317
1400.22316626978848397
1500.1859507592277053
1600.22007759757363546
1700.20406936638195144
1800.1412102467834332
1900.20677550885049117
└─────────────┴─────────────────────┘

20 rows in set. Elapsed: 0.167 sec. Processed 15.07 million rows, 101.74 MB (90.51 million rows/s., 610.98 MB/s.)

励みになることに、我々のコメント % はかなり安定していて、著者が寄与する期間が長くなるほど劣化しません。

コードが書き換えられるまでの平均時間と中央値(コードの decay の半減期)は?

すべてのファイルを考慮して書き換えを特定するために、最も多く再書き換えられたファイルや作者をリストすると同じ原則を用いることができます。ウィンドウ関数を使用して、各ファイルの書き換え間隔の時間を計算します。これから、すべてのファイルにわたって平均と中央値を計算できます。

プレイ

WITH
changes AS
(
SELECT
path,
commit_hash,
max_time,
type,
num_added,
num_deleted,
sum(num_added - num_deleted) OVER (PARTITION BY path ORDER BY max_time ASC) AS current_size,
if(current_size > 0, num_added / current_size, 0) AS percent_add,
if(current_size > 0, num_deleted / current_size, 0) AS percent_delete
FROM
(
SELECT
path,
max(time) AS max_time,
commit_hash,
any(lines_added) AS num_added,
any(lines_deleted) AS num_deleted,
any(change_type) AS type
FROM git.file_changes
WHERE (change_type IN ('Add', 'Modify')) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY
path,
commit_hash
ORDER BY
path ASC,
max_time ASC
)
),
rewrites AS
(
SELECT
*,
any(max_time) OVER (PARTITION BY path ORDER BY max_time ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_rewrite,
dateDiff('day', previous_rewrite, max_time) AS rewrite_days
FROM changes
WHERE (type = 'Modify') AND (percent_add >= 0.5) AND (percent_delete >= 0.5) AND (current_size > 50)
)
SELECT
avgIf(rewrite_days, rewrite_days > 0) AS avg_rewrite_time,
quantilesTimingIf(0.5)(rewrite_days, rewrite_days > 0) AS half_life
FROM rewrites

┌─avg_rewrite_time─┬─half_life─┐
122.2890625[23]
└──────────────────┴───────────┘

1 row in set. Elapsed: 0.388 sec. Processed 266.05 thousand rows, 22.85 MB (685.82 thousand rows/s., 58.89 MB/s.)

どのような時がコードが書き換えられる可能性が最も高いのか?

コードが書き換えられるまでの平均時間と中央値(コードの decay の半減期)最も多く再書き換えられたファイルや作者をリストするに似ていますが、こちらは曜日によって集計します。必要に応じて月ごとなどに調整できます。

プレイ

WITH
changes AS
(
SELECT
path,
commit_hash,
max_time,
type,
num_added,
num_deleted,
sum(num_added - num_deleted) OVER (PARTITION BY path ORDER BY max_time ASC) AS current_size,
if(current_size > 0, num_added / current_size, 0) AS percent_add,
if(current_size > 0, num_deleted / current_size, 0) AS percent_delete
FROM
(
SELECT
path,
max(time) AS max_time,
commit_hash,
any(file_lines_added) AS num_added,
any(file_lines_deleted) AS num_deleted,
any(file_change_type) AS type
FROM git.line_changes
WHERE (file_change_type IN ('Add', 'Modify')) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY
path,
commit_hash
ORDER BY
path ASC,
max_time ASC
)
),
rewrites AS
(
SELECT any(max_time) OVER (PARTITION BY path ORDER BY max_time ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_rewrite
FROM changes
WHERE (type = 'Modify') AND (percent_add >= 0.5) AND (percent_delete >= 0.5) AND (current_size > 50)
)
SELECT
dayOfWeek(previous_rewrite) AS dayOfWeek,
count() AS num_re_writes
FROM rewrites
GROUP BY dayOfWeek

┌─dayOfWeek─┬─num_re_writes─┐
1111
2121
391
4111
590
664
746
└───────────┴───────────────┘

7 rows in set. Elapsed: 0.466 sec. Processed 7.54 million rows, 701.52 MB (16.15 million rows/s., 1.50 GB/s.)

どの著者のコードが最も安定しているか?

「安定している」とは、著者のコードが書き換えられるまでの時間を定義します。以前の質問コードが書き換えられるまでの平均時間と中央値(コードの decay の半減期)と同じ指標を使用した重みを付けます。つまり、ファイルの追加および削除の比率が50%であることです。各著者ごとの平均書き換え時間を計算し、2ファイル以上の寄稿者のみを考慮します。

プレイ

WITH
changes AS
(
SELECT
path,
author,
commit_hash,
max_time,
type,
num_added,
num_deleted,
sum(num_added - num_deleted) OVER (PARTITION BY path ORDER BY max_time ASC) AS current_size,
if(current_size > 0, num_added / current_size, 0) AS percent_add,
if(current_size > 0, num_deleted / current_size, 0) AS percent_delete
FROM
(
SELECT
path,
any(author) AS author,
max(time) AS max_time,
commit_hash,
any(file_lines_added) AS num_added,
any(file_lines_deleted) AS num_deleted,
any(file_change_type) AS type
FROM git.line_changes
WHERE (file_change_type IN ('Add', 'Modify')) AND (file_extension IN ('h', 'cpp', 'sql'))
GROUP BY
path,
commit_hash
ORDER BY
path ASC,
max_time ASC
)
),
rewrites AS
(
SELECT
*,
any(max_time) OVER (PARTITION BY path ORDER BY max_time ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_rewrite,
dateDiff('day', previous_rewrite, max_time) AS rewrite_days,
any(author) OVER (PARTITION BY path ORDER BY max_time ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_author
FROM changes
WHERE (type = 'Modify') AND (percent_add >= 0.5) AND (percent_delete >= 0.5) AND (current_size > 50)
)
SELECT
prev_author,
avg(rewrite_days) AS c,
uniq(path) AS num_files
FROM rewrites
GROUP BY prev_author
HAVING num_files > 2
ORDER BY c DESC
LIMIT 10

┌─prev_author─────────┬──────────────────c─┬─num_files─┐
│ Michael Kolupaev │ 304.64
│ alexey-milovidov │ 81.833333333333334
│ Alexander Kuzmenkov │ 64.55
│ Pavel Kruglov │ 55.86
│ Alexey Milovidov │ 48.41666666666666490
│ Amos Bird │ 42.84
│ alesapin │ 38.08333333333333612
│ Nikolai Kochetov │ 33.1842105263157926
│ Alexander Tokmakov │ 31.86666666666666712
│ Alexey Zatelepin │ 22.54
└─────────────────────┴────────────────────┴───────────┘

10 rows in set. Elapsed: 0.555 sec. Processed 7.54 million rows, 720.60 MB (13.58 million rows/s., 1.30 GB/s.)

ある著者による最も連続したコミットの日数

このクエリは、まず著者がコミットした日を計算する必要があります。ウィンドウ関数を使用して著者ごとに分割し、コミット間の日数を計算できます。各コミットについて、前回のコミットからの時間が1日であればそれを連続(1)としてマークし、そうでない場合は0として consecutive_day に結果を保存します。

その後、配列関数を使って各著者の最長の連続1のシーケンスを計算します。まず、groupArray 関数を使って著者ごとのすべての consecutive_day 値をまとめます。この1と0の配列は、0の値で分割され、サブ配列に変換されます。最後に、最も長いサブ配列を計算します。

プレイ

WITH commit_days AS
(
SELECT
author,
day,
any(day) OVER (PARTITION BY author ORDER BY day ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_commit,
dateDiff('day', previous_commit, day) AS days_since_last,
if(days_since_last = 1, 1, 0) AS consecutive_day
FROM
(
SELECT
author,
toStartOfDay(time) AS day
FROM git.commits
GROUP BY
author,
day
ORDER BY
author ASC,
day ASC
)
)
SELECT
author,
arrayMax(arrayMap(x -> length(x), arraySplit(x -> (x = 0), groupArray(consecutive_day)))) - 1 AS max_consecutive_days
FROM commit_days
GROUP BY author
ORDER BY max_consecutive_days DESC
LIMIT 10

┌─author───────────┬─max_consecutive_days─┐
│ kssenii │ 32
│ Alexey Milovidov │ 30
│ alesapin │ 26
│ Azat Khuzhin │ 23
│ Nikolai Kochetov │ 15
│ feng lv │ 11
│ alexey-milovidov │ 11
│ Igor Nikonov │ 11
│ Maksim Kita │ 11
│ Nikita Vasilev │ 11
└──────────────────┴──────────────────────┘

10 rows in set. Elapsed: 0.025 sec. Processed 62.78 thousand rows, 395.47 KB (2.54 million rows/s., 16.02 MB/s.)

ファイルの行ごとのコミット履歴

ファイルは名前が変更されることがあります。この場合、path カラムはファイルの新しいパスに設定され、old_path は前の場所を示します。例えば:

プレイ

SELECT
time,
path,
old_path,
commit_hash,
commit_message
FROM git.file_changes
WHERE (path = 'src/Storages/StorageReplicatedMergeTree.cpp') AND (change_type = 'Rename')

┌────────────────time─┬─path────────────────────────────────────────┬─old_path─────────────────────────────────────┬─commit_hash──────────────────────────────┬─commit_message─┐
2020-04-03 16:14:31 │ src/Storages/StorageReplicatedMergeTree.cpp │ dbms/Storages/StorageReplicatedMergeTree.cpp │ 06446b4f08a142d6f1bc30664c47ded88ab51782 │ dbms/ → src/
└─────────────────────┴─────────────────────────────────────────────┴──────────────────────────────────────────────┴──────────────────────────────────────────┴────────────────┘

1 row in set. Elapsed: 0.135 sec. Processed 266.05 thousand rows, 20.73 MB (1.98 million rows/s., 154.04 MB/s.)

これにより、ファイルの完全な履歴を表示するのは難しいです。すべての行またはファイルの変更を接続する単一の値が存在しないからです。

これに対処するために、ユーザー定義関数(UDF)を使用することができます。これらは現在再帰的ではないため、ファイルの履歴を特定するためには、互いに明示的に呼び出す一連の UDF を定義する必要があります。

これにより、名前変更を最大深度まで追跡できるようになります - 次の例は5深です。ファイルがこの回数以上に名前が変更される可能性は低いため、現段階ではこれで十分です。

CREATE FUNCTION file_path_history AS (n) -> if(empty(n),  [], arrayConcat([n], file_path_history_01((SELECT if(empty(old_path), Null, old_path) FROM git.file_changes WHERE path = n AND (change_type = 'Rename' OR change_type = 'Add') LIMIT 1))));
CREATE FUNCTION file_path_history_01 AS (n) -> if(isNull(n), [], arrayConcat([n], file_path_history_02((SELECT if(empty(old_path), Null, old_path) FROM git.file_changes WHERE path = n AND (change_type = 'Rename' OR change_type = 'Add') LIMIT 1))));
CREATE FUNCTION file_path_history_02 AS (n) -> if(isNull(n), [], arrayConcat([n], file_path_history_03((SELECT if(empty(old_path), Null, old_path) FROM git.file_changes WHERE path = n AND (change_type = 'Rename' OR change_type = 'Add') LIMIT 1))));
CREATE FUNCTION file_path_history_03 AS (n) -> if(isNull(n), [], arrayConcat([n], file_path_history_04((SELECT if(empty(old_path), Null, old_path) FROM git.file_changes WHERE path = n AND (change_type = 'Rename' OR change_type = 'Add') LIMIT 1))));
CREATE FUNCTION file_path_history_04 AS (n) -> if(isNull(n), [], arrayConcat([n], file_path_history_05((SELECT if(empty(old_path), Null, old_path) FROM git.file_changes WHERE path = n AND (change_type = 'Rename' OR change_type = 'Add') LIMIT 1))));
CREATE FUNCTION file_path_history_05 AS (n) -> if(isNull(n), [], [n]);

file_path_history('src/Storages/StorageReplicatedMergeTree.cpp')を呼び出すことによって、名前変更の履歴を再帰的にたどります。各関数は old_path で次のレベルを呼び出します。結果は arrayConcat を使用して組み合わされます。

例えば、

SELECT file_path_history('src/Storages/StorageReplicatedMergeTree.cpp') AS paths

┌─paths─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
['src/Storages/StorageReplicatedMergeTree.cpp','dbms/Storages/StorageReplicatedMergeTree.cpp','dbms/src/Storages/StorageReplicatedMergeTree.cpp']
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

1 row in set. Elapsed: 0.074 sec. Processed 344.06 thousand rows, 6.27 MB (4.65 million rows/s., 84.71 MB/s.)

この機能を使用して、ファイルの完全な履歴のためのコミットを組み立てることができます。この例では、各 path 値のために1つのコミットを示します。

SELECT
time,
substring(commit_hash, 1, 11) AS commit,
change_type,
author,
path,
commit_message
FROM git.file_changes
WHERE path IN file_path_history('src/Storages/StorageReplicatedMergeTree.cpp')
ORDER BY time DESC
LIMIT 1 BY path
FORMAT PrettyCompactMonoBlock

┌────────────────time─┬─commit──────┬─change_type─┬─author─────────────┬─path─────────────────────────────────────────────┬─commit_message──────────────────────────────────────────────────────────────────┐
2022-10-30 16:30:51 │ c68ab231f91 │ Modify │ Alexander Tokmakov │ src/Storages/StorageReplicatedMergeTree.cpp │ fix accessing part in Deleting state │
2020-04-03 15:21:2438a50f44d34 │ Modify │ alesapin │ dbms/Storages/StorageReplicatedMergeTree.cpp │ Remove empty line │
2020-04-01 19:21:271d5a77c1132 │ Modify │ alesapin │ dbms/src/Storages/StorageReplicatedMergeTree.cpp │ Tried to add ability to rename primary key columns but just banned this ability │
└─────────────────────┴─────────────┴─────────────┴────────────────────┴──────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────┘

3 rows in set. Elapsed: 0.170 sec. Processed 611.53 thousand rows, 41.76 MB (3.60 million rows/s., 246.07 MB/s.)

未解決の質問

Git blame

これは、状態を配列関数で保持できないことに起因して特に困難です。これは、各反復で状態を保持できる arrayFold または arrayReduce で可能になります。

高レベルの分析に十分な近似的な解決策は次のようになるかもしれません:

SELECT
line_number_new,
argMax(author, time),
argMax(line, time)
FROM git.line_changes
WHERE path IN file_path_history('src/Storages/StorageReplicatedMergeTree.cpp')
GROUP BY line_number_new
ORDER BY line_number_new ASC
LIMIT 20

┌─line_number_new─┬─argMax(author, time)─┬─argMax(line, time)────────────────────────────────────────────┐
1 │ Alexey Milovidov │ #include <Disks/DiskSpaceMonitor.h> │
2 │ s-kat │ #include <Common/FieldVisitors.h> │
3 │ Anton Popov │ #include <cstddef> │
4 │ Alexander Burmak │ #include <Common/typeid_cast.h> │
5 │ avogar │ #include <Common/ThreadPool.h> │
6 │ Alexander Burmak │ #include <Common/DiskSpaceMonitor.h> │
7 │ Alexander Burmak │ #include <Common/ZooKeeper/Types.h> │
8 │ Alexander Burmak │ #include <Common/escapeForFileName.h> │
9 │ Alexander Burmak │ #include <Common/formatReadable.h> │
10 │ Alexander Burmak │ #include <Common/thread_local_rng.h> │
11 │ Alexander Burmak │ #include <Common/typeid_cast.h> │
12 │ Nikolai Kochetov │ #include <Storages/MergeTree/DataPartStorageOnDisk.h> │
13 │ alesapin │ #include <Disks/ObjectStorages/IMetadataStorage.h> │
14 │ alesapin │ │
15 │ Alexey Milovidov │ #include <DB/Databases/IDatabase.h> │
16 │ Alexey Zatelepin │ #include <Storages/MergeTree/ReplicatedMergeTreePartHeader.h> │
17 │ CurtizJ │ #include <Storages/MergeTree/MergeTreeDataPart.h> │
18 │ Kirill Shvakov │ #include <Parsers/ASTDropQuery.h> │
19 │ s-kat │ #include <Storages/MergeTree/PinnedPartUUIDs.h> │
20 │ Nikita Mikhaylov │ #include <Storages/MergeTree/MergeMutateExecutor.h> │
└─────────────────┴──────────────────────┴───────────────────────────────────────────────────────────────┘
20 rows in set. Elapsed: 0.547 sec. Processed 7.88 million rows, 679.20 MB (14.42 million rows/s., 1.24 GB/s.)

ここでの正確で改善された解決策を歓迎します。

関連コンテンツ