日本語 ▾ トピック ▾ 最新バージョン ▾ 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方向) マージ、または3方向マージを実行できます。-m と一緒に使用する場合、-u フラグは、マージ結果でワークツリー内のファイルも更新させます。

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

オプション

-m

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

--reset

-m と同じですが、未マージのエントリは失敗するのではなく破棄されます。-u と併用した場合、ワーキングツリーの変更や追跡されていないファイルまたはディレクトリの喪失につながる更新は、操作を中断しません。

-u

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

-i

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

-n
--dry-run

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

-v

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

--trivial

git read-tree による3方向マージを、ファイルレベルのマージが一切不要な場合にのみ行うように制限します。単純なケースでマージを解決し、競合するファイルをインデックスに未解決のまま残すことはしません。

--aggressive

通常、git read-tree による3方向マージは、本当に単純なケースのマージを解決し、その他のケースはインデックスに未解決のまま残すため、ポーセレンは異なるマージポリシーを実装できます。このフラグは、コマンドが内部でさらにいくつかのケースを解決するようにします。

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

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

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

--prefix=<prefix>

現在のインデックスの内容を維持し、指定されたツリーオブジェクトの内容を <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方向マージです。

単一ツリーマージ

ツリーが1つだけ指定されている場合、git read-tree はユーザーが -m を指定しなかったかのように動作します。ただし、元のインデックスに特定のパス名の項目があり、そのパスの内容が読み込まれるツリーと一致する場合、インデックスからの stat 情報が使用されます。(つまり、インデックスの 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方向マージ

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

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

これは、次のようにできることを意味します。

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

これにより、すべての <tree1> エントリが「ステージ1」に、すべての <tree2> エントリが「ステージ2」に、すべての <tree3> エントリが「ステージ3」にインデックスに追加された状態になります。別のブランチを現在のブランチにマージするときは、共通の祖先ツリーを <tree1>、現在のブランチのヘッドを <tree2>、他のブランチのヘッドを <tree3> として使用します。

さらに、git read-tree には特殊なロジックがあり、次のような状態でファイルがすべての点で一致する場合、「ステージ0」に「折りたたむ」とされています。

  • ステージ2と3が同じ場合;どちらかを取る (違いはない - ステージ2の自分のブランチとステージ3の相手のブランチで同じ作業が行われた)。

  • ステージ1とステージ2が同じでステージ3が異なる場合;ステージ3を取る (ステージ2の自分のブランチはステージ1の祖先から何も変更していないが、ステージ3の相手のブランチは作業を行った)。

  • ステージ1とステージ3が同じでステージ2が異なる場合;ステージ2を取る (自分が何か作業をしたが、相手は何もしなかった)。

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

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

既に内容が書き込まれているインデックスファイルで3方向マージを開始する場合、ステージ1、2、3の順序(したがって、3つの <tree-ish> コマンドライン引数の順序)は重要です。アルゴリズムがどのように機能するかを以下に示します。

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

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

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

    • インデックスを順番にウォークし、ステージ0のエントリはすでに処理されているためすべて無視します。

    • 「ステージ1」が見つかったが、対応する「ステージ2」または「ステージ3」が見つからない場合、それは両方のツリーから削除されたこと(元のツリーにのみ存在したこと)を知っており、そのエントリを削除します。

    • 一致する「ステージ2」と「ステージ3」ツリーが見つかった場合、どちらか一方を削除し、もう一方を「ステージ0」エントリに変更します。対応する「ステージ1」エントリが存在する場合も削除します。..すべての通常の単純なルール..

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

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

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

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

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

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

あなたのワークツリーはまだあなたのHEAD ($JC) に基づいていますが、それ以降にいくつかの編集を加えています。3方向マージは、$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 は大声で警告し、何も変更せずに失敗します)。そのような場合、作業の途中だったことをそのまま続行し、ワーキングツリーの準備が整ったら (つまり、進行中の作業を完了したら)、再度マージを試みることができます。

スパースチェックアウト

注: 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