Gitマスターへの道 part2 ローカル編


この記事はpart1の続きです。

cover-illust

はじめに

part2では実際のコマンドの挙動を解説します。 とりあえず良く使う部分だけにはなりますが、前回よりはわかりやすい内容になっていると思います。

add, remove, commit

オブジェクトデータベースにコミットを追加するには、ワーキングツリーの編集、git addgit 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 1790122cとの差分を取り、git revert -m 2640d9d8との差分を取ります。

コンフリクトの解消

merge、rebase、revertコマンドではコンフリクトが発生することがあります。このコンフリクトの対応はgitと躓きポイントであると思っているので解説します。

merge-branch

例えば、上図のようにファイルの中身をそれぞれのブランチで変更してマージしようとするとコンフリクトが発生します。

$ 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)です。

コンフリクト解消の流れは以下の流れになることが多いです。

  1. 何らかの方法でワーキングツリーを編集する
  2. ステージングエリアにコンフリクトしているファイルを追加する
  3. コミットする

例えばカレントでない側(3)を利用する場合のコマンドは以下のようになります。

$ git checkout -3
$ git add apple.txt
$ git commit

最終的にコンフリクトしているファイルの0をステージングしてコミットすればよいという目的がわかっていれば、 混乱することなくコンフリクトに対応できると思います。

おわりに

実際には各コマンドにいろいろとオプションが用意されていたり、紹介していないコマンドがあったりしますが、 解説した部分でざっくりとどういう動作をしているかを掴んでいただければいいなーと思います。
適当なリポジトリを作って適当に動かしてみるのも良いですね。

次回はリモートリポジトリを利用する解説をする予定です。


  1. ちなみに参照されなくなったコミットにはgit reflogコマンドでアクセスできます。

© 2020 Manicreator