🗝️

Gitコマンド入門::restore(marge,その3,reflog)「第十三回」

8 min read

「第十二回」までの振り返り

みなさん、こんにちは! 前回は、ブランチmainから、ブランチsubを作成して、それぞれで、README.md を追加編集して、mergeするところまで学習してきましたが、いかがでしょうか?
とにかく、コミットさえして置けば、その履歴を辿って元に戻すことが容易にできるのが、gitの便利なところですね。また、両者が同一ファイルを編集していても、そのファイルの差分に対して、一応は自動でファイルにコメント挿入してくれるので、それもちょっと助かりそうです。まあ~ここまでにも何回か話題にましたけど、基本は複数に人数で同じファイルを編集しないように、ファイルを分けるのが、基本中の基本ですので、なんでもかんでも、gitに頼りすぎないようにしましょう!(苦笑)

前回の記事はこちらになります。

https://zenn.dev/shiozumi/articles/0a0c0c4fa53c3b

git reset を元に戻すには? git reflog を使う!

そして、第十一回使った、git reset --head コマンドですが、これとワンセットで覚えて欲しいのが、git reflog ですね。まずは、実際に、コマンドを打ってみましょう!

$ git reflog
c72eda2 (HEAD -> main) HEAD@{0}: reset: moving to HEAD@{6}
b8c6432 HEAD@{1}: reset: moving to HEAD^
c06db10 HEAD@{2}: reset: moving to HEAD^
c72eda2 (HEAD -> main) HEAD@{3}: reset: moving to c72eda2
c06db10 HEAD@{4}: reset: moving to HEAD^
c72eda2 (HEAD -> main) HEAD@{5}: checkout: moving from sub to main
73bb8d5 (sub) HEAD@{6}: checkout: moving from main to sub
c72eda2 (HEAD -> main) HEAD@{7}: commit (merge): main sub merge
c06db10 HEAD@{8}: commit: 3rd main
b8c6432 HEAD@{9}: checkout: moving from sub to main
73bb8d5 (sub) HEAD@{10}: commit: 3rd sub
b8c6432 HEAD@{11}: checkout: moving from main to sub
b8c6432 HEAD@{12}: checkout: moving from main to main
// 以下、HEADが変更された履歴分が表されますね。

c72eda2 (HEAD -> main) HEAD@{7}: commit (merge): main sub merge
7行目が、ブランチmainと、ブランチsubのREADME.mdのコンフィクトを修正したコミットですね。ハッシュ値7桁を、再確認して置きましょう!

HEADは、常に最新のコミットした位置を指していますが、その値は、一意のハッシュ値となりますので、過去へも未来にも、どの時点にも移動可能です。また、ハッシュ値と、HEAD@{xx} が紐づけされているので、どちらを指定しても、勿論、選択可能です。[1]

HEADが移動するのは、以下3つのパターンですね。[2]

  1. commit: コミットしたとき。ここでは、README.mdを、commit した時
  2. checkout: ブランチ間で移動したとき。ここでは、main,sub の切り替え!
  3. reset: 任意の地点に移動したとき。過去にも未来にも移動可!

それでは実際に、3rd subに戻って、また今の地点に戻ってくるように、コマンド操作をしてみましょう!

$ git log --oneline
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git log --oneline のオプションを付けると、簡易表示になります!
更に補足ですが、$ git log コマンドでは、HEADが省略されていることも、ちょっとだけ、頭の片隅に入れて置きましょう! ということで、$ git log --oneline HEAD ですよ!

ハッシュ値の長さは40桁ですけど、指定するときの最小桁数は、どこまで短くできるのかな?![考察]

ここまでは、40桁ものハッシュ値を指定きましたが、実際には、先頭7桁指定でよさそうですね。ぶっちゃけていえば、重複していなければ、頭4桁ぐらいでも、選択可能です。ということで、試してみましたよ!(爆笑)

$ git log --oneline HEAD
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git log --oneline c72eda2 // まずは、通常の7桁 good! 
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git log --oneline c72eda  // 6桁 good!「末尾の2を削って!」
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git log --oneline c72ed  // 5桁 good!「末尾のa2を削って!」
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git log --oneline c72e  // 4桁 good!「末尾のda2を削って!」
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git log --oneline c72  // 3桁 bad!「削りすぎでした!」
fatal: ambiguous argument 'c72': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

まあ~、当初から、所々で40桁ではなく、7桁で省略表示されていたので、どうしてかな? って、私も不思議でしたけどね。どうやらgit側で勝手にそう暗示してくれていたんですね。7桁も指定すれば、問題なく指定できますよ!・・・ってことなんです。(笑)

完全に脱線してしまいましたけど、本題のHEADを過去に戻して、また、現在に戻す方法を実行しましょう!

'git reset --hard' は、なんとなく、完全消去のイメージが強いですが、実際はそんなことはなくて、'git reflog' コマンドで、実際に戻りたい、HEADの位置を指定すれば、それで無事に戻れます。

(sub) 3rd subへ、git reset --hard 73bb8d5[3]

$ git log --oneline
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub    // ここに戻るので、7桁のハッシュ値を確認!
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ git reset --hard 73bb8d5  // git reset --hard sub も可
HEAD is now at 73bb8d5 3rd sub

$ git log --oneline
73bb8d5 (HEAD -> main, sub) 3rd sub  // 成功!
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ cat README.md
# test
# (^^)
# sub add
// ブランチsub側で、行を追加した状態に戻っています!

