日本語 ▾ トピック ▾ 最新バージョン ▾ git-read-tree は 2.43.0 で最終更新されました

名前

git-read-tree - ツリー情報をインデックスに読み込む

概要

git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)
		[-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
		(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])

説明

<tree-ish> で指定されたツリー情報をインデックスに読み込みますが、実際に "キャッシュ" したファイルを更新することはありません。(参照: git-checkout-index[1])

オプションで、-m フラグを使用して、ツリーをインデックスにマージしたり、ファストフォワード (2-way) マージ、または 3-way マージを実行したりできます。-m とともに使用すると、-u フラグによって、ワークツリー内のファイルもマージ結果で更新されます。

git read-tree 自体によって行われるのは、自明なマージのみです。git read-tree が戻るとき、競合するパスのみがマージされていない状態になります。

オプション

-m

読み込みだけでなくマージを実行します。インデックスファイルにマージされていないエントリがある場合、以前に開始したマージを完了していないことを示しているため、このコマンドは実行を拒否します。

--reset

-m と同じですが、失敗する代わりにマージされていないエントリが破棄されます。-u とともに使用すると、ワークツリーの変更、または追跡されていないファイルやディレクトリの損失につながる更新によって操作が中止されることはありません。

-u

マージが成功した後、マージ結果でワークツリー内のファイルを更新します。

-i

通常、ローカルの変更が失われないように、マージにはインデックスファイルとワークツリー内のファイルが現在のヘッドコミットと同期している必要があります。このフラグはワークツリーとのチェックを無効にし、現在のワークツリーの状態に直接関連しないツリーのマージを一時的なインデックスファイルに作成する場合に使用することを目的としています。

-n
--dry-run

実際にインデックスやワークツリー内のファイルを更新せずに、コマンドがエラーになるかどうかを確認します。

-v

ファイルのチェックアウトの進行状況を表示します。

--trivial

git read-tree による三者マージを、ファイルレベルのマージが不要な場合にのみ実行されるように制限し、自明なケースのマージを解決して、競合するファイルをインデックスに未解決のまま残すのではなく、この動作を強制します。

--aggressive

通常、git read-tree による三者マージは、非常に自明なケースのマージを解決し、それ以外のケースはインデックスに未解決のまま残すため、ポーセリンは異なるマージポリシーを実装できます。このフラグは、コマンドが内部でさらにいくつかのケースを解決するようにします。

  • 片方がパスを削除し、もう片方がパスをそのままにする場合。解決策はパスを削除することです。

  • 両方がパスを削除する場合。解決策はパスを削除することです。

  • 両方が同じパスを追加する場合。解決策はパスを追加することです。

--prefix=<prefix>

現在のインデックスの内容を保持し、名前付き tree-ish の内容を <prefix> で指定されたディレクトリの下に読み込みます。このコマンドは、元のインデックスファイルにすでに存在していたエントリを上書きすることを拒否します。

--index-output=<file>

$GIT_INDEX_FILE に結果を書き出す代わりに、結果のインデックスを名前付きファイルに書き込みます。コマンドの実行中、元のインデックスファイルは通常と同じメカニズムでロックされます。ファイルは、通常のインデックスファイルの隣に作成される一時ファイルから rename(2) できる必要があります。通常、これはインデックスファイルと同じファイルシステム上にある必要があり、インデックスファイルとインデックス出力ファイルが存在するディレクトリへの書き込み権限が必要であることを意味します。

--[no-]recurse-submodules

--recurse-submodules を使用すると、read-tree を再帰的に呼び出すことにより、スーパープロジェクトに記録されているコミットに従って、すべてのアクティブなサブモジュールの内容を更新し、サブモジュールの HEAD もそのコミットでデタッチされた状態に設定します。

--no-sparse-checkout

core.sparseCheckout が true であっても、スパースチェックアウトのサポートを無効にします。

--empty

ツリーオブジェクトをインデックスに読み込む代わりに、単にインデックスを空にします。

-q
--quiet

静かに、フィードバックメッセージを抑制します。

<tree-ish#>

読み込む/マージするツリーオブジェクトの ID。

マージ

-m が指定されている場合、git read-tree は 3 種類のマージを実行できます。1 つのツリーが指定されている場合は単一ツリーマージ、2 つのツリーが指定されている場合はファストフォワードマージ、または 3 つ以上のツリーが指定されている場合は 3-way マージです。

単一ツリーマージ

1 つのツリーのみが指定されている場合、git read-tree はユーザーが -m を指定しなかったかのように動作しますが、元のインデックスに特定のパス名のエントリがあり、そのパスの内容が読み込まれるツリーと一致する場合、インデックスからの統計情報が使用されます。(つまり、インデックスの stat() がマージされたツリーの stat() よりも優先されます)。

git read-tree -m <newtree> の後に git checkout-index -f -u -a を実行すると、git checkout-index は実際に変更されたもののみをチェックアウトします。

これは、git read-tree の後に git diff-files を実行したときに不要な誤検出を避けるために使用されます。

2 ツリーマージ

通常、これは git read-tree -m $H $M として呼び出されます。ここで $H は現在のリポジトリのヘッドコミット、$M は外部ツリーのヘッドであり、単純に $H より先行しています (つまり、ファストフォワード状況にあります)。

2 つのツリーが指定されている場合、ユーザーは git read-tree に次のことを伝えています。

  1. 現在のインデックスとワークツリーは $H から派生していますが、ユーザーは $H 以降にそれらにローカルな変更を加えている可能性があります。

  2. ユーザーは $M にファストフォワードしたいと考えています。

この場合、git read-tree -m $H $M コマンドは、この "マージ" の結果としてローカルな変更が失われないことを保証します。ここに "キャリーフォワード" ルールがあります。ここで "I" はインデックスを表し、"clean" はインデックスとワークツリーが一致していることを意味し、"exists"/"nothing" は指定されたコミットにパスが存在するかどうかを参照します。

	I                   H        M        Result
       -------------------------------------------------------
     0  nothing             nothing  nothing  (does not happen)
     1  nothing             nothing  exists   use M
     2  nothing             exists   nothing  remove path from index
     3  nothing             exists   exists,  use M if "initial checkout",
				     H == M   keep index otherwise
				     exists,  fail
				     H != M

        clean I==H  I==M
       ------------------
     4  yes   N/A   N/A     nothing  nothing  keep index
     5  no    N/A   N/A     nothing  nothing  keep index

     6  yes   N/A   yes     nothing  exists   keep index
     7  no    N/A   yes     nothing  exists   keep index
     8  yes   N/A   no      nothing  exists   fail
     9  no    N/A   no      nothing  exists   fail

     10 yes   yes   N/A     exists   nothing  remove path from index
     11 no    yes   N/A     exists   nothing  fail
     12 yes   no    N/A     exists   nothing  fail
     13 no    no    N/A     exists   nothing  fail

	clean (H==M)
       ------
     14 yes                 exists   exists   keep index
     15 no                  exists   exists   keep index

        clean I==H  I==M (H!=M)
       ------------------
     16 yes   no    no      exists   exists   fail
     17 no    no    no      exists   exists   fail
     18 yes   no    yes     exists   exists   keep index
     19 no    no    yes     exists   exists   keep index
     20 yes   yes   no      exists   exists   use M
     21 no    yes   no      exists   exists   fail

すべての「インデックスを保持」ケースでは、インデックスエントリは元のインデックスファイルのままです。エントリが最新でない場合、git read-tree は -u フラグの下で操作するときにワークツリーのコピーをそのまま保持します。

この形式の git read-tree が正常に戻った場合、git diff-index --cached $M を実行することで、作成した「ローカルな変更」のどれが引き継がれたかを確認できます。これは、このような 2 ツリーマージの前に git diff-index --cached $H が生成した内容と必ずしも一致するとは限らないことに注意してください。これはケース 18 および 19 のためです。すでに $M に変更があった場合 (たとえば、パッチ形式でメールで受け取った場合など)、このマージの前に git diff-index --cached $H はその変更について通知しましたが、2 ツリーマージ後に git diff-index --cached $M の出力には表示されません。

ケース 3 は少し厄介で説明が必要です。このルールの結果は、ユーザーがパスの削除をステージングし、新しいブランチに切り替えた場合、論理的にはパスを削除することであるべきです。しかし、それは初期チェックアウトを防ぐことになるため、インデックスの内容が空の場合にのみ M (新しいツリー) を使用するようにルールが変更されます。それ以外の場合、H と M が同じである限り、パスの削除は保持されます。

