Skip to content

Instantly share code, notes, and snippets.

@bleis-tift
Created November 1, 2011 13:14
Show Gist options
  • Save bleis-tift/1330468 to your computer and use it in GitHub Desktop.
Save bleis-tift/1330468 to your computer and use it in GitHub Desktop.

=========== Gitについて

リポジトリ

データを貯めておく所。 ソースの管理をファイルサーバでやっているなら共有フォルダ、SVNでやっているならリポジトリ、Gitもリポジトリ。 SVNのリポジトリとGitのリポジトリが異なる点は、直接の操作対象となるリポジトリが手元にある(Git)か、リモートにある(SVN)か。 (一人で使っててSVNだけど手元にあるよー、とかは無視)

また、SVNでは一つのリポジトリに対して複数の作業ツリーができるが、Gitでは一つのリポジトリに対して最大でも一つの作業ツリー。 これによって、Gitでは手元のリポジトリに対するコミットでは競合が発生しえない。

SVNでの多人数開発では、基本的には一リポジトリ複数作業ツリーで行う。それに対してGitでは各個人が(場合によっては複数)リポジトリを持つ(これは図にした方が分かりやすい)。 業務アプリ開発などでは、各個人のリポジトリとは別に共有のリポジトリも用意する。 このリポジトリと各個人のリポジトリの同期は任意のタイミングで行う。 基本的には、共有のリポジトリからpullによって他人の変更を自分のリポジトリに取り込み、個人のリポジトリからpushによって自分の変更を公開する。 もちろん各個人のリポジトリ同士でpullしあうこともできるが、あまりメリットはない。

SVNでは作業ツリー中心になるため、通信先は元となったリポジトリしかない。つまり、リモートは必ず一つ。 それに対してGitは一つのプロジェクトに複数のリポジトリが存在するので、リポジトリ間の通信が考えられ、リモートを複数持つことも可能。 →色々な組み合わせ方が考えられ、非常に柔軟なのがGitのリポジトリの特徴

Gitのオブジェクト

Gitは4つのオブジェクトを持っている。これらはイミュータブルとなっていて、一度作ったら破棄されるまで変更されることはない。 Gitのリポジトリは、主にこの4つのオブジェクト(blob/tree/commit/tag)のグラフ(積み重ね?組み合わせ?分かりやすい言い回し募集)で表される。 tagは少し特殊なので、この4つのうちのblob/tree/commitについて説明する。

blobオブジェクトとtreeオブジェクト

blobオブジェクトは、ファイルの中身を表す。blobオブジェクト自身にはファイル名は保持しない。

treeオブジェクトはディレクトリとそこに含まれる子ファイル、子ディレクトリを表す。自身のディレクトリ名はやはり保持しないが、子ファイルの名前、子ディレクトリの名前はこいつが保持する。 名前は違うが同じ内容のファイルを、Gitでは1つのblobオブジェクトとして扱うため、空間効率がいい。このような共有を行うため、オブジェクトはイミュータブルになっている。

また、もちろんリビジョンを超えてもオブジェクトは共有される。 そのため、最初のリビジョンではa/b/cという階層にあったdというファイルを、次のリビジョンでa/zという階層に移動したとしても、blobオブジェクトはそのままに、新たなtreeオブジェクトが作られるだけ。

commitオブジェクト

commitオブジェクトはコミットを表す。コミットした人、時間、コミットメッセージ、親コミット、ルートとなるtreeオブジェクトへの参照などを持つ。 親コミットは複数持つことができ、親コミットを複数持つコミットはマージコミット。

また、一番最初のコミットは親コミットを持たない。 一つのリポジトリに複数の親コミットを持たないcommitオブジェクトが存在することもありうるが、通常は一つだけ。

ハッシュ値

GitのすべてのオブジェクトはSHA-1ハッシュ値を持っており、これによってオブジェクトの等価性チェックを行う。 ファイルやディレクトリの中身をすべて比較するのではなく、ハッシュ値のみの比較でいいため非常に高速。

また、比較対象の少なくとも1つのハッシュ値は(リポジトリに格納されているため)計算が不要。 連番のリビジョン番号では、リポジトリ内での一意性しか保証できないのに対して、ハッシュ値は全世界中のリポジトリでの一意性を保証してくれる。

ファイル本体ではなく、まずはハッシュ値を送ることでファイル本体を通信する必要があるかどうか(通信先のリポジトリにすでに存在するobjectを送ろうとしていないか)を判定可能。

Mercirialのように、ハッシュ値も使いつつ、リポジトリローカルな連番を振るようなDVCSも存在する。

ブランチ

Gitでのブランチは、commitオブジェクトのハッシュ値を保持したファイルにすぎない(packされてーとかは無視)。 commitオブジェクトは親のコミットのハッシュ値を持っているため、commitするたびにブランチファイルの中身を新しいcommitオブジェクトのハッシュ値に書き換えれば、ブランチを表すことができる。 ブランチファイルの保持するハッシュ値からたどれるすべてのcommitオブジェクトの列が、そのブランチに属するコミット群ということ。 もちろん、あるcommitが複数のブランチに属するということもありうる。

SVNではブランチをディレクトリのコピーとして扱ううえ、差分の積み重ねとしてリポジトリを構成するため、ブランチに対する操作は非常に重い。 それに対してGitではブランチはただのポインタなので、作成、削除などを高速に行える。 更に、Gitは差分の積み重ねでリポジトリを構成せず、スナップショットとしてリポジトリを構成するため、ブランチの切り替えも高速。 ハッシュ値はここでも効いてくる。切り替え前のブランチと切り替え後のブランチで、共有している部分はスキップできる。この判断をハッシュ値で行える。。 もちろん、SVNでのブランチの切り替えはリモートとの通信が必要になるのに対して、Gitではローカルで完結するのも大きい。

TODO

後はブランチの種類(短期ブランチと長期ブランチとか)の話?と、歴史書き換えあたり?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment