ステージング領域のコミットされていないファイルでgit reset --hardを元に戻す


109

仕事を取り戻そうとしています。私は愚かでしたがgit reset --hard、その前に私はやっただけget add .でやっていませんgit commitでした。助けてください!これが私のログです:

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

git reset --hardこの状況で元に戻すことはできますか?


@MarkLongair素晴らしい男!あなたは私の仕事を取り戻しました!すべての出力のファイルを作成するPythonスクリプトを作成しました。スクリプトを回答として追加します
Boy

4
「ばかげて」ではなく、「単純に」...私が同じことをしただけだから!
Rosdi Kasim 2014

まだばかげている可能性があります;-)
Duncan McGregor

これを逆にする方法についてのすばらしい記事はここにあります。手作業が必要です。
Jan Swart 2016年

@MarkLongair `` `find .git / objects / -type f -printf '%TY-%Tm-%Td%TT%p \ n' | ソート `` `がうまくいきました。日付も表示され、最後からblobのチェックを開始します。
ZAky

回答:


175

git add .少し手間はかかるかもしれませんが、インデックスに追加したすべてのファイルを復元できます(たとえば、状況によっては)。インデックスにファイルを追加するために、gitはそれをオブジェクトデータベースに追加します。つまり、ガベージコレクションがまだ行われていない限り、ファイルを回復できます。これを行う方法の例は、JakubNarębskiの回答にあります。

しかし、それをテストリポジトリで試してみたところ、いくつかの問題がありました-ある--cachedはずですが--cache、実際には.git/lost-foundディレクトリが作成されないことがわかりました。ただし、次の手順でうまくいきました。

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

これにより、オブジェクトデータベース内のすべてのオブジェクトが出力されます。これらのオブジェクトは、参照によって、インデックス内で、または参照ログを介して到達できません。出力は次のようになります。

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

...そしてそれらの各ブロブに対して、あなたは次のことができます:

git show 907b308

ファイルの内容を出力します。


出力が多すぎますか?

以下のseheのコメントに応じて更新:

そのコマンドの出力に多くのコミットとツリーがリストされていることがわかった場合は、参照されていないコミットから参照されているオブジェクトを出力から削除することができます。(通常、いずれにせよreflogを介してこれらのコミットに戻ることができます-インデックスに追加されたが、コミットを介して見つけることができないオブジェクトにのみ関心があります。)

最初に、コマンドの出力を次のように保存します。

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

これらの到達不能なコミットのオブジェクト名は、次のようにして見つけることができます。

egrep commit all | cut -d ' ' -f 3

したがって、インデックスに追加されているが、どの時点でもコミットされていないツリーとオブジェクトだけを見つけることができます。

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

これにより、考慮しなければならないオブジェクトの数が大幅に削減されます。


更新: 以下のPhilip Oakleyは、検討するオブジェクトの数を減らす別の方法を提案しています.git/objects。あなたはこれらを見つけることができます:

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(このfind呼び出しはここで見つかりまし。)そのリストの最後は次のようになります。

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

この場合、これらのオブジェクトは次のように表示できます。

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

/オブジェクト名を取得するには、パスの最後にあるを削除する必要があることに注意してください。)


回答を投稿する前に、これらの手順を正確に含めることを検討しました。しかし、私のローカルリポジトリの1つでこれを試すと、何百もの到達不能なメッセージが表示され、送信日でそれらを並べ替えるなど、適切なアプローチを見つけることができませんでした。今それは素晴らしいでしょう
sehe

3
最後のコミットより後の最新のオブジェクトのオブジェクトタイムスタンプを確認することはできませんか?または私は何かが欠けていますか?
Philip Oakley

1
@Philip Oakley:その提案に感謝し、オブジェクトデータベースで最近変更されたオブジェクトを見つけるための提案を追加しました。
マークロングエアー、2011

2
おい、すごい!たった今、私の$$$を節約しました。そして私はいくつかのGit科学を学びました。インデックスに追加する内容をよく見る...
bowsersenior '28 / 07/28