3-Way マージ

各「インデックス」エントリには、2 ビット相当の「ステージ」状態があります。ステージ 0 は通常のものであり、通常の用途ではこれしか見ません。

ただし、3 つのツリーで git read-tree を実行すると、「ステージ」は 1 から始まります。

つまり、次のことができます。

$ git read-tree -m <tree1> <tree2> <tree3>

結果として、すべての <tree1> エントリが「stage1」に、すべての <tree2> エントリが「stage2」に、すべての <tree3> エントリが「stage3」にあるインデックスになります。別のブランチを現在のブランチにマージする場合、共通の祖先ツリーを <tree1>、現在のブランチのヘッドを <tree2>、もう一方のブランチのヘッドを <tree3> として使用します。

さらに、git read-tree には特別なロジックがあり、次の状態ですべての点で一致するファイルを見つけると、「stage0」に「折りたたむ」と規定されています。

  • ステージ 2 と 3 は同じです。どちらか一方を選択します (違いはありません。ステージ 2 のブランチとステージ 3 のブランチで同じ作業が行われています)

  • ステージ 1 とステージ 2 が同じで、ステージ 3 が異なる場合。ステージ 3 を選択します (ステージ 2 のブランチはステージ 1 の祖先以降何もしていませんが、ステージ 3 のブランチは作業を行っています)

  • ステージ 1 とステージ 3 が同じで、ステージ 2 が異なる場合。ステージ 2 を選択します (私たちは何かをしましたが、彼らは何もしていません)

git write-tree コマンドは、意味のないツリーを書き込むことを拒否し、ステージ 0 ではない単一のエントリを見つけると、マージされていないエントリについて警告します。

さて、これらはまったく無意味なルールの集まりのように聞こえますが、実際には、高速マージを行うために必要なものです。異なるステージは、「結果ツリー」(ステージ 0、別名「マージ済み」)、元のツリー(ステージ 1、別名「orig」)、およびマージしようとしている 2 つのツリー(それぞれステージ 2 および 3)を表します。

既に投入されているインデックスファイルで 3-way マージを開始する場合、ステージ 1、2、3 の順序 (したがって、3 つの <tree-ish> コマンドライン引数の順序) は重要です。アルゴリズムの概要は次のとおりです。

  • ファイルが 3 つのツリーすべてで同一の形式で存在する場合、git read-tree によって自動的に「マージ済み」状態に折りたたまれます。

  • 3 つのツリーのいずれかで何らかの差異があるファイルは、インデックスに個別のエントリとして残ります。ステージ 0 以外のステージを削除し、マージされたバージョンを挿入する方法を決定するのは「ポーセリンポリシー」次第です。

  • インデックスファイルは、このすべての情報を保存および復元するため、段階的にマージできますが、ステージ 1/2/3 にエントリがある限り(つまり、「未マージのエントリ」がある限り)、結果を書き込むことはできません。したがって、マージアルゴリズムは非常にシンプルになります。

    • インデックスを順番にウォークし、ステージ 0 のエントリはすべて無視します。既に処理済みだからです。

    • 「stage1」が見つかるが、一致する「stage2」または「stage3」が見つからない場合、両方のツリーから削除されたことになります(元のツリーにのみ存在しました)。そのエントリを削除します。

    • 一致する「stage2」と「stage3」ツリーが見つかった場合、どちらかを削除し、もう一方を「stage0」エントリにします。一致する「stage1」エントリも存在する場合は削除します。…すべての通常の自明なルール…​

この最後のステップを実行するには、通常、提供された git merge-one-file とともに git merge-index を使用します。このスクリプトは、各パスをマージする際に、またマージが成功した最後に、作業ツリー内のファイルを更新します。

既に投入されているインデックスファイルで 3-way マージを開始する場合、それがワークツリー内のファイルの状態を表していると想定され、インデックスファイルに未記録の変更を持つファイルも存在できます。さらに、この状態はステージ 2 ツリーから「派生」していると想定されます。元のインデックスファイルにステージ 2 と一致しないエントリが見つかった場合、3-way マージは実行を拒否します。

これは、進行中の変更が失われるのを防ぎ、ランダムな変更を無関係なマージコミットに混入させるのを防ぐために行われます。例として、リポジトリに最後にコミットされたものから開始するとします。

