Git

gitの勘違いしたメモ

そういえばQiitaにアカ作ってから何も書いてなかったので、メモ。

おさーんにもかかわらず、最近になってgitに触れるようになって、
http://koseki.hatenablog.com/entry/2014/04/22/inside-git-1
でちょっと構造を頭に入れたので解ったつもりになってたのですが(<無茶)
色々わかりにくいなーと思って、
https://qiita.com/kyoyyy/items/161b6905f45bee2efe21
この辺読んだら、割と腑に落ちた……というか、誤解してた所とかあったので軽くまとめ。
というか、上のリンクは良くまとまっててオススメです。

コミット前の作業状態

例えばgit resetの説明とか見ると
https://qiita.com/shuntaro_tamura/items/db1aef9cf9d78db50ffe
コミットの流れの延長上にインデックスやワーキングツリーがあって、resetの戻し方が解りやすいよね?って感じに書かれることが多いのですが、
そもそも、未コミットのファイルってgitでどういう保持してるんだっけ?ってなると、ちょっと使うの怖い。(だって事故るってよく言うし)
というか、矢印がポインタ的に前後するイメージで書かれると、ブランチやHEADみたいに参照だったっけ?ってなりますが……

でも、実際は、インデックスはaddでワーキングツリーから作るわけで、最終コミットから一直線に伸びてくようなイメージではない訳です。
じゃあ、内部的にどうなってるのか、って言うと、たぶん、
・ワーキングツリーはただのファイルを変更した状態そのもの(だから、gitは直接管理していない、その差分を都度確認するだけ)
・インデックスは、addした時に
 .git/objects(ディレクトリ)にファイル内容の変更情報
 .git/index(ファイル)に追加した(コミット予定の)ファイルの情報
 を保持する
なので、例えば、resetでHEADを戻した場合、コミットはHEADから参照されなくなる、のは良いのですが、そのままだと、インデックスにはないことになります。(ワーキングツリーにはそのまま残る。コミットの変更もワーキングツリーには反映されているので)
要するに、reset --softでHEAD「だけ」戻す場合、インデックスに取り消したコミットの変更情報を書き戻すことになる(はずな)訳です。

逆に、reset --hardならHEADを戻して、ワーキングツリーから修正部分を削除、となり、
いわゆる省略時の挙動であるreset --mixedが、HEADの参照だけ戻す、一番シンプルな挙動って事になります。

と、理解がすっきりした……と思ってたのですが、よく考えてみたら……公式に
https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E3%83%AA%E3%82%BB%E3%83%83%E3%83%88%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E8%A9%B3%E8%AA%AC
ちゃんと説明がありますね、よく読め(>自分)的な。
(でも、あまりググっても引っかからないんですよね……ググりワードのせいか)

トラッキング、いわゆる追跡ブランチとリモート追跡ブランチ

なんか長くなったけどもう一つ。
最初のリンクでは(たぶん)説明が入ってなくて、すっかり理解から外れてました。

SourceTreeとか使ってると、pullとかした時に、「なんとなく良いあんばいに」作成元のブランチを反映してるようなイメージになっちゃうのですが、実際は追跡ブランチ、の機能で、この追跡元(上流、って言うっぽいですが)から変更を引っ張ってくるみたいです。

で、それなら「リモート追跡ブランチ」は「リモートが上流の追跡ブランチですね」で終わりそうですが……(タダのブランチです、って言うくらいだし)
実は、リモート追跡ブランチは、追跡ブランチの一種かもしれませんが、リモートが上流なだけのタダの追跡ブランチ、ではないです。

追跡ブランチはfetchしても更新はされませんし、pullすればローカル修正とのマージが発生します、が、リモート追跡ブランチはfetchすればその時点で更新されてしまいます。

実際は、(普通の)追跡ブランチは、ローカルで作業しているブランチで、そのためローカルでコミットすれば更新されてしまいますが、リモート追跡ブランチはfetchでリモートのブランチ状態をそのまま持ってくるだけなので、ローカルでコミットできないわけです。
ただ、実体はどちらもブランチ、即ち参照なので、同じと言ってしまえば同じです。

では、なんでふるまいが変わるのか……というと、参照の入ってる場所が
・普通のブランチ……heads
・リモートブランチ……remotes
と分けてあって、これで切り替えてるって事みたいです。
(扱いが違うから、入れる場所が違うんだ、かも知れませんが;
 あと、ここにはスタッシュもありますね)

リモート関連の機能説明は、リモート側の情報を指すのか、ローカルに複製されたリポジトリ内のリモート関係の情報を指すのか、混乱しやすいですね。
(fetch以外では、リモート側の情報を扱うことは少ないと思いますが)
関連の説明を読むのはちょっとしんどかったかも。理解してから読めば楽なんでしょうけどね……

gitは挙動はシンプルだけど、イメージしやすいとは限らない……かも……

なんかまだ詳細の理解に不安がある感じですが、なんとなく「よく分かってないけど使ってた」部分の理解は進んだかな……と。
(あと、まだマージとか……抜けてるところあるんですけどね)

gitはresetとかもそうですが、仕組みは単純(HEADの巻き戻しって参照の書き換えだけですし)なのですが、イメージ(例えば変更が時系列で進行するような)通りの実装、とは限らないので注意が必要ですね。
と言うか、挙動はエンジニアはイメージできると思いますが、非エンジニアの人に理解させようとすると大変じゃないかな……とは思います。