web-dev-qa-db-ja.com

Gitで落下した隠し場所を回復するにはどうすればいいですか?

作業ツリーの変更を保存および復元するために、git stashおよびgit stash popを頻繁に使用します。昨日、私は自分の作業ツリーにいくつかの変更を加え、それを隠してポップしました。それから作業ツリーにさらに変更を加えました。私は昨日の隠された変更を遡って復習したいのですが、git stash popは関連するコミットへのすべての参照を削除するようです。

私がgit stashを使用する場合、 .git/refs/stashには、 stashを作成するために使用されたコミットの参照が含まれることがわかります。そして .git/logs/refs/stashは stash全体を含みます。しかし、それらの参照はgit stash popの後に消えています。コミットがまだ私のリポジトリのどこかにあることを私は知っていますが、それが何であるかはわかりません。

昨日のstash commit参照を簡単に回復する方法はありますか?

毎日のバックアップがあり、昨日の作業ツリーに戻って変更を反映できるため、これは今日の私にとっては重要ではありません。もっと簡単な方法がなければならないので私は尋ねています!

1489
Greg Hewgill

削除したstashコミットのハッシュがわかったら、それをstashとして適用できます。

git stash apply $stash_hash

または、あなたはそれのために別のブランチを作成することができます。

git branch recovered $stash_hash

その後、あなたはあなたが望むすべての通常のツールで何でもすることができます。完了したら、分岐を吹き飛ばします。

ハッシュを見つける

ポップしたばかりで端末がまだ開いているのであれば、 画面にgit stash popでハッシュ値が表示されたままになります (Dolda、ありがとう).

それ以外の場合は、Linux、Unix、またはGit Bash for Windowsでこれを使用して見つけることができます。

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

...またはWindows用のPowershellを使用する:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}

これにより、コミットグラフの先端にあるブランチやタグから参照されなくなったコミットがすべて表示されます。これまでに作成したすべてのstashコミットを含め、失われたコミットはすべてそのグラフのどこかになります。

必要なstashコミットを見つける最も簡単な方法は、おそらくそのリストをgitkに渡すことです。

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

...または emraginsからの回答 Powershell for Windowsを使用している場合は/を参照してください。

これは、到達可能かどうかにかかわらず、リポジトリブラウザで リポジトリ内でコミットされたすべてのコミット を表示します。

別のGUIアプリケーションよりもコンソール上のNiceグラフを好む場合は、gitkgit log --graph --oneline --decorateのようなものに置き換えることができます。

Stashコミットを見つけるには、次の形式のコミットメッセージを探してください。

WIPオン 多分岐: commithashいくつかの古いコミットメッセージ

git stashを実行したときにメッセージを指定しなかった場合、コミットメッセージはこの形式( "WIP on"で始まる)にのみなります。

2402

端末を閉じなかった場合は、git stash popからの出力を見るだけで、ドロップされた隠し場所のオブジェクトIDがわかります。通常これは次のようになります。

$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

git stash dropも同じ行を生成することに注意してください。)

この問題を解決するには、git branch tmp 2cae03eを実行するだけでブランチとして入手できます。これを隠し場所に変換するには、次のコマンドを実行します。

git stash apply tmp
git stash

それを枝として持つことで、自由に操作することもできます。たとえば、チェリーピックやマージなどです。

664
Dolda2000

承認されたソリューションへのこの追加について言及したいだけでした。私がこの方法を最初に試したときはすぐにはわかりませんでしたが(おそらくそうなっているはずですが)、ハッシュ値から隠し場所を適用するには、単に「git stash apply」を使用します。

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

私がgitに慣れていなかったとき、これは私には明らかではありませんでした、そして私は "git show"、 "git apply"、 "patch"などのさまざまな組み合わせを試していました。

252
Wade

まだリポジトリに残っているがもうアクセスできないstashのリストを取得するには

git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

あなたがあなたの隠し場所にタイトルを付けた場合、コマンドの最後にある-grep=WIPの "WIP"をあなたのメッセージの一部に置き換えてください。 -grep=Tesselation

