git基礎（理論）


# git基礎（理論）
環境:Windows10,Git2.28.0,(Backlog git)
この記事では、gitのデータモデルを見ていきます。
実際に動かしたいという方は、[実践編](https://qiita.com/drafts/4dc0eb8bb65e0fd2dc1a/)をみてください。
## §1 gitとは
gitは、分散型バージョンコントロールシステムです。
例えば、あるプロジェクトを複数人で取り組むときに、"誰が","いつ","どのような"変更をコードにしたのかを管理ます。各個人が、ローカルで作成した部品を統合したり、また、ある時点でエラーが発生した場合、それ以前の状態に簡単に戻すことができます。

gitを使うにはgitの理解しづらいインターフェースを覚えなければなりませんが、その根底にあるデザインとアイデアは非常に美しいです。ここでは、gitのデータモデルから始めて、コマンドラインインターフェースについて実際に触って学びます。gitのデータモデルを理解すると、コマンドがどのようにデータを操作しているかという点をよりよく理解できるようになっています。

gitは、ローカル環境で変更履歴を保存することができるのでリモートサーバーに接続していなくても大丈夫です。
GitHub(ここでは、Backlogを使います)とは、リモートサーバーの役割を果たしていて、世界中の人々とgitの仕組みを利用してコラボレーションしてプロジェクトに取り組めるようなプラットフォームです。

それでは、gitを始めてみましょう！
## §2 gitを始める
Windows10でGitを始めてみましょう！
まずは、[Git](https://gitforwindows.org/)から、Windowsバージョンをダウンロードしましょう。

## §3 データモデル
バージョン管理をする方法は実は、git以外にもたくさん存在します。
その中でも、gitはとてもよく機能するデータモデルを採用していて、変更履歴やサポートブランチ等がチーム開発を潤滑に行えるようにしています。(*ここでは、ディレクトリとフォルダは同じ意味と捉えて下さい)

gitの用語として、

```
"tree":フォルダ(ディレクトリ)
"blob":ファイル
"commit":コミット
"snapshot":スナップショット
```
を使いながらgitのデータ構造を見ていきます。

### 1.スナップショット
gitはファイルとフォルダの集合の変更履歴を、一連のスナップショットとして親ディレクトリに作ります。gitの用語では、ファイルは"blob"と呼ばれ、ディレクトリは"tree"と呼ばれます。ディレクトリは、"blob"や"tree"のを持つことができます。（親ディレクトリの中に存在する子ディレクトリも含まれます）スナップショットは、親ディレクトリで、変更履歴を常に保管します。例えば、下のような"tree"があったとします。

```
\<root(親ツリー(ディレクトリ))>(tree)
|
+- foo(tree)
|  |
|  +-bar.txt(blob, contents="gitは楽しい(マイナビ))"
|
+-baz.txt(blob, contents="gitへようこそ(マイナビ))"
```
　親ツリーは、2つの要素を持ちます。tree:"foo"(1つの要素blob:"bar.txt"をそれ自身に含む)と、blob:"baz.txt"です。
### 2.変更履歴
gitのバージョン管理は、どのようにスナップショットと関係しているのでしょうか？
一つの簡単なモデルは、変更履歴を時間系列で保管することでしょう。

```
〇<----〇<----〇 (〇は日付時点でのファイル状態を表す,矢印は親を表す)
```
しかし、gitはこのような簡単なモデルでバージョン管理をしていません。
gitは、変更履歴をDAG(Directed Acyclic Graph,有向非巡回グラフ)として持ちます。すべてのスナップショットは点(Edge)の集合として、その親(変更前のスナップショット)を必ず持っています。必ずしも、親はユニークとは限らず、複数の親集合を持つような子も存在します(マージされたとき等)

```
〇<----〇<----〇(新機能追加)<------〇(バグ修正と新機能追加をマージ)
　　　　↑　　　　　　　　　　　　　　|
　　　　 ------〇(バグ修正)<-------|
```

### 3.データモデル(疑似コード)
gitのデータモデルを疑似コード（雰囲気だけ）で書くとこんな感じです。

```
// ファイルはbyteの集合です
type blob = array<byte>

//ディレクトリは、ファイルとディレクトリの辞書集合です  
type tree = map<string, tree | blob>

// コミットは、親集合（コミットの）、メタデータ（作成者,メッセージ等）、親ツリーを持ちます。
type commit = struct {
    parent: array<commit>
    author: string
    message: string
    snapshot: tree
}
```

### 4.オブジェクト、アドレス
gitの"オブジェクト"は、"blob"(ファイル),"tree"(フォルダ),"commit"(コミット↑)です.

```
type object = blob | tree | commit
```
gitのデータは、[SHA-1 hash](https://en.wikipedia.org/wiki/SHA-1)（Secure Hash Algorithmシリーズの暗号学的ハッシュ関数）というハッシュ関数で暗号化されたアドレスで保管されています。

```
objects = map<string, object>

def store(object):
    id = sha1(object)
    objects[id] = object

def load(id):
    return objects[id]
```
blob,tree,commitは上のように統一されています。
実際にコミットをしたときにすべてのファイルを、そのまま保管しているわけではなく、ハッシュ関数によってアドレス(40文字の16進数)を参照しています。例えば、こんな感じです(環境によって違います)

```
100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85    baz.txt
040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87    foo
```
ツリーは、その要素のポインタを持っています。

```
\<root(親ツリー(ディレクトリ))>(tree)
|
+- foo(tree)(ポインタを親ツリー(root)が持つ)
|  |
|  +-bar.txt(blob, contents="gitは楽しい(マイナビ)")(ポインタをツリー(foo)が持つ)
|
+-baz.txt(blob, contents="gitへようこそ(マイナビ)")(ポインタを親ツリー(root)が持つ)
```
例えば、

```
git cat-file -p 4448adbf7ecd394f42ae135bbeed9676e894af85(baz.txtのポインタ)
```
とすると "gitへようこそ(マイナビ)"が出力されます。

### 5.参照
すべてのスナップショットが(ハッシュ関数で暗号化された)アドレス(40文字の16進数)で識別されますが、人間にとってこれを読むのは困難です。gitは、この問題に"参照"で対処しています。参照とは、コミットのポインタです。git、人間にとって識別しやすい文字列で、スナップショットを参照できるようにしています。オブジェクト自体("blob","tree","commit")と違って、参照は変更可能です。例えば、ポインタ"master"は通常最新のコミットを参照しています。

```
references = map<string, string>

def update_reference(name, id):
    references[name] = id

def read_reference(name):
    return references[name]

def load_reference(name_or_id):
    if name_or_id in references:
        return load(references[name_or_id])
    else:
        return load(name_or_id)
```
上のような疑似コードで、gitは人間にとって読みやすい名前を使って参照しています。
よく、今どのリビジョン（コミットの状態の単位）にいるのかを確認したいことがありますが、これはしばしば,"HEAD"で参照されます。
### 6.リポジトリ
データモデル最後です
リポジトリとは、大雑把にオブジェクトと参照の集合です。
gitが保管しているのは、オブジェクトと参照の集合です。gitコマンドが、マッピングしているのはコミットDAG(ファイルを追加したり、参照を更新したり)の操作です。

これから、gitのコマンドを打つときは、そのコマンドが一体どんな操作をしているのかを考えましょう。
逆に、何かしらの操作をコミットDAGに行うときには、どのようなgitコマンドを打てばいいか推測しましょう。
例えば、"未コミットの変化を削除して、参照をコミットポイント5d83f9eにもどしたい"とき、

```
git checkout master; git reset --hard 5d83f9e
```
というコマンドになります。
##
git基礎です。初心者向けから、実際どのように動いているのかを解説します。

https://www.youtube.com/watch?reload=9&v=2sjqTHE0zok&ab_channel=MissingSemester
*参考
https://www.youtube.com/watch?reload=9&v=2sjqTHE0zok&ab_channel=MissingSemester