git stash popが、追跡されていないファイルをstashエントリから復元できなかったと言うのはなぜですか?


103

ステージングされた変更とステージングされていない変更がたくさんあり、すぐに別のブランチに切り替えてから元に戻したいと思いました。

そこで、以下を使用して変更をステージングしました。

$ git stash push -a

(後から考えると、--include-untracked代わりに使用できたはずです--all

次に、隠し場所をポップしようとすると、次の行に沿って多くのエラーが発生します。

$ git stash pop
foo.txt already exists, no checkout
bar.txt already exists, no checkout
...
Could not restore untracked files from stash entry

隠し場所から復元された変更はないようです。

私も試しました$ git stash branch tempが、同じエラーが表示されます。

私はこれを回避する方法を見つけました。

$ git stash show -p | git apply

今のところ災害は回避されていますが、これはいくつかの疑問を提起します。

そもそもなぜこのエラーが発生したのですか?次回はどうすれば回避できますか?


31
:私が使用していたgit stash show -p | git apply --3
xmedeko

2
私のために働いた唯一のものは上記のコメントです!
MehrajMalik19年

4
@xmedekoに感謝します、git stash show -p |の違いを誰かに教えてもらえますか gitapplyとgitstash show -p | git apply --3?
DeepakMohandas19年

4
パニックになった場合は、隠しファイルをgit stash showレスキューファイルと一緒にリストしてください$ git show stash@{0}:your/stashed/file.txt > your_rescued_file.txt。これにより、stashからファイルが取得され、別の名前で保存されます。これで、適切なレスキュー方法を安全​​に試すことができます(以下の回答を参照)。物事がうまくいかない場合は、常に最後のリソースとしてレスキューされたファイルがあります。
ダニエル

わあ、@ xmedekoに感謝します!もう一度、あなたのコメントだけがうまくいきました、そしてそれはとても単純でした。+1!
pcdev

回答:


104

少し追加の説明として、git stash2つのコミットまたは3つのコミットのいずれかを行うことに注意してください。デフォルトは2です。--allまたは--include-untrackedオプションのスペルを使用すると、3つになります。

これらの2つまたは3つのコミットは、1つの重要な点で特別です。つまり、ブランチ上にありません。Gitは特別な名前でそれらを見つけますstash1 最も重要なことは、しかし、Gitはあなたを-とすることができます何になり、これらの二、三コミットしてあなたが-行います。これを理解するには、これらのコミットの内容を確認する必要があります。

隠し場所の中身

すべてのコミットは、1つ以上のコミットをリストできます。これらはグラフを形成し、後のコミットは前のコミットを指し示します。スタッシュは通常2つのコミットを保持します。これiは、インデックス/ステージング領域のコンテンツとwワークツリーのコンテンツに対して呼び出すのが好きです。各コミットはスナップショットを保持することにも注意してください。通常のコミットでは、このスナップショットはインデックス/ステージング領域のコンテンツから作成さます。したがって、iコミットは実際には完全に正常なコミットです。それはどのブランチにもありません:

...--o--o--o   <-- branch (HEAD)
           |
           i

通常の隠し場所を作成している場合、git stashコードはw追跡されたすべての作業ツリーファイルを(一時的な補助インデックスに)コピーすることで作成します。Gitは、このwコミットの最初の親がコミットを指すHEADように設定し、2番目の親がコミットを指すように設定しiます。最後に、stashこのwコミットを指すように設定します。

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash

--include-untrackedまたはを追加すると--all、Gitはu作成iw。の間に追加のコミットを作成します。のスナップショットの内容uは、追跡されていないが無視されていない--include-untrackedファイル()、または無視されても追跡されていないファイル(--all)です。これは、余分なuコミットがありません何の親を、その後、ときgit stashになりw、それはセットw第三これに親をuあなたが得るように、コミット:

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash
            /
           u

また、Gitは、この時点で、コミットで終了したすべてのワークツリーファイルを削除しますu(これを使用git cleanして)。

隠し場所の復元

隠し場所を復元するときは、--index使用するか使用しないかを選択できます。これは、現在のインデックスの変更を試みるためにコミットを使用する必要あることをgit stash apply(または内部で使用するコマンドapplyなどpop)に通知します。この変更は次の方法で行われます。i

git diff <hash-of-i> <hash-of-i's-parent> | git apply --index

(多かれ少なかれ、ここでは基本的な考え方の邪魔になる細かい詳細がたくさんあります)。

を省略する--indexgit stash applyiコミットは完全に無視されます。

スタッシュにコミットが2つしかない場合はgit stash applywコミットを適用できるようになりました。これを行うには、git merge2を呼び出し(結果を通常のマージとしてコミットまたは処理することを許可せずに)、スタッシュが作成された元のコミット(iの親、およびw最初の親)をマージベースwとして使用します。--theirscommit、および現在の(HEAD)コミットをマージのターゲットとして使用します。マージが成功した場合、すべてが良好であり(少なくとも、Gitはそう考えていgit stash applyます)、それ自体が成功します。以前git stash popはスタッシュを適用していた場合、コードはスタッシュを削除するようになりました。3 マージが失敗した場合、Gitは適用が失敗したと宣言します。使用した場合git stash pop、コードはスタッシュを保持し、と同じ障害ステータスを提供しgit stash applyます。

しかし、その3番目のコミットがある場合(u適用しているスタッシュにコミットがある場合)、状況は変わります。 コミットが存在しないふりをするオプションはありuません。4 Gitはそのuコミットから現在のワークツリーにすべてのファイルを抽出することを主張します。つまり、ファイルはまったく存在しないか、uコミットと同じ内容である必要があります。

これを実現するには、git clean自分で使用できますが、追跡されていないファイル(無視されているかどうかに関係なく)はGitリポジトリ内に他に存在しないため、これらのファイルをすべて破棄できることを確認してください。または、一時ディレクトリを作成し、そこにファイルを移動して保管することも、別のgit stash save -uまたはgit stash save -aを実行git cleanすることもできます。しかし、それuは後で対処するための別のスタイルの隠し場所をあなたに残すだけです。


1これは実際refs/stashです。これstashrefs/heads/stash、:という名前のブランチを作成する場合に重要です。ブランチのフルネームは、であるため、これらは競合しません。しかし、そうしないでください。Gitは気にしませんが、混乱するでしょう。:-)

