Skip to content

Instantly share code, notes, and snippets.

@satoshin2071
Created April 2, 2015 10:15
Show Gist options
  • Save satoshin2071/dc3cd62eaad4f26397df to your computer and use it in GitHub Desktop.
Save satoshin2071/dc3cd62eaad4f26397df to your computer and use it in GitHub Desktop.
Git チュートリアル 履歴の書き換え

Git チュートリアル 履歴の書き換え

##概要

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 mergegit 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を取り消すことができる。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment