Git のちゃんとした入門 (になる筈だった文書)

前書き

Git はなかなか入門障壁が高い。原因の一つはその辺の解説サイトではないかと思っている。 世の中には解説サイトが色々あるけれども、下手に喩えを使っていたり微妙に概念を隠蔽したりしていて、結局何がどうなっているのか腑に落ちてこないサイトが殆どである。 有名どころでは「サルでもわかるGit入門」というページがある。 このサイトは取り敢えず何も理解せずに盲目的に使うだけならば良いのだが、Git についてほとんど理解を得られない。 例えば、学生に「Git を勉強しておいて欲しい」というと大体このサイトに辿り着いて読んでくるようだが、 どれくらい理解しているか確認すると Git はリモート・リポジトリが存在しないと使えないものである と勘違いしていたり、 Git とは GitHub にアクセスするためのツールのようで実は違うらしい良くわからない何かである という曖昧模糊たる理解だったり、 それどころかそもそも Git の何たるかを皆目把握できていなかったりする。 検索の上の方に来る他の入門サイトも似たり寄ったりである。 そんな次第で、結局学生には Web で読んでもらった内容は一旦全部忘れてもらって、自ら一つずつ Git の何たるかを説く羽目になる。 最近では寧ろその辺のページは分かりにくいから読まなくて良いとさえ言っている。

何か良いサイトがあればそれを学生に教えれば良いのだが、なかなかない。 結構良さそうだなと思う解説サイトも幾らかあるが、(これは自分の我儘なのだが) なんとなく不満を感じる。 唯一素晴らしいと感じられるのは Git の公式に置いてある以下のページである。PDF 版も置いてある。 これのたった一つの難点は長いということである。538 ページある。 それでも、長い翻訳文を読むのは苦じゃないよという人はこれを二三日かけて読めばとても良いと思う。

他に、出版されている書籍であれば流石にちゃんとしているのだとは予想するのだが、読んでいないので分からない。 それに、Git の使い方なんぞは研究のほんの (感覚) 1% にも満たないので、わざわざ本を買えとも言いづらい。他に買うべき本が沢山ある。 それでも、お金を出す気があるならば、Git の良さそうな書籍を見繕って買うという選択肢はありだと考える。

そういう訳で、上に紹介した長い解説ページ (や書籍) と巷の分かりにくいページの間に横たわる大きな隔たりを埋める形で、 ここではできるだけコンパクトに、それでいて本質を逸しないような Git の解説記事を書きたいと思うのである。 ちゃんと書くことができるかは分からないが、巷の分かりにくい Git 入門ページが存在を許されているのだから、多少駄目なものになったとしても良かろう。 後、別に僕は Git の専門家でもなんでもないので間違ったことがあったら IssuePR か Twitter @akinomyoga で教えてくれると助かる。 それから、既にお察しのことと思うがかなり文章文章した解説ページになると思う。 文章の方が分かりやすいと思う人・文章が苦手な人など色々いるので、万人向けに書くのは無理である。 自分は文章の方が分かりやすいので、こういう記事を書く。 文章が苦手な人向けの記事は、また誰か別の人がどんどん書いてくれたらいいなと思っている。

前提知識

基本的な CLI (コマンドラインインターフェイス・特に Unix シェル) での操作の知識は前提とする。 世の中には GUI を通して Git を間接的に操作してくれるソフトウェアが色々あるようだが、そういったものの解説はしない。 Git は、その本来のインターフェイスである CLI や内部構造に大分引きずられたデザインになっている。 特に、或る程度の内部構造について分からないと Git を理解できた気にならないと思う。 その点になると、GUI を通した間接的な操作では隔靴掻痒の感が拭えず (個人的に) 気分が悪い。 また GUI クライアントは沢山あってどれか一つ選ぶのも難しいし、流行り廃りがあるのですぐ陳腐化しよう。 従って、この記事では CLI での Git の操作方法を、 内部的な状態を「変更」する操作だけでなく、 内部的な状態を「取得」する操作も強調して紹介して行けたらいいなと思っている。

また、Git のインストールの方法についても説明しない。 環境によって色々だろうし、インストールぐらいであればどのサイトでも分かりやすさは大差ない。 なので、分からなければ適当に他のサイトを参考にして入れてくれれば良い。 因みに、もしパッケージシステムがある環境であれば、大抵「git」という名前のパッケージがある筈なので単にそれを入れれば良い。 その結果、シェルで git というコマンドが見つかれば、Git は入っている。

あとはできるだけ Git に関する知識が皆無でも大丈夫なように説明したい。 なので、或る程度他のサイトで勉強した人にとってみれば、しばらくだるい説明が続くかもしれず申し訳ないが、我慢して欲しい。

1. 基本概念 ~最低限必要なこと~

ここでは最低限必要なこととして、Git がどんな物なのかについてと、 Git の基本的な登場人物 (リポジトリ、作業ツリー、インデックス、コミット) について説明することにする。 他にも様々な便利な概念・機能はあるが、それらは必要になった時に個別に知れば問題ない。 例えば、その辺のサイトでローカルリポジトリ・リモートリポジトリのような単語を見たことがあるかもしれない。 Git を活用する上でこれらの機能は大変に便利であるが、正直 Git の構造的に言えばこれらはおまけのような物である。 Git による管理はローカル・リモートなどなくても独立に成立するものである。 なので、初めはこれらの機能は知らなくて良い。

1.1 Git はバージョン管理システム

Git (ギット) (ジットではない) はバージョン管理システム (VCS, version control system) という種類のソフトウェアの一つである。 取り敢えず VCS とは "ファイルたちの過去の変更履歴を管理するツールセット" のことだ ぐらいの理解で良い。 ウェブブラウザに Google Chrome や Mozilla Firefox や Microsoft Edge など色々あるように、 VCS にも Git の他に Subversion (SVN) や CVS や Mercurial など色々ある。 中でも Git は分散型と呼ばれる種類の VCS だが、これは他の VCS との比較論の話であって Git を理解する上では完全に蛇足なので、説明しない。 取り敢えず――ここのところ Git は VCS の中でも一番人気であり、Linux カーネルの開発に始まり多くのプロジェクトで採用されている――とか何とか、そういう触れ込みである。

VCS を使うことの利点は何だろう。これは使ったことのない人に本当の意味で理解してもらうのは不可能である。 そして、使えば分かる。なので「何も考えず使え」と書くのが恐らく一番正しい解説である。 でも筆が寂しいので何か書く。プログラムを書いていると遭遇する色々のトラブルたち

に見舞われた時に、気持ちよく始末をつけられるのが VCS の意義である。後は使えば分かる。

さて、いきなり "VCS は過去の履歴を管理する" と言われても具体的にどういう手順で何をどうするのか要領を得ない筈だ。 それで良い。結論から書くと、なにか一つ筋の通った論理だとか手順だとか、そんなものは VCS には存在しない※1VCS の実態は履歴に関係した雑多な操作の集合に過ぎないのである。 なので、一つ一つの操作について何をしているのか理解すれば良い。 その後で雑多の操作を好きに組み合わせれば良い。 自ずと流れっぽいものはできたりするが、それは別に固定的なものではない。

※1 世の中では○○フローなどの名前で Git の使い方の手順を定めていたりするが、取り敢えず気にしなくて良い。 あれらは「Git の使い方」というよりは Git による「人のこき使い方」を定めた人と人の間の決めごとなので、Git 自体の理解にはまるで役に立たない。 Git を理解した後で、人をこき使いたい、もしくは、こき使われたいときに参考にすれば良い。

Git では git という名前のコマンドを使う。 「VCS は雑多な操作の集合」と書いたが、Git では

$ git 機能名 引数...

という形式で雑多な機能を呼び出す。基本的にはいつもこの形式のコマンドで操作する。 ここで 後はそれぞれの機能について一つ一つ知っていけば良い と書きたい所だが、 その前に、最低限、操作の対象が何なのかについては理解しておかなくちゃならない。 取り敢えず、リポジトリ (repository)・作業ツリー (working tree)・インデックス (index)・コミット (commit) を知ってもらえれば良さそうである。 これらの登場人物を理解するためには、「手動でバージョン管理」することを考えるのが手っ取り早いと思うので、次の節でそれをやってみる。

1.2 手動バージョン管理をしてみよう

ここでは手動バージョン管理の実演のようなことをする。 後でちゃんとした Git の説明はするので、この節は気楽に追って頂ければ良い。

まずディレクトリ (= working tree) を作る

バージョン管理では、単一のファイルの履歴を追うのではなく、複数のファイルをセットとして履歴を追う。 特に、或るディレクトリを対象として、その中にあるファイル群を管理することにする。 という訳で、先ずはディレクトリを作る。そしてテキストファイル hello.cpp を作る。

[user@host ~]$ mkdir working-tree
[user@host ~]$ cd working-tree
[user@host working-tree]$ editor hello.cpp # 好きなテキストエディタで編集する

※念の為に書いておく。 まず "[user@host ディレクトリ名]$ " の部分はシェルのプロンプトなので入力しない。 その右側にあるのが実際に入力して実行するコマンドである。 ディレクトリ名は現在のディレクトリを表す。 上の例ではたまたまホームディレクトリにディレクトリを作ったが、もちろん何処でやっても良い。 上記、editor とした部分は、自分の好きなエディタでそのファイルを編集するということを表すことにする。 emacs なり vim なり nano なり、自分の好きなものを使えば良い。 何なら GUI のエディタを使っても良い。 " #" 以降はコメントなので実際には入力しない。

この時、ディレクトリ working-tree の中のファイルの階層構造は以下のようになっている。 tree コマンドは、ファイルの階層構造を表示してくれるコマンドである。 つまり、子ディレクトリの中の孫ファイル・子孫ファイルたちまでツリー形式で表示してくれる ls のようなものと思えば良い。 因みに、tree コマンドは Git とは関係なく GNU/Linux に入っている。 macOS では自分で入れる必要があるらしいが、このコマンドは以降の節では使わないので入れなくて良い。

[user@host working-tree]$ tree
working-tree
`-- hello.cpp

今は未だ1個しかファイルがないけれど、今後ファイルを増やしていく。 このディレクトリの中のファイル達 (による木構造) を作業ツリー (working tree) と呼ぶことにする。

バックアップを取る (→ repository 導入)

ここで hello.cpp の機能を変更したくなったとする。 編集を開始する前に、念のために古いバージョンを保持しておきたい。 ところでバックアップの取り方というか、過去のバージョンを保持する方法は二通り考えられる。

  1. hello.cpphello2.cpp に複製して、新しい hello2.cpp を編集する。 以降は、hello.cpphello2.cpp は別々の機能を持った類似のプログラムとして別個に管理する。
  2. hello.cpphello.20160101.cpp などに複製して、hello.cpp の方を引き続き編集する。 hello.20160101.cpp は過去の記録として残すに留め、もう編集しない。 古い機能を今後使う可能性があるのであれば、新しい機能は hello.cpp の追加機能として実装する (古い機能と新しい機能を設定で切り替えられる様に設計する)。

初心者がやりがちなのが前者。この方法は呪われているので駄目※3。 この方法を続けると似たようなプログラムがどんどん増えて、その内にどれが何なのか分からなくなり、必ず管理が破綻する。 過去のバージョンを使いたくなってもどれか分からないしメンテナンスされているかも分からなくなるので、結局改めて実装する羽目になる。

※3 但し、ちょっと試しに実装してみるなどの目的で、一時的に別バージョンを作ってみるのは良い。 しかし、その場合でも一区切りしたらすぐに一つのバージョンに統合する。 でも、これには技術が要る、というより VCS の助けがないと辛いので、今は未だその選択肢はないと思って良い。 後で VCS を用いたバージョンの統合について取り扱うことになると思う。

という訳で断然後者の方法で行く。 先ずは過去の記録を残しておく為のバックアップディレクトリを作る。 今回は repository という名前にする。

[user@host working-tree]$ mkdir repository
[user@host working-tree]$ cp hello.cpp repository/hello.20160101.cpp

更に、編集する毎に逐一バックアップを取っていく。

[user@host working-tree]$ editor hello.cpp
[user@host working-tree]$ cp hello.cpp repository/hello.20160102.cpp
[user@host working-tree]$ editor hello.cpp
[user@host working-tree]$ cp hello.cpp repository/hello.20160103.cpp

この段階で、ディレクトリの中身は以下のような構成になっている。

[user@host working-tree]$ tree
working-tree
|-- hello.cpp
`-- repository
    |-- hello.20160101.cpp
    |-- hello.20160102.cpp
    `-- hello.20160103.cpp

1 directory, 4 files

複数のファイルを編集する (→ commit 導入)

管理対象のファイルとそうでないファイルがある (→ index 導入)

Copyright © 2018, @akinomyoga Koichi Murase <myoga.murase at gmail.com>,

連絡先 Issue, PR, Twitter