Gitコマンド入門::rebase(-i,drop,その2,#A)「第十八回」

8 min read読了の目安(約7800字

みなさんこんにちは! 前回は、drop を試していかがでしたか? これで、新しいコミットから削除していくことは、おそらく問題なく出来るようになったと思います。また、2つ、3つと、複数のコミットを同時に削除することも大丈夫ですね。ということで、今日は、途中のコミットを削除すると、どうなるのか? 実際に試して行きたいと思います。

あ、、、あと、ここまで説明してきてなんですが、本家本元のサイトも、よろしければどうぞ!
私も、ちょこちょこ読んでいますが、gitのことを正しく理解することの重要性も感じますし、さらに自分で伝えるときにも、それをうまく取り入れたいと思います。

https://git-scm.com/book/ja/v2
https://git-scm.com/docs

尚、開発者としての私の資質は、そんなに素晴らしいとも思っていません。たまたま、ゲーム開発に入社する話が21歳のときにあり、アセンブラを使ってゴリゴリソースを書いてから、ここまでやってきただけの事なんですね。やっぱり今時とはスタイルも違うと思いますので、読者の方には、ちょっと申し訳ないのですが、その分、慌てず焦らずに、じっくりと解説して行きたいと考えております。それでは、前置きが長くなりましたけど、さっそく、進めて行きましょう!

前回の記事はこちらです!

https://zenn.dev/shiozumi/articles/16a37391306777

前回は、新しいコミットから削除

ハッシュ値 コメント README.md 動作
c0f998d 1st # rebase pick
0fa830d A # A pick
f8b5ccd B # B pick
2d138a2 C # C pick
cb468d1 D # D drop 前回は、最新のコミット

今回は、A コミットを削除してみましょう!

ハッシュ値 コメント README.md 動作
c0f998d 1st # rebase pick
0fa830d A # A drop 今回は、ここを削除!
f8b5ccd B # B pick
2d138a2 C # C pick
cb468d1 D # D pick

ハッシュ値は、みなさんの環境とは異なりますので、ご注意ください。

git rebase -i --root を実行!

$ git rebase -i --root

vi エディターの画面が起動します!

.git/rebase-merge/git-rebase-todo

pick c0f998d 1st
drop 0fa830d A // <-- ここを、pick から、drop に変更!
pick f8b5ccd B
pick 2d138a2 C
pick cb468d1 D

# Rebase cb468d1 onto 2d138a2 (5 commands)

// 以下~ 中略

いつものREADME.mdのマージコンフィクト

Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: could not apply f8b5ccd... B
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply f8b5ccd... B

$ cat README.md で内容確認!

$ cat README.md
<<<<<<< HEAD
# rebase
=======
# B
>>>>>>> f8b5ccd... B

A が、dropされて、1stとBのマージでエラー!

ハッシュ値 コメント README.md -
c0f998d 1st # rebase ここ!
0fa830d A # A drop削除
f8b5ccd B # B ここ!
2d138a2 C # C -
cb468d1 D # D -

現状のステータスも確認してみましょう!

git status
interactive rebase in progress; onto 886efcf
Last commands done (3 commands done):
   drop 0fa830d A
   pick f8b5ccd B
  (see more in file .git/rebase-merge/done)
Next commands to do (2 remaining commands):
   pick 2d138a2 C
   pick cb468d1 D
  (use "git rebase --edit-todo" to view and edit)
You are currently rebasing branch 'main' on '886efcf'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
        both modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

Google翻訳: $ gitステータス
進行中のインタラクティブなリベース。 886efcfに、最後に実行されたコマンド(3つのコマンドが実行されました):
ドロップ 0fa830dA
f8b5ccdBを選択します
(詳細については、ファイル.git / rebase-merge / doneを参照してください)

ドロップ 0fa830dA 当たり前ですけど、dropで選択した、Aのハッシュ値ですね。

次に実行するコマンド(残りの2つのコマンド):
2d138a2Cを選択します
cb468d1Dを選択します
(「gitrebase --edit-todo」を使用して表示および編集します)

残りの2つのコマンドと表示されますが、pickなので何も変更しません。[1]

現在、ブランチ「main」を「886efcf」にリベースしています。

新しいコミットのハッシュ値「886efcf」にリベースする! 何をどうしてくれるのかな?

(競合を修正してから、「git rebase --continue」を実行します)
(このパッチをスキップするには、「git rebase --skip」を使用してください)
(「gitrebase --abort」を使用して元のブランチをチェックアウトします)

こちらのコマンドは、3つの選択肢があります。

  1. git rebase --continue // README.md の変更を行ったあとで、入力します!
  2. git rebase --skip // 何もせず、次のrebaseへ進むとき!
  3. git rebase --abort // 強制終了で、元に戻します。

マージされていないパス:
(「git restore --staged <file> ...」を使用してステージングを解除します)
(「git add <file> ...」を使用して解決をマークします)
両方が変更されました:README.md
コミットに変更が追加されていません(「git add」または「git commit-a」、あるいはその両方を使用)

以下の2つの案内がされていますね。

  1. restoreコマンドで、README.mdをステージングから作業ディレクトリーコピーするか?
  2. README.md をマージして、add、commit するか?

それでは、まず最初は、git rebase --abort ですね。

$ git rebase --abort
// とにかく、意図しないことが起こったときは、--abort !

$ git log --oneline --reverse
c0f998d 1st
0fa830d A
f8b5ccd B
2d138a2 C
cb468d1 (HEAD -> main, origin/main) D

$ cat README.md
# D

// はい、元通り!

毎度の話ですけど、gitは、全てのログを残していますので、復活できないことは、おそらく、ほぼ、なにもないんですよね~

では、最初からやり直し、git rebase -i --root を実行、そして、git rebase --skip!

$  git rebase -i --root

// vi エディターが起動するので、先ほど同様、
// Aを、pickから、drop に変更して、Esc + wq

$ git rebase --skip
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: could not apply cb468d1... D
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply cb468d1... D

README.mdの処理を行わないと、次には進めません。許してくれませんでした!(苦笑)

git rebase --skip は、何度入力しても、上記の警告文が繰り返し出ます。次は、実際に、README.mdのファイルを編集してみましょう![2]

README.mdのマージ処理!

git restore --staged README.md

$ cat README.md
<<<<<<< HEAD
# rebase
=======
# D

rebase コマンドを実行する前の環境は、HEAD位置が、Dでしたから、ステージングにある、README.mdファイル中身も、当然、"# D" ですね。また、c0f998d 1st との、コンフィクト部分も、すぐさまに判定してくれていますね。

ハッシュ値 コメント README.md 補足解説
c0f998d 1st # rebase rebase 実行中のHEADの位置
0fa830d A # A drop削除
f8b5ccd B # B -
2d138a2 C # C -
cb468d1 D # D rebase を実行する前のHEADの位置

rebaseコマンドで、HEADは移動しますが、ステージング「Staging Area」には影響ないようです。[3]

README.mdの内容が、# D それを、commit --amend します。コミット先は、現在のHAED c0f998d 1st ですね。

$ git add README.md
$ git commit --amend

// viが起動するので、新しいコメントを、1st + D として、Esc + wq
// 保存して終了させます。

// まだ、rebaseの処理が続くかどうかわかりませんが、
// 基本的には、drop するのは、Aのみなのでなにもない筈!
git rebase --continue

// そして、log を見てびっくり! ガビョーン!
$ git log --oneline --reverse
d2987fb (HEAD -> main) 1st + D

あちゃ! Aどころか、ABCDのコミット全部消えました!

ハッシュ値:過去 コメント README.md 補足解説
c0f998d 1st + D # D ハッシュ値も、d2987fbとなり、こんな結末に!
0fa830d A # A dropで削除を指定
f8b5ccd B # B gitが自動で削除
2d138a2 C # C gitが自動で削除
cb468d1 D # D gitが自動で削除

README.md のファイルの編集のしかたによっては、このような事態を招くんですよね。
実は、今の私の知識でも、この状態を解説できません。
ただ、言えることは、いつもの、git reset --hard で、簡単に元に戻せること、そして、きっと、これで問題はないのでしょう。つまり自動で履歴をdropしても、README.md以外のファイルにも影響がないから、このような処理を、git側で自動で行ったと思います。おそらく、他のファイルがあった場合は、また、動作が異なると予測していますよ!(笑)

又、次回以降で説明しますが、README.md のファイルを、"# B" と編集すると、特にエラーもなくイメージどおり、rebase dropの処理結果となります。[4]

git reset --hard で元の状態に戻して![5]

// ハッシュ値を検索! コメント文字列は、"commit: D"
$ git reflog | grep "commit: D"
cb468d1 HEAD@{81}: commit: D  // 無事、ハッシュ値が分かりましたね!

// ハッシュ値は、cb468d1 を指定して実行!
git reset --hard cb468d1 
HEAD is now at cb468d1 D

git log --oneline --reverse
c0f998d 1st
0fa830d A
f8b5ccd B
2d138a2 C
cb468d1 (HEAD -> main, origin/main) D

// はい、無事に元通り!(^^)v

それでは、今回はここまで、お疲れ様でした!

https://zenn.dev/shiozumi/articles/47c5464f2ec25c
https://twitter.com/esmile2013
脚注
  1. README.md のマージの仕方によっては、さらにこのコミットでも、コンフィクトが発生しますので、ご注意ください。 ↩︎

  2. skipで、マージ&コンフィクト処理が、B,C,Dと実際は進んでくれます。終了すると、1stのみ、README.mdの中身は、マージしないと、"# rebase" となります。 ↩︎

  3. このあたりの動作の理解をイメージできていないと、実践で混乱してくるので、気を付けてください。 ↩︎

  4. 今回は、gitのrebaseを深堀するためにも、エラーになるケースから進めてみました。恐らく、途中のコミットをdropする使い方は、きっと、レアケースだと思うので無駄になるかもしれませんけどね。(苦笑)まあ~、本来なら、squashあたりがお手軽だと思います。勿論、いまのところの私の感覚ですけどね。 ↩︎

  5. 実際に指定するハッシュ値は、それぞれの環境で異なります。ハッシュ値の探し方は、git reflog | grep "commit: D" で、検索可能です! ↩︎