12

基本的には同じはずだと思っていたのですが、やってみると

$ git stash show -p stash@{N}

$ git show stash@{N}

後者はいくつかの追加のコミット情報を示していますが、実際の差分ははるかに短かったです。(前者は約 12 個のファイルを示していますが、後者は 1 つしか示していません。)

では、両者の正確な違いは何で、なぜ異なるのでしょうか?

正しいことなどに頼ることもできgit diff stash@{M} stash@{N}ますか?

4

2 に答える 2

21

スタッシュバッグ

救われたのは、私が「スタッシュバッグ」git stashと呼んでいるものです。これは、「インデックス」コミット (ステージング領域) と「作業ツリー」コミットの 2 つの個別のコミットで構成されます。ワークツリー コミットは面白い種類のマージ コミットです。

適切に説明するために、ここでこれをもう一度描いてみましょう (はるかに長いバージョンについては、参照されている回答を参照してください)。簡単にするために、1 つのブランチと 3 つのコミットしかない小さなレポがあるとしAますC。あなたは1つのブランチにいて、いくつかの変更を加えてから実行しますgit stash save(または単にプレーンですgit stash)。これはあなたが得るものです:

A - B - C     <-- HEAD=master
        |\
        i-w   <-- the "stash"

ここで、別のブランチを作成 (または別のブランチに切り替える) ことができますが、説明のために、そのスタッシュをそこに残し、より「通常の」コミットを行うとしましょうmaster:

A - B - C - D - E    <-- HEAD=master
        |\
        i-w   <-- stash

ここでのポイントは、index とwork-tree のコミットのペアである「stash-bag」が、以前と同じコミットにまだ掛けられているということです。コミットは変更できません。これは stash-bag コミットにも当てはまります。

しかし、ここで、いくつかの変更を加えて (まだ on の状態で)、再度実行することで、新しいスタッシュを作成します。mastergit stash save

古い収納袋はどうなりますか?「参照名」2は、 新しいstash-bagstashを指すようになりました。しかし、古い隠し袋のコミットはまだそこにあります。「reflog」スタイル名が必要になりました。3stash@{1}

とにかく、あなたが今持っているのはこれです:

A - B - C - D - E     <-- HEAD=master
        |\      |\
        i-w     i-w   <-- stash
          .
           -------------- stash@{1}

( を使用するgit stash dropと、stash スクリプトは ref の reflog を操作して、ドロップされたstashstash-bag の ID を削除するだけです。そのため、すべての「より高い」ものに番号が付け直されます。実際の stash-bag 自体は、次の でガベージ コレクションされgit gcます。 )

この次のビットは、何が起こっているのかを理解するための鍵です。

git が特定のコミットに名前を付ける必要があるときはいつでも、さまざまな方法でそれを行うことができます。

各コミットには「本当の名前」があります。これは、ご覧のとおり、大きく醜い SHA-1 ハッシュで、値は676699a0e0cdfd97521f3524c763222f1c30a094. あなたはそれを書くことができます。常に同じコミットを意味します。コミットは決して変更できず、それはコミットの内容全体の暗号化ハッシュであるため、その特定のコミットが存在する場合、その値は常にその名前になります。

しかし、それは人々にとって良い名前ではありません。したがって、エイリアスがあります。ブランチ名やタグ名、 や などの相対名、HEADHEAD~2などの reflog スタイルの名前などHEAD@{yesterday}ですmaster@{1}git rev-parse(このような名前文字列をハッシュ値に変換するコマンド , があります。試してみてください: run git rev-parse HEADgit rev-parse stashなど。git のほとんどのものはgit rev-parsegit rev-list名前を SHA- 1 つの値。)

(リビジョンの命名方法の完全な説明については、gitrevisionsを参照してください。Gitはコミット以外にも SHA-1 を使用しますが、ここではコミットについて考えてみましょう。)

Git stash show、git show、および git diff

git showOK、ついに、あなたのvsgit stash showにたどり着くことができますgit diffこれは、スタッシュで使用することになっgit stash showているものなので、最初に取り組みましょう。さらに、サブコマンドは、名前を付けたコミット (または、名前を付けなかった場合は、参照を介して見つかったコミット) が隠し場所のように見えること、つまり、これらの面白いマージ コミットの 1 つであることを確認します。git stashstash

を実行するgit stash show -pと、git は差分 ( -patch) を表示します。しかし、それは正確に何を示していますか?

隠し袋のある図に戻ります。各 stash-bag は、特定のコミットにぶら下がっています。上記の「メイン」スタッシュは commit からぶら下がりEstash@{1}以前のスタッシュは からぶら下がっていCます。

何をするかgit stash show -pというと、そのスタッシュの作業ツリー コミットでwある を、スタッシュがハングするコミットと比較します。4

もちろん、これは自分で行うことができます。commit をぶら下げているwinと、 branch-name で名前を付けることができるcommitを比較したいとしましょう。したがって、次を実行できます。ここで、名前は (現在の) stash commitを参照し、 commitを参照するため、これは とまったく同じパッチを生成します。(そして、 inと commitを比較したい場合は、これら 2 つのコミットに名前を付けるように実行するだけで済みます。もちろん、単に.stashEEmastergit diff master stashstashwmasterEgit stash show -p stashwstash@{1}Cgit diffgit stash show -p stash@{1}

プレーンはgit showどうですか?これはもう少し複雑です。 git showコミットを喜んで表示し、参照を与えましたstash(stashそれ自体、または reflog バリアントのいずれか)。これは有効なコミット ID でありw、stash-bags の 1 つの作業ツリー コミットの 1 つに解決されます。ただし、マージコミットが表示git showされると、動作が異なります。ドキュメントが言うように:

また、 によって作成された特別な形式でマージ コミットを表示しgit diff-tree --ccます。

コミットがコミットとの通常のマージであるとgit show stash@{1}仮定すると、「結合された差分」が表示されます。結局のところ、これは通常のマージではありませんが、何を見ているかを知っていれば、結合された diffが実際に役立つ場合があります。以下のドキュメントを読んで、それがどのように機能するかを詳細に確認してください。wCiw--ccgit diff-tree--cc-c

... すべての親から変更されたファイルのみを一覧表示します。

の場合、 を実行する前にファイルを編集したstash場合、-vs- diff が空になると、これらのファイルは出力に表示されません。git addgit stashiw

最後に、これは異なるork-tree コミットを比較するgit diff stash@{M} stash@{N}よう求めているだけです。それがどれほどの意味を持つかは、比較対象によって異なります。これは、通常、スタッシュ バッグが取り付けられている場所によって異なります。git diffw


1本当は 2 つまたは 3 つですが、2 つとして描きます。git stash save(またはプレーンgit stash、つまり) で2 つのコミットを取得しますgit stash save-uまたは-aオプションを追加して追跡されていないファイルまたはすべてのファイルを保存すると、3 つのコミットが得られます。これは stash の復元に影響しますが、git stash showコマンドからの出力には影響しません。

2「参照名」は単なる名前であり、ブランチ名やタグ名のようなものです。参照名には多くの形式があります。ブランチとタグは、特別な目的を持つ単なる名前です。「リモート ブランチ」はこれらの参照の別の形式であり、「スタッシュ」も参照です。

実際、これは非常に特別なリファレンスHEADではありますが、別のリファレンスにすぎません。ファイルを削除すると、git はリポジトリがもはやリポジトリではないと判断することは非常に重要です。HEAD

いくつかの特殊な例外 (<code>HEAD、ORIG_HEADMERGE_HEADなど) を除いて、参照はすべて文字列 で始まりますrefs/。ブランチは で始まりrefs/heads/、タグは で始まりrefs/tags/、「リモート ブランチ」は で始まりrefs/remotes/ます。言い換えれば、参照には「名前空間」があり、通常は名前空間で始まり、refs/その下に別の単語を取得して、それらがどこにあるかを識別します。

stash 参照は綴られていますrefs/stash(そしてそこで止まりrefs/stash/jimmy_kimmelます。何もないか、そのようなものはありません)。

3実際、これは実際に reflog を使用しています。これは、とりわけ、「メイン」のもの以外のスタッシュが期限切れrefs/stashなる可能性があることを意味します。(幸いなことに、musiphil が指摘しているように、git 1.6.0 以降のデフォルトでは、これらは期限切れになりません。これを実現するには、有効期限を設定する必要があります。これは、おそらくあなたが望むものではありません。)

4接尾辞表記を使用してこれを行う巧妙な方法は、私の他の回答で詳しく説明されています。^

5iこれらの stash バッグの ndex-commitを見たい場合はどうしますか? あ、いい質問です!:-) stash スクリプトには適切な答えがありません。これらを確認する簡単な方法は、^2サフィックスを使用して、各スタッシュの 2 番目の親 (iコミット) に名前を付けることです。そして、追跡されていないファイルまたはすべてのファイルを含む 3 番目のコミットを含むスタッシュがある場合、それが 3 番目の親です。コミットwは 3 つの親のマージのように見え、3 番目の親に到達しますstash^3。ただしw、これは通常のマージではないため、注意が必要です。おそらく、stash のすべての部分を確認する最も簡単な方法は、git stash branch.

于 2014-02-13T00:56:35.200 に答える