web-dev-qa-db-ja.com

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

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

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

220
Casebash

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)
45
Ben Jackson

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

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

簡単です。

236
jmoz

編集:ポップセクションのgit help stashドキュメントから:

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

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

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

git stash showそして、気になったらその出力をどこかに保存します。

その後:git stash drop競合するスタッシュをドロップし、その後:git reset HEAD

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

===

私はあなたの問題を再現しようとしていますが、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が変更をマージしようとしていないのがわかりますが、失敗します。私たちがあなたを助けるために従うことができる再現手順がありますか?

43
DavidG

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

git reset .
git checkout .
git clean -f
6

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^

簡単な例を使って説明しました。スタッシュがポップされる直前、ローカルの変更、スタッシュがまだポップできる状態に戻ります。

4
agentgonzo

これをやや異なる方法で解決しました。ここで何が起こったのかです。

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

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

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

3
pid

いくつかのアイデア:

  • git mergetoolを使用して、マージファイルを元の部分と新しい部分に分割します。願わくば、それらの1つが、非stashの変更を含むファイルです。

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

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

2
asmeurer

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

Ifマージの競合で、適用しようとしたスタッシュが消えなかった場合、git show stash@{0}(オプションで--oursまたは--theirs)を調べて比較することができます。 git statisおよびgit diff HEAD。スタッシュの適用によってどの変更が行われたかを確認できるはずです。

1
Jakub Narębski

マージの競合のためにスタッシュがポップされなかったというDavidGが正しい場合、作業ディレクトリをクリーンアップするだけで済みます。すぐにgit commit気になります。 (完了しない場合は、後でコミットをresetまたはsquashすることができます。)その後、あなたが安全に気を配るすべてのことで、git resetgit stash popがあなたの作業にダンプしますディレクトリ。

1
robrich

git reflogを使用して、git履歴で行われたすべての変更を一覧表示します。アクションIDをコピーしてgit reset ACTION_IDと入力します

1
Fatih

質問のように、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によって導入された追跡されていないファイルを削除します。

man git stashから:The working directory must match the index. @DavidGが指摘するように、現在ステージ化されていない変更されたファイルが競合する場合、stash popは失敗します。そのため、HEADに戻る以外に、マージの競合を解消することを心配する必要はありません。残りの変更されたファイルはスタッシュとは無関係であり、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
here

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

0
Victor Camacho