隠し場所のデフォルトのコミットメッセージはWIP on mybranch: [previous-commit-hash] Message of the previous commit.の形式になっているため、コマンドは "WIP"を使用しています。

79
Senthil A Kumar

失われたstashコミットを見つけるのに役立つコマンドを作成しました。

for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

これは、.git/objectsツリー内のすべてのオブジェクトを一覧表示し、commitタイプのものを見つけて、それぞれの概要を表示します。この時点から、適切な "WIP on work:6a9bb2"( "work"は私のブランチです。619bb2は最近のコミットです)を見つけるためにコミットを調べることだけの問題でした。

「git stash pop」の代わりに「git stash apply」を使用してもこの問題は発生しません。また、「git stash save message 」を使用した場合はコミットが見つけやすくなった可能性があります。

更新:Nathanの考えで、これは短くなります:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
71
Greg Hewgill

それが返すリストはかなり大きいかもしれませんが、git fsck --unreachable | grep commitはsha1を表示するべきです。 git show <sha1>は、それがあなたが望むコミットであるかどうかを示します。

git cherry-pick -m 1 <sha1>はコミットを現在のブランチにマージします。

38
Nathan Jones

紛失した隠し場所を再度隠したい場合は、まず紛失した隠し場所のハッシュを見つける必要があります。

Aristotle Pagaltzisがgit fsckがあなたを助けるべきであると示唆したように。

個人的にはlog-allエイリアスを使用して、すべてのコミット(回復可能なコミット)を表示して状況を把握しています。

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)

「WIP on」メッセージだけを探している場合は、さらに高速な検索を実行できます。

Sha1がわかったら、stash reflogを変更して古いstashを追加します。

git update-ref refs/stash ed6721d

おそらく-mのように関連メッセージを持っているほうがいいでしょう。

git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d

そしてこれをエイリアスとして使用したいと思うでしょう。

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1
27
Colin Hebert

Gitkを使用したWindows PowerShellの同等機能

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

1つのパイプでこれを行うより効率的な方法がおそらくありますが、これは仕事をします。

19
emragins

私はAristotleのアプローチが好きでしたが、GITKを使うのは好きではありませんでした。

代わりに、ダングリングコミットを行い、コードエディタで確認するためにコードをDIFFファイルに出力しました。

git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff

これで、作成したdiff/txtファイル(ホームフォルダにあります)をtxtエディタにロードして、実際のコードと結果のSHAを確認できます。

それからちょうど使用しなさい

git stash apply ad38abbf76e26c803b27a6079348192d32f52219
16
Shaheen Ghiassy

Git v2.6.4がインストールされたOSXでは、誤ってgit stash dropを実行しただけです。

あなたが隠し場所の名前を知っているならば、それから使用してください:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

それ以外の場合は、結果から手動でIDを見つけることができます。

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

それならcommit-idが見つかったらgit stash applyを実行してください{commit-id}

これがすぐに誰かに役立つことを願っています

12
Can Tecim

なぜ人々はこの質問をするのですか?なぜなら彼らはまだreflogについて知らないし理解していないからです。

この質問に対する回答のほとんどは、だれも覚えていないようなオプション付きの長いコマンドを提供します。それで人々はこの質問に入って、彼らが彼らが必要と思うものは何でもコピーペーストをコピーして、そしてほとんど直後にそれを忘れます。

私はこの質問をしている人全員に、reflog(git reflog)だけをチェックするように勧めます。それ以上のことはしません。すべてのコミットの一覧が表示されたら、探しているコミットを見つけてチェリーピックするか、そこからブランチを作成する100の方法があります。その過程で、reflogとさまざまな基本的なgitコマンドの便利なオプションについて学びました。

11
RobbyD

Gitkが利用できないか、出力にXがない場合に、すべての変更を確認するもう1つの良い方法を、承認済みの解決策に追加します。

git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits

for h in `cat tmp_commits`; do git show $h | less; done

それからそれらのハッシュのすべての差分が次々に表示されます。次の差分に移動するには 'q'を押してください。

10
Phil

単純なコマンドウィンドウ(私の場合はWindows 7)でWindows上で作業するための答えが得られませんでした。 awkgrep、およびSelect-stringはコマンドとして認識されませんでした。だから私は別のアプローチを試してみました:

  • 最初の実行:git fsck --unreachable | findstr "commit"
  • 出力をメモ帳にコピーする
  • "unreachable commit"をstart cmd /k git showに置き換えます

このようになります:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • .batファイルとして保存して実行する
  • スクリプトは各コミットを表示するコマンドウィンドウの束を開きます
  • 探しているものが見つかったら、実行してください:git stash apply (your hash)

最善の解決策ではないかもしれませんが、私のために働いた

9
kromakollision

あなたはターミナルでこのコマンドを書くことによってすべての到達不可能なコミットをリストすることができます -

git fsck --unreachable

到達不能なコミットハッシュを確認します -

git show hash

あなたが隠しアイテムを見つけた場合は最後に適用されます -

git stash apply hash
9
Vivek Kumar

アリストテレスの承認された答えは、隠れていないようなコミットを含むすべての到達可能なコミットを表示します。ノイズを除去するには:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3

これはちょうど3つの親コミットを持つコミット(stashが持つもの)を含み、そのメッセージは "WIP on"を含みます。

覚えておいてください、あなたの隠し場所をメッセージ(例えばgit stash save "My newly created stash")で保存した場合、これはデフォルトの "WIP on ..."メッセージを上書きします。

各コミットについてより多くの情報を表示することができます。コミットメッセージを表示するか、またはgit stash showに渡します。

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3 | \
xargs -n1 -I '{}' bash -c "\
  git log -1 --format=medium --color=always '{}'; echo; \
  git stash show --color=always '{}'; echo; echo" | \
less -R
9
Brad Feehan

次の手順でそれを見つけました:

  1. 削除された隠し場所ハッシュコードを識別します。

    gitk --all $(git fsck --no-reflog | awk '/ dangling commit/{print $ 3}')

  2. チェリーピック・ザ・スタッシュ:

    git cherry-pick -m 1 $ stash_hash_code

  3. 使用している場合は、競合を解決します。

    git mergetool

さらに、あなたがgerritを使っているのならコミットメッセージに問題があるかもしれません。次の代替案に従う前に、変更を隠してください。

  1. 前回のコミットにハードリセットを使用してから、この変更を再コミットします。
  2. あなたはまた、変更を隠し、リベースし、再コミットすることができます。
4
Abhijeet

私がここで探していたのは、チェックアウトしたものに関係なく、実際にスタッシュを取り戻す方法です。特に、私は何かを隠し、それから古いバージョンをチェックアウトし、そしてそれをポップしました、しかしその隠し場所はその早い時点で何もしていなかったので隠し場所は消えました。 git stashを実行してスタックに戻すことはできませんでした。これは私のために働いた:

$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179)
$ git checkout 27f6bd8ba3c
$ git reset HEAD^    # Make the working tree differ from the parent.
$ git stash # Put the stash back in the stack.
Saved working directory and index state WIP on (no branch): c2be516 Some message.
HEAD is now at c2be516 Some message.
$ git checkout somethingOld # Now we are back where we were.

振り返ってみると、私はgit stash applyではなくgit stash popを使っていたはずです。私はbisectをやっていて、すべてのbisectステップで適用したい小さなパッチを持っていました。今私はこれをやっている:

$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
4
Ben

私のお気に入りはこのワンライナーです:

git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )

これは基本的に this answer と同じ考え方ですが、はるかに短いです。もちろん、--graphを追加してツリーのような表示にすることもできます。

リストにコミットが見つかったら、次を使用して適用します

git stash apply THE_COMMIT_HASH_FOUND

私にとって、--no-reflogsを使用すると、失われたstashエントリが明らかになりましたが、--unreachable(他の多くの回答に見られるように)は見つかりませんでした。

Windowsの場合は、git bashで実行します。

クレジット:上記のコマンドの詳細は https://Gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf

1
Adrian W