Gitがコミットを維持する方法
Gitリポジトリは単純なキーと値のストレージを使用します。キーはSHA-1ハッシュであり、値は3つのタイプのいずれかのコンテナです:コミットの説明、ファイルツリーの説明、またはファイルの内容。 このリポジトリをデータベースとして使用するための低レベルの配管コマンドもあります。
echo 'test content' | git hash-object -w --stdin
このアーキテクチャ上の特徴により、Gitはファイルの内容によって名前を変更することを追跡するという泥だらけの言葉が生まれました。 「コミット」オブジェクトの名前を変更すると、「ファイルコンテンツ」オブジェクトへのリンクが含まれますが、コンテンツが変更されていない場合は、すでにリポジトリにあるオブジェクトへのリンクになります。
開発者がコミットを作成すると、Gitは1つのコミット記述オブジェクトと、ファイル構造とファイルの内容を記述するオブジェクトの束をリポジトリに配置します。 したがって、「コミット」は、キーと値のストレージ内の関連するGitオブジェクトです。
デフォルトでは、Gitはファイルのコンテンツ全体を保存します。100キロバイトのソースの行を変更すると、zlibで圧縮された100キロバイトすべてのオブジェクトがリポジトリに追加されます。 リポジトリが過度に膨張するのを防ぐために、Gitはプッシュコマンドの実行時に実行されるガベージコレクターを提供しますが、オブジェクトは元のファイルと次のリビジョン(diff)の違いを含むパックファイルに再パッケージされます。
コミットが死ぬとき
場合によっては、コミットは必要ないかもしれません。 たとえば、開発者がfooをコミットしてから、resetコマンドを使用して変更をロールバックしました。 Gitは、コミットをすぐに削除しないように設計されているため、開発者は最も破壊的なアクションでも「やり取り」できます。 特別なreflogコマンドを使用すると、リポジトリへのすべての変更へのリンクを含む操作ログを表示できます。
ただし、「不必要な」コミットは、resetコマンドを使用したときだけでなく発生します。 たとえば、一般的なリベース操作は、コミットに関する情報をコピーするだけで、誰にも必要のない「オリジナル」をリポジトリに残します。 そのような「失われた」オブジェクトがGitに蓄積するのを防ぐために、プッシュコマンドの実行時に自動的に呼び出されるか手動で呼び出されるガベージコレクションメカニズム(前述のガベージコレクタ)が提供されます。
ガベージコレクターは、参照されなくなったオブジェクトを検索し、それらをストレージから削除します。 reflog操作ログはこれに大きな役割を果たします。その中のリンクの寿命は限られています。デフォルトでは、リンクのないオブジェクトでは30日間、リンクのあるオブジェクトでは90日間です。 ガベージコレクターは、最初に期限切れのリンクをすべてreflogから削除し、次にリポジトリから参照されなくなったオブジェクトを削除します。 このアーキテクチャにより、開発者は30日間で「不要な」コミットを復元できます。そうしないと、この期間が過ぎるとリポジトリから完全に削除されます。
GitHubで何が起こったのですか?
すでに推測していると思います。 指定されたコミットは不要でした。おそらく、作成者がリベースを作成しました。 ただし、GitHubには、プッシュコマンドが実行されないサーバーリポジトリの内容が表示されます。 ガベージコレクターは、ほとんどの場合、誰も呼び出しません。 同時に、このようなリポジトリのクローンを作成するとき、Gitはリンクが存在するコミットのみをネットワーク経由で送信し、「失われたコミット」(ルーズオブジェクトとして知られている)はサーバー側のデッドウェイトのままです。
Gitの内部へのこの短い余談は、たとえばバグトラッカーによって参照される「コミットの欠落」を検索する際に貴重な時間を節約できることを願っています。 私がどこかで間違いをしたか、コメントがある場合、私は喜んでコメントで話します。