Gitでのスタッシュポップの中止


257

私は隠し場所を飛び出し、マージの競合がありました。重複としてリストされている質問とは異なり、私はすでに、ディレクトリに保存したいコミットされていない変更があります。マージの競合を解消したいだけでなく、ディレクトリをポップ前の状態に戻したいとも思っています。

私は試しましたgit merge --abortが、gitはマージが進行中ではないと主張しました。ディレクトリに元々あった変更を破棄せずにポップを中止する簡単な方法はありますか?


コミットされていない変更について:これらの変更はすでにインデックスにありましたか?
ヨルゲンセン

使用しているgitのバージョンを投稿できますか。
Tinman


2
受け入れられた答えは複雑に見えます。私はgit stash pop、汚れた作業ディレクトリでの試行のようなことを絶対に行わないことは、かなり良い習慣だと思います。その場合、あなたは単純にgit reset --hard、あなたの隠し場所はまだ無傷です。(これは多かれ少なかれ@BradKochのリンクされたトピックが示唆するものです)
Steven Lu

1
@StevenLu、同意しますが、変更を別のブランチに移動するために変更を隠しておくと、問題がクリーンな作業ディレクトリで発生する可能性があります。stashは、古いブランチには存在しなかった、新しいブランチに存在するコミットと競合します。
ジェイクスティーブンスハース

回答:


55

OK、私は「git stash unapply」を解決したと思います。git apply --reverseによってマージが行われた場合にリバースマージアクションが必要になるため、より複雑ですgit stash apply

リバースマージでは、現在のすべての変更をインデックスにプッシュする必要があります。

  • git add -u

次に、merge-recursiveそれによって反転されたgit stash apply

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

これで、スタッシュ以外の変更だけが残ります。それらはインデックスに登録されます。必要git resetに応じて、変更をステージング解除するために使用できます。

オリジナルがgit stash apply失敗したことを考えると、元に戻したいものがいくつか完了していないため、逆も失敗する可能性があると思います。

(を介してgit status)作業コピーが再びクリーンになる方法を示す例を次に示します。

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)

2
これを行った後、以前に隠しておいた編集は永久に失われますか、それとも元に戻されますか?
ブライアンH.

7
git stash applyスタッシュを落とさず、マージが失敗した場合git stash popもスタッシュを保持します
Xerus

元の
スタッシュ

286

私の使用例:間違ったブランチにポップしようとして、競合が発生しました。必要なのは、ポップを元に戻すだけで、それをスタッシュリストに保持して、正しいブランチでポップできるようにすることです。これは私がしました:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

簡単です。


22
ポップが成功するまで、必要なスタッシュはスタッシュリストに残りますか?
Ryan Clark

52
これは、コミットされなかったローカルの変更を消去するため、元の質問に対する回答ではありません。
ドミトリー

13
@RyanClark 以下のDavidGの回答を参照してください。基本的に、はい、それはスタッシュリストに残ります。
fkorsa 2017

1
このような状況にいるユーザーの大多数は、このソリューションが理想的だと思うでしょう。作業ディレクトリを変更する前に、変更をコミットしていない状況は想像できませんstash pop。それは災害のレシピのように聞こえます。
Shadoninja

2
いいね。これを読んだ後、私はgit stash pop docsを見て、「状態の適用は競合で失敗する可能性があります。この場合、それはstashリストから削除されません。」と述べています。したがって、リセット/チェックアウト後にスタッシュを再度ポップできるのはこのためです。
Brady Holt

48

編集:git help stashpopセクションのドキュメントから:

状態の適用は競合で失敗する可能性があります。この場合、スタッシュリストからは削除されません。競合を手動で解決し、後で手動でgit stash dropを呼び出す必要があります。

--indexオプションを使用すると、作業ツリーの変更だけでなく、インデックスの変更も復元しようとします。ただし、競合がある場合、これは失敗する可能性があります(競合はインデックスに格納されているため、元の変更を適用できなくなります)。

すべてのリポジトリを新しいディレクトリにハードコピーしてみて(そのコピーがあるため)、次のコマンドを実行します。

git stash show 気になったらその出力をどこかに保存してください。

次に:git stash drop競合しているスタッシュをドロップします。git reset HEAD

これで、レポは以前の状態のままになります(うまくいけば、私はまだ問題を再現できませんでした)

===

私はあなたの問題を再現しようとしていますが、usin git stash popが私であるときに私が得るすべて:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

きれいなディレクトリで:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

gitが変更をマージしようとしているのが見えません。失敗します。あなたを助けるために私たちが従うことができる再現手順はありますか?


変更を別のファイルに隠してみてください。
asmeurer

別のファイルに隠そうとしても機能しませんでした(新しい再現手順を参照)。問題を再現できない...
DavidG

1
作業ディレクトリにコミットされていない変更がある場合、このソリューションは機能しません。
ここ

@hereこれは実際の解決策ではありません。これは、OPが抱えている問題を再現できないことと、OPが現在の位置に到達するための手順を提供していないことを示すためです。
DavidG 14

1
実際のシナリオは次のとおりです:1)ファイルAを変更します。これで、競合とローカルの変更があります。通常、それらは別のファイルにありますが、stashから競合していない変更されたファイルと、ローカルのステージングされていない変更がどれであるかは決してわかりません。
ドミトリー

16

私はいつも使っていました

git reset --merge

私はそれが失敗したことを思い出せません。


@GauravPaliwal、次回私はその様子を見ていきgit resetます。機能的に同じかどうか知っていgit reset --mergeますか?
ケンセベスタ

