サルでもわかるGit入門(入門編&発展編)をざっくりと呼んだメモ
サルでもわかるGit入門の入門編と発展編を読んだ時のメモです。
リポジトリとインデックスとワークツリー
Gitには3つの階層がある。
ワークツリー
作業場。変更中のファイルがある場所。
インデックス
git add されたファイルが置かれる場所。 git commitでコミットされたファイルはリポジトリに移動する。
リポジトリ
コミットされたファイルが保存されている場所。
ブランチ
統合ブランチとトピックブランチ
ブランチは、統合ブランチとトピックブランチに分かれる。
統合ブランチは、いつでもリリースできる状態のブランチで通常、masterブランチを用いる。masterブランチはCIツールによる自動ビルドやテストを定期的に行うことで常に明らかなバグが無いようにしておくことが大切。
トピックブランチは、機能追加やバグの修正時に作る一時的なブランチのこと。作業が終われば統合ブランチにマージして、トピックブランチは削除する。
stash
コミットしていない変更内容や追加したファイルがインデックスやワークツリーに残ったままで、他のブランチへのチェックアウトを行うと、その変更内容はもとのブランチから移動先にブランチに対して移動する。
ただし、移動先のブランチで同じファイルが既に何らかの変更が行われている場合はチェックアウトに失敗する。
このような場合には変更内容を一度コミットするか、stashを使って一時的に変更内容を退避させてからチェックアウトする必要がある。
具体的なコマンドは以下の通り。
# ワークツリーの変更を保存する。stashに保存された変更分は取り消される。 $git stash save # 以下のコマンドで保存しているstashの一覧を確認できる。 $ git stash list stash@{0}: WIP on localize: 67f7992 localized # 以下のコマンドでそのstash内の変更内容を確認できる。 $ git stash show stash@{0} sample1.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) # 以下のコマンドでstashを適用できる $ git stash apply stash@{0} Auto-merging sample1.txt CONFLICT (content): Merge conflict in sample1.txt # stashを削除するにはapplyをdropに変えればよい。 # applyとdropを同時にしたい場合は、popとする。
ブランチの統合
ブランチの統合にはmergeを使う方法と、rebaseを使う方法がある。
merge
mergeを使うと2つのブランチを1つのブランチに統合することができる。通常、マージ先のブランチは統合(master)ブランチでマージ元がトピックブランチとなる。
具体的なコマンドは以下の通り。
$ git merge <ブランチ名>
もし、マージ先にも変更がある場合は、コミットメッセージを入力する画面がvimで表示される。コミットしない場合は :q でvimを終了する。
マージが完了したら、以下のコマンドで用済みのトピックブランチを削除する。
$ git branch -d <ブランチ名>
rebase
rebaseを使うと、統合元ブランチの履歴が統合先ブランチ(通常master)ブランチの後ろに付け替えられれる。統合元のブランチは削除され、統合先ブランチに一本化される。
うーん。難しいので具体的な手順は省略ー。
リモートリポジトリ
pull
リモートリポジトリの履歴を取得することが出来る。リモートリポジトリが変更されていて、ローカルリポジトリが変更されていない場合は、fast-forwardマージが行われる。
ローカルリポジトリも変更されている場合は、競合の有無を確認し、競合が無い場合は自動的にマージコミットされる。競合がある場合は、やっぱり手動解決&コミットが必要。
fetch
pullを実行すると、リモートリポジトリの内容が自動的にマージされてしまう。しかし、単にリモートリポジトリの内容を確認したいだけのときはマージしたくない場合もある。そんなときに、fetchを使う。
もしmergeしたくなれば、
$ git merge origin/master
でマージする。 なので、pull = fetch + mergeであるとも言える。
push
リモートリポジトリにローカルリポジトリの変更を反映したい場合は、pushを実行する。ただし、競合が発生する場合は拒否されるので、事前にリモートリポジトリをpullして競合を解決しておく必要がある。
タグ
# コミットに注釈なしタグを付ける。今回はappleというタグを付ける。 $ git tag apple # 付けたタグを確認する $ git tag apple # 今度は注釈ありのタグを付ける。タグ名は banana。 $ git tag -am "サルでも分かるGit" banana # タグの一覧を注釈付きで確認する $ git tag -n apple first commit banana サルでも分かるGit # タグを削除する $ git tag -d <タグ名>
コミットの書き換え
commit --amend を使って直前のコミットを編集する
まずは、ログを確認する。
$ git log commit 326fc9f70d022afdd31b0072dbbae003783d77ed Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:17:56 2012 +0900 addの説明を追加
次に、ファイルを編集する。ここでは、sample.txtにcommitに関する説明を追記する。編集が終わったら commit --amend コマンドで直前のコミットを修正する
$ git commit --amend [master 0f911e9] addとcommitの説明を追加 Date: Mon Jul 16 23:17:56 2012 +0900 1 file changed, 3 insertions(+), 1 deletion(-)
再度ログを確認すると・・・。
$ git log commit 0f911e9ebc8b373eeb62b56b999c02280440f737 Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:17:56 2012 +0900 addとcommitの説明を追加
無事、直前のコミットが編集されているのでOK。
revert を使って直前のコミットを打ち消す
まずはログを確認。
$ git log commit 0d4a808c26908cd5fe4b6294a00150342d1a58be Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:19:26 2012 +0900 pullの説明を追加 commit 9a54fd4dd22dbe22dd966581bc78e83f16cee1d7 Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:19:01 2012 +0900 commitの説明を追加
git revert コマンドで直前のコミットを取り消す。
$ git revert HEAD [master d47bb1d] Revert "pullの説明を追加" 1 files changed, 1 insertions(+), 2 deletions(-)
ログを確認してみる。
$ git log commit 7bcf5e3b6fc47e875ec226ce2b13a53df73cf626 Author: yourname <yourname@yourmail.com> Date: Wed Jul 18 15:46:28 2012 +0900 Revert "pullの説明を追加" This reverts commit 0d4a808c26908cd5fe4b6294a00150342d1a58be. commit 0d4a808c26908cd5fe4b6294a00150342d1a58be Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:19:26 2012 +0900 pullの説明を追加
直前のコミットを打ち消すコミットが追加されているのが分かる。つまり、直前のコミットとその一つ前のコミットの差分を自動的に求め、その差分を打ち消すようなコミットがされているということ。
reset を使ってコミットを削除する
revertと違ってこっちは物理的にコミットを削除するコマンド?(正直あんまよくわかってません。。)
$ git reset --hard HEAD~ HEAD is now at 9a54fd4 commitの説明を追加
これで直前の履歴が削除される。
ただし、GitHubのパブリックリポジトリなど既に公開されているものに対してこれをやるのは絶対に駄目!
chrry-pick で別ブランチのコミットを取り込む
masterブランチとトピックブランチがある時、トピックブランチの1つ前のコミットをmasterブランチに取り込みたい場合などに使う。
$ git branch * issue1 master # issue1の履歴を確認 $ git log commit 08084a5c58e9ca3672292c3883c44e623f817b72 Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:22:17 2012 +0900 pullの説明を追加 commit 99daed25b45fcae2ce9d707a3434951cf69f253a Author: yourname <yourname@yourmail.com> Date: Mon Jul 16 23:21:57 2012 +0900 commitの説明を追加
この issue1 の 99daed25.. のコミットをmasterブランチに取り込む。
# まずmasterブランチをチェックアウト $ git checkout master Switched to branch 'master' # あとはトピックブランチのコミットをmasterブランチの先頭に持ってくる $ git cherry-pick 99daed2 error: could not apply 99daed2... commit hint: after resolving the conflicts, mark the corrected paths hint: with 'git add <paths>' or 'git rm <paths>' hint: and commit the result with 'git commit'
競合が発生しているので、ファイルを開いて競合箇所を修正してからコミットする。
rebase -i でコミットをまとめる
後からコミットをまとめることも出来る。例えば以下のような履歴があった時
$ git log --oneline 0d4a808 pullの説明を追加 9a54fd4 commitの説明を追加 326fc9f addの説明を追加 48eec1d first commit
直前2つのコミットをrebase -iを使ってまとめる。
$ git rebase -i HEAD~~
rebase -iコマンドを実行すると、以下のようなvimが開く。
pick 9a54fd4 commitの説明を追加 pick 0d4a808 pullの説明を追加 # Rebase 326fc9f..0d4a808 onto 326fc9f (2 command(s)) # # Commands: # 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 # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
2行目の pick を squash に変更して保存・終了する(squash は押しつぶすという意味らしい)。すると今度はまとめたコミットのメッセージを入力するためのエディタが開くので適切なメッセージを入力して保存・終了する。今回は、「pullとcommitを追加」とする。
再び履歴を確認してみると・・。
$ git log --oneline 866d3e3 pullとcommitの説明を追加 326fc9f addの説明を追加 48eec1d first commit
直前2つのコミットが1つのコミットになっているのでOK。
rebase -i でコミットを修正する
rebase -iを使ってコミットを修正することも出来る・・。
こんな履歴があった時
$ git log --oneline 0d4a808 pullの説明を追加 9a54fd4 commitの説明を追加 326fc9f addの説明を追加 48eec1d first commit
2つ前の 9a54fd4 コミットを修正するとする。
$ git rebase -i HEAD~~
するとvimが開く。
pick 9a54fd4 commitの説明を追加 pick 0d4a808 pullの説明を追加 # Rebase 326fc9f..0d4a808 onto d286baa # # Commands: # 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 # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
編集対象(ここでは0d4a808)のpickを edit に変更して保存・終了する。
すると次のような出力が表示され、修正するコミットがチェックアウトされた状態になる。
Stopped at d286baa... commitの説明を追加 You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue
修正対象のファイルを開いて適当に変更する。
終わったら、commit --amendで保存する。
$ git add sample.txt $ git commit --amend
最後に、次のコマンドを実行して作業が終了した事を知らせる必要がある。
$ git rebase --continue
merge --squash でトピックブランチのコミットをまとめてマージする
$ git branch * issue1 master $ git log --oneline 08084a5 pullの説明を追加 99daed2 commitの説明を追加 48eec1d first commit $ git checkout master Switched to branch 'master' $ git merge --squash issue1 Auto-merging sample.txt CONFLICT (content): Merge conflict in sample.txt Squash commit -- not updating HEAD Automatic merge failed; fix conflicts and then commit the result.
競合が発生したので、sample.txtを開いて競合箇所を修正してからコミットする。
$ git add -A $ git commit [master a6a3be4] merge issue1 1 file changed, 3 insertions(+), 1 deletion(-)