2git stashのコードは、実際に使用していますgit merge-recursive直接ここに。これは複数の理由で必要であり、競合を解決してコミットするときにGitがマージとして扱わないようにするという副作用もあります。

3これがgit stash pop、を優先して、を回避することをお勧めする理由ですgit stash apply。適用されたものを確認し、実際に正しく適用されたかどうかを判断する機会があります。そうでない場合でも、あなたはまだあなたの隠し場所持っています、それはあなたがgit stash branchすべてを完全に回復するために使うことができることを意味します。まあ、その厄介なuコミットの欠如を想定しています。

4本当にあるべきです:git stash apply --skip-untrackedまたは何か。また、これらすべてのuコミットファイルを新しいディレクトリドロップすることを意味するバリアントもあるはずgit stash apply --untracked-into <dir>です。


うわー!あなたはメダルに値する!このような詳細なgitの説明におすすめの本はありますか?驚くばかり!🙇♂️
seelts

1
@seelts残念ながら、Gitは継続的に進化しているため、本はすぐに古くなります。ただし、Gitは、一連のツール(コミットいっぱいのファイルを操作したり、コミットグラフを操作したりするコマンドなど)として理解する必要があります。これらのツールを組み合わせて、役立つものを作成します。それに近づく本はそれほど多くないようです。どちらかの方法。
torek

3
私は問題の解決策を理解できません。追加するだけですか--indexgit stash apply --index
ダニエル

