##概要
Git チュートリアル 履歴の書き換えの学習メモ
コミット済みのスナップショットを書き換える必要が発生する理由あるあると、どうやって事故を防ぐかについての説明
##git commit --amend
ステージに上がっている変更内容と直前のコミットの結合を行い、全く別のコミットとして直前のコミットと置き換える。
以下の図では *
のコミットがそれに該当する置き換わったコミット
###ユースケース
現場では不完全なコミットが実行されることが日常的に起こる。あるファイルのステージングを忘れたり、コミットメッセージのフォーマット間違えなど。そのような軽微な修正をするときに便利。
###公開済みコミットの修正はダメ、絶対。
Gitチュートリアル 変更を戻す2.mdのgit reset
でも説明しているが、公開済みのコミットの修正はそれを元に開発を進めている他の人がいる可能性があるので絶対行ってはならない。git commit --amend
は全く新しいコミットと置き換えるということをよく理解しよう。
###使用例
2つのファイルを編集して一つのスナップショットとしてコミットするはずが、片方のファイルをステージングにあげるわすれてしまっていた。
# hello.pyとmain.pyを編集していた
git add hello.py
git commit -m 'add'
# ここでmain.pyを忘れた事に気づいた!! amendを使おう
# コミットメッセージは同じでいいので --no-editフラグを利用する
git add main.py
git commit -ammend --no-edit
これで不完全なコミットが完全なコミットと置き換わる。
##git rebase
ブランチの起点となるコミットを、別のコミットに移動する
見かけや内容は同じでも *
のコミットは移動前のものとは全く異なるので注意。
###コマンド例
現在のブランチの起点を<base>
に変更する。指定できるものはコミットID
,ブランチ名
,タグ
,HEAD
git rebase <base>
###補足
git rebaseの主要な目的はプロジェクト履歴の直線性を維持する
ことにある。
例えば以下の図のようにmasterブランチで作業は発生して分岐した状況を考える。このFeatureブランチをMasterブランチに統合するには git merge
と git rebaseを行ってから git merge
の2つの方法が考えられる。
git rebase を行ってから git merge
なら早送りマージ
が可能になるので履歴の直線性を維持することができる。
###ユースケース
rebaseは 上流側の変更をローカルレポジトリに統合する一般的な方法。
git mergeで上流側の変更をpullする際、プロジェクトの進行状況を確認するたびに余計なマージコミットが発生してしまう。
リベースは 自分の変更作業はみんなが変更を完了したものをベースに行いたい
ときに使う
###公開レポジトリのリベースもやっぱりダメ、絶対
git reset , git commit --amend 同様に。
###使用例
#masterを起点とした新機能のブランチをつくって移動
git checkout -b new-feature master
#ファイル編集してコミット
git add <file>
git commit -m 'add file'
#新機能を開発していく途中でmasterにセキュリティホールが発見された!
#masterを起点にバグフィックス用のブランチを作成
git checkout -b hotfix master
#修正パッチをつくる
git commit -a -m 'fix security hole'
#masterにマージ
git checkout master
git merge hotfix
git branch -d hoffix
#マージしたので履歴に分岐が発生した。
#直線性を保つためにrebaseを行ってnew-featureの起点をmasterの先端に
git checkout new-feature
git rebase master
#これによってmasterとの通常の早送りマージが可能になる
git checkout mastere
git merge new-feature
##git rebase -i
git rebaseを対話的に行うフラグ
###ユースケース
対話式リベースを利用することで乱雑なコミットを整理することができる。
git開発の習わしとしてmasterブランチにマージする前にフィーチャーブランチの見栄をよくする、というものがあるのでその際に利用する。
重要性の低いコミットをまとめ、不要なコミットを削除して整理することでプロジェクトの履歴を清潔に保つことができる。
###使用例
rebaseの使用例と最初は同じ。
# 省略
# ここまではrebaseの使用例と同じ。
# 対話で開始
git checkout new-feature
git rebase -i master
git rabase -i
でエディタが開くのでコミットメッセージ等を修正する
pick hash Start developing a feature
pick hash Fix something from the previous commit
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
コミットメッセージを統合したいので squashを利用してエディタを保存して終了
pick hash Start developing a feature
squash hash Fix something from the previous commit
終了と同時にコミットメッセージを編集するために再度エディタが立ち上がるのでお好みで編集して保存して終了
Mac-mini-PC93% git rebase -i master
[detached HEAD 59babf2] add file
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 doko.txt
create mode 100644 soko.txt
Successfully rebased and updated refs/heads/newone.
git log で結合されているか確認できる
commit 59babf29983376dc1bf1c26a1b1afb9b89bb6ac5
Author:
Date: Thu Apr 2 18:06:44 2015 +0900
pick hash Start developing a feature
Fix something from the previous commit
最後に早送りマージを実行してmasterに統合する
git checkout master
git merge newone
##git reflog
git reflog
はブランチの先端に対する更新の追跡を表示するコマンド。
現在のHEADにおいて、ブランチの切り替え/変更のpull/履歴の書き換え/コミットなどの更新のたびにreflogに項目が追加される。
Mac-mini-PC93% git reflog --relative-date
59babf2 HEAD@{23 minutes ago}: checkout: moving from master to newone
164613e HEAD@{24 minutes ago}: checkout: moving from newone to master
59babf2 HEAD@{24 minutes ago}: rebase -i (finish): returning to refs/heads/n
59babf2 HEAD@{24 minutes ago}: rebase -i (start): checkout master
59babf2 HEAD@{35 minutes ago}: rebase -i (finish): returning to refs/heads/n
59babf2 HEAD@{36 minutes ago}: rebase -i (squash): add file
47205c3 HEAD@{36 minutes ago}: rebase -i (pick): add file
....
###ユースケース
以下のreflogを例にみてみる
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2
古い順に追っていくと
masterから2.2というブランチのチェックアウトを行い
2.2からmasterへ戻るためにチェックアウトを行い
hardフラグを指定してコミット二回分前に戻している。このHEAD@{0}のresetをなかったことにしたい
なのでHEAD@{0}の一つ前を指定して
git reset HEAD@{1}
とするとHEAD@{0}のgit resetを取り消すことができる。