質問

次のコマンドによる変更を元に戻すことはできますか?もしそうなら、どのようにして?

git reset --hard HEAD~1
役に立ちましたか?

解決

パット・ノッツは正しい。数日以内であればコミットを取り戻すことができます。git は、新しい BLOB を削除するように明示的に指示しない限り、約 1 か月程度後にのみガベージ コレクションを実行します。

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

この例では、ハード リセットの結果として file2 が削除されましたが、reflog 経由でリセットすると元の位置に戻されたことがわかります。

他のヒント

ここで行うことは、復元先のコミットの sha1 を指定することです。reflog を調べることで sha1 を取得できます (git reflog)そしてそれから

git reset --hard <sha1 of desired commit>

でも、あまり長く待たないでください...数週間後、git は最終的にそのコミットを未参照として認識し、すべての BLOB を削除します。

答えは上記の詳細な応答に隠されています。次のようにするだけです。

$> git reset --hard HEAD@{1}

(出力を参照してください) git reflog ショー)

Git がまだガベージ コレクションを行っていない場合は、回復することが可能です。

未解決のコミットの概要を取得するには、 fsck:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

リベースを使用して未解決のコミットを回復します。

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

私のように本当に運が良ければ、テキスト エディターに戻って「元に戻す」を押すことができます。

これが実際には適切な答えではないことは承知していますが、これで半日の作業が節約できたので、他の人にも同じようになれば幸いです。

IRL ケースの例:

$ git fsck --lost-found

Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
dangling blob 025cab9725ccc00fbd7202da543f556c146cb119
dangling blob 84e9af799c2f5f08fb50874e5be7fb5cb7aa7c1b
dangling blob 85f4d1a289e094012819d9732f017c7805ee85b4
dangling blob 8f654d1cd425da7389d12c17dd2d88d318496d98
dangling blob 9183b84bbd292dcc238ca546dab896e073432933
dangling blob 1448ee51d0ea16f259371b32a557b60f908d15ee
dangling blob 95372cef6148d980ab1d7539ee6fbb44f5e87e22
dangling blob 9b3bf9fb1ee82c6d6d5ec9149e38fe53d4151fbd
dangling blob 2b21002ca449a9e30dbb87e535fbd4e65bac18f7
dangling blob 2fff2f8e4ea6408ac84a8560477aa00583002e66
dangling blob 333e76340b59a944456b4befd0e007c2e23ab37b
dangling blob b87163c8def315d40721e592f15c2192a33816bb
dangling blob c22aafb90358f6bf22577d1ae077ad89d9eea0a7
dangling blob c6ef78dd64c886e9c9895e2fc4556e69e4fbb133
dangling blob 4a71f9ff8262701171d42559a283c751fea6a201
dangling blob 6b762d368f44ddd441e5b8eae6a7b611335b49a2
dangling blob 724d23914b48443b19eada79c3eb1813c3c67fed
dangling blob 749ffc9a412e7584245af5106e78167b9480a27b
dangling commit f6ce1a403399772d4146d306d5763f3f5715cb5a    <- it's this one

$ git show f6ce1a403399772d4146d306d5763f3f5715cb5a

commit f6ce1a403399772d4146d306d5763f3f5715cb5a
Author: Stian Gudmundsen Høiland <stian@Stians-Mac-mini.local>
Date:   Wed Aug 15 08:41:30 2012 +0200

    *MY COMMIT MESSAGE IS DISPLAYED HERE*

diff --git a/Some.file b/Some.file
new file mode 100644
index 0000000..15baeba
--- /dev/null
+++ b/Some.file
*THE WHOLE COMMIT IS DISPLAYED HERE*

$ git rebase f6ce1a403399772d4146d306d5763f3f5715cb5a

First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.

私の知る限りでは、 --hard コミットされていない変更は破棄されます。これらは git によって追跡されないためです。しかし、元に戻すことはできます discarded commit.

$ git reflog

ウィルは以下をリストします:

b0d059c HEAD@{0}: reset: moving to HEAD~1
4bac331 HEAD@{1}: commit: added level introduction....
....

どこ 4bac331 それは discarded commit.

次に、ヘッドをそのコミットに移動するだけです::

$ git reset --hard 4bac331