1
@torekに感謝します。最初に実行しgit stash save --all、次にすぐに実行しましたがgit stash apply、名前を変更してから(隠しておく前に)再度作成したため、一部のファイルが欠落していました。助けになったのは、今はすべて問題ないように見えるので、git checkout stash@{0} -- .私も気にしないことgit checkout stash^3 -- .です。何が起こっているのかを本当に理解する時間がないのは残念です。ありがとう。
ダニエル

1
@ user1169587:それはそれが言うことを意味します。たぶん例が役立つでしょう。3番目のuコミットにpath/to/file(コンテンツを含む)という名前のファイルが含まれているとします。さらに、ワークツリーにすでに名前が付けられたファイルがあるとpath/to/fileします。uコミットの内容を抽出すると、既存のファイルの内容が新しい内容に置き換えられ、という名前のファイルにあった作業がすべて破棄されpath/to/fileます。したがって、Gitはそれを行いません。最初に(ファイルを完全に削除して)これらのコンテンツを自分で破棄するか、(ファイルを移動して)害のない場所に移動する必要があります。
torek

110

なんとか問題を再現できました。追跡されていないファイルを隠してからそれらのファイルを作成すると(この例ではfoo.txtbar.txt)、適用時に上書きされる追跡されていないファイルへのローカルな変更があるようですgit stash pop

この問題を回避するには、次のコマンドを使用できます。これにより、保存されていないローカルの変更上書きされるため、注意してください。

git checkout stash -- .

これは、前のコマンドで見つけたいくつかの詳細情報です。


無視されたファイルに変更が加えられた可能性はありますが、私の作業ツリーは間違いなくクリーンでした。
steinybot 2018

1
--all/-a 使用しているように見えますが、無視されたファイルが含まれるため、関連する可能性があります。
ダニエルスミス

1
同じロジックが無視されたファイルに適用されると想定し、これを回答としてマークします(ただしgit merge --squash --strategy-option=theirs stash、この場合はアプローチの方が優れていると思います)。
steinybot 2018

同意しました、私はその2番目のアプローチが好きです!あなたの仕事で頑張ってください!
ダニエルスミス

これは役に立ちましたが、追跡されていないファイルは復元されませんでした-同じ問題(already exists, no checkout)がある場合は、以下の私の答えを確認してください。
ErikKoopmans19年

26

Daniel Smithの答えを拡張すると、そのコードは、スタッシュの作成時に(または)を使用した場合でも、追跡されたファイルのみを復元ます。必要な完全なコードは次のとおりです。--include-untracked-u

git checkout stash -- .
git checkout stash^3 -- .
git stash drop

# Optional to unstage the changes (auto-staged by default).
git reset

これにより、追跡されたコンテンツ(in stash)と追跡されていないコンテンツ(in stash^3)が完全に復元され、スタッシュが削除されます。いくつかの注意:

  • 注意してください-これはあなたの隠し場所の内容ですべてを上書きします!
  • でファイルを復元すると、git checkoutすべてのファイルが自動的にステージングされるため、すべてのステージングを解除するために追加git resetしました。
  • 一部のリソースは使用stash@{0}しますstash@{0}^3が、私のテストでは、テストの有無にかかわらず同じように機能します@{0}

出典:


1
なぜあなたは隠し場所を削除するのですか、なぜ安全上の理由からしばらくそこに置いておくのですか?
ダニエル

@Danijel確かにあなたは隠し場所を保つことができます-私が推測するあなたのユースケースに依存します。私の目的のために、それが復元されたら、私は隠し場所で終わりました。
ErikKoopmans19年

5

他の答えとは別に、私はちょっとしたトリックをしました

  • すべての新しいファイル(質問のfoo.txtやbar.txtなどの既存のファイル)を削除しました
  • git stash apply (apply、popなどの任意のコマンドを使用できます)

動作していません..削除されたファイルはstashapplyに再び表示されます..エラーも同様です!
AdityaRewari20年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.