$ JC=`git rev-parse --verify "HEAD^0"`
$ git checkout-index -f -u -a $JC

git update-index を実行せずに、ランダムな編集を行います。そして、プルして以来、「upstream」ツリーの先端が進んでいることに気づきます。

$ git fetch git://.... linus
$ LT=`git rev-parse FETCH_HEAD`

あなたのワークツリーはまだ HEAD ($JC) に基づいていますが、それ以降いくつかの編集を行っています。三者マージは、$JC 以降、インデックスエントリを追加または変更していないことを確認し、そうでない場合は適切な処理を行います。したがって、次のシーケンスでは

$ git read-tree -m -u `git merge-base $JC $LT` $JC $LT
$ git merge-index git-merge-one-file -a
$ echo "Merge with Linus" | \
  git commit-tree `git write-tree` -p $JC -p $LT

コミットされるのは、$JC と $LT の間の純粋なマージであり、進行中の作業中の変更は含まれず、ワークツリーはマージの結果に更新されます。

ただし、このマージによって上書きされる可能性のあるローカルの変更が作業ツリーにある場合、git read-tree は、変更が失われるのを防ぐために実行を拒否します。

言い換えれば、作業ツリーにのみ存在するものを心配する必要はありません。プロジェクトの一部でローカルな変更があり、それがマージに関与していない場合、その変更はマージに干渉せず、そのまま保持されます。干渉する場合、マージは開始されません(git read-tree は大声で不平を言い、何も変更せずに失敗します)。そのような場合、進行中の作業をそのまま続け、作業ツリーの準備ができたら(つまり、進行中の作業を終えたら)、再度マージを試みることができます。

SPARSE CHECKOUT

注:git-update-index[1] および read-tree の skip-worktree 機能は、git-sparse-checkout[1] が導入される前から存在していました。ユーザーは、スパースチェックアウト/skip-worktree 関連のニーズに対して、これらのプラミングコマンドよりも sparse-checkout コマンドを使用することを推奨します。ただし、以下の情報は、sparse-checkout コマンドの非コーンモードで使用されるパターン形式を理解しようとしているユーザーにとって役立つ場合があります。

「スパースチェックアウト」により、作業ディレクトリを疎に埋めることができます。skip-worktree ビット(git-update-index[1] を参照)を使用して、作業ディレクトリ内のファイルが注目する価値があるかどうかを Git に伝えます。

git read-tree およびその他のマージベースのコマンド (git merge, git checkout…​) は、skip-worktree ビットマップと作業ディレクトリの更新を維持するのに役立ちます。$GIT_DIR/info/sparse-checkout は、skip-worktree 参照ビットマップを定義するために使用されます。git read-tree が作業ディレクトリを更新する必要がある場合、このファイルに基づいてインデックスの skip-worktree ビットをリセットします。このファイルは .gitignore ファイルと同じ構文を使用します。エントリがこのファイルのパターンと一致する場合、またはエントリが作業ツリーに存在するファイルに対応する場合、そのエントリには skip-worktree は設定されません。それ以外の場合、skip-worktree が設定されます。

次に、新しい skip-worktree の値と以前の値を比較します。skip-worktree が設定済みから未設定に変わる場合、対応するファイルが再度追加されます。未設定から設定済みに変わる場合、そのファイルは削除されます。

$GIT_DIR/info/sparse-checkout は通常、どのファイルが含まれているかを指定するために使用されますが、否定パターンを使用して、どのファイルが含まれていないかを指定することもできます。たとえば、ファイル unwanted を削除するには、次のようになります。

/*
!unwanted

もう1つ厄介なのは、スパースチェックアウトを不要になったときに、作業ディレクトリを完全に再構築することです。skip-worktreeビットがインデックスに残っており、作業ディレクトリが疎に設定されたままであるため、単に「スパースチェックアウト」を無効にすることはできません。次のように、$GIT_DIR/info/sparse-checkoutファイルの内容で作業ディレクトリを再構築する必要があります。

/*

その後、スパースチェックアウトを無効にできます。git read-tree および同様のコマンドにおけるスパースチェックアウトのサポートはデフォルトで無効になっています。スパースチェックアウトのサポートを有効にするには、core.sparseCheckout をオンにする必要があります。

GIT

git[1]スイートの一部

scroll-to-top