5

行った他の変更について心配する必要がなく、最後のコミットに戻りたいだけの場合は、次のようにすることができます。

git reset .
git checkout .
git clean -f

4

OK、私はなんとかして、必要な場所に戻るワークフローを見つけることができたと思います(ポップをしなかったかのように)。

事前にバックアップを取ってください!! これがうまくいくかどうかわかりませんので、うまくいかない場合に備えてリポジトリ全体をコピーしてください。

1)マージの問題を修正し、パッチからのすべての変更を選択してすべての競合を修正します(tortoisemergeでは、これはone.REMOETE(それら)として表示されます)。

git mergetool

2)これらの変更をコミットします(変更は、mergetoolコマンドを使用して既に追加されています)。「マージ」または覚えている何かのコミットメッセージを送信します。

git commit -m "merge"

3)これで、パッチからの新しいコミットを使用して、最初に開始したローカルのステージングされていない変更が残ります(これは後で削除できます)。ステージングされていない変更をコミットします

git add .
git add -u .
git commit -m "local changes"

4)パッチを反転します。これは、次のコマンドで実行できます。

git stash show -p | git apply -R

5)これらの変更をコミットします。

git commit -a -m "reversed patch"

6)パッチ/パッチ解除コミットを取り除きます

git rebase -i HEAD^^^

これから、「merge」と「reversed patch」が含まれている2行を削除します。

7)インスタンス化されていない変更を取り戻し、「ローカル変更」コミットを元に戻します

git reset HEAD^

私は簡単な例でそれを実行しました、そしてそれはあなたが望む場所にあなたを戻します-stashがポップされる直前に、あなたのローカルな変更とstashがまだポップできる状態にあります。


それでもうまくいかない場合は、うまくいけばほとんどの方法でうまくいくでしょう。:-)
agentgonzo 2013年

git stash show -p | git apply -Rgit stash apply実際のマージが行われた場合は機能しません。私の答えを参照してください...
ベンジャクソン

3

私はこれを多少異なる方法で解決しました。これが起こったことです。

最初に、私は間違ったブランチをポップして、衝突しました。隠し場所はそのまま残りましたが、インデックスは競合の解決中で、多くのコマンドをブロックしていました。

単純なものgit reset HEADは、競合の解決を中止し、コミットされていない(そしてUNWANTED)変更を残しました。

いくつかgit co <filename>は、インデックスを初期状態に戻しました。最後に、私はブランチを切り替えgit co <branch-name>て新しいを実行しましたがgit stash pop、競合することなく解決されました。


2

いくつかのアイデア:

  • git mergetoolマージファイルを元の部分と新しい部分に分割するために使用します。うまくいけば、それらの1つは、スタッシュ以外の変更が含まれているファイルです。

  • これらの変更のみを元に戻すには、スタッシュの差分を逆に適用します。おそらく、マージの競合があるファイルを手動で分割する必要があります(うまくいけば、上記のトリックでうまくいきます)。

私はこれらのどちらもテストしなかったので、それらが機能するかどうかはわかりません。


1

git stash popコミットされていない変更で「ダーティ」ディレクトリをクリーンに再現できましたが、まだマージ競合を生成するポップはしていません。

場合あなたが適用しようとしたスタッシュは消えませんでしたマージ競合上、あなたは検討しようとすることができますgit show stash@{0}(オプション付き--oursまたは--theirs)で比較するgit statisgit diff HEAD。stashを適用したことによる変更を確認できるはずです。


1

マージの競合のためにstashをポップしなかったとDavidGが正しければ、作業ディレクトリをクリーンアップするだけで済みます。git commit気になるすべてをすばやく。(あなたがすることができますresetまたはあなたがsquash終わっていない場合は後でコミットします。)そしてあなたが安全に気をつけているすべてのものとあなたの作業ディレクトリにダンプされたgit reset他のすべてのものでgit stash pop


1

git stash pop質問のように、段階的な変更がの前になかった場合、次の2つのコマンドが機能するはずです。

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

1つ目は、成功したかどうかにかかわらず、スタッシュからのマージを元に戻します。2番目は、stashによって導入された追跡されていないファイルを削除します。

From man git stashThe working directory must match the index. @DavidGが指摘しているstash popように、現在ステージングされていない変更済みファイルが競合すると、は失敗します。したがって、に戻るだけでマージの競合が解消されることを心配する必要はありませんHEAD。変更された残りのファイルはstashとは無関係であり、stash pop

段階的な変更があった場合、同じコマンドに依存できるかどうかはわかりません。@ Ben Jacksonのテクニックを試してみてください。提案は大歓迎です。

これは、さまざまなケースすべてのテスト設定です https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c

これが今のところ最も正しい答えだと思います。とてもよく考え抜かれた。
エージェント、

0

git refloggit履歴で行われたすべての変更を一覧表示するために使用します。アクションIDとタイプをコピーするgit reset ACTION_ID


2
Gitはポップする前にreflogエントリを作成しません(コミットが必要です)
Casebash

-1

私はここに投稿しています。私が隠していたブランチとは別のブランチでスタッシュポップを実行しようとしたときにも、同様の問題が発生しました。私の場合、コミットされていないファイルまたはインデックス内のファイルはありませんでしたが、それでもマージ競合のケース(@pidと同じケース)に入りました。他の人が以前に指摘したように、失敗したgit stash popは実際に私のstashを保持しました。そして、迅速なgit reset HEAD plusを元のブランチに戻し、そこからstashを実行すると問題が解決しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.