1
ここで誰もライムライトを盗んではいけません。しかし、同じ問題に遭遇し、すべての仕事を失ったと思ったので、ちょうどメモとして。IDEを使用している場合、IDEには通常、ワンクリックの回復ソリューションがあります。PHPstormの場合は、ディレクトリを右クリックして[ローカル履歴を表示]を選択し、最後の有効な状態に戻す必要がありました。
Shalom Sam

58

私はちょうどやったgit reset --hardと1つのコミットを失った。しかし、私はコミットハッシュを知っていたので、それをgit cherry-pick COMMIT_HASH復元することができました。

コミットが失われてから数分以内にこれを行ったので、一部の人にとってはうまくいくかもしれません。


5
ありがとうありがとうありがとうこの答えのおかげで何日も仕事を取り戻すことができました
Dace

21
おそらく、あなたが使用してこれらのハッシュを見ることができます言及する価値があるgit reflog例えば、git reset --hard> - git reflog({1}ハッシュ@ HEADを見て)、最後にgit cherry-pick COMMIT_HASH
ハビエル・ロペス

2
これを読んでいる人は、これが単一のコミットハッシュに対してのみ機能することに注意してください。単一のコミットよりも多い場合、それはすぐには問題を解決しません!
Ain Tohvri 2014年

4
実際に「転送」をリセットできます。したがって、渡された複数のコミットをリセットした場合、別の「git reset --hard <commit>」を実行すると、その時点までのコミットチェーン全体が復元されます。(まだGCされていないことが前提)
akimsko

6
で失われたコミットを回復するためのワンライナーはgit reset --hard(そのコマンドの後に他に何もしなかったと仮定して)git reset --hard @{1}
Ajedi32

13

Mark Longairのおかげで、荷物が戻ってきました。

まず、すべてのハッシュをファイルに保存しました。

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

次に、それらをすべて(「到達できないblob」のものを削除して)リストに入れ、データをすべて新しいファイルに入れます...ファイルを選択して、必要に応じて再度名前を変更する必要があります... files ..これが誰かを助けることを願っています...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1

1
はい!これはまさに私が必要としたものです。すべてのファイルを再作成するためのPythonスクリプトも好きです。他の回答では、ガベージコレクションでデータが失われることに不安を感じたので、ファイルをダンプすることは私にとって有利です:)
Eric Olson

1
私はこの答えに基づいてこのスクリプトを書きました。そのまま使用できます:github.com/pendashteh/git-recover-index
Alexar