ほとんどの場合、そうです。

コマンドを実行したときのリポジトリの状態に応じて、 git reset --hard 簡単なものから取り消し可能なもの、基本的に不可能なものまで多岐にわたります。

以下に、考えられるさまざまなシナリオと、そこから回復する方法をリストしました。

すべての変更はコミットされましたが、コミットは消えてしまいました。

この状況は通常、次のコマンドを実行したときに発生します。 git reset 引数を指定すると、次のようになります git reset --hard HEAD~. 。心配しないでください。これは簡単に回復できます。

ただ走っていただけなら git reset それ以来何もしていない場合は、次のワンライナーで元の状態に戻ることができます。

git reset --hard @{1}

これにより、現在のブランチが最後に変更される前の状態がリセットされます (この場合、ブランチに対する最新の変更は、元に戻そうとしているハード リセットになります)。

ただし、あなたが 持っている リセット後にブランチに他の変更を加えた場合、上記のワンライナーは機能しません。代わりに、実行する必要があります git reflog <branchname> ブランチに加えられたすべての最近の変更 (リセットを含む) のリストを表示します。そのリストは次のようになります。

7c169bd master@{0}: reset: moving to HEAD~
3ae5027 master@{1}: commit: Changed file2
7c169bd master@{2}: commit: Some change
5eb37ca master@{3}: commit (initial): Initial commit

このリストで「元に戻す」操作を見つけます。上の例では、最初の行、つまり「reset:」という行になります。HEADに移動します〜」。次に、コミットの表現をコピーします 前に (下)その操作。私たちの場合、それは次のようになります master@{1} (または 3ae5027, 、両方とも同じコミットを表します)、実行します git reset --hard <commit> 現在のブランチをそのコミットにリセットします。

変更をステージングしました git add, 、しかしコミットしたことはありません。これで、変更内容が消えてしまいました。

これは回復するのが少し難しいです。ギット する 追加したファイルのコピーはありますが、これらのコピーは特定のコミットに関連付けられていないため、変更を一度にすべて復元することはできません。代わりに、git のデータベース内で個々のファイルを見つけて手動で復元する必要があります。これを使用してこれを行うことができます git fsck.

詳細については、「」を参照してください。 ステージング領域内のコミットされていないファイルを使用して git restart --hard を元に戻す.

作業ディレクトリ内のファイルに、ステージングしたことのない変更が加えられました git add, 、決してコミットしませんでした。これで、変更内容が消えてしまいました。

ええとああ。こんなことは言いたくないのですが、あなたはおそらく運が悪いでしょう。git は、ユーザーが追加またはコミットしない変更を保存しません。 のドキュメント git reset:

- 難しい

インデックスと作業ツリーをリセットします。 それ以降の作業ツリー内の追跡ファイルに対する変更 <commit> 捨てられます。

おそらくあなたは かもしれない ある種のディスク回復ユーティリティや専門のデータ回復サービスを使用して変更を回復することはできますが、現時点では、それはおそらく価値以上に面倒です。

リポジトリのガベージ コレクションをまだ行っていない場合 (例:使用して git repack -d または git gc, ただし、ガベージ コレクションが自動的に行われる場合もあるので注意してください)。その場合、コミットはまだそこにありますが、HEAD 経由で到達できなくなるだけです。

の出力を調べてコミットを見つけてみてください。 git fsck --lost-found.

新しいバージョンの Git には「reflog」と呼ばれるものがあり、これは (リポジトリの内容に加えられた変更とは対照的に) ref に加えられたすべての変更のログです。したがって、たとえば、頭を切り替えるたびに(つまり、をするたびに git checkout ブランチを切り替えるため)、ログに記録されます。そしてもちろん、あなたの git reset HEAD も操作したため、ログにも記録されました。リポジトリの古い状態にアクセスするのと同じ方法で、refs の古い状態にアクセスできます。 @ の代わりに署名する ~, 、 のように git reset HEAD@{1}.

HEAD@{1} と HEAD~1 の違いを理解するのに時間がかかったので、ここで少し説明します。

git init
git commit --allow-empty -mOne
git commit --allow-empty -mTwo
git checkout -b anotherbranch
git commit --allow-empty -mThree
git checkout master # This changes the HEAD, but not the repository contents
git show HEAD~1 # => One
git show HEAD@{1} # => Three
git reflog

それで、 HEAD~1 「HEAD が現在指しているコミットの前のコミットに移動する」ことを意味します。 HEAD@{1} これは、「HEAD が現在指している場所を指す前に HEAD が指していたコミットに移動する」ことを意味します。

これにより、失われたコミットを簡単に見つけて回復することができます。

これが古いスレッドであることはわかっています...しかし、多くの人が Git で内容を元に戻す方法を探しているので、ここでヒントを提供し続けるのが良いのではないかと私は考えています。

「git add」を実行するか、git gui で何かを左上から左下に移動すると、ファイルのコンテンツは BLOB に保存され、ファイルのコンテンツはその BLOB から復元できます。

したがって、ファイルがコミットされていない場合でも、ファイルが追加されている必要がある場合でも、ファイルを回復することは可能です。

git init  
echo hello >> test.txt  
git add test.txt  

これで BLOB が作成されましたが、インデックスによって参照されるため、リセットするまで git fsck でリストされることはありません。それでリセットしました...

git reset --hard  
git fsck  

ぶら下がっているブロブ ce013625030ba8dba906f756967f9e9ca394464a が得られます。

git show ce01362  

ファイルの内容「hello」を返します

参照されていないコミットを見つけるために、これを示唆するヒントをどこかで見つけました。

gitk --all $(git log -g --pretty=format:%h)  

私は git gui のツールとしてこれを持っていますが、非常に便利です。

答える前に背景を追加して、これが何であるかを説明しましょう HEAD.

First of all what is HEAD?

HEAD は、現在のブランチ上の現在のコミット (最新) への単なる参照です。
1 つだけ存在できます HEAD いつでも。(除く git worktree)

の内容 HEAD 内部に保管されています .git/HEAD 現在のコミットの 40 バイトの SHA-1 が含まれています。


detached HEAD

最新のコミットに参加していない場合 - つまり、 HEAD 履歴内の以前のコミットを指しており、そのコミットが呼び出されます detached HEAD.

enter image description here

コマンドラインでは、ブランチ名の代わりに SHA-1 のように表示されます。 HEAD 現在のブランチの先端を指していません

enter image description here


切り離された HEAD から回復する方法に関するいくつかのオプション:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

これにより、目的のコミットを指す新しいブランチがチェックアウトされます。
このコマンドは、指定されたコミットにチェックアウトします。
この時点でブランチを作成し、この時点から作業を開始できます。

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

いつでも使用できます reflog 同じように。
git reflog を更新した変更があれば表示されます。 HEAD 目的の reflog エントリをチェックアウトすると、 HEAD このコミットに戻ります。

HEAD が変更されるたびに、 reflog

git reflog
git checkout HEAD@{...}

これにより、目的のコミットに戻ります

enter image description here


git reset HEAD --hard <commit_id>

頭を目的のコミットに「移動」します。

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
  • 注記:(Git 2.7以降)
    を使用することもできます git rebase --no-autostash 同じように。


git revert <sha-1>

指定されたコミットまたはコミット範囲を「元に戻す」。
リセット コマンドは、指定されたコミットで行われた変更をすべて「元に戻します」。
元のコミットも履歴に残りますが、元に戻すパッチを含む新しいコミットがコミットされます。

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

このスキーマは、どのコマンドが何を行うかを示しています。
そこにあるように reset && checkout を修正する HEAD.

enter image description here

間違ったプロジェクトをハードリセットしました。私の命を救ってくれたのは、Eclipse のローカルヒストリーでした。IntelliJ Idea にもその機能があると言われています。編集者も同様なので、チェックしてみる価値があります。

  1. ローカルヒストリーに関する Eclipse ヘルプトピック
  2. http://wiki.eclipse.org/FAQ_Where_is_the_workspace_local_history_stored%3F

探しているコミットを少し見つけやすくするために、小さなスクリプトを作成しました。

git fsck --lost-found | grep commit | cut -d ' ' -f 3 | xargs -i git show \{\} | egrep '^commit |Date:'

はい、awk などを使用するとかなりきれいに作成できますが、シンプルなので必要なだけです。他の人なら 30 秒節約できるかもしれません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top