gitつかいはじめたばっかりですが、何となくわかった部分についてメモ。忘れないように。
gitよくわからんって言うひと向けかもしれません。
ものすごく低レベルな話をします。よろしくおねがいします。
※間違っている部分があったら指摘して下さい。
-
gitってなに
分散型バージョン管理システム……
って言われてもよくわからないので、もっと簡単に、
- ディレクトリの中身をそのまま保存して自由に書き戻したりできるシステム
みたいな。
加えて、
- たくさんの人と同じディレクトリを共有したり
- そのディレクトリの新しいとこ同士をマージしたり
できる。べんり!
というこんな感じだとおもいました。
gitをつかう
インストールの仕方とかそんなもんはぐぐってください。
コミットの仕方とか使い方とかそういうものもぐぐればでてくるので書かなくてだいじょうぶですね。
gitのしくみ
gitは、コミットと呼ばれるかたまりがたくさん集まってできています。
コミットとは、作成時のディレクトリの状態を記録した1つのオブジェクトです。
ただの物体です。かたまりです。ここ大事。
このコミットをたくさん作って保管するのがgitの役目ですね。
そして特定の古いコミットにチェックアウト(コミットの中身を作業ディレクトリに書き戻す)することで、
まるでバージョン管理っぽく動くわけです。
コミットは自分の親を指し示す
コミットはみんな自分のIDをもっています。(sha1ハッシュだけどIDと書いていきます)
で、コミットの中には、自分の親のIDが入っています。
コミットを見れば、そのコミットの元になったコミットがわかるわけです。
コミットA ←(親を指し示す)コミットB
で、その親コミットにも親がいて、ずっと辿っていくと…………
... ← コミット8 ← コミット9 ← コミットA ←(親を指し示す)コミットB
ログが出来ました。
git log
はこうやって今いるコミット(HEADっていう)から親コミットをずっとたどって表示するだけのコマンドです。
HEADって?ブランチって?
HEADもブランチもタグも全部同じものです。(極論)
どれもただの特定のコミットを指し示す別名です。
この中だとタグが一番わかりやすいです。名前の通り、
- 特定のコミットに貼り付けるタグ
ですし。そのまんま。
それではブランチとはなにか?
- 特定のコミットに貼り付けられたタグ
です。タグの説明と変わらない……。
つまり、ブランチを作ったり削除したりするっていうのは、
ただブランチという名前タグをつけたり外したりするだけです。
つまりこういうこと
いつものログがこんなかんじです。
↓HEADという名のタグ
コミット11 - コミット22 - コミット33 - コミット44 - コミット55
↑masterという名のタグ(ブランチ)
コミット33のところでbranch01というブランチ名をつけてからコミットをしたらこうなります。
↓HEADという名のタグ
コミット11 - コミット22 - コミット33 - コミット34 - コミット35
↑branch01という名のタグ(ブランチ)
ともに1本線ですが、これを一緒に表示すると……
↓master
コミット11 - コミット22 - コミット33 - コミット44 - コミット55
\ コミット34 - コミット35
↑branch01
ブランチができてる!!
親を辿るだけのログが途中で合流した!→ブランチのようにみえる→ブランチです
ブランチってそういうもんです。
git tag "tag01" コミット55
git tag "tag02" コミット35
git branch -D branch01
を実行してみる。
↓tag01タグ
コミット11 - コミット22 - コミット33 - コミット44 - コミット55
\ コミット34 - コミット35
↑tag02タグ
今度はただのタグしかつけてないのにログを見る限りブランチにしか見えない。
……
ブランチ = ただのタグ
ブランチとタグの違いは?
コミットしたときに勝手に子供に付け替えてくれるのがブランチ。
一つのコミットに付いたままなのがタグ。
HEADはワーキングツリーの位置を指し示すので、コミットしたりチェックアウトしたりする度に動きます。
マージコミットってなに
コミットは親を指し示します。
いつから親が1人だと錯覚していた?
1つのコミットは2人の親を指すことができます。
つまりこんな状態。
コミット11 \ ←親が2人!
コミット33
コミット22 /
これを……
コミット10 ← コミット11 \ ←親が2人!
コミット33
コミット20 ← コミット22 /
こうやって……
コミット00 ←コミット10 ← コミット11 \ ←親が2人!
\ コミット33
\コミット20 ← コミット22 /
マージだこれ
ログを辿って2つに分かれていれば、そこでマージが行われたってことです。
コンフリクトとか解消とかそういうもっと高レベルの話はどっかで適当に調べてね!
git reset HEAD^ でコミットをなかったことにする?
この解説よく見るけど理解が遠くなるのできらいです。
git reset HEAD^ ≠ コミットをなかったことにするコマンド
結果的にコミットがなかったことになるのであって、なかったことにするコマンドじゃないんです。
上に書いたとおり、gitのコミット構造体には、自分の親コミットのIDがはいってますね。
それを辿ることでコミットの歴史を表示できるというわけです。
こんな感じですね。
コミット1 ←(親を指し示す)コミット2 ← コミット3
↑HEADです
この時、コミット3を取り消したいとします。
git reset HEAD^
というのは、「リセット」という言葉で見るといまいちピンと来ないですが、
どっちかというと 「Re-Set(セットしなおす)」と言ったほうがいいです。
その字の通り [HEAD]を [HEAD^](HEADの親)に「Re-Set」(セットしなおす)んです。
するとどうなるか……
コミット1 ←(親を指し示す)コミット2 ← コミット3
↑HEADです
こうなります。 これだけです。
git log
をみると、
コミット1 ←(親を指し示す)コミット2
↑HEADです
こうですね。
git log
は上に書いたとおり、「HEADから親を順番に辿る」ので、
HEADからたどりつくことの出来ないコミット3はどっかいっちゃいましたね。
これでコミット3に辿りつく方法がなくなったので、
コミット3は無事取り消されました!!
ということになります。
当然コミット3は存在していて誰からもリンクが張られていない状態で宇宙空間を漂っているので、コミット3のIDに対して直接HEADをくっつけてやれば、最初の状態にそのまま戻ります。
コミットを書き換える?
git commit --amend
git rebase branch-xxx
git merge --squash
コミットを書き換えるといわれるコマンドはたくさんあります。
でも、コミットっていうのは作った時点で不変に存在するものじゃないんです?
これをこうしてこうじゃ
コミット11 - コミット22 - コミット33 - コミット44 - コミット55
↑HEAD&master
git commit --amend --message "コミット55を書き換えたものでござる"
コミット11 - コミット22 - コミット33 - コミット44 - コミット55
\コミット55を書き換えたものでござる
↑HEAD&master
git log
コミット11 - コミット22 - コミット33 - コミット44 - コミット55を書き換えたものでござる
↑HEAD&master
そういうことです。
どういうことなの
参照する方法がなくなった「コミット55」はログに映らないでしょ?
応用で、ブランチをrebaseとかするまえに更にブランチを作って重ねておくと、それまでのブランチを残したままrebaseしたりできます。
つまりgitって
- 無数の「コミット」という名のかたまりが、それぞれ他の「コミット」を指し示しているだけの空間
です。
実際にgitを使うときは、
- 「コミット」を作ったり、「コミット」を指定しているタグ(タグだったりHEADだったりブランチだったり)をつけたり外したり動かしたりして運用する
ということです。
凄まじくシンプルでおもしろいですね。
コミットそのものは一度作ると永久に唯一存在する(いいすぎ(GCさんがお掃除しちゃう))けれど、それを参照するタグをいろいろ付け替えたりできるので、コミットをなかったことにしたり複数のコミットを1つにまとめたコミットを作ったりと、かなり自由自在に操作出来ます。
なので、実際に運用するときはCtrl+S(保存)する感覚でガンガンコミットすればいいとおもいます。どうせ後で自由にいじれるので。stashはただのコミットなので、stashを使ってもコミットを使ってもどっちでもいいとおもいます。
コミットされたものはだいたい引っ張り出せますが、コミットされてないものは消えたらおわりなので、そこだけは気をつけてください。
おわり
最後まで低レベルなとこの話題だけで終わっちゃいましたが、gitの基本的な仕組みが分かったのでこれから楽しくなりそうです。
もう一度、もしなにか間違っている部分があったら指摘して下さい。よろしくおねがいします。