SlideShare a Scribd company logo
【幕張読書会】
コンセプトから理解する
Gitコマンド
基礎編
@ktateish
はじめに
Gitのコマンドってわかりにくいですよね?
Git内部のコンセプトを理解するとGitコマンドにつ
いての理解がはかどります。
この資料はGitのコンセプトからコマンドを理解する
ことを目的として作成しました。
注意
Gitを使ったことのあるエンジニア向けです。
● add/commit/pushしたことくらいならあるよ、的
な人を想定。他VCSの経験があればGitを使っ
たことがなくてもOK
● Unix関連のベーシックなスキル・コンセプトは解
説しません
● 正確さよりもわかりやすさ優先
(正確な情報はマニュアル読んでね)
目次
第1章 コミットとオブジェクト(p.5)
第2章 参照: ブランチとタグ(p.74)
第3章 インデックスとワーキングツリー(p.134)
第4章 コンセプトから理解するGitコマンド
(p.161 - p405)
第1章
コミットとオブジェクト
そもそもバージョン管理とは?
memo-20140425.txt
memo-20140428.txt
memo-20140430.txt
・・・
こういうやつですね。
これも一応バージョン管理
何でバージョン管理したいのか?
いろいろありますが根源的なのはだいたい
1. 加えた変更がまずかったら元にもどしたい
2. 何処をどう変更したのか知りたい
3. 誰がなぜ変更したのか知りたい
あたりだと思います
Gitにおけるバージョン
Gitにおいてバージョンは「コミット」という単位で管
理します
他のVCSだとリビジョンやチェンジセットなどと言わ
れるやつのことです。
コミットを作成する行為も「コミット(する)」と言いま
す。これは他VCSと同様。
Gitにおけるコミット
ひとつのコミットは以下の情報からなります
ディレクトリツリーのスナップショット
+
ひとつ前のコミット(親コミット)
+
その他(コミッタ名、コミットメッセージとか)
ちなみに
1コミットごとに完全なスナップショットを
保持しています。変化を記録しているわけではな
いので「チェンジセット」とは呼びません。
親コミットを記録しているので、差分は必要になっ
たときに作ればいいや、という思想です。
コミットが
バージョン管理の要求を満たす
1. 加えた変更がまずかったら元にもどしたい
➔ いつでも昔のコミットを取り出して戻せる
2. 何処をどう変更したのか知りたい
➔ コミット同士を比較することで可能
3. 誰がなぜ変更したのか知りたい
➔ コミットメッセージを見れば良い
コミット
コミットの詳細
コミット =
ツリーのスナップショットへのポインタ
+ 親コミットへのポインタ
+ 著作者・著作日時
+ コミッター・コミット日時
+ コミットメッセージ
コミットを図にすると
コミット
親コミット = ひとつ前のコミット
ツリーのスナップショット
著作者等の情報
コミッター等の情報
コミットメッセージ
ツリーへのポインタ
親コミットへの
ポインタ
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
cat-file: Gitに登録されたオブジェクト(データ)をダンプするコマンド
-p: いい感じの出力にするオプション
HEAD: 最新のコミット
git cat-file -p HEAD
→「最新のコミットオブジェクトをいい感じに表示してくれ」
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
tree: ディレクトリツリーのスナップショット
(2e4038…: treeオブジェクトの名前)
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
parent: 親コミット
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
author: コミット内容の作者+作成日時
committer: authorが作った内容を実際のコミットオブジェクトにした人+日時
大抵の場合 author=committer なので今のところ気にしなくて良い
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
“Add new…”: コミットメッセージ
実際に見てみよう
% git cat-file -p HEAD
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
コミット =
ツリーのスナップショットへのポインタ
+ 親コミットへのポインタ
+ 著作者情報+コミッター情報
+ コミットメッセージ
つまりコミットとは
コミット
親コミット = ひとつ前のコミット
(897d06...)
ツリーのスナップショット
(2e4038…)
コミッター等の情報
コミットメッセージ
ツリーへのポインタ
親コミットへの
ポインタ
少し周辺の情報を補うと
コミット
(03ccb3...)
親コミット = ひとつ前のコミット
(897d06...)
ツリーのスナップショット
(2e4038…)
ツリーのスナップショット
(57e952…)
少し周辺の情報を補うと
コミット
(03ccb3...)
親コミット = ひとつ前のコミット
(897d06...)
ツリーのスナップショット
(2e4038…)
ツリーのスナップショット
(57e952…)
親の名前(897d06…)と同様、全てのコミットには必
ず名前(03ccb3...のような文字列)がある
親コミットもツリーへのポインタと・・・
そのさらに親コミットへのポインタを持つ
コミットが積み重なると
コミット
ツリー
コミット
ツリー
ツリー
ツリー
コミット
ツリー
コミット
→ コミットグラフになる
(コミットが決まればツリーも一意
に決まるので、図示する際は普通
ツリーを省略します。)
コミット
コミットは親を複数持つことも可能
コミット
ツリー
コミット
ツリー
ツリー
ツリー
コミット
ツリー
コミット
いわゆるマージコミットです。マー
ジコミットを作成する行為を「マー
ジする」と言います。
本資料では深く解説しません
コミット
コミット
ツリー
コミット
ツリー
ツリー
ツリー
コミット
コミット
ところで名前 (03ccb3…)って結局何?
コミット
(03ccb3...)
名前=オブジェクトの内容のSHA1値です
先頭数桁だけでもユニークな名
前になるので、この省略名
(abbrev)をしばしば使う。
つまりコミット 03ccb3…の場合は
コミット
(03ccb3...)
内容のSHA1を計算
実際にはオブジェクトの型(commit, tree, blob,etc…)、サイズ情報等を含む
% git cat-file -p 03ccb3
tree 2e4038cce3649c830daaf38f6e411b4d55d5b7ac
parent 897d06bcb89a12574271e74e90048cb26fe5f6bb
author John Smith <js@example.com> 1398774567 +0900
committer John Smith <js@example.com> 1398774567 +0900
Add new file: greeting2.txt
03ccb30ce6b8b7d157b6d28fb479257eb424af02
名前 (オブジェクト名)
そういえばツリーにも名前があった
コミット
親コミット
ツリーのスナップショット
(2e4038…)
コレがツリーのオブジェクト名
ツリー
図にすると
tree
2e4038...
(root)
tree
f18bca…
img
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
こんな感じです。先ほどの図で
コミットから指されてたやつを詳細化してます
コミット
親コミット
ツリーのスナップショットtree: 2e4038
ツリーの中を実際に見てみよう
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
ツリーオブジェクトの中身=3種類の情報をエントリの数だけ記録
(1行1エントリで表示されています)
3種類の情報=
1. モード(タイプ)
2. オブジェクト名
3. パス名
ツリーエントリ:モード
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
モード(タイプ):
tree=ツリーオブジェクト、blob=tree以外≒普通のファイル
symlinkやスクリプト等の実行ビットも記録可能
ツリーエントリ:オブジェクト名
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
オブジェクト名:
greeting.txt や greeting2.txt のオブジェクト名。treeやcommitと同様、それぞれ
の内容のSHA1値(ce013…や64e50…)になっている。
ツリーエントリ:パス名
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
パス名:
人間が読みやすい名前をオブジェクトにつけている。
いわゆるファイル名やディレクトリ名のこと。
ツリーは再帰構造
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
treeエントリには他のtreeを含められます = 再帰構造をとれます。
ここには f18bca... というオブジェクト名のtreeが含まれています。
% git cat-file -p f18bca
100644 blob 6935eb316843d05f389830ecb4022fbc9debbea5 screenshot.jpg
もちろん f18bca… の中身も確認できます。screenshot.jpg というエントリ名
で 6935e… というblobが含まれているのがわかります。
続いてツリーに記録されたブロブ
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
について見ていきます。greeting.txtの実体は
ce0136という名前のブロブオブジェクト(blob)で
す。
ブロブ
ブロブの中を実際に見てみよう
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
ブロブ(blob)は型のないオブジェクトのこと。大抵の場合は普通のファイルです。
greeting.txtはce0136...というオブジェクトです。
% git cat-file -p ce0136
hello
中身を見てみると “hello" というテキストです。
ブロブの中を実際に見てみよう
実際に “hello” (+改行) という文字列をhash-objectコマンドでGit オブジェクト化するこ
とができます。
オブジェクト化すると、やはり ce0136… という名前のオブジェクトになりました。
% git cat-file -p ce0136
hello
% echo hello | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a
ブロブを作ってみる
hash-object -w でGitのオブジェクトデータベースに書き込めます。
“tekitou na mojiretsu”(+改行) は 2da935 というオブジェクトになりました。
% echo "tekitou na mojiretsu " | git hash-object --stdin -w
2da9358db1fe68b3c8cf0196bfaf82ee0d6f9103
% ls .git/objects/ 2d/a9358db1fe68b3c8cf0196bfaf82ee0d6f9103
.git/objects/2d/a9358db1fe68b3c8cf0196bfaf82ee0d6f9103
% git cat-file -p 2da935
tekitou na mojiretsu
作成したオブジェクトをcat-file -pで2da935を確認すると、書き込んだ内容と同じ”
tekitou na mojiretsu”(+改行)を取り出せました。
ところでファイル名がでてきませんね
% echo "tekitou na mojiretsu " | git hash-object --stdin -w
2da9358db1fe68b3c8cf0196bfaf82ee0d6f9103
% ls .git/objects/ 2d/a9358db1fe68b3c8cf0196bfaf82ee0d6f9103
.git/objects/2d/a9358db1fe68b3c8cf0196bfaf82ee0d6f9103
% git cat-file -p 2da935
tekitou na mojiretsu
ブロブオブジェクトの作成・取り出しにファイル名(パス名)が一切登場しなかったことを
少し覚えておいてください。
Gitデータベースにおける全てのオブジェクトの名前は常にSHA1値です。
ではファイル名はどこにあるのか
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
greeting.txtの実体はce0136というオブジェクトで
すが、ce0136自体には “greeting.txt” という名前
は記録してませんでした。
”greeting.txt”という名前を記録しているのは、 tree
オブジェクト(2e4038)です。
再び
ツリー
ツリーとは
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
● オブジェクトの属性、名前、パス名(ファイル名)
を記録するためのオブジェクト
● 再帰的にツリー構造を形成
● パス名はツリーが記録しており、記録されるオブ
ジェクト側では持っていない
これ、何かに似てるデータ構造ですよね。
いったい何に似ているって言うんだー
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
% git cat-file -p f18bca
100644 blob 6935eb316843d05f389830ecb4022fbc9debbea5 screenshot.jpg
% ls -liR
808494 -rw-r--r-- 1 js js 6 May 1 08:25 greeting.txt
808590 -rw-r--r-- 1 js js 13 May 1 08:25 greeting2.txt
917641 drwxr-xr-x 2 js js 4096 May 1 08:25 img/
./img:
917647 -rw-r--r-- 1 js js 109350 May 1 08:25 screenshot.jpg
明らかにUnixのディレクトリですね
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
% git cat-file -p f18bca
100644 blob 6935eb316843d05f389830ecb4022fbc9debbea5 screenshot.jpg
% ls -liR
808494 -rw-r--r-- 1 js js 6 May 1 08:25 greeting.txt
808590 -rw-r--r-- 1 js js 13 May 1 08:25 greeting2.txt
917641 drwxr-xr-x 2 js js 4096 May 1 08:25 img/
./img:
917647 -rw-r--r-- 1 js js 109350 May 1 08:25 screenshot.jpg
モード/stat オブジェクト名/inode パス名/リンク
再び図にしてみます
tree
2e4038...
(root)
tree
f18bca…
img
% git cat-file -p 2e4038
100644 blob ce013625030ba8dba906f756967f9e9ca394464a greeting.txt
100644 blob 64e50281e4e0ebbbdc438095b6a222931ec0240f greeting2.txt
040000 tree f18bca942070f88dfc217a58d8766376fb642abc img
% git cat-file -p f18bca
100644 blob 6935eb316843d05f389830ecb4022fbc9debbea5 screenshot.jpg
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
どう見ても
ディレクトリです。
本当に(ry
オブジェクトの格納状況はこんな感じ
ce0136
2e4038
f18bca64e502
6935eb
オブジェクト名が
Gitデータベース内に格納されたコンテンツを指定
% find .git/objects -type f
.git/objects/b0/de5d4beb96ad900811b3d9e115487fac54f99a
.git/objects/50/d659153f572c2ee2655c54d8084d987613d796
.git/objects/8c/6d0205c764e18d26f411770c081628e076e51f
.git/objects/f1/8bca942070f88dfc217a58d8766376fb642abc
.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
.git/objects/89/7d06bcb89a12574271e74e90048cb26fe5f6bb
.git/objects/cf/2d7699c6f20728bf126c8af08e7874a84b8696
.git/objects/8e/19af5536b93bcdcdf9d7c5b2df89d15c5876e8
.git/objects/b4/9b64efff762751cbc746633bc39be3e32090ad
.git/objects/69/35eb316843d05f389830ecb4022fbc9debbea5
.git/objects/49/6cd51216313fc969a1f61d5ebf96a843eb57e0
.git/objects/57/e9529754dc514a3ec10db2ff882018fbe1fcbf
.git/objects/2e/4038cce3649c830daaf38f6e411b4d55d5b7ac
.git/objects/03/ccb30ce6b8b7d157b6d28fb479257eb424af02
.git/objects/64/e50281e4e0ebbbdc438095b6a222931ec0240f
.git/objects/a8/0202bb5efd9949dc61fc7a8da48d0c8ac4faf0
各treeからの矢印にすると
ce0136
2e4038
f18bca64e502
6935eb
こんな感じ
% find .git/objects -type f
.git/objects/b0/de5d4beb96ad900811b3d9e115487fac54f99a
.git/objects/50/d659153f572c2ee2655c54d8084d987613d796
.git/objects/8c/6d0205c764e18d26f411770c081628e076e51f
.git/objects/f1/8bca942070f88dfc217a58d8766376fb642abc
.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
.git/objects/89/7d06bcb89a12574271e74e90048cb26fe5f6bb
.git/objects/cf/2d7699c6f20728bf126c8af08e7874a84b8696
.git/objects/8e/19af5536b93bcdcdf9d7c5b2df89d15c5876e8
.git/objects/b4/9b64efff762751cbc746633bc39be3e32090ad
.git/objects/69/35eb316843d05f389830ecb4022fbc9debbea5
.git/objects/49/6cd51216313fc969a1f61d5ebf96a843eb57e0
.git/objects/57/e9529754dc514a3ec10db2ff882018fbe1fcbf
.git/objects/2e/4038cce3649c830daaf38f6e411b4d55d5b7ac
.git/objects/03/ccb30ce6b8b7d157b6d28fb479257eb424af02
.git/objects/64/e50281e4e0ebbbdc438095b6a222931ec0240f
.git/objects/a8/0202bb5efd9949dc61fc7a8da48d0c8ac4faf0
Unixのファイルシステムの場合は
808494
808312
917641808590
917647
inodeがデータブロックを(間接的に)指定します
スーパーブロック
iノードテーブル
データ領域
各ディレクトリのデータブロックから
808494
808312
917641808590
917647
矢印を引くとこんな感じ
スーパーブロック
iノードテーブル
データ領域
そっくりだ!
Gitのtreeと
オブジェクトデータベース
Unixのディレクトリと
ファイルシステム
ce0136
2e4038
f18bca64e502
6935eb
% find .git/objects -type f
.git/objects/b0/de5d4beb96ad900811b3d9e115487fac54f99a
.git/objects/50/d659153f572c2ee2655c54d8084d987613d796
.git/objects/8c/6d0205c764e18d26f411770c081628e076e51f
.git/objects/f1/8bca942070f88dfc217a58d8766376fb642abc
.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
.git/objects/89/7d06bcb89a12574271e74e90048cb26fe5f6bb
.git/objects/cf/2d7699c6f20728bf126c8af08e7874a84b8696
.git/objects/8e/19af5536b93bcdcdf9d7c5b2df89d15c5876e8
.git/objects/b4/9b64efff762751cbc746633bc39be3e32090ad
.git/objects/69/35eb316843d05f389830ecb4022fbc9debbea5
.git/objects/49/6cd51216313fc969a1f61d5ebf96a843eb57e0
.git/objects/57/e9529754dc514a3ec10db2ff882018fbe1fcbf
.git/objects/2e/4038cce3649c830daaf38f6e411b4d55d5b7ac
.git/objects/03/ccb30ce6b8b7d157b6d28fb479257eb424af02
.git/objects/64/e50281e4e0ebbbdc438095b6a222931ec0240f
.git/objects/a8/0202bb5efd9949dc61fc7a8da48d0c8ac4faf0
808494
808312
917641808590
917647
スーパーブロック
iノードテーブル
データ領域
つまりGitのオブジェクトは
tree
2e4038...
(root)
tree
f18bca…
img
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
Unixのファイルシステムと言えます
もちろんそれだけじゃありません
tree
2e4038...
(root)
tree
f18bca…
img
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
各オブジェクトはzlibで圧縮されて格納されます
つまり圧縮ファイルシステムです
内容が同じファイルを追加すると
コンテンツが同じ
= SHA1値が同じ = 同じオブジェクト=もうある
% find .git/objects -type f
.git/objects/b0/de5d4beb96ad900811b3d9e115487fac54f99a
.git/objects/50/d659153f572c2ee2655c54d8084d987613d796
.git/objects/8c/6d0205c764e18d26f411770c081628e076e51f
.git/objects/f1/8bca942070f88dfc217a58d8766376fb642abc
.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
.git/objects/89/7d06bcb89a12574271e74e90048cb26fe5f6bb
.git/objects/cf/2d7699c6f20728bf126c8af08e7874a84b8696
.git/objects/8e/19af5536b93bcdcdf9d7c5b2df89d15c5876e8
.git/objects/b4/9b64efff762751cbc746633bc39be3e32090ad
.git/objects/69/35eb316843d05f389830ecb4022fbc9debbea5
.git/objects/49/6cd51216313fc969a1f61d5ebf96a843eb57e0
.git/objects/57/e9529754dc514a3ec10db2ff882018fbe1fcbf
.git/objects/2e/4038cce3649c830daaf38f6e411b4d55d5b7ac
.git/objects/03/ccb30ce6b8b7d157b6d28fb479257eb424af02
.git/objects/64/e50281e4e0ebbbdc438095b6a222931ec0240f
.git/objects/a8/0202bb5efd9949dc61fc7a8da48d0c8ac4faf0
ce0136
ce0136
同じ内容なら同じオブジェクト
tree
……...
(root)
tree
……...
img
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
つまり重複排除圧縮ファイルシステムです
blob
ce0136...
hello.txt
改竄検知
さきほどのページの図
おかしなところがあったことを
おわかりいただけただろうか
Google Docsって隷書フォント使えないの ?(´・ω・`)
treeのオブジェクト名が変わった…
tree
.........
(root)
tree
……...
img
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
...とでもいうのだろうか。
blob
ce0136...
hello.txt
隷書フォント(´・ω・`)
はい。treeはツリーエントリを
内容とするオブジェクトなので
tree
771c10...
(root)
tree
716186…
img
ファイルの追加・削除等々、構造が変わればtreeオ
ブジェクトは別のものになります
←ファイルを追加
tree
2e4038...
(root)
tree
f18bca…
img
←変化
←変化
さらにtreeはblobの名前を持つので
tree
fe5448...
(root)
tree
f34fd4…
img
blob=ファイルの内容が1bitでも変われば連鎖的に
rootのtreeオブジェクトが変わります
↑”hellp”に変えてコミット
=1bit変更してコミット
tree
771c10...
(root)
tree
716186…
img
↑中身は”hello”でした
blob
ce0136...
hello.txt
blob
d7a963...
hello.txt
←変化
←変化
←変化
ツリーを構成する全情報のうち1bitでも変
更されればrootの名前で検知可能
tree
fe5448...
(root)
tree
f34fd4…
img
blob
ce0136...
greeting.txt
blob
64e502...
greeting2.txt
blob
6935eb...
screenshot.jpg
つまり
改竄(破損)検知
重複排除圧縮ファイルシステムです
blob
d7a963...
hello.txt
逆にrootのオブジェクトが同じなら
tree
fe5448...
(root)
下をチェックしなくても全体が同じだとわかる
→ ツリー同士の比較が爆速
tree
fe5448...
(root)
=
イコール
diff-treeの場合。ファイル内容まで取り出して比較するとそれなりに遅い
tree:
fe5448
さらにさらに
tree
(fe5448)
ツリーはコミットごとに記録
されるのでした。
→バージョンを記録
commit
(8b0e1d)
commit
(f78673)
tree:771c10
parent:
f78673
parent:
...
tree
(771c10)
改竄(破損)検知はコミットにも伝搬
コミットは親コミットの名前を含んで
います。
→ツリー全体を含む祖先のコミット
の情報のうち「何か」が1bitでも異
なれば、それ以外すべて同一でも
子孫は全て別物になります。
ef1046
09a80f
5c0ed0
8b0e1d
f78673
003b47
995f60 995f60
fe5448
9d2296
771c10
0eaea3
6a4fcc 6a4fcc
fe5448
771c10
変更が伝搬
改竄(破損)検知
バージョン管理
重複排除
圧縮
ファイルシステム
それがGitだ!(ドヤァ
まとめると
tree
commit
tree
commit
tree
commit
tree
commit
tree
commit
1bitでも違えば異なるオブジェクト
→データ量が肥大化(packで解決)
ツリーのスナップショットで管理
→単一ファイルの変化の追跡が苦手
(経由する全てのコミットでいちいちtree
をたどってオブジェクトが変わっている
か調べる必要がある)
もちろん欠点もあります
tree
commit
tree
commit
tree
commit
tree
commit
tree
commit
すでに散々出てきましたが
最後に改めてオブジェクトについてまとめておきま
す。
オブジェクトとはGitが管理する情報の単位です。
Gitオブジェクトの特徴
1. commit, tree, blob 等の型がある
2. オブジェクト名はコンテンツのSHA1値
→同じ内容のオブジェクトは区別されない
3. 各commitから親commitとtreeを辿れる
4. treeから他のtree/blobを辿れる
5. 辿る先はオブジェクト名で記録している
→その結果...
Gitオブジェクトの特徴
ふたつのオブジェクトの名前が同じなら、そこから
辿れる全オブジェクトが構成する構造全体にわ
たって同じ内容であることが保証される
e.g.
● blob同士が同じ名前なら同じファイル
● tree同士が同じ名前なら同じディレクトリ構成
● commit同士が同じ名前なら、先祖代々のcommitにわ
たって同じ
ここまでに出てきた主なコンセプト
コミット
親コミット
コミットグラフ
ツリー
ブロブ
オブジェクト
改竄検知
コマンドの解説はもう少し先です
コミット
親コミット
コミットグラフ
ツリー
ブロブ
オブジェクト
改竄検知
参照
シンボリックレフ
ブランチ
タグ(参照)
タグ(オブジェクト)
インデックス
ワーキングツリー
第2章
参照: ブランチとタグ
参照
参照とは
オブジェクト(主にコミット)につけられる、人間が読
みやすい名前のこと。
オブジェクト名(SHA1値)は覚えにくいので、覚えて
おいたほうが便利なオブジェクトには参照をつけま
す。
参照はレフ(ref, reference)とも呼ばれます。
こんな感じです。master という参照は有名です
よね。
参照を図にすると
ef1046
09a80f
master
これで “master” と書いてやれば、わざわざ ”
ef1046”とかSHA1値を書かないですみます。
commit
commit
参照の実体を見てみよう
参照の実体はオブジェクト名を含んだただのテキ
ストファイルです。
% cat .git/refs/heads/ master
ef1046a0117da8872b73fe72528c1fd9facd95ab
ef1046
master
gcやcloneした直後のリポジトリでは .git/packed-refs にまとめて書かれている場合もあります
保存場所で参照の種類が決まる
refs/heads/ 配下の参照=ブランチ
refs/remotes/ 配下
=リモート追跡ブランチ
refs/tags/ 配下=タグ
% cat .git/refs/ heads/master
ef1046a0117da8872b73fe72528c1fd9facd95ab
% cat .git/refs/ remotes/origin/master
ef1046a0117da8872b73fe72528c1fd9facd95ab
% cat .git/refs/ tags/backup
09a80f7e839b0ba298652eeaff523c0ac9427df8
ef1046
09a80f
master
backup
origin/master
アノテーションタグについては後述
GCとの関係
参照には他に重要な役割がひとつあります。
それは参照から到達可能なコミットはgit gcで削除
されない、というものです。
(削除されてほしくなければ参照を作っておく)
master
←削除対象
←削除対象
参照を参照する参照もあります
origin/HEAD -> origin/master という表示は見たこ
とがあるのではないでしょうか。
この手の参照をシンボリックレフといいます。
% git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/maint
remotes/origin/master
remotes/origin/next
remotes/origin/pu
シンボリックレフ
参照を参照する参照=シンボリックレフ
実体であるファイルの中身はコミットではなく別の
参照のパスです。
図示すると右のような感じ。
コミットを直接は指さず、参照を指しています
% cat .git/refs/remotes/ origin/HEAD
ref: refs/remotes/origin/master
origin/master
origin/HEAD
最もよく使うシンボリックレフ
みんなが一番お世話になっているシンボリック
レフは HEAD です。
foo
% cat .git/HEAD
ref: refs/heads/foo
HEAD がブランチを指している場
合*、指されているブランチをカレ
ントブランチと言いいます。
上図の状態だとカレントブランチは foo
HEAD
* HEADがただの参照の場合もある
ブランチ
ブランチとは
ブランチもタグも同じ参照だとすると何が違うの
でしょうか?
タグは「ある時点のコミットを定常的に指す」とい
う役割があり、
ブランチには「コミットを新しく作る場所を指す」と
いう役割があります。
動作の違いはコミットしたとき
カレントブランチにコミットすると、ブランチは自
動的に新しくできたコミットを指しますが、タグの
場合は動きません。
branch
tag tagbranch
“branch”をチェックアウトしてコミット作成 “tag”をチェックアウトしてコミット作成
新しいコミット 新しいコミット
HEAD
HEAD
HEAD
HEAD
この動作により
ブランチであればコミットするたびにブランチが
成長していきます。
branch
branch
HEAD
HEAD
branch
HEAD
branch
HEAD
より正確には
ref
HEADがrefを指している
HEAD
ref
新しいコミット
HEAD
HEADが参照を指している場合(≒HEADがシ
ンボリックレフの場合)、コミット時に指されてい
る参照が新しいコミットに追従します。
実はHEADが「新コミット作成場所」
新しいコミット
HEAD
HEAD
新しいコミットは HEADに作られる
新しいコミットはチェックアウトしているコミット、
つまりHEADをたどって得られるコミットを親とし
て作成されます。
実はコミット作成にブランチは関係なかったんで
す。
じゃあブランチは一体何?
正確には「HEADがシンボリックレフで指すこと
ができる参照」がブランチです。
ブランチ以外の参照はチェックアウトしても
HEADはその先のコミットを直接指すただの参
照になってしまいます。
ref
HEAD
ref
HEAD
チェックアウトしたのがブランチ チェックアウトしたのがブランチ以外 (タグ等)
もちろんHEADをエディタ等で無理やり編集すればタグをシンボリックレフで指せます。その場合コミットすると・・・
ただの参照(=コミットを
直接指している)
シンボリックレフ
(=参照を指す参照)
結果として
「コミットを新しく作る場所を指す参照」という役
割を効果的に果たせるのはブランチだけ、という
ことになります。
branch
tag tagbranch
“branch”をチェックアウトしてコミット作成 “tag”をチェックアウトしてコミット作成
新しいコミット 新しいコミット
HEAD
HEAD
HEAD
HEAD
動くのはHEADが指す参照だけ
例えばブランチmasterの場所にブランチtopicを作
成した直後、2つのブランチは同じコミットをさして
います。
master topic
動くのはHEADが指す参照だけ
masterをチェックアウトしている場合、HEADは
masterを指します。
master
HEAD
topic
動くのはHEADが指す参照だけ
この状態でコミットをつくると、HEAD が指すコミット
(=HEADが指す参照(master)が指すコミット)を親
とした新しいコミットが作られ
master
HEAD
topic
動くのはHEADが指す参照だけ
この状態でコミットをつくると、HEAD が指すコミット
(=HEADが指す参照(master)が指すコミット)を親
とした新しいコミットが作られ、 master がそれに追
従して動きます。topicは動きません。
master
HEAD
topic
新コミット作成と HEADが指すブランチの移動はユー
ザから見るとアトミックです。
動くのはHEADが指す参照だけ
masterをチェックアウトしている限り(つまりHEAD
がmasterを指している限り)、コミットするたび
masterが動き続けます。
master
HEAD
topic
動くのはHEADが指す参照だけ
今度はtopicをチェックアウトしました。
ということは、HEADはtopicを指します。
master
HEAD
topic
動くのはHEADが指す参照だけ
この状態でコミットすると、HEADが指すコミットを
親とする新しいコミットが作られ
HEAD
topic
master
動くのはHEADが指す参照だけ
この状態でコミットすると、HEADが指すコミットを
親とする新しいコミットが作られ、HEADが指す参
照が追従します。
HEAD
topic
master
動くのはHEADが指す参照だけ
さらにコミットしても同様です。
HEAD
topicmaster
動くのはHEADが指す参照だけ
もし図の場所に"backup”というタグがあったとして
HEAD
topicmaster
backup
動くのはHEADが指す参照だけ
backupをチェックアウトしてもブランチではないの
でHEADはbackupが指すコミットを直接指すことに
なります。
topicmaster
backup
HEAD
動くのはHEADが指す参照だけ
HEADがコミットを直接参照している状態をデタッ
チトヘッド(Detached HEAD)と言い、コミットは可
能ですが、
topicmaster
backup
HEAD
動くのはHEADが指す参照だけ
それに追従する参照はHEAD以外ありません。つ
まり別の参照をチェックアウトすると...
topicmaster
HEAD
backup
動くのはHEADが指す参照だけ
先ほどまでチェックアウトしていたコミット(☆)に戻
ることは難しくなります。参照を付けず放置すると、
やがてGCで完全に削除されてしまいます。
topicmaster
HEAD
backup
☆ <良い子の諸君
動くのはHEADが指す参照だけ
Detached HEADはあくまで特定のコミットを見るた
めだけに使い、コミットを作るにはブランチを使いま
しょう。
topicmaster
HEAD
backup
ちなみに
コミットオブジェクトの中身は「ツリー」「親コミット」
「著作者+日時」「コミッター+日時」「コミットメッ
セージ」でした。
topicmaster
つまり
全てのコミットオブジェクトはブランチの情報をどこ
にも持っていません。(コミットメッセージに残すこと
は可能)
topicmaster
他のVCSだと
ブランチ(たとえばtopic)は下図のように分岐したあ
との枝全体をイメージしてしまいがちですが・・・
topic
master
他のVCSだと
ブランチ(たとえばtopic)は下図のように分岐したあ
との枝全体をイメージしてしまいがちですが・・・
topic
master
Gitにおけるブランチとは
あくまでコミットを指している参照に過ぎません。枝
というよりも枝の先端(頭)だけを指し示しているイ
メージです。
topicmaster
ここだけがブランチ
HEAD
実際、Gitではブランチの参照を
「ブランチヘッド」とか「ヘッドレフ」などと言います。
保存先も refs/heads/ 配下でしたよね。「HEAD」も
その流れで考えれば自然な名前です。
topicmaster
ブランチヘッド
HEAD
ブランチを指定すれば枝全体も一意に決まる場合が多
いので、マニュアル等で「ブランチ」という単語が枝全体
を意味する場合はあります。区別の要否は文脈で判断し
ましょう。
コミットがブランチを全く記録しない
このコンセプトは賛否両論ありますが、「ブランチを
捨てるのが簡単」という大きな利点があります。例
えば・・・
topicmaster
トピックブランチを利用した開発では
「トピックに関係ないコミットはするな」ということに
なってますが・・・
feature-foomaster fix-bar
ちゃんと機能とバグ
フィックスはブランチ分
けろよ
開発リーダーの声
Gitの場合は
あまりこだわらずノリノリで仕上げて・・・
ああああ
master
後から
適切に分割し・・・
master feature-foo
fix-bar
コミットをコピー
ああああ
いらないブランチを消す
なんてことをよくやります。
master feature-foo
fix-bar
このとき
最初に作ったブランチの情報がfeature-fooやfix-
barのコミットに残るとしたら、(消す方法が用意され
ていたとしても)ちょっと面倒ですよね
master feature-foo
fix-bar
元ああああ
元ああああ
元ああああ
元ああああ
ちょっと視点を変えて
Gitにおけるトピックブランチ開発では、最初から
topicの切り方が完璧でなくても良い、と考えると、
このコンセプトの強力さがわかると思います。
master feature-foo
fix-bar
でもまぁ、コンセプトの良し悪しは
置いておいて、Gitのブランチとはそういうものだと
覚えてしまいましょう。
ブランチヘッドをつけたり外したり、移動したりが簡
単そうに見えますが、実際簡単です。
後ほど具体的なコマンドで説明します。
タグ(参照)
すでに散々出てきましたが
タグは特定のコミットを指すただの参照です。
タグをチェックアウトしても、HEADはシンボリックレ
フとしてタグを指せないので、ブランチとしては使え
ません。
tag
一度指した場所が変わらない
というのは利点にもなります。
Gitではトピックのコミットを組み替
える作業(rebase等)をしばしば行
うので、
topic
master
一度指した場所が変わらない
もともとの場所にタグをつけておけ
ば、元に戻すのも簡単です backup
master
topic
もちろんブランチでも
コミットさえしなければ同じ感覚で
使えますが、指す場所が変わらな
いというのは作業するうえでの安
心感につながります。
backup
master
topic
でもそんな軽い存在で良いのか?
リリース用のタグにはタグ付けした日時や人の情
報とか残したいんだけど、と思うかもしれません。
また、改竄耐性の強いGitのコミットと比較して、あ
まりの軽さに違和感を覚えるかもしれません。
大丈夫
実はアノテーションタグと呼ばれる、作者やメッ
セージなどの注釈情報(アノテーション)を残せるタ
グもあります。
アノテーションはタグオブジェクト(tag)使って残しま
す。
タグ(オブジェクト)
タグオブジェクト
タグオブジェクトはコミットに似ています。
tag = タグ付け対象のオブジェクト名
+ その型(commit, tree, blob等)
+ タグ名
+ タグ作成者
+ タグメッセージ
tag
タグオブジェクトを実際に見てみよう
“v1.0”という名前のアノテーションタグがある場合、
下記のように中身を確認できます
v1.0
もちろんタグ名を直接指定して git cat-file -p v1.0 でも可能
タグオブジェクト
(275b7f)
タグが指すコミット
(ef1046a)
% cat .git/refs/tags/ v1.0
275b7fa60415bd5103e91f96f8689e8536492f55
% git cat-file -p 275b7f
object ef1046a0117da8872b73fe72528c1fd9facd95ab
type commit
tag v1.0
tagger John Smith <js@example.com> 1399263544 +0900
Project foo Release 1.0
tag obj内にもタグ名がありますが
参照としてのタグ(refs/tags/...)の付与は必須で
す。参照を削除すると、やがてはタグオブジェクトも
GCで削除されます。
が、アノテーションタグを作れば
自動で参照も作成されるので
あまり心配はいりません。
v1.0
タグオブジェクト
タグ(参照)
signed tagは解説を省略
参照についての話題は以上です
コミット
親コミット
コミットグラフ
ツリー
ブロブ
オブジェクト
改竄検知
参照
シンボリックレフ
ブランチ
タグ(参照)
タグ(オブジェクト)
インデックス
ワーキングツリー
第3章
インデックスとワーキングツリー
インデックス
インデックスとは
新しく作るコミットのtreeを事前に組み立てる場所、
というイメージです。
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
index
初期状態(チェックアウト直後)では
インデックスはHEADの指すコミットのtree(fe5448)
と同じtree(fe5448)を保持しています
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
index
新しいコミットを作る準備として
インデックスのtreeを更新します(git add)。
インデックスのtreeは621a50になったとします
tree
fe5448
commit
ef1046
master
HEAD
新しいtreeで
indexを更新
tree
fe5448
commit
ef1046
master
HEAD
tree
621a50
index
tree
fe5448
index
インデックスを更新後コミットすると
インデックスに作ったtree(621a50)がそのまま新し
いコミットの指すtreeになります
tree
fe5448
commit
ef1046
master
HEAD tree
fe5448
commit
ef1046
master
HEAD
コミット
tree
fe5448
commit
ef1046
master
HEAD
tree
621a50
commit
6cc41b
新しいtreeで
indexを更新
tree
621a50
index
tree
fe5448
index
tree
621a50
index
コミット直後は
インデックスのtree(621a50)とHEADのtree
(621a50)が同じである状態に戻ってます。
tree
fe5448
commit
ef1046
master
HEAD tree
fe5448
commit
ef1046
master
HEAD
コミット
tree
fe5448
commit
ef1046
master
HEAD
tree
621a50
commit
6cc41b
新しいtreeで
indexを更新
tree
621a50
index
tree
fe5448
index
tree
621a50
index
インデックス=treeオブジェクト?
正確には違うものです。したがってcat-fileでイン
デックスを表示することはできません。
しかし、インデックスはtreeを作るために必要な情
報を保持しており、treeを持っているとイメージして
問題ありません
というわけでインデックスに treeオブジェクトがある図は厳密には誤りですが、わかりやすさを優先しています。
インデックスの中身を見たい!
HEADからの変化をサマリで見るにはgit statusが
適していますが、git ls-files(引数なし)でインデック
スに格納されたエントリを一覧できます。
treeオブジェクトとして見たい場合、git write-tree
を実行するとインデックスの内容でtreeをGitデータ
ベースに書き出すので、表示されたSHA1値をcat-
fileすればOKです。
インデックスの詳細
インデックスは他にもマージ中に複数のバージョン
のツリーのキャッシュを保持するなどの役割があり
ます。
詳細を知りたい人はGitソースツリーの
Documentation/technical/index-format.txt
ほか、関連マニュアルを見ましょう。
ワーキングツリー
これまでのコンセプトは全て
リポジトリの.git/配下の話でしたが、ワーキングツ
リーは .git/ 以外の部分のことです。
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
index
.git/ 配下
working tree
fe5448
equivalent
ここ(.git/の外側)の話
ワーキングツリーとは
Gitの管理するtreeが通常のUnixディレクトリツリー
としてチェックアウトされたものです。
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
indexworking tree
fe5448
equivalent
初期状態(チェックアウト直後)では
ワーキングツリーにはindexの内容がコピーされて
います。つまりHEADのtree(fe5448)相当の内容
です。
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
indexworking tree
fe5448
equivalent
ワーキングツリーの中身は
通常のディレクトリ・ファイルですので、
自由に追加・削除・変更できます。
.git/の世界の状況は関係なく編集可能です。
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
indexworking tree
locally
changed
コミットを作成するには
インデックスを更新してから、インデックスのツリー
を次のコミットとして登録するのでした。
tree
fe5448
commit
ef1046
master
HEAD
indexworking tree
locally
changed
tree
621a50
tree
621a50
commit
6cc41b
コミット
このインデックス更新の元ネタが
ワーキングツリーの内容です。下図のtree 621a50
はワーキングツリーのファイルでインデックスを更
新してできたtreeだったのです。
working tree
621a50
equiv
tree
fe5448
commit
ef1046
master
HEAD
index
tree
621a50
tree
621a50
commit
6cc41b
コミット
インデックス更
新
元ネタとは言っても
コミット時にワークツリーとインデックスを完全に一
致させる必要はありません。コミットはあくまでもイ
ンデックスの内容で作られます。
working tree
0efb8b
equiv
tree
fe5448
commit
ef1046
master
HEAD
index
tree
621a50
tree
621a50
commit
6cc41b
コミット
インデックス更
新
インデックス=コミット
とは違う内容
コミットと同じ内容
ワーキングツリーの状態とコミットは
独立している、とも言えます。
これは非常に強力なコンセプトです。例えば・・・
working tree
0efb8b
equiv
tree
fe5448
commit
ef1046
master
HEAD
index
tree
621a50
tree
621a50
commit
6cc41b
コミット
無関係
開発中についつい
気になったtypoなど些細な修正をしたくなることが
あります。(あるいは、あとでやろうと思ってそのま
ま忘れてしまう)
tree
fe5448
commit
ef1046
master
HEAD
tree
fe5448
indexworking tree
locally
changed
本来の開発とは
無関係な修正
本来の開発
ひとつの修正に
意味的に複数の変更を加えるのは行儀の悪い行
為とされています。(後から取り消すのが面倒だっ
たりするので)
tree
fe5448
commit
ef1046
tree
5b94ca
indexworking tree
5b94ca
equiv
本来の開発とは
無関係な修正
本来の開発
コミット
インデックス更
新
tree
5b94ca
commit
393ce0
master
HEAD
無関係な修正が一緒
になったコミット
そういう場合も
Gitなら気にせずワーキングツリーを修正して問題
ありません。その変更はインデックスに送らずにコ
ミットすれば良いのです。
tree
fe5448
commit
ef1046
tree
621a50
indexworking tree
5b94ca
equiv
本来の開発とは
無関係な修正
本来の開発
コミット
インデックス更
新
tree
621a50
commit
6cc41b
master
HEAD
本来の開発だけイン
デックスに反映
本来の開発だけを含
むコミットになる
その後
残っていた修正を改めてインデックスに送り、コミッ
トすれば、行儀の悪いコミットを作らなくてすみま
す。
tree
fe5448
commit
ef1046
tree
5b94ca
indexworking tree
5b94ca
equiv
本来の開発とは
無関係な修正
コミット
インデックス更
新
tree
621a50
commit
6cc41b
改めてインデックスに
反映
tree
5b94ca
commit
861bb7
master
HEAD
つまりGitでは
ワーキングツリーの編集内容と、次回コミット内容
(=インデックス)が分離されているわけです。
tree
621a50
indexworking tree
5b94ca
equiv
ワーキングツリー
編集内容
次回コミットの内容
分離
視点を変えると
Gitでは次回コミットの事を気にしてワーキングツ
リーを編集する必要はない、というわけです。(イン
デックスに送る時に気にすればよい)
tree
621a50
indexworking tree
locally
changed
自由に編集してよい
分離
インデックスに送るコマンドは
主にgit addですが、これにはワーキングツリーの
変更を部分的にインデックスに送る機能が用意さ
れています。具体的には次章で。
tree
621a50
indexworking tree
5b94ca
equiv
git add
ちなみに
インデックスは次回コミット、ワーキングツリーはそ
れ以降のコミットの内容を含んでいるので、コミット
グラフではHEADの続きのコミットのように図示す
る場合があります。
index working tree
master
HEAD
master
HEAD
index working tree
第4章
コンセプトから理解するGitコマンド
いよいよコマンドの解説です
これまでに解説したコンセプトをもとにコマンドを解
説します。
具体的な解説を見る前に個々のコンセプトを思い
出しておきましょう。
解説した主なコンセプト
コミット
親コミット
コミットグラフ
ツリー
ブロブ
オブジェクト
改竄検知
参照
シンボリックレフ
ブランチ
タグ(参照)
タグ(オブジェクト)
インデックス
ワーキングツリー
追加で: commit-ishとは
Gitのマニュアルにはcommit-ishという表記がよく
出てきます。commit自身や参照など、commitが
一意に決まるものがcommit-ishです。
v1.0
コミット
master
タグ
ブランチ
タグオブジェクト
commit-ish
HEAD
シンボリックレフ
この章で紹介するコマンド
● ワーキングツリー操作
○ checkout, reset
● インデックス操作
○ add, reset
● コミット操作
○ commit, merge, cherry-pick, revert
● ブランチ操作
○ branch, reset
この章で紹介するコマンド(2)
● コミット・ブランチ複合操作
○ commit(--amend), rebase
● タグ操作
○ tag
● さまざまな調査
○ log, gitk, diff, reflog, fsck
● その他の複合操作
○ 出てきたコマンドの組み合わせ
ワーキングツリー操作系
ワーキングツリーの内容を変更するコマンドを解説
します。
ワーキングツリー操作系
1. 全体を指定ブランチの内容に切り替える
checkout <branch>
checkout <commit-ish>
2. ワーキングツリーの変更を捨てる
checkout -- <path>
checkout <commit-ish> -- <path>
3. ワーキングツリーの変更を捨てる(2)
reset --hard
ワーキングツリー操作系
1. 全体を指定ブランチの内容に切り替える
checkout <branch>
checkout <commit-ish>
2. ワーキングツリーの変更を捨てる
checkout -- <path>
checkout <commit-ish> -- <path>
3. ワーキングツリーの変更を捨てる(2)
reset --hard
1. ワーキングツリー全体を指定したブラン
チの内容に切り替える
① checkout <branch>
② checkout <commit-ish>
①ワーキングツリーの内容を <branch> のツリー
の内容に切り替えます。同時にインデックスと
HEADも<branch>に従って更新されます。
checkout <branch>
checkout topicであれば、カレントブランチをtopic
に切り替えるわけですが、動きとしては・・・
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
5b94ca
index
working tree
5b94ca
equiv
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
checkout <branch>
HEADがtopicに移動し(カレントブランチが切り替
わり)・・・
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
topic
HEADtree
5b94ca
index
working tree
5b94ca
equiv
HEAD
checkout <branch>
HEADのツリーでインデックスの内容を更新
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
topic
HEADtree
621a50
index
working tree
5b94ca
equiv
checkout <branch>
同じくワーキングツリーも内容を更新します。
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
topic
HEADtree
621a50
index
working tree
621a50
equiv
checkout <branch>
これがカレントブランチを切り替えるということの正
確な意味です。
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
topic
index
working tree
621a50
equiv
tree
621a50
HEAD
1. ワーキングツリー全体を指定したブラン
チの内容に切り替える
① checkout <branch>
② checkout <commit-ish>
②実は <branch> に限定されず、<commit-ish>な
らなんでも指定可能です。
checkout <commit-ish>
動きとしては<branch>指定のときとほとんど同じ
で・・・
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
5b94ca
index
working tree
5b94ca
equiv
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
checkout <commit-ish>
HEADがブランチを指すシンボリックレフではなく、
コミットを直接指す参照になる、という点が違うだけ
で・・・
tree
5b94ca
index
working tree
5b94ca
equiv git checkout 6cc41b
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
5b94ca
index
working tree
5b94ca
equiv
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
HEAD
checkout <commit-ish>
インデックスとワーキングツリーが新しいHEADの
ツリーの内容で更新されるのは同じです
tree
5b94ca
index
working tree
5b94ca
equiv git checkout topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
topic
index
working tree
621a50
equiv
tree
621a50
HEAD
注意: チェックアウト実行前に
ワーキングツリー/インデックスがコミットされてない
変更を含んでいた場合、差分がチェックアウト後の
ツリーに適用されます。(変更が保持される)
tree
5b94ca
index
working tree
local change
on 5b94ca git checkout 6cc41b
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
HEAD
topic
tree
621a50
index
working tree
local change
on 621a50
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
master
topic
HEAD
ワーキングツリー操作系
1. 全体を指定ブランチの内容に切り替える
checkout <branch>
checkout <commit-ish>
2. ワーキングツリーの変更を捨てる
checkout -- <path>
checkout <commit-ish> -- <path>
3. ワーキングツリーの変更を捨てる(2)
reset --hard
2. ワーキングツリーの内容を捨てる
① checkout -- <path>
② checkout <commit-ish> -- <path>
①インデックスの<path>の内容を取り出し、そいつ
でワーキングツリー配下の<path>を上書きしま
す。
checkout -- <path>
hello.txtの変更でインデックスを更新し、ワーキン
グツリーのhello.txtを更に編集、greeting.txtにも編
集を加えた状態を考えます。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
checkout -- <path>
checkout -- greeting.txt を実行すると、インデック
スのgreeting.txtでワーキングツリーのgreeting.txt
を上書きします。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout -- greeting.txt
checkout -- <path>
インデックスのgreeting.txtはもともとHEADから変
更されていないので、HEAD=インデックス=ワー
キングツリーな内容になります
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout -- greeting.txt
checkout -- <path>
続いて checkout -- hello.txt を実行すると、同様に
インデックスのhello.txtでワーキングツリーのhello.
txtを上書きします。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout -- hello.txt
checkout -- <path>
ワーキングツリーのhello.txtがインデックスの
hello.txtと同じ内容になりました。これはHEADと
は異なる内容です。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout -- hello.txt
checkout -- <path>
どちらの例もワーキングツリーの編集内容は捨て
られます。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout -- hello.txtgit checkout -- greeting.txt
checkout -- <path>
インデックスが変更されている場合はHEADと同じ
ならないことに注意。
HEADの内容で上書きしたい場合は次に出てくる
書式を使います。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout -- hello.txtgit checkout -- greeting.txt
2. ワーキングツリーの内容を捨てる
① checkout -- <path>
② checkout <commit-ish> -- <path>
②<commit-ish>を指定した場合、そのコミットの内
容でインデックスとワーキングツリーの<path>を上
書きします。
②は<commit-ish>と書いてるけど実は tree オブジェクト指定でも OK.(<tree-ish>)
checkout <commit-ish> -- <path>
さっきの続きです。ここで checkout HEAD --
hello.txt を実行すると・・・
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
checkout <commit-ish> -- <path>
インデックスのhello.txtがHEADのhello.txtで上書
きされ・・・
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout HEAD -- greeting.txt
checkout <commit-ish> -- <path>
ワーキングツリーのhello.txtも同じ内容で上書きさ
れます。
どちらの書式も編集内容が捨てられるので注意し
てください。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
git checkout HEAD -- greeting.txt
ワーキングツリー操作系
1. 全体を指定ブランチの内容に切り替える
checkout <branch>
checkout <commit-ish>
2. ワーキングツリーの変更を捨てる
checkout -- <path>
checkout <commit-ish> -- <path>
3. ワーキングツリーの変更を捨てる(2)
reset --hard
3. ワーキングツリーの内容を捨てる(2)
reset --hard [ HEAD ]
インデックスとワーキングツリーをHEADの内容で
上書きします。HEADは省略可能。
HEADの位置に他の<commit-ish>を指定可能ですが、カレントブランチが指す場所を変更する副作用がある
ので、要注意。
reset --hard
動作は checkout HEAD -- . (pathにカレントディレ
クトリを指定)と同じで・・・
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
HEAD
hello.txt
greeting.txt
reset --hard
ツリー全体がHEADの内容で上書きされ、インデッ
クスとワーキングツリーの変更は捨てられます。
working tree index HEAD
hello.txt
greeting.txthello.txt
greeting.txthello.txt
greeting.txt
git reset --hard
インデックス操作系
インデックスの内容を変更するコマンドを解説しま
す。
インデックス操作系
1. インデックスに変更を登録する
add <path>
add -p <path>
2. インデックスの変更を捨てる
reset HEAD -- <path>
インデックス操作系
1. インデックスに変更を登録する
add <path>
add -p <path>
2. インデックスの変更を捨てる
reset HEAD -- <path>
1. インデックスに変更を登録する
① add <path>
② add -p <path>
①<path>で指定したワーキングツリーの内容でイ
ンデックスの同名の<path>を上書きします。
add <path>
ワーキングツリーの greeting.txt と hello.txt が
ローカルな変更を持っている(編集されている)とし
ます。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
add <path>
git add hello.txt を実行すると、ワーキングツリー
にある hello.txt の内容でインデックスの hello.txt
を上書きすることになります。
greeting.txt はインデックスに含まれません。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
git add hello.txt
greeting.txt
add <path>
インデックス=次回コミットの内容です。
コミットとしてふさわしい内容になるまで git add
<path> や git reset HEAD -- <path> (後述)で調
整しましょう。
working tree index
hello.txt
greeting.txthello.txt
git add <path> git reset HEAD -- <path>
HEAD
hello.txt
greeting.txt
greeting.txt
add <path>
なお、ファイルの削除を反映する場合も Git 2.0 か
らは add コマンドで可能です。(古いGitの場合は
git rm <path>や git add -A <path>で反映します)
working tree index
hello.txt
greeting.txthello.txt
git add hello.txt
1. インデックスに変更を登録する
① add <path>
② add -p <path>
②<path>で指定したワーキングツリーの内容とイ
ンデックスの同名の<path>の差分を選択してイン
デックスに登録します
greeting.txt
add -p <path>
ワーキングツリーの hello.txt がローカルな変更を
持っているとします。
working tree index
hello.txt
greeting.txthello.txt
greeting.txt
add -p <path>
add -p hello.txt を実行すると、対話的な処理が開
始され、ワーキングツリーとインデックスのhello.txt
の差分のハンクごとにインデックスにaddするかど
うかを選択できます。
working tree index
hello.txt
greeting.txthello.txt
git add -p hello.txt
greeting.txt
add -p <path>
結果としてワーキングツリーともHEADとも違う内
容の hello.txt をインデックスに登録することができ
ます。
working tree index
hello.txt
greeting.txthello.txt
インデックス操作系
1. インデックスに変更を登録する
add <path>
add -p <path>
2. インデックスの変更を捨てる
reset HEAD -- <path>
2. インデックスの変更を捨てる
① reset HEAD -- <path>
①<path>で指定したインデックスの内容を捨てて
HEAD の内容に戻すことができます。
greeting.txt
reset HEAD -- <path>
ワーキングツリーの hello.txt がローカルな変更を
持っており、インデックスにそれが登録されている
とします。
working tree index
hello.txt
greeting.txthello.txt
HEAD
hello.txt
greeting.txt
greeting.txt
reset HEAD -- <path>
git reset HEAD -- hello.txt によって、HEADの
hello.txt の内容でインデックスを更新します。つま
り、ワーキングツリーをもとに追加されていたイン
デックスの変更が捨てられます。
working tree index
hello.txt
greeting.txthello.txt
HEAD
hello.txt
greeting.txt
git reset HEAD -- hello.txt
greeting.txt
reset HEAD -- <path>
ちなみに git init した直後だと git rm --cached
<path> を使う必要があります。いずれにしろ git
status にどうしたら良いか表示されているので、
迷ったら status の出力を見ましょう。
working tree index
hello.txt
greeting.txthello.txt
HEAD
hello.txt
greeting.txt
git reset HEAD -- hello.txt
コミット操作系
コミットを作成するコマンドを解説します。
コミット操作系
1. コミットを作成する
commit
merge <commit-ish>
cherry-pick <commit-ish>
revert <commit-ish>
コミット操作系
1. コミットを作成する
commit
merge <commit-ish>
cherry-pick <commit-ish>
revert <commit-ish>
1. コミットを作成する
① commit
② merge <commit-ish>
③ cherry-pick <commit-ish>
④ revert <commit-ish>
① インデックスの内容で、カレントブランチに新し
いコミットを作成します。
commit
現在のインデックスの内容で新しいコミットをカレン
トブランチに作ります。すでに解説したとおり、この
効果は正確には・・・
tree
5b94ca
index
working tree
local
change
tree
621a50
commit
6cc41b
HEAD
topic
tree
5b94ca
index
working tree
local
change
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
HEAD
topic
git add hello.txt
新しい
コミット
commit
HEADを親とした新しいコミットを作成し、HEADが
指すブランチヘッドの参照先を今作ったコミットに
書き換える、という動作の結果がそのように見えて
います。
tree
5b94ca
index
working tree
local
change
tree
621a50
commit
6cc41b
HEAD
topic
tree
5b94ca
index
working tree
local
change
tree
621a50
commit
6cc41b
tree
5b94ca
commit
861bb7
HEAD
topic
git add hello.txt
新しい
コミット
1. コミットを作成する
① commit
② merge <commit-ish>
③ cherry-pick <commit-ish>
④ revert <commit-ish>
② HEADと指定した <commit-ish> をマージした
マージコミットをHEADに作成します。
merge <commit-ish>
マージは他のコミットによる変更を取り込む操作で
す。
ここではfooをチェックアウトした状態で bar をマー
ジしてみます。
foo
bar
HEAD
git merge bar
merge <commit-ish>
HEADとfooの両方の編集結果を持つ(マージし
た)新しいコミットを作成し・・・
New commit
foo
HEAD
foo
HEAD
bar
bar
git merge bar
merge <commit-ish>
HEADが指している参照を新しいコミットに移動し
ます。これがマージです。
foo
HEAD
foo
HEAD
bar
bar
New commitgit merge bar
マージコミット
このとき新しくできたコミットをマージコミットと言い
ます。親コミットとして複数のコミットが記録されて
いるコミットがマージコミットです。
foo
HEAD
foo
HEAD
bar
bar
merge
commitgit merge bar
foo
HEAD
bar
3wayマージ
Gitで用いられるマージの手法は3wayマージと呼
ばれます。
3wayマージ
これはマージするふたつのコミットと、それらから最
も近い共通祖先の3つを使ってマージを行う、とい
うものです。
foo
HEAD
bar
3wayマージ
今回の例で言えば、foo, bar, およびそれらの共通
祖先(図示)を使うということです。
common
ancestor
foo
HEAD
bar
merge
commit
3wayマージ
マージする際に最適な共通祖先はマージベースと
も呼ばれます。これはコマンド git merge-base foo
bar で表示できます。
merge-base
foo
HEAD
bar
merge
commit
3wayマージ
3wayマージの特徴のひとつは、途中の変更(コミッ
ト)は考慮しないという点です。マージベース→foo
の差分、マージベース→barの差分をもとにマージ
コミットを作成します。
merge-base
foo
HEAD
bar
差分
差分
merge
commit
3wayマージ
マージベースとマージ対象のコミットそれぞれの間
に複雑な変更がどれだけあろうとも、3つのコミット
を材料にマージされます。
merge-base
foo
HEAD
bar
merge
commit
HEAD
bar
3wayマージが使われないパターン
ふたつの差分のうちのひとつがなくなってしまう場
合は3wayマージになりません。
例えば図のような状態で git merge bar することを
考えます。
foo
HEAD
bar
3wayマージが使われないパターン
つまりマージベースとfoo(HEAD)が一致している
場合です。barがfooの子孫である場合とも言えま
す。
merge-base
foo
HEAD
bar
Fast-Forwardマージ
この場合は foo を単に bar まで進めます。これは
Fast-Forward マージ(FFマージ)とよばれていま
す。
merge-base
foo
HEAD
foo
merge --no-ff でマージコミットを強制することも可能
bar
3wayマージが使われないパターン
逆のパターンも見てみましょう。つまり、上図の状
態で bar をチェックアウトし、 git merge foo する場
合です。
HEAD
foo
bar
3wayマージが使われないパターン
この場合もやはりマージベースは foo になります。
マージしようとしているコミット(foo)がマージベース
の場合、つまり祖先のコミットをマージしようとした
場合...
merge-base HEAD
foo
bar
マージ済み(Already up-to-date)
Already up-to-date と表示されるだけで何もおこり
ません。
すでにマージ済みであるとみなされるからです。
merge-base HEAD
foo
マージの方向に注意
barをfooにマージした結果(左)とfooをbarにマージ
した結果(右)はツリーは同じになったとしても、別
物です。マージコミットに記録される親コミットの順
番が異なるからです。
foo
HEAD
foo
bar
bar
merge
commit
merge
commit HEAD
ファーストペアレント
マージしたときのカレントブランチ側の親
=先に記録される親
=1番目に記録される親
=ファーストペアレントとなります。
foo
HEAD
foo
bar
bar
HEAD
first-parent
first-parent
ファーストペアレント
ファーストペアレントは他のVCSで言えばtrunk的
な意味合いがあります。
Gitでもファーストペアレントは特別扱いされる場合
があり、意識しておくと有用です。
foo
HEAD
foo
bar
bar
HEAD
first-parent
first-parent
ファーストペアレント
特にトピックブランチによる開発をする場合には統
合ブランチ(master等)側が常にファーストペアレン
トになるようにマージしましょう
foo
HEAD
foo
bar
bar
first-parent
first-parent HEAD
ファーストペアレント
ちなみにマージコミットを図示する場合、ファースト
ペアレントの系列はふつう直線上に書きます。例
外はありますが、fooからbarをマージしたときは大
抵左のように書きます。
foo
HEAD
foo
bar
bar
まっすぐ
HEAD
あまりこういう
風には描かな
い
マージ対象はコミットなら何でも可
マージ対象はブランチである必要はありません。
bar^ (「X^」はXの親コミットを指定する書き方) を
マージすれば、上図のようになります。SHA1指定
ももちろんOK
foo
HEAD
foo
HEAD
bar
bar
マージ
コミットgit merge bar^
1. コミットを作成する
① commit
② merge <commit-ish>
③ cherry-pick <commit-ish>
④ revert <commit-ish>
③ 指定した <commit-ish> と同じ差分のコミットを
HEADに作成します。
cherry-pick <commit-ish>
チェリーピックはマージと同様、他のコミットによる
変更を取り込む操作です。ただし、マージとは異な
り単一のコミットを取り込みます。
foo に bd48f1 をチェリーピックしてみます。
foo
HEAD
bar
git cherry-pick bd48f1
bd48f1
cherry-pick <commit-ish>
取り込むと言っても、 bd48f1 のコミットやツリーが
そのままfooの上に作られるわけではありません。
そんなことできても嬉しくないですし。
foo
HEAD
bar
git cherry-pick bd48f1
bd48f1
foo
HEAD
barbd48f1
bd48f1
cherry-pick <commit-ish>
実際のところ bd48f1^ と bd4f1 のツリーの差分
(patch)を取り出し…
foo
HEAD
bar
git cherry-pick bd48f1
bd48f1
foo
HEAD
barbd48f1
patch
cherry-pick <commit-ish>
そのパッチを foo に適用したツリーでコミットを新し
く作成する、という動作になります。
著作者、コミットメッセージはbd48f1のものを引き
継ぎますが、コミッターは更新されます。
foo
HEAD
bar
git cherry-pick bd48f1
bd48f1
foo
HEAD
barbd48f1
apply
cherry-pickの使いどころ
開発ブランチから生えたトピックのコミットはより安
定したブランチにマージすると、トピックの開始点ま
での開発ブランチの内容も一緒に安定ブランチに
マージされてしまいます。
master
devel
master
devel
git merge X
X
cherry-pickの使いどころ
そのような場合に、チェリーピックであれば、コミット
の場所に関係なく、安全にそのコミットだけを取り
込むことができます。
master
devel
master
devel
git cherry-pick X
X
X’
cherry-pickの使いどころ
欲しいコミットが複数の場合はrebase --onto でま
るごと取ってくることも可能です。いずれにしろ、ブ
ランチ開始点が間違っている場合の対処法であ
り、常用するものではありません。
master
devel
devel
git branch Z Y
git rebase --onto master Z~2 Z
Y
Z
Y
master
cherry-pickの使いどころ
トピックの開始点に注意して、コミットの取込みはで
きるだけマージを使いましょう。(マージする必要の
ある統合ブランチのうち、最も古い(下流の)ものか
ら開始するのがコツです)
master
devel
devel
git branch Z Y
git rebase --onto master Z~2 Z
Y
Z
Y
master
1. コミットを作成する
① commit
② merge <commit-ish>
③ cherry-pick <commit-ish>
④ revert <commit-ish>
④ 指定した <commit-ish> を打ち消すような差分
のコミットをHEADに作成します。
revert <commit-ish>
コミット32ff01をrevertするとします。
まず 32ff01 から 32ff01^ への差分(patch)を取り
出します。これは子から親へのパッチ(リバース
パッチ)です。
foo
HEAD
git revert 32ff01
32ff01
foo
HEAD
32ff01
reverse patch
revert <commit-ish>
そのリバースパッチをfooのツリーに適用して新し
いコミットを作ります。
foo
HEAD
git revert 32ff01
32ff01
foo
HEAD
32ff01
apply
revert <commit-ish>
32ff01 と revert commitの変更は互いに打ち消し
あい、結果としてHEADのツリーは 32ff01 による
変更がなかったかのような状態になります。
foo
HEAD
git revert 32ff01
32ff01
foo
HEAD
32ff01
ブランチ操作系
ブランチを作成・変更するコマンドを解説します。
ブランチ操作系
1. ブランチを作成/削除/名前変更する
branch <branch> [ <commit-ish> ]
branch -d | -D <branch>
branch -m [ <old> ] <new>
2. ブランチの指すコミットを変更する
reset [ --hard ] <commit-ish>
ブランチ操作系
1. ブランチを作成/削除/名前変更する
branch <branch> [ <commit-ish> ]
branch -d | -D <branch>
branch -m [ <old> ] <new>
2. ブランチの指すコミットを変更する
reset [ --hard ] <commit-ish>
1. ブランチを作成/削除/名前変更する
① branch <branch> [ <commit-ish> ]
② branch -d | -D <branch>
③ branch -m [ <old> ] <new>
① 指定したコミットに新しいブランチの参照を
<branch> という名前で作成します。<commit-ish>
を省略すると HEAD に作成します。
branch <branch> [ <commit-ish> ]
branch は単にブランチヘッドとなる参照を作成し
ます。図はコミット32ff01にbarというブランチを作
成した場合。
foo
HEAD
git branch bar 32ff01
32ff01
foo
HEAD
32ff01 bar
branch <branch> [ <commit-ish> ]
コミットを指定しなければHEADに新しい参照を作
ることになります。
foo
HEAD
git branch bar
foo
HEAD
bar
1. ブランチを作成/削除/名前変更する
① branch <branch> [ <commit-ish> ]
② branch -d | -D <branch>
③ branch -m [ <old> ] <new>
② 指定した <branch> を削除します。 -d では
HEAD から到達可能なコミットに付けられた参照で
ないと(=カレントブランチにマージされてないと)削
除できません。 -D であれば無条件に削除します。
branch -d | -D <branch>
branch -d はブランチヘッド参照を削除します。-d
だとHEADの祖先に付いているブランチのみ削除
できます。
foo
HEAD
git branch -d bar
foo
HEAD
bar
buz
buz
branch -d | -D <branch>
これはマージされていないブランチを削除しないた
めの安全装置です。上の場合、 buz を削除するこ
とはできません。
foo
HEAD
git branch -d buz
buz
foo
HEAD
branch -d | -D <branch>
HEADの祖先でないブランチを削除したければ、
branch -D を使います。
foo
HEAD
git branch -D buz
foo
HEAD
buz
1. ブランチを作成/削除/名前変更する
① branch <branch> [ <commit-ish> ]
② branch -d | -D <branch>
③ branch -m [ <old> ] <new>
③ ブランチ <old> の名前を <new> に変更しま
す。<old> を指定しない場合はカレントブランチの
名前が <new> に変更されます。
branch -m [ <old> ] <new>
branch -m でブランチヘッドの名前を変更できま
す。
foo
HEAD
git branch -m buz bar
foo
HEAD
buz
bar
ブランチ操作系
1. ブランチを作成/削除/名前変更する
branch <branch> [ <commit-ish> ]
branch -d | -D <branch>
branch -m [ <old> ] <new>
2. ブランチの指すコミットを変更する
reset [ --hard ] <commit-ish>
2. ブランチの指すコミットを変更する
① reset [ --hard ] <commit-ish>
① カレントブランチの参照が指すコミットを指定し
たコミットに変更し、そのコミットでインデックスを更
新します。--hardを指定した場合はワーキングツ
リーもそのコミットの内容で上書きします。
言い換えると、ブランチヘッドをコミットグラフの中
の別の場所へ移動する効果があります。
reset [ --hard ] <commit-ish>
reset はカレントブランチが指す先を変更します。
例えば今作ったコミットの内容が間違えていた時
に、そのコミットをなかったことにする用途で git
reset HEAD^ を使います。
foo
HEAD
git reset HEAD^
foo
HEAD
bar
bar
reset [ --hard ] <commit-ish>
--hard をつけるとワーキングツリーが指定したコ
ミットで上書きされます。ブランチヘッドを他のコミッ
トに付け替えたい場合によく使います。
foo
HEAD
git reset --hard bar
foo
HEAD
bar
bar
コミット・ブランチ複合操作系
コミット・ブランチを複合的に操作するコマンドを解
説します。実質的にはコミットの修正のために用い
られます。
コミット・ブランチ複合操作系
1. コミットを修正する
commit --amend
rebase <commit-ish>
コミット・ブランチ複合操作系
1. コミットを修正する
commit --amend
rebase <commit-ish>
1. コミットを修正する
① commit --amend
② rebase <commit-ish>
① インデックスの内容でHEADコミットを修正しま
す。実際にはHEADの兄弟として新しくコミットを作
り、カレントブランチをそこに移動します。
commit --amend
foo
HEAD
git commit --amend
tree
5b94ca
index
commit --amendはHEADを修正するコマンドで
す。修正内容をインデックスにaddした状態を考え
ます。
commit --amend
foo
HEAD
git commit
tree
5b94ca
index
本来ここでコミットすれば、目的の内容を持った新
しいコミットが作成されますが、--amendをつけて
いると…
foo
HEAD
amendなしの場合
commit --amend
foo
HEAD
git commit --amend
tree
5b94ca
index
インデックスの内容でHEAD^を親とした新しいコ
ミット、つまりHEADの兄弟のコミットが作られ…
foo
HEAD
commit --amend
foo
HEAD
git commit --amend
tree
5b94ca
index
ブランチfooがHEADとともに新しく作られた兄弟コ
ミットの方に移動します。
foo
HEAD
commit --amend
foo
HEAD
git commit --amend
tree
5b94ca
index
以前のHEADは見えなくなりますから、結果として
ブランチfooの最新コミットが修正されたように見え
ます。
foo
HEAD
1. コミットを修正する
① commit --amend
② rebase <commit-ish>
② 指定したコミット(これを含まない)からHEADま
でのコミットを一括して再作成します。
rebase <commit-ish>
ブランチbarを開発中、masterから分岐後のbar全
体を現在のmasterの先端に移動したい場合に
rebaseを使います。動きとしては…
master
HEAD git rebase master
bar
master
bar
HEAD
rebase <commit-ish>
まず master のコミットにHEADを移動し…
master
HEAD git rebase master
bar
master
bar
HEAD
rebase <commit-ish>
masterから分岐後のbarのコミットを順次 cherry-
pickのように差分を取り出しては適用し…
master
HEAD git rebase master
bar
master
bar
HEAD
cherry-pick相当
rebase <commit-ish>
参照 bar を新しいコミットに付け替えてカレントブラ
ンチをbarに切り替えます。
master
HEAD git rebase master
bar
master
HEAD
bar
rebase <commit-ish>
応用として rebase -i を使ってcherry-pickする際に
編集を加えたり、コミットを取捨選択したり、といっ
たことを対話的に指定できます。
図では2つだったコミットを1つにまとめてます
master
HEAD git rebase -i master
bar
master
HEAD
bar
rebase <commit-ish>
あるいは<commit-ish>に上流ブランチではなく、
祖先のコミットを指定することで、それ以降のコミッ
トを(ブランチを付け替えせずに)作りなおしたりでき
ます。
master
HEAD git rebase -i HEAD~2
bar
master
HEAD
bar
タグ操作系
タグを作ったり削除したりといった操作です。
タグ操作系
1. タグを作成/削除する
tag <tag> [ <commit-ish> ]
tag -a <tag> [ <commit-ish> ]
tag -d <tag>
1. タグを作成/削除する
① tag <tag> [ <commit-ish> ]
② tag -a <tag> [ <commit-ish> ]
③ tag -d <tag>
① 指定したコミットを指すタグ(参照)を作成します
tag <tag> [ <commit-ish> ]
指定したコミットに参照のタグを作成します。コミッ
トを指定しない場合、HEADが対象になります。
master
HEAD
git tag backup bar
bar
master
HEAD
bar
backup
1. タグを作成/削除する
① tag <tag> <commit-ish>
② tag -a <tag> <commit-ish>
③ tag -d <tag>
② 指定したコミットを指すアノテーションタグを作成
します。
tag -a <tag> [ <commit-ish> ]
-a を指定するとアノテーションタグを作成します。
実際には指定したコミットを指すタグオブジェクト
(△)を作成し、それを指す参照のタグを作成しま
す。
master
HEAD
git tag -a backup bar
bar
master
HEAD
backup
bar
1. タグを作成/削除する
① tag <tag> <commit-ish>
② tag -a <tag> <commit-ish>
③ tag -d <tag>
③ 指定したタグ(参照)を削除します。
tag -a <tag> [ <commit-ish> ]
-d を指定するとタグを削除します。アノテーションタ
グが指定された場合、タグオブジェクトはすぐに削
除されるわけではありませんが、そのうちGCで削
除されます。
master
HEAD
git tag -d backup
bar
master
HEAD
bar
backup
さまざまな調査系
いろいろなものを調査するためのコマンドです。実
に様々なコマンド・使い方があり、有名なものも多
いのですが、理解しておくと便利なもの・使い方を
中心に解説します。
さまざまな調査系
1. コミットの履歴を見る
log (範囲指定について)
gitk
2. 差分を見る
diff
log
3. なくしたものを探す
reflog, fsck
さまざまな調査系
1. コミットの履歴を見る
log (範囲指定について)
gitk
2. 差分を見る
diff
log
3. なくしたものを探す
reflog, fsck
1. コミットの履歴を見る
① log (範囲指定について)
② gitk
① 指定したコミット範囲の著作者やコミットメッセー
ジを表示します。
log (範囲指定について)
log はコミットメッセージ等のコミットの情報を表示
します。ここではコミットの範囲指定について有用
なものを紹介します。
master
HEAD
bar
foo
backup
log
log に何も指定しなければHEADを指定したことと
等価です。この場合、HEADから辿れる全コミット
が対象になります。
master
HEAD
bar
foo
backup
log <commit-ish>
コミットを指定した場合はその祖先全体が対象に
なります。この状態では master を指定すれば
HEAD を指定した場合と同じで…
master
HEAD
bar
foo
backup
log <commit-ish>
bar を指定すれば、barの祖先全体が対象になり
…
master
HEAD
bar
foo
backup
log <commit-ish>
foo を指定すれば、foo の祖先全体が対象になり
ます。
master
HEAD
bar
foo
backup
log <commit-ish>^
既出ですが、コミットを指す名前に ^ を加えると、そ
の親(ファーストペアレント)を指定したことになりま
す。master^ を指定すれば図の範囲になります。
master
HEAD
bar
foo
backup
log <commit-ish>^
ちなみに本来 master^ が指すのはこのコミット(黄
色)だけである点に注意してください。log が
master^から辿れる祖先全体を表示するのは、log
がそのような仕様だからです。
master
HEAD
bar
foo
backup
log <commit-ish>^
^は繰り返し指定可能です。master^^であれば図
の範囲で…
master
HEAD
bar
foo
backup
log <commit-ish>^
master^^^であれば図の範囲になります。
^のn回指定には ~n という省略記法が用意されて
います。 master^^^ は master~3 と等価です。
master
HEAD
bar
foo
backup
log <commit-ish>^<n>
^ はファーストペアレントですが、 ^ の後ろには数
字で何番目の親かを指定できます。 backup^2 で
あれば図のようにbackupの2番めの親になりま
す。 ^^ や ~2 との違いに注意。
master
HEAD
bar
foo
backup
log <commit-ish>^<n>
「^」の覚え方ですが、上図のようにマージしたコミッ
トの左側にファーストペアレント、右側に2番の親が
ある状態の矢印の形と「^」を関連付けると覚えや
すいです。
1 2
^2
^と~の組み合わせ
^[n] 指定(と~m 指定)を繰り返すことで祖先であれ
ば任意のコミットを指定できます。
master^^2^であれば図の範囲…
master
HEAD
bar
foo
backup
^と~の組み合わせ
master~2^2であれば図の範囲になります。
先に書いたとおりどんな祖先でも指定可能です
が、実際には log や gitk などで目的のコミットの
SHA1を調べて直接指定するほうが楽です…
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
コミットをふたつのドットでつないだ範囲指定法が
あります。X..Y であれば、Y およびその祖先に含
まれるコミットからXおよびその祖先をのぞいた範
囲、つまり Y - X を表します。
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
master..bar を見てみましょう。maser..bar は bar
の祖先から master の祖先を取り除いたものです
が、まず bar の祖先は図のような黄色の範囲で
す。
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
master の祖先は図の青の範囲ですから、これを
取り除くと…
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
最終的に上図の黄色い部分がのこります。 これが
master..bar という書き方が表す範囲です。
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
逆に bar..master を見てみます。master の祖先
は図の青の範囲で…
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
bar の祖先は図の黄色い部分ですから、これを取
り除くと…
master
HEAD
bar
foo
backup
log <commit-ish>..<commit-ish>
bar..master は図の青の範囲ということになりま
す。
master
HEAD
bar
foo
backup
log <commit-ish>...<commit-ish>
なお、ドットを3つにすると、 (X - Y) ∪ (Y - X)の意
味になります。 これは bar..master と master..bar
の両方という意味です。bar...master であれば図
の範囲になります。
master
HEAD
bar
foo
backup
log --first-parent
ファーストペアレントだけをたどるオプションもあり
ます。log --first-parent master であれば図の範囲
になります。統合ブランチのログのサマライズに非
常に便利です。
master
HEAD
bar
foo
backup
1. コミットの履歴を見る
① log
② gitk
② 指定したコミット範囲のコミットグラフをGUIで表
示します。
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド
コンセプトから理解するGitコマンド

More Related Content

コンセプトから理解するGitコマンド