マージリクエスト差分開発ガイド

このドキュメントでは、マージリクエストの差分のバックエンドの設計と流れについて説明します。貢献者の助けになるはずです:

  • コード設計の理解
  • 貢献することによる改善点の特定。

実装の詳細は頻繁に変更される可能性があるため、意図的にあまり詳しく書いていません。これらの詳細については、コードを読んでください。ここで述べられているコンポーネントは、マージリクエストの差分がどのように生成され、保存され、ユーザーに返されるのかについてのアプリケーションの主要な部分です。

note
このページは生きているドキュメントです。このドキュメントで触れられているコードベースの部分が変更されたり削除されたりしたとき、あるいは新しいコンポーネントが追加されたときには、適宜更新してください。

データモデル

4つの主要なActiveRecordモデルは、私たちが総称して_diffと_呼ぶものを表しています。これらのデータベースでバックアップされたレコードは、プロジェクトのGitリポジトリに含まれるデータを複製し、Gitalyへの過剰なアクセス要求に対するキャッシュの一部となっています。さらに、これらは論理的な場所を提供します:

  • diffの断片に関するメタデータの計算と取得。
  • 一般的なクラスおよびインスタンスベースのロジック。
erDiagram MergeRequest ||--|{ MergeRequestDiff: "" MergeRequestDiff |{--|{ MergeRequestDiffCommit: "" MergeRequestDiff |{--|| MergeRequestDiffDetail: "" MergeRequestDiff |{--|{ MergeRequestDiffFile: "" MergeRequestDiffCommit |{--|| MergeRequestDiffCommitUser: ""

MergeRequestDiff

MergeRequestDiffapp/models/merge_request_diff.rb で定義されています。このクラスは、一連のコミットから得られる diff に関連するメタデータとコンテキストを保持します。このクラスは、diff の内容、個々のコミット、変更を含むファイルを操作するための主な手段となるメソッドを定義します。

#<MergeRequestDiff:0x00007fd1ed63b4d0
 id: 28,
 state: "collected",
 merge_request_id: 28,
 created_at: Tue, 06 Sep 2022 18:56:02.509469000 UTC +00:00,
 updated_at: Tue, 06 Sep 2022 18:56:02.754201000 UTC +00:00,
 base_commit_sha: "ae73cb07c9eeaf35924a10f713b364d32b2dd34f",
 real_size: "9",
 head_commit_sha: "bb5206fee213d983da88c47f9cf4cc6caf9c66dc",
 start_commit_sha: "0b4bc9a49b562e85de7cc9e834518ea6828729b9",
 commits_count: 6,
 external_diff: "diff-28",
 external_diff_store: 1,
 stored_externally: nil,
 files_count: 9,
 patch_id_sha: "d504412d5b6e6739647e752aff8e468dde093f2f",
 sorted: true,
 diff_type: "regular",
 verification_checksum: nil>

diff の内容には、通常このクラスを通してアクセスします。ユーザーに返す前に、diff やファイル、コミットの内容にロジックが適用されることがよくあります。

MergeRequestDiff#commits_count

MergeRequestDiff が保存されると、関連するMergeRequestDiffCommit レコードがカウントされ、commits_count 列にキャッシュされます。この数はコミットタブのカウンターとしてマージリクエストページに表示されます。

MergeRequestDiffCommit レコードが削除された場合、カウンターは更新されません。

MergeRequestDiffCommit

MergeRequestDiffCommitapp/models/merge_request_diff_commit.rb で定義されています。このクラスは対応するMergeRequestDiff に含まれる単一のコミットに対応し、コミットに関するヘッダ情報を保持します。

#<MergeRequestDiffCommit:0x00007fd1dfc6c4c0
  authored_date: Wed, 06 Aug 2022 06:35:52.000000000 UTC +00:00,
  committed_date: Wed, 06 Aug 2022 06:35:52.000000000 UTC +00:00,
  merge_request_diff_id: 28,
  relative_order: 0,
  sha: "bb5206fee213d983da88c47f9cf4cc6caf9c66dc",
  message: "Feature conflcit added\n\nSigned-off-by: Sample User <sample.user@example.com>\n",
  trailers: {},
  commit_author_id: 19,
  committer_id: 19>

すべてのMergeRequestDiffCommit には対応するMergeRequest::DiffCommitUser レコードがあり、ActiveRecord の用語では:belongs_to です。これらのレコードは:commit_author:committerで、別個の個体である可能性があります。

MergeRequest::DiffCommitUser

MergeRequest::DiffCommitUserapp/models/merge_request/diff_commit_user.rb で定義されます。これは、指定されたコミットのnameemail をキャプチャしますが、User レコードへの接続自体は含まれません。

#<MergeRequest::DiffCommitUser:0x00007fd1dff7c930
  id: 19,
  name: "Sample User",
  email: "sample.user@example.com">

MergeRequestDiffFile

MergeRequestDiffFileapp/models/merge_request_diff_file.rb で定義されています。このクラスのこのレコードは、MergeRequestDiff に含まれる単一のファイルの差分を表します。メタ情報と、ファイルの変更との関係についての具体的な情報の両方を保持します:

  • 追加されたか、名前が変更されたか。
  • diffでの順序。
  • 生のdiff出力そのもの。

外部diffストレージ

デフォルトでは、MergeRequestDiffFile の diff データはmerge_request_diff_files テーブルのdiff カラムに保存されます。インストールによってはテーブルが大きくなりすぎることがあるため、外部ストレージに diff を保存して容量を節約するように設定されています。設定方法はマージリクエストの diff 保存を参照してください。

外部ストレージを使用するように設定されている場合:

  • データベースのdiff 列は、NULL のままです。
  • 関連するMergeRequestDiff レコードは、MergeRequestDiff の作成時にstored_externally 属性をtrue に設定します。

ScheduleMigrateExternalDiffsWorker という名前のcronジョブも毎時15分にスケジュールされます。これにより、まだデータベースに保存されているdiff が内部ストレージにマイグレーションされます。

MergeRequestDiffDetail

MergeRequestDiffDetailapp/models/merge_request_diff_detail.rb で定義されています。このクラスは Geo レプリケーションの検証情報を提供しますが、それ以外はユーザー向けの差分には使用されません。

#<MergeRequestDiffFile:0x00007fd1ef7c9048
  merge_request_diff_id: 28,
  relative_order: 0,
  new_file: true,
  renamed_file: false,
  deleted_file: false,
  too_large: false,
  a_mode: "0",
  b_mode: "100644",
  new_path: "files/ruby/feature.rb",
  old_path: "files/ruby/feature.rb",
  diff:
   "@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n",
  binary: false,
  external_diff_offset: nil,
  external_diff_size: nil>

フロー

これらのフローチャートは、コントローラから各機能のモデルまでの流れを説明するのに役立つはずです。このページは、diffへのアクセスやdiffを操作するためのオプションのすべてを文書化することを意図したものではなく、最も一般的なものだけに焦点を当てています。

batch_diffs.json

diff を見るための最も一般的な方法は、GitLab UI のマージリクエストページの一番上にあるChangesタブです。これを選択すると、Projects::MergeRequests::DiffsController#diffs_batch が提供する/-/merge_requests/:id/batch_diffs.json へのページ分割リクエストによって diff が読み込まれます:

sequenceDiagram Note over .#diffs_batch: Preload diffs and ivars .#diffs_batch->>+.#define_diff_vars: &nbsp; .#define_diff_vars ->>+ @merge_request: @merge_request_diffs = Note right of @merge_request: An ordered collection of all diffs in MR @merge_request-->>-.#define_diff_vars: &nbsp; .#define_diff_vars ->>+ @merge_request: @merge_request_diff = Note right of @merge_request: Most recent merge_request_diff (or commit) @merge_request-->>-.#define_diff_vars: &nbsp; .#define_diff_vars ->>+ .#define_diff_vars: @compare = Note right of .#define_diff_vars:: param-filtered merge_request_diff(s) .#define_diff_vars -->>- .#diffs_batch: &nbsp; Note over .#diffs_batch: Preloading complete .#diffs_batch->>+@merge_request: Calculate unfoldable diff lines Note right of @merge_request: note_positions_for_paths.unfoldable @merge_request-->>-.#diffs_batch: &nbsp; Note over .#diffs_batch: Build options hash Note over .#diffs_batch: Build cache_context Note over .#diffs_batch: Unfold files in diff .#diffs_batch->>+Gitlab_Diff_FileCollection_MergeRequestDiffBase: diffs.write_diff Gitlab_Diff_FileCollection_MergeRequestDiffBase->>+Gitlab_Diff_HighlightCache: Highlight diff Gitlab_Diff_HighlightCache -->>-Gitlab_Diff_FileCollection_MergeRequestDiffBase: Return highlighted diff Note over Gitlab_Diff_FileCollection_MergeRequestDiffBase: Cache diff Gitlab_Diff_FileCollection_MergeRequestDiffBase-->>-.#diffs_batch: &nbsp; Note over .#diffs_batch: render JSON