2018年9月23日日曜日

1: Gitの全体像

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

Gitは、集中制御型レポジトリ群をネットワーキングする分散型バージョン管理システムではない、そこが大抵のチュートリアルが冒頭で明らかにし損ねている点だ

話題


About: Git

この記事の目次


開始コンテキスト


  • 読者は、バージョン管理システムとは一般に何であるか(その目的や、様々なタイプのバージョン管理システムに共通のメカニズム)の知識を持っており、いくつかの用語(例えば、'レポジトリ'や'ワーキングツリー'('ワーキングディレクトリ'ともいう))が何を意味するかを知っている(または、少なくとも、確信をもって推測できる)。
  • 読者は、何らかのGitのチュートリアルを読んでおり、しかし、Gitの全体像を理解できなかった。

ターゲットコンテキスト



  • 読者は、Gitの全体像をより良く理解する。

オリエンテーション


Hypothesizer 7
Git初心者である私は自分のシステムにGitを導入する目的でチュートリアルをいくつか読んだが、それらはどれも、同様に説明をし、同様に私の関心事を無視していた。

私は、自分のドキュメント群をレポジトリで管理し、古いバージョンも保存されて適宜参照できるようにするためにGitを使用したかったのだ(そのレポジトリを他の人と共有する意図はなかった)。ついては、私のつつましい関心事は、単一のレポジトリをどこか(例えば、'~/myData/gitRepositries/documents')に構築し、どこか他の場所にある(例えば、'~/myData/documents')自分のドキュメント群を、その場所で編集し続けながら、そのリポジトリに登録することだった。

その関心事に無理があるだろうか?...実は、私にはいくつかの集中制御型バージョン管理システム(集中制御型バージョン管理システムは、複数のレポジトリを持つことができるが、各レポジトリが複数のワーキングツリーから協調的に(典型的には複数ユーザによって)編集できる(同一ブランチに対しても)バージョン管理システムだ)を使った経験が若干あるが、その関心事は、任意の集中制御型バージョン管理システムに対して、間違いなく普通の、もしく標準的というべきものだ。

確かに、Gitが分散型バージョン管理システムであることは知っていたが、分散型バージョン管理システムであるというのは、単に、複数のレポジトリを同期するということだと思っていた(単一のレポジトリを操作する際は、Gitを集中制御型バージョン管理システムとして扱うことができるであろう)。実際のところ、なぜそう予期すべきでないのだろうか?

そこで、私はGitのあるチュートリアルを読んだが、それは、あるディレクトリにレポジトリを作成し(それはよい)、そして...そのディレクトリにファイルを直接入れ始めた、まるで、それが当然の、唯一の想像できる行動であるかのように。...「いや、いや、いや、そんな事をするつもりはないのですよ。リポジトリは全て(今回のを含めて)ある1つのディレクトリ配下において(リポジトリを散らして置きたくないのです)、ファイルたちは別の場所で編集したいのです(私にとっては、リポジトリ格納ディレクトリはリポジトリ群を格納するためのものであって、ファイルを編集するためのものではなく、リポジトリ格納ディレクトリ配下でファイルを編集しなければならないというのは受け入れられません)。」

そこで、私は別のチュートリアルを読んだが、...それは同じことをした...はあ?それから別のものを読んだが、それには、以下のような図が示されていた。


「そうです!'ワーキングディレクトリ'です!私は自分のファイルたちを'ワーキングディレクトリ'に置いておきたいのです、レポジトリディレクトリではなく!」

そこで私はそのチュートリアルを、気負い込んで読んだが、それは、あるディレクトリにレポジトリを作成し、そして...そのディレクトリにファイルを直接入れ始めた、'ワーキングディレクトリ'に何も言及することなく...

はあ??「'ワーキングディレクトリ'はどうなったのですか?'ワーキングディレクトリ'をセットアップする方法を知らせていただけませんか?」

...まあ、確かに、集中制御型バージョン管理システムユーザにとって自然な予期を抱いてやって来た私が責められるべきなのだが、しかし、集中制御型バージョン管理システムユーザはそれらのチュートリアルでは歓迎されないのだろうか?...どうやらそのようなので、'開始コンテキスト'を満たす誰をも歓迎する説明を私が試みよう。


本体


1: 注釈


Hypothesizer 7
注意として、この記事では'ブランチ'への言及を省略する。それは、複数ブランチを操作することはこの記事の関心事ではなく、レポジトリに対して常に単一のブランチしか考えられていないのに'ブランチ'という用語を記事中に散らばらせるのは、明確化よりは混乱の元になるように思われるためだ。例えば、私は、厳密にいえば「あるレポジトリのあるブランチのコンテンツ」であるときに「あるレポジトリのコンテンツ」のような表現を用いる。

さらに、この記事では、'リンクされたワーキングツリー'を私は意図的に無視する。それは、'リンクされたワーキングツリー'が、1つのレポジトリの複数ブランチを同時に操作する(それはこの記事の関心事ではない)ためのものであるからであり、'リンクされたワーキングツリー'が実験的な未完成機能だからでもある。そこで、この記事では、'メインワーキングツリー'('メインワーキングディレクトリ'とも呼ぶ)のことをただ'ワーキングツリー'と呼ぶ。


2: 厳密に言うと、あるGitコマンド実行に対してワーキングツリーとして指定されたディレクトリがそのGitコマンド実行にとってのワーキングツリーである


Hypothesizer 7
私が予期していたのは、あるディレクトリをあるレポジトリのワーキングツリーとしてセットアップすれば、その後は(その設定を解除するまで)、そのディレクトリをそのレポジトリのワーキングツリーとして使えるようになるというものだった。実際は、Gitはそのように動作しない。

'git'コマンドは'--git-dir='および'--work-tree='というスイッチを受け付け、これらによって、私たちは、操作するリポジトリディレクトリとワーキングツリーを指定することができる。したがって、そのワーキングツリーとしてどのディレクトリを指定しようが、それが、そのコマンド実行にとってのワーキングツリーになる。


3: しかし、レポジトリのコンテンツは、同時には単一のワーキングツリーから編集されるように、基本的には想定されている


Hypothesizer 7
しかし、複数のワーキングツリーからステージングされた変更の競合のチェックをGitはしないので、基本的には、レポジトリのコンテンツは、同時には単一のワーキングツリーから編集されるように想定されている(「同時に」で私が意味しているのは、既存のワーキングツリーを削除して、次に、新たなワーキングツリーを作成する(レポジトリをチェックアウトすることによって)ことはでき、それは、レポジトリのコンテンツを複数のワーキングツリーから編集することを意味するが、複数のワーキングツリーから同時にではない)。


4: 実際には、裸ではない(non-bare)レポジトリにはデフォルトワーキングツリーがある


Hypothesizer 7
'裸ではない(non-bare)レポジトリ'とは何なのか?実は、デフォルトワーキングツリーがあるレポジトリのことだ。

ワーキングツリーを'--work-tree='スイッチで首尾一貫して指定すれば問題ないのだが、普通、それは面倒だ。そこで、レポジトリはデフォルトワーキングツリーを持つことができ、ワーキングツリーを毎回指定しなくて済むようになっている。


5: それでは、'裸の(bare)レポジトリ'とは何か?


Hypothesizer 7
明らかだと思うが、'裸の(bare)レポジトリ'とは、デフォルトワーキングツリーを持たないレポジトリのことだ。

しかし、実は、それは、裸のレポジトリのコンテンツがどのワーキングツリーからも編集できないことを意味していない。コンテンツは、'--work-tree='スイッチで指定したどのワーキングツリーからでも編集できる(そうするように奨励されているかどうかは別問題だが)。


6: それでは、'git init'コマンドは、本当には何をするのか?


Hypothesizer 7
ほとんどのチュートリアルがただ「'git init'コマンドはレポジトリを作成する」と言うから、私はこう疑問を持たざるを得ない。「...それで、ワーキングツリーはどうなるんでしょうか?ワーキングツリーはどうすれば作成できるのでしょうか?」

実際には、パラメータなしの'git init'コマンドは、カレントディレクトリの'.git'ディレクトリにレポジトリを作り、カレントディレクトリを、そのレポジトリのデフォルトワーキングツリーに指定する。あれらのチュートリアルがそう親切に説明してくれていれば、私が途方に暮れることはなかったのだが...

とにかく、それが、レポジトリとそのデフォルトワーキングツリーというペアのデフォルトのディレクトリ構成だ。しかし、実は、'git init'コマンドに'--git-dir='および'--work-tree='のスイッチを指定することにより、レポジトリとそのデフォルトワーキングツリーの各々を任意の場所に置くことができる。

実のところ、デフォルトワーキングツリーの位置は、レポジトリディレクトリにある'config'というファイルにセットされていて(デフォルトのディレクトリ構成の場合には、明示的にはセットされていない)、そのファイルを編集することで、後から変更することができる。


7: 結局のところ、Gitは、ローカル型バージョン管理システムのレポジトリ群をネットワーキングする分散型バージョン管理システムである


Hypothesizer 7
もう既に分かったはずだが、Gitは、ローカル型バージョン管理システムのレポジトリ群をネットワーキングする分散型バージョン管理システムであって、私が予期していたような集中制御型バージョン管理システムのレポジトリ群をネットワーキングする分散型バージョン管理システムではない。

なぜそういう誤った予期を私は抱いたのか?まあ、一つには、それが集中制御型バージョン管理システムユーザにとって自然だったから。二つには、バージョン管理システムはローカル型バージョン管理システムから集中制御型バージョン管理システムを経て分散型バージョン管理システムへと進化してきたと思い、分散型バージョン管理システムというものはすべからく集中制御型バージョン管理システムを強化したものだろうと推測したから。

とにかく、レポジトリを1つしか持たないGitは、集中制御型バージョン管理システムではなく、ローカル型バージョン管理システムだ。


8: 意図された使用法においては、どのワーキングツリーにもそれ専用レポジトリが与えられ、それら2つが排他的ペアをなす


Hypothesizer 7
Gitをどのように使いうるかということとGitがどのように使われるように想定されているかということを分けて考えよう。'--git-dir='および'--work-tree='というスイッチを奔放に使用することによって、複数レポジトリ、複数ワーキングツリーを自由なコンビネーションで使用することはできるが、Gitは次のように使われるように想定されているようだ:どのワーキングツリーにもそれ専用レポジトリが与えられ、それら2つが排他的ペアをなす。

集中制御型バージョン管理システムユーザ的視点からすると、Gitの、レポジトリ毎に1つのワーキングツリーしか持てないというのは、ただ不都合な制限に過ぎない。しかし、Git的視点からすると、それは逆方向に理解されるように意図されているようだ:各ワーキングツリーに、親切にもそれ専用レポジトリが与えられる。

その決定が、Gitにとって、とても軽いというメリットを可能にしたことは認める:レポジトリは、ただ1つのワーキングツリーによって操作されるように意図されているため、複数ワーキングツリーからのリクエストをコーディネートするサーバーソフトウェアなしに、単に1群のファイルとしてアクセスすることができる。

他方では、正直に言って、私は、各ワーキングツリーに、押しつけで与えられるレポジトリなど欲しくない:余計なレポジトリは、ディスクスペースには無駄を、私には面倒(レポジトリ間を同期する作業を強いられるため)を意味する。


9: 誰かに直接/間接に操作されているレポジトリが、その人にとってその瞬間に、ローカル/リモートだ


Hypothesizer 7
'ローカルレポジトリ'および'リモートレポジトリ'という用語の、多くのチュートリアルにおける使用法に私は悩まされている。それは、レポジトリがそれ自体としてローカルまたはリモートであるかのように語られるからだ。実際には、ローカルまたはリモートであるというのは、レポジトリの属性ではなく、レポジトリが誰かによってその瞬間にどのように使用されているかについての概念だ:そのレポジトリは、その人その瞬間に対してのみローカルまたはリモートだ。したがって、「ローカルレポジトリを作成する」といった表現はナンセンスに思える:そのレポジトリは、各使用機会にしたがって、ローカルにもリモートにもなりうる。

実際には、誰かがあるレポジトリを直接に操作しているとき、そのレポジトリは、その瞬間、その人にとってローカルレポジトリであり、誰かがあるレポジトリを間接に操作している('別のレポジトリを通して'という意味)とき、そのレポジトリは、その瞬間、その人にとってリモートレポジトリである。

また、'ローカル/リモートレポジトリ'を'ローカル/リモートコンピュータに存在するレポジトリ'としても使うのは混乱のもとだ。'ローカル/リモートレポジトリ'が何を意味するか、心を決めるべきだ。


10: 集中制御型バージョン管理システムニーズにGitを使うべきか?


Hypothesizer 7
Gitは分散型バージョン管理システムとして、集中制御型バージョン管理システムとして働くことができるから、集中制御型バージョン管理システムを使う必要はもうないと(実質上)主張するチュートリアルを読んだ。

うーん、...そうかもしれないが、Gitを使うべきかは別問題だ。第一に、Gitは、いくつかの典型的な集中制御型バージョン管理システムニーズに応えないようだ。例えば、誰かが編集しているファイルは他の人達には読み取り専用にする(または、少なくとも、誰かが編集していると分かるようにする)というニーズがあるかもしれない。そのような操作モデルは不良もしくは少なくとも不要だと言う人が いるかもしれないが、そうした操作モデルは、Gitのものより、一部のプロジェクトにとってはより効果的だ。

第二に、Gitを集中制御型バージョン管理システムニーズに使おうとすると、余計なワーキングツリー専用レポジトリが必要になり、これはディスクスペースにとっては無駄であり、ユーザにとっては面倒の元である。

結論として、集中制御型バージョン管理システムニーズに特にGitを使うべきだとは私は思わない。誰かが使うという決定をしたのであれば、勿論、それはそれでよいのであるが。


11: それで、私のシステムはどうすればよいのか?


Hypothesizer 7
単一レポジトリのGitに集中制御型バージョン管理システムを期待していたのであるが、実際には、私が必要としているのは、単に、レポジトリとワーキングツリーを任意の位置に置くことのできるローカル型バージョン管理システムである。

私のニーズには、単一のレポジトリと単一(同時には)のワーキングツリーをそれぞれカスタマイズされた位置に置いたGitで問題ないようだ。


12: 結びとその先


Hypothesizer 7
これで、Gitの全体像をより良く理解したようだ。

簡潔に言うと、Gitは、集中制御型バージョン管理システムのレポジトリ群をネットワーキングする分散型バージョン管理システムではなく、ローカル型バージョン管理システムのレポジトリ群をネットワーキングするものである。

意図された使用法においては、どのワーキングツリーにもそれ専用のレポジトリが与えられ、そうした裸ではない(non-bare)レポジトリ群と、裸の(bare)レポジトリ群がネットワーキングされる。どのレポジトリも、同時には最大1個のみのワーキングツリーを持つように想定されているが、そのワーキングツリーは、任意の位置に置くことができる。

そのアーキテクチャにはメリットとデメリット(少なくとも一部の人々には)があり、それらが、Gitが各自のニーズに最適であるかを判断する材料となるだろう。

ところで、Gitはファイル変更日時を格納できない(そう、'格納できない'のだ、単に'チェックアウト時に復元できない'のではなく)ことを発見したが、これは致命的な問題だ(少なくとも私には)。そのようなGitの決定を支持する主張を論駁しこの問題を解決する方法を理解するよう試みよう、将来の記事にて。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>