Gitマスターへの道 part2 ローカル編
05/03 2020
この記事はpart1の続きです。
はじめに
part2では実際のコマンドの挙動を解説します。 とりあえず良く使う部分だけにはなりますが、前回よりはわかりやすい内容になっていると思います。
add, remove, commit
オブジェクトデータベースにコミットを追加するには、ワーキングツリーの編集、git add
、git commit
という手順を踏みます。
git add
はワーキングツリーから指定したファイルに対するblobを生成し、ステージングエリアにblobへの参照を追加します。
git commit
はステージングエリアの状態を表現するcommitオブジェクトとtreeオブジェクトを追加し、HEADが指すブランチの参照先を
生成したcommitオブジェクトに付け替えます。
ファイルの削除をコミットするには、git rm --cached
でステージングエリアから対象のファイルの情報を取り除いてgit commit
します。
checkout
git checkout
はステージングエリアに乗っているファイルに対応するblobオブジェクトをワーキングツリーに展開します。
git checkout
は過去のバージョンにファイルを戻すこともでき、コミットを指定した場合はblobオブジェクトをワーキングツリーに展開する前に、
ステージングエリアのファイルの参照先を変更します。
git checkout
はブランチの切り替えの際にも利用されますが、その挙動は上記の過去時点のblobオブジェクトをワーキングツリーに展開する挙動をしています。
reset
git reset
は特定のバージョンに戻すためのコマンドです。3領域のうち、どこを戻すかに応じて3種類のオプションがあります。
soft reset
git reset --soft
はオブジェクトデータベースだけを戻します。カレントブランチの参照先を付け替えるだけなので単純ですね。
mixed reset
git reset --mixed
はオブジェクトデータベースとステージングエリアを戻します。git reset
のデフォルト動作はgit reset --mixed
です。
hard reset
git reset --hard
は全て戻します。ワーキングツリーを戻すと変更が失われてしまうので、hardリセットをするときはとりあえずコミットしてから戻すと良いです。
どうせ戻すんですから。1
merge
git merge
は複数のブランチの変更を統合するコマンドです。マージアルゴリズムは扱いません。
fast-forward
マージ対象のブランチの親にカレントブランチが含まれる場合、fast-forwardマージが可能です。fast-forward可能な場合のデフォルト動作はfast-forwardです。
fast-forwardマージはカレントブランチをマージ対象ブランチが指すコミットに移動して完了です。
non-fast-forward
fast-forwardができない場合や明示的にfast-forwardを行わない場合、non-fast-forwardマージが行われます。 non-fast-forwardマージは2つ以上の親コミットを持つマージコミットを生成します。
fast-forward可能な場合にnon-fast-forwardを使うのは、明示的にマージしたという履歴を残したい場合ぐらいですかね。
rebase
git rebase
は歴史を改変するコマンドです。ブランチの根元の付け替えができるので、しばしばmergeの代わりに使われます。
git rebase
はブランチのパッチを作って適用するコマンドです。
git rebase -i
の対話的rebaseもパッチを作って適用するという挙動を取っています。
revert
revert
は履歴に影響を与えず、特定のコミットを無かったことにするコマンドです。
このコマンドは指定したコミットと直前のコミットの逆差分パッチを生成し、それをHEADに適用します。
merge commit の revert
マージコミットは直前のコミットが複数あるので、明示的にどのパッチを作成するか指定する必要があります。
$ git show M
commit 0ffd...0f1e
Merge: 790122c 640d9d8
Author: xxx
Date: xxx
Merge branch xxx into master
git show
の結果が上記のようであれば、git revert -m 1
は790122c
との差分を取り、git revert -m 2
は640d9d8
との差分を取ります。
コンフリクトの解消
merge、rebase、revertコマンドではコンフリクトが発生することがあります。このコンフリクトの対応はgitと躓きポイントであると思っているので解説します。
例えば、上図のようにファイルの中身をそれぞれのブランチで変更してマージしようとするとコンフリクトが発生します。
$ git merge topic
Auto-merging apple.txt
CONFLICT (content): Merge conflict in apple.txt
Automatic merge failed; fix conflicts and then commit the result.
$ git ls-files --stage
100644 1f70...799b 1 apple.txt
100644 42cf...81be 2 apple.txt
100644 1ac8...da39 3 apple.txt
$ git cat-file -p 1f70
ringo
$ git cat-file -p 42cf
りんご
$ git cat-file -p 1ac8
apple
$ cat apple.txt
<<<<<<< HEAD
りんご
=======
apple
>>>>>>> topic
コンフリクトが発生すると、ステージングエリアに番号付きの参照が追加されます。 それぞれの番号付き参照の参照先blobは、1がコンフリクト前のファイル(base)、2がカレントブランチの変更(ours)、3がカレントブランチでない方の変更(theirs)です。
コンフリクト解消の流れは以下の流れになることが多いです。
- 何らかの方法でワーキングツリーを編集する
- ステージングエリアにコンフリクトしているファイルを追加する
- コミットする
例えばカレントでない側(3)を利用する場合のコマンドは以下のようになります。
$ git checkout -3
$ git add apple.txt
$ git commit
最終的にコンフリクトしているファイルの0をステージングしてコミットすればよいという目的がわかっていれば、 混乱することなくコンフリクトに対応できると思います。
おわりに
実際には各コマンドにいろいろとオプションが用意されていたり、紹介していないコマンドがあったりしますが、
解説した部分でざっくりとどういう動作をしているかを掴んでいただければいいなーと思います。
適当なリポジトリを作って適当に動かしてみるのも良いですね。
次回はリモートリポジトリを利用する解説をする予定です。
-
ちなみに参照されなくなったコミットには
↩git reflog
コマンドでアクセスできます。