1
このように自動化する方が簡単だと思いますmkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh。ファイルは最終的にlost/*.blob
Matija Nalis

6

コメントでの@ Ajedi32の解決策は、まさにこの状況でうまくいきました。

git reset --hard @{1}

これらのすべてのソリューションはgit gcがないことに依存しているため、一部のソリューションではgit gcが発生する可能性があるため、何かを試す前に.gitディレクトリの内容を圧縮して、スナップショットがある場合に戻るスナップショットを用意します。あなたのために働かない。


コミットされていないファイルがたくさんありました。次に、1つのファイルをコミットし、を実行しましたgit reset --hard。これにより、不明な方法でリポジトリが混乱しました。@ダンカン、@ {1}は何をするの?そして、あなたはどのコメントを参照していますか?リセットしていgit resetますか?
alpha_989 2018年

@ {1}は最後の1つだけのコミットに対する相対的な参照だと思いますが、私は専門家ではなく、何がうまくいったかを報告するだけです
Duncan McGregor

3

同じ問題が発生しましたが、インデックスに変更が加えられていません。したがって、上記のすべてのコマンドで、目的の変更を取り戻すことはできませんでした。

上記のすべての手の込んだ答えの後、これは単純なヒントですが、私がしたように、最初にそれについて考えていなかった人を救うかもしれません。

絶望して、開いているタブごとに1回、エディター(LightTable)でCTRL-Zを押してみました。これにより、そのタブのファイルがの前の最新の状態に回復しましたgit reset --hard。HTH。


1
まったく同じ状況で、インデックスの追加に失敗し、ハードリセットを実行しましたが、上記のすべての解決策はありませんでした。これはスマートな動きでした。おかげでCtrl + Zを押して1日を節約できました。私のエディター:SublimeText。私の側から1つ上に!
Vivek 2017

1

これはおそらくgitの専門家には明らかですが、私は必死に検索したときにこれが表示されなかったので、私はそれを上げたかったのです。

私はいくつかのファイルをステージングし、git reset --hardを実行し、少しおかしくなりました。その後、私のステータスにすべてのファイルがまだステージングされており、すべての削除がステージングされていないことがわかりました。

この時点で、削除をステージングしない限り、それらのステージングされた変更をコミットできます。この後、git reset --hardもう一度行う勇気を働かせるだけで済みます。これにより、ステージングして今コミットしたばかりの変更に戻ることができます。

繰り返しますが、これはおそらくほとんど新しいことではありませんが、それが私を助け、これを示唆するものを見つけられなかったので、それが他の誰かを助けるかもしれないことを願っています。


1

この質問とその回答に出くわすまで、私は髪を引っ張りました。質問に対する正しい簡潔な回答は、上記のコメントを2つ一緒にまとめた場合にのみ利用できるため、ここではすべて1か所にまとめています。

  1. chilicuilが述べたように、実行git reflogして、そこに戻したいコミットハッシュを特定します。

  2. akimskoが述べたように、コミットを1つだけ失っていない限り、チェリーピックをしたくないので、実行する必要があります。 git reset --hard <hash-commit-you-want>

egit Eclipseユーザーへの注意:egitを使用してEclipse内でこれらのステップを実行する方法を見つけることができませんでした。Eclipseを閉じ、ターミナルウィンドウから上記のコマンドを実行してから、Eclipseを再度開くと、問題なく動作しました。


0

上記の解決策はうまくいくかもしれませんが、git-sの複雑な取り消しを行う代わりに、これから回復する簡単な方法があります。ほとんどのgit-resetsは少数のファイルで発生すると思います。すでにVIMを使用している場合は、これが最も時間効率の良いソリューションになる可能性があります。警告は、ViM's永続的な取り消しを既に使用している必要があることです。これは、無制限の数の変更を取り消す機能を提供するため、どちらの方法も使用する必要があります。

手順は次のとおりです。

  1. vimで:コマンドを押して入力しset undodirます。でをpersistent undoオンにしている場合は.vimrc、と同様の結果が表示 されますundodir=~/.vim_runtime/temp_dirs/undodir

  2. あなたのリポジトリgit logで、最後にコミットした最後の日付/時間を見つけるために使用します

  3. シェルでundodirusingに 移動しますcd ~/.vim_runtime/temp_dirs/undodir

  4. このディレクトリでこのコマンドを使用して、最後のコミット以降に変更したすべてのファイルを検索します

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    ここで、「2018-03-20 11:24:44」は最後のコミットの日時です。行った日付git reset --hardが「2018-03-22」の場合、「2018-03-22」を使用し、次に「2018-03-23」を使用します。これは、下限が包括的で上限が排他的であるfindの癖のためです。 https://unix.stackexchange.com/a/70404/242983

  5. 次に、各ファイルに移動してそれらをvimで開き、「以前の20m」を実行します。「以前」の詳細については、「h以前」を使用してください。ここで earlier 20mは、20分前にファイルを実行したgit hard --resetと仮定して、20分前のファイルの状態に戻ります。findコマンドから吐き出されたすべてのファイルに対してこれを繰り返します。きっと誰かがこれらを組み合わせるスクリプトを書くことができると思います。


0

私はIntelliJを使用しており、各ファイルを単純に調べて実行することができました。

Edit -> reload from disk

幸い、git status作業中の変更を一掃する直前に行ったので、リロードする必要があるものを正確に把握していました。


0

ファイル階層を覚えて、Phil Oakleyの修正を加えたMark Longairのテクニックを使用すると、劇的な結果が得られます。

基本的に、少なくともファイルをリポジトリに追加したがコミットしなかった場合は、対話的にを使用して復元しgit show、ログを検査し、シェルリダイレクトを使用して各ファイルを作成します(目的のパスを覚えています)。

HTH!


0

最近レビューした場合git diff、変更をまだステージングしていない場合でも、このようなインシデントから回復する別の方法があります。出力git diffがまだコンソールバッファーにある場合は、上にスクロールしてコピーして貼り付けます。ファイルにdiffし、patchツールを使用してツリーにdiffを適用しますpatch -p0 < file。このアプローチは私を数回救いました。

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