git reflog で、リストの中から、commit (merge): main sub merge の行を探して、ハッシュ値を取得!

$ git reflog | grep 'main sub merge'
c72eda2 HEAD@{8}: commit (merge): main sub merge

ここでは、コミット時の文字列を覚えていたので、grep 'main sub merge' を指定しましたけど、最初は、イメージをつかめていないと思うので、単純に、git reflog とみなさんは、入力してくださいね!

ということで、ハッシュ値、c72eda2 の7桁を指定するか、HEAD@{8} のどちらかを使って、元の状態に、HEADを戻しましょう! git reset --hard c72eda2 or HEAD@{8}

$ git reset --hard c72eda2 // 今回は7桁のハッシュ値を指定
HEAD is now at c72eda2 main sub merge

$ git log --oneline // HEADが元の最新の位置に戻ったか?
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

// 無事に戻っていますので、最後に、README.mdの中身も!

$ cat README.md
# test
# (^^)
# main add
# sub add
// main subの両方をマージした内容に戻っています!

$ git reflog // 最後に、もう一度、reflogを見てみましょう!
c72eda2 (HEAD -> main) HEAD@{0}: reset: moving to c72eda2
73bb8d5 (sub) HEAD@{1}: reset: moving to sub

// 最初の2行のみ、以下、省略。

これで、一通りの操作が可能になりましたね。もう、git reset コマンドの恐怖や不安も解消されてきたと思います。頭が真っ白になることもない事でしょう。そう、、、全ての履歴が残っているのですからね。

では、初期状態に戻る練習をして、終わりにしましょう!

$ git log --oneline
: // コロンマークが出たら、q=quitで終了、改行で一行表示、スペースキーを押すと、
最後まで出力します。ここでは、最終行の末尾を確認したいので、スペースキー[改行]

a467fbd (origin/main) HEAD@{129}: checkout: moving from main to main
a467fbd (origin/main) HEAD@{130}: Branch: renamed refs/heads/master to refs/heads/main
a467fbd (origin/main) HEAD@{132}: commit (initial): first commit

// ハッシュ値の確認ができましたね。first commit 懐かしい!(笑)
// 一応、git log で詳細常時で確認!

$ git log a467fbd // HEAD@{129} HEAD@{130} HEAD@{132} も同じ結果
commit a467fbdd7e989b224f388e048823068a0f122d51 (origin/main)
Date:   Wed Feb 10 15:28:42 2021 +0900

    first commit

$ git reset --hard a467fbd
HEAD is now at a467fbd first commit

$ git log
commit a467fbdd7e989b224f388e048823068a0f122d51 (HEAD -> main, origin/main)
Date:   Wed Feb 10 15:28:42 2021 +0900

    first commit

$ cat README.md
# test
// ファイルの中身が一行のみ、初期状態の内容になっています。
// これで、初期状態のコミットに移動できました!
// では、また、最新のHEADに戻しましょう!

$ git reflog // 今回も最初の3行ぐらいまで表示しておきます。
a467fbd (HEAD -> main, origin/main) HEAD@{0}: reset: moving to a467fbd
c72eda2 HEAD@{1}: reset: moving to c72eda2
73bb8d5 (sub) HEAD@{2}: reset: moving to sub

// 3行目は、前回、HEADをsubに移動したログが記録されてます。
// 2行目は、それを元の最新のHEADに戻したログが記録!
// 1行目は、そして今、一番最初の状態にHEADを移動したログが記録!
// 戻す方法は、以下の2通りどちらでも!

// $ git reset --hard c72eda2 // 7桁のハッシュ値を指定
// $ git reset --hard HEAD@{1} // 前回のコマンドを再利用も可能!

// 今回は、HEAD@{1}で、挑戦してみます!
$ git reset --hard HEAD@{1}
HEAD is now at c72eda2 main sub merge
// はい、無事に、HEAD@{1}==c72eda2 となっています! 

$ git log --oneline
c72eda2 (HEAD -> main) main sub merge
c06db10 3rd main
73bb8d5 (sub) 3rd sub
b8c6432 2nd commit
a467fbd (origin/main) first commit

$ cat README.md
# test
# (^^)
# main add
# sub add
// ハイ!・・・手品ではありませんけど!ワオー!歓声!

これで、もう、そろそろ慣れたと思いますが、いかがでしょうか?
又、HEAD@{1}=='c72eda2' だとイメージすることが、今回のコマンド操作でも理解できたと思います。まあ、HEADを移動するたびに「commit,checkout,resetコマンド操作」HEAD@{xx} の配列の中に、その時のハッシュ値を保存しているんですよね~ 尚、{xx}のインデックスは、随時変化するので、その都度、git reflog で確認してくださいね。

それでは、今日はここまで、長い間、本当にお疲れさまでした!

https://zenn.dev/shiozumi/articles/cc7f18e09d5037
https://twitter.com/esmile2013
脚注
  1. commit,checkout,resetを実行すると、HEAD@{xx}は変わってしまうので、その都度、git reflogで、HEAD@{xx}を取得してください。 ↩︎

  2. その他のコマンドでも、HEADが移動するかもしれませんが、ここまでの取得範囲で3つですね。 ↩︎

  3. ブランチ名のsubを指定しても動作可能。git reset --hard sub ↩︎

Discussion

ログインするとコメントできます