SYNOPSIS
git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--index-output=<file>] [--no-sparse-checkout] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
DESCRIPTION
<tree-ish> によって指定されたツリー情報をインデックスに読み込みますが、「キャッシュする」ファイルは実際には「更新」されません。 (git-checkout-index(1) 参照)
オプションで、ツリーをインデックスにマージしたり、早送り(fast-forward)(つまり、2方向)マージを実行したり、 -m
フラグを使用して3方向マージを実行したりできます。 -u
フラグを -m
と一緒に使用すると、マージの結果で作業ツリー内のファイルも更新されます。
trivial(些細な)マージは、 git read-tree
自体によって行われます。 git read-tree
から戻ると、競合するパスのみがマージされていない状態になります。
OPTIONS
-
-m
-
読み取りだけでなく、マージを実行します。インデックスファイルにマージされていないエントリがある場合、コマンドは実行を拒否します。これは、以前に開始したマージが完了していないことを示します。
-
--reset
-
-m
と同じですが、マージされていないエントリが失敗する代わりに破棄される点が異なります。-u
と一緒に使うと、作業ツリーの変更が失われる原因になる更新によって、または、追跡されていないファイルやディレクトリが失われる原因になる更新によって、操作を中止(abort)されることはありません。 -
-u
-
マージが成功したら、マージの結果で作業ツリーのファイルを更新します。
-
-i
-
通常、マージでは、ローカルの変更が失われないように、インデックスファイルと作業ツリー内のファイルが現在のヘッドコミットで最新である必要があります。 このフラグは、作業ツリーでのチェックを無効にし、現在の作業ツリーのステータスに直接関係しないツリーを一時的なインデックスファイルにマージするときに使用することを目的としています。
-
-n
-
--dry-run
-
作業ツリー内のインデックスまたはファイルを実際に更新せずに、コマンドがエラーになるかどうかを確認します。
-
-v
-
ファイルのチェックアウトの進行状況を表示します。
-
--trivial
-
trivial(些細な)ケースでマージ解決作業し、競合するファイルをインデックスに未解決のままにする代わりに、
git read-tree
による3方向マージを、ファイルレベルのマージが必要ない場合にのみ発生するように制限します。 -
--aggressive
-
通常、
git read-tree
による3方向のマージは、本当にtrivial(些細な)ケースのマージを解決し、他のケースはインデックスに未解決のままにして、磁器コマンドがさまざまなマージポリシーを実装できるようにします。このフラグにより、コマンドはさらにいくつかのケースを内部的に解決します:-
一方の側がパスを削除し、もう一方の側がパスを変更しないままにする場合。 解決策は、そのパスを削除することです。
-
両側でパスを削除したとき。 解決策は、そのパスを削除することです。
-
両側で同じようにパスを追加する場合。 解決策は、そのパスを追加することです。
-
-
--prefix=<prefix>
-
現在のインデックスの内容を保持し、
<prefix>
のディレクトリの下にある名前付きtree-ishの内容を読み取ります。このコマンドは、元のインデックスファイルにすでに存在しているエントリの上書きを拒否します。 -
--index-output=<file>
-
結果を
$GIT_INDEX_FILE
に書き出す代わりに、結果のインデックスを名前付きファイルに書き込みます。コマンドの動作中は、元のインデックスファイルが通常と同じメカニズムでロックされます。ファイルは、通常のインデックスファイルの隣に作成される一時ファイルから名前を変更できるようにする必要があります(rename(2))。通常、これは、インデックスファイル自体と同じファイルシステム上にある必要があり、インデックスファイルとインデックス出力ファイルが配置されているディレクトリへの書き込み権限が必要であることを意味します。 -
--[no-]recurse-submodules
-
--recurse-submodules
を使用すると、スーパープロジェクトに記録されたコミットに従ってすべてのアクティブなサブモジュールのコンテンツが更新され、read-treeが再帰的に呼び出され、そのコミット時にサブモジュールのHEADが切り離される(detach)ように設定されます。 -
--no-sparse-checkout
-
`core.sparseCheckout`がtrueの場合でも、スパースチェックアウト(sparse checkout)のサポートを無効にします。
-
--empty
-
ツリーオブジェクトをインデックスに読み込む代わりに、空にします。
-
-q
-
--quiet
-
静かにし、フィードバックメッセージを抑制します。
- <tree-ish#>
-
読み取られる/マージされる ツリーオブジェクトのID。
MERGING
-m
が指定されている場合、 git read-tree
は3種類のマージを提供します。1つのツリーのみが指定されている場合は単一のツリーマージ(single tree merge)、2つのツリーとの早送り(fast-forward)マージ(two tree merge)、3つ以上のツリーが指定されている3方向マージです(3-way merge)。
Single Tree Merge
ツリーが 1 つだけ指定されている場合、 git read-tree
はユーザーが -m
を指定しなかったかのように動作しますが、 元のインデックスに特定のパス名のエントリがあり、パスの内容が読み取られるツリーと一致する場合、インデックスの統計情報が使用されます。(言い換えれば、インデックスの stat() はマージされたツリーのものよりも優先されます)。
つまり、 git read-tree -m <newtree>
の後に git checkout-index -f -u -a
を実行すると、 git checkout-index
は本当に変更されたものだけをチェックアウトします。
これは、 git read-tree
の後に git diff-files
を実行したときに不要な誤検出を避けるために使用します。
Two Tree Merge
通常、これは git read-tree -m $H $M
として呼び出されます。ここで、$H は現在のリポジトリのヘッドコミットであり、$M は $H の前にある外部ツリーのヘッドです(つまり、早送り(fast-forward)の状況にあります)。
2つのツリーが指定されている場合、ユーザーは git read-tree
に以下のように指示している事になります:
-
現在のインデックスと作業ツリーは$Hから派生していますが、 ユーザーは$H以降にローカルで変更を加えている可能性があります。
-
ユーザーは$Mに早送り(fast-forward)したいと考えています。
この場合、 git read-tree -m $H $M
コマンドは、この「マージ」の結果としてローカルの変更が失われないことを確認します。 「繰越」(carry forward)ルールは次のとおりです。「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 Merge
各「インデックス」エントリには、2ビット相当の「ステージ」状態があります。ステージ0は通常のステージであり、通常の使用で見られる唯一のステージです。
しかしながら、あなたが3つのツリーで git read-tree
を実行すると、「ステージ」は1から始まります。
これはあなたが以下のようにできることを意味します
$ git read-tree -m <tree1> <tree2> <tree3>
そうすると、「stage1」にすべての<tree1>エントリ、「stage2」にすべての<tree2>エントリ、「stage3」にすべての<tree3>エントリを持つインデックスが作成されます。 別のブランチを現在のブランチにマージする場合、共通の祖先ツリーを<tree1>として、現在のブランチヘッドを<tree2>として、他のブランチヘッドを<tree3>として使用します。
さらに、 git read-tree
には、以下のような特殊なケースのロジックがあります。以下の状態ですべての点で一致するファイルを見つけると、「折りたたみ」(collapse)して stage0
に戻します:
-
ステージ2と3は同一です。 どちらか一方を取ります(違いはありません。ステージ2のブランチとステージ3のブランチで同じ作業が行われました)
-
ステージ1とステージ2は同じで、ステージ3は異なります。ステージ3を取得します(ステージ2のブランチは、ステージ3のブランチが作業している間、ステージ1の祖先以降何もしませんでした)
-
ステージ1とステージ3は同じで、ステージ2は異なります。ステージ2を使用します(私達は何もしなかったのに何かをしました)
git write-tree
コマンドは、無意味なツリーの書き込みを拒否し、ステージ0ではない単一のエントリを検出すると、マージされていないエントリについて文句を言います。
ええ、これはすべてまったく無意味なルールのコレクションのように聞こえますが、実際には、高速マージを実行するために必要なものです。 異なるステージは、「結果ツリー」(result tree)(ステージ0、別名「merged」)、元のツリー(original tree)(ステージ1、別名「orig」)、およびマージしようとしている2つのツリー(それぞれステージ2と3)を表します。
すでに入力されているインデックスファイルを使用して3方向マージを開始する場合、ステージ1、2、および3の順序(つまり、3つの<tree-ish>コマンドライン引数の順序)は重要です。アルゴリズムの仕組みの概要は以下のとおりです:
-
ファイルが3つのツリーすべてに同じ形式で存在する場合、ファイルは
git read-tree
によって自動的に「マージされた」状態(merged state)に折りたたまれ(collapse)ます。 -
3つのツリーに違いがあるファイルは、インデックス内の別々のエントリとして残ります。0以外のステージを削除し、マージされたバージョンを挿入する方法を決定するのは、「磁器ポリシー」(porcelain policy)次第です。
-
インデックスファイルはこれらすべての情報を保存および復元するため、段階的にマージできますが、ステージ 1/2/3 のエントリ(つまり、「マージされていないエントリ」)がある限り、結果を書き込むことはできません。したがって、マージアルゴリズムは非常に単純になります:
-
あなたはインデックスを順番に歩きます。ステージ0のすべてのエントリはすでに完了しているため、無視します。
-
「stage1」が見つかったが、一致する「stage2」または「stage3」がない場合は、両方のツリーから削除されたことがわかり(元のツリーにのみ存在した)、そのエントリを削除します。
-
一致する「stage2」および「stage3」ツリーが見つかった場合は、それらの1つを削除し、もう1つを「stage0」エントリに変換します。 一致する「stage1」エントリも存在する場合は削除します。 .. すべての通常のtrivial(些細な)ルール ..
-
この最後のステップを実行するには、通常、提供された 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
を実行せずに、ランダムに編集します。 そして、あなたは彼(him)からpullしてからあなたの「上流」のツリーの先端が進んだことに気づきます:
$ 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
は大声で文句を言い、何も変更せずに失敗します)。 このような場合は、実行中の作業を続行し、作業ツリーの準備ができたら(つまり、進行中の作業が終了したら)、マージを再試行します。
SPARSE CHECKOUT
注意: git-update-index(1) と read-tree
の skip-worktree 機能は、git-sparse-checkout(1) の導入以前の機能です。 ユーザーは、 sparse-checkout/skip-worktree 関連の需要に対して、 これらの配管コマンドよりも優先して sparse-checkout
コマンドを使用することをお勧めします。 ただし、以下の情報は、sparse-checkout
コマンドの非円錐(non-cone)モードで使用されるパターン・スタイルを理解しようとしているユーザーにとって役立つ場合があります。
「スパースチェックアウト」(Sparse checkout)を使用すると、作業ディレクトリにまばらに(sparsely)データを入力できます。 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がsetからunsetに変わると、対応するファイルが追加されます。 unsetからsetに変わると、そのファイルは削除されます。
通常、 $GIT_DIR/info/sparse-checkout
はどのファイルが含まれているかを指定するために使用されますが、否定パターンを使用して、どのファイルが含まれていないかを指定することもできます。 たとえば、ファイル unwanted
を削除するには以下のようにします:
/*
!unwanted
もう1つの注意が必要なのは、スパースチェックアウトが不要になったときに作業ディレクトリを完全に再設定することです。 skip-worktreeビットはまだインデックスにあり、作業ディレクトリはまだまばらに(sparsely)配置されているため、「スパースチェックアウト」を無効にすることはできません。 以下のように、作業ディレクトリに $GIT_DIR/info/sparse-checkout
ファイルの内容を再入力する必要があります:
/*
その後、スパースチェックアウトを無効にできます。 git read-tree
および同様のコマンドでのスパースチェックアウトのサポートはデフォルトで無効になっています。 スパースチェックアウトをサポートするには、 core.sparseCheckout
をオンにする必要があります。
GIT
Part of the git(1) suite