いいかげんgitのことをちゃんと知りたいあなたへ（Gitリポジトリ編）



[ここ](https://qiita.com/atchy/items/6fad25597f48025f7b24)からの続きです

### GitHubでリポジトリ作ったときの例のアレ

![github-sandbox.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25351/1b252943-8627-5f18-c74e-e52d5412b2ec.png)

GitホスティングサービスといえばGitHubですよね。
GitHubにリポジトリを作ると、リポジトリの初期化を行うシェルが例示されますが、
いっつもコピペしてガッってやってるだけで、いまいち理解していません。
これを機に、それをひとつずつ追っていきたいと思います。
mac上でポチポチやってます。

---

### まずはGithub上に新しいリポジトリを作ってみましょう
GitHubさんにお世話になります。
アカウントがなければ作って、Repositories -> new から適当にリポジトリを作りましょう。
今回は```sandbox```というリポジトリを作りました。privateでもpublicでもどちらでもかまいません。
たったいま作られたリポジトリにアクセスしてみると、例のご指示が。
コメントはじぶんの脳内補完です。

```
### ファイルを作って
$ echo "# sandbox" >> README.md
### ローカルにGitリポジトリを作って
$ git init
### ファイルをステージングして
$ git add README.md
### ステージングした内容をコミットして
$ git commit -m "first commit"
### mainブランチを作ってそこに移動して
$ git branch -M main
### originというリモート追跡ブランチをURLを紐付けたうえで作成して
$ git remote add origin git@github.com:atchy/sandbox.git
### プッシュする
$ git push -u origin main
```

いっこずつ見ていきましょう

### 1. README.mdを作成する
適当なディレクトリを作成&移動してから、言われるがまま、したがいましょう。

```
$ mkdir ~/sandbox && cd ~/sandbox
$ echo "# sandbox" >> README.md
```

### 2. `git init`  する
パラメータを指定せずに`git init`すると、空のリポジトリを作成します。

```
$ git init
Initialized empty Git repository in /Users/atchy/sandbox/.git/
```
ローカルにGitリポジトリが作成されました！
作成したての構成はこうなっています。

```
$ ls -Al
total 8
drwxr-xr-x  9 atchy  staff  288  2  7 07:47 .git
-rw-r--r--  1 atchy  staff   10  2  7 07:42 README.md
```

`.git`ディレクトリができていますね！
つまり、**Gitリポジトリの実体は、`.git`ディレクトリである**ということが言えます。
ただこの時点では、**ローカルにGitリポジトリが作られただけ**で、GitHub上のリポジトリとは（まだ）何の関係もありません。

`.git`ディレクトリの中をのぞいてみましょう。

```
$ ls -Al .git 
total 24
-rw-r--r--   1 atchy  staff   23  2  7 07:42 HEAD
-rw-r--r--   1 atchy  staff  137  2  7 07:42 config
-rw-r--r--   1 atchy  staff   73  2  7 07:42 description
drwxr-xr-x  14 atchy  staff  448  2  7 07:42 hooks
drwxr-xr-x   3 atchy  staff   96  2  7 07:42 info
drwxr-xr-x   4 atchy  staff  128  2  7 07:42 objects
drwxr-xr-x   4 atchy  staff  128  2  7 07:42 refs
```

`.git/objects`にはGitオブジェクトが配置されています。
[いいかげんgitのことをちゃんと知りたいあなたへ（Gitオブジェクト編）](https://qiita.com/atchy/items/6fad25597f48025f7b24)をご参照ください。

`.git/config`ファイルにはリポジトリの情報が記録されています。
のぞいてみましょう。

```
$ cat .git/config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
```

`core`というセクションだけ書き込まているようです。


Gitリポジトリの重要な要素に`HEAD`があります。
[Pro Git](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%81%AE%E5%8F%82%E7%85%A7#r_the_head)によると、`HEAD`とは**現在作業中のブランチに対するシンボリック参照**とのこと。

`HEAD`はファイルなので、中身ををのぞいてみます。

 ```
$ cat .git/HEAD 
ref: refs/heads/master
```

`HEAD`、つまり**現在作業中のブランチ**は、`refs/heads/master`とのこと。
そ、そですか、じぶん`master`ブランチで作業中だったですか・・・
中身をのぞいてみましょう。

```
$ cat .git/refs/heads/master
cat: .git/refs/heads/master: No such file or directory
```

あり？そんなファイルないけど・・・

`git init`のマニュアルを見てみます。

```
$ man git-init
```

> This command creates an empty Git repository - basically a .git directory with subdirectories 
> for objects, refs/heads, refs/tags,and template files. 
> An initial HEAD file that references the HEAD of the master branch is also created.
> 
> このコマンドは、空のGitリポジトリを作成します。基本的には、objects, refs/heads, refs/tags、
> およびテンプレートファイルのサブディレクトリを持つ.gitディレクトリです。
> masterブランチのHEADをさす初期のHEADファイルも生成します。

ふむ。
`git init`することで、`.git/refs/heads/master`をさす`HEAD`ファイルが作られるようです。
`.git/refs`にはブランチ（を指すコミットオブジェクト）やタグを指す参照（ポインタ）が記述されたファイルが格納されていきます。
ただ、現時点ではGitオブジェクトひとつも作成していないので、当然何も登録されていない、ということです。

```
$ tree -a .git/refs 
.git/refs
├── heads
└── tags

2 directories, 0 files
```

たしかに何もありませんね。

### 2. `git add`してみる
最初似作成した`README.md`をステージングします。

```
$ git add README.md
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   README.md
```

ステージングされてますね。
`.git/refs`まわりに何か変化はあったでしょうか？

```
$ tree -a .git/refs 
.git/refs
├── heads
└── tags

2 directories, 0 files
```

変化なし、と。
次いきます。

### 3. `git commit`してみる
```
$ git commit -m "first commit"
[master (root-commit) 1c25cf6] first commit
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
```
commitできました。
`.git/refs`まわりを確認してみます。

```
$ tree -a .git/refs           
.git/refs
├── heads
│   └── master
└── tags

2 directories, 1 file
```

お！
`.git/refs/heads/master`ができてます！

つまり、

1. `git commit`することによりコミットオブジェクトが作られた
1. `git init`したときに`HEAD`、つまり作業中のブランチは`master`ブランチを指すようになっていた
1. 作業中の`master`ブランチでコミットしたから、`.git/refs/heads/master`が作られた

中身は

```
$ cat .git/refs/heads/master 
34f185d743f99494056929d23e4101d0ec9ba885

$ git cat-file -p 34f185d743f99494056929d23e4101d0ec9ba885
tree 7ea9d65bc6c0b96b08b44bebce84563812dabac1
author atchy <atchy@example.com> 1612655598 +0900
committer atchy <atchy@example.com> 1612655598 +0900

first commit
```
たしかにさっきのコミットにより作成されたコミットオブジェクトを指しているようです。

### 4. `git branch`してみる

引き続き、指示にしたがいます。

```
$ git branch -M main
```

何も起きてないようにも見えますが・・・

```
$ tree -a .git/refs 
.git/refs
├── heads
│   └── main
└── tags

2 directories, 1 file
```

さっきまでは`master`という名前だったのが`main`になっています。

マニュアルによると、

```
man git-branch
```

> With a -m or -M option, <oldbranch> will be renamed to <newbranch>. 
> If <oldbranch> had a corresponding reflog, it is renamed tomatch <newbranch>, 
> and a reflog entry is created to remember the branch renaming. 
> If <newbranch> exists, -M must be used to force the rename to happen.

どうやら、

* 今のブランチ（`HEAD`が指すブランチ、すなわち`master`ブランチ）のブランチ名を変更して、
* `main`というブランチがすでにあればそれを上書きする

という挙動のようです。
つまり、`master`が`main`という名前に変更された、と。

中身をのぞいてみましょう。

```
$ cat .git/refs/heads/main 
34f185d743f99494056929d23e4101d0ec9ba885
```

さっきとは同じですね。
つまり、`master`という名前が`main`に変わっただけ、ということですね。
今後、GitHubさんとしては、[デフォルトブランチ名を`main`とするようです](https://www.publickey1.jp/blog/20/githubmainmastermain.html) 

### 5. `git remote add`してみる

ここまではローカルリポジトリをコネコネしてるだけでしたが、
いよいよリモートと何やら何かをするような雰囲気です。
だって`git remote`とか言うてますから！

その前に現状を確認しておきましょう。


```
git remote show
```

反応ありません。

`.git/config`の内容は、

```
$ cat .git/config         
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
```

`git init`後と変わりないですね。
では、

```
$ git remote add origin git@github.com:atchy/sandbox.git
```

しーん。
いったいなにが起こったのでしょうか・・・

```
$ git remote show
origin
```

お！
地味ながら、変化がありましたね。
`origin`というリモートが追加されたぽいです

`.git/config`ファイルを見てみます

```
$ cat .git/config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = git@github.com:atchy/sandbox.git
	fetch = +refs/heads/*:refs/remotes/origin/*
```

`remote`セクションが追加されてますね！
`git remote add`はリモート的な何かを追加するコマンドのようです。
順をおって見ていきましょう。

まずは`git remote`コマンドを見直してみましょう。

```
$ git remote add origin git@github.com:atchy/sandbox.git
```

という状況から類推するに、

* `origin`という名前の**リモート**が追加された
* `url`プロパティは`git@github.com:atchy/sandbox.git`
* `fetch`プロパティは`+refs/heads/*:refs/remotes/origin/*`

`url`プロパティはなんとなく分かりますよね。
たぶん、`git`という`github.com`サーバの`atchy/sandbbox`ディレクトリを指してるかと思います。
`scp`コマンドと似てますね。
で、問題は`fetch`プロパティの方ですが・・・
このあたりを考え出すと、もうひとつ記事が書けそうなので、別の機会にします。
とりあえず、**リモートリポジトリ**が登録された、とだけ考えましょう。

リモートリポジトリが追加された、ということは、もうそこへ`push`することができるんでしょうか？

```
$ git push
fatal: The current branch main has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin main
```

怒られましたね・・・

> 現在のブランチにはupstreamブランチがありません。
> 現在のブランチをプッシュして、upstreamをリモートとして設定するには、
> git push --set-upstream origin main
> を実行してね

と。
むぅぅ、`upstream`とはなんぞ？
このあたりを考え出すと（ｒｙ
上流ブランチについてはまた次の機会に・・・

`git remote add`によって、リモートとしてGitHub上のリポジトリが登録されましたが、
現在のブランチ（`master`ブランチ）を、リモート上のどのブランチにプッシュするか、
が分からない（設定されていない）ため、現状ではプッシュが失敗します。
まあ、言われてみればそうですよね・・・
その問題を解決するために最後のステップに進みます。

### 6. `git push`してみる
さあいよいよプッシュします！

```
$ git push -u origin main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 221 bytes | 221.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:atchy/sandbox.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
```

成功したようですね。
GitHub上も表示に変化がありました（ファイルいっこしかないですがｗ）

![after_push.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25351/f6bbb3ac-2a2b-f26d-81d0-4b2180baba5a.png)

コマンドの意味をみてみましょう。
まずはマニュアルを参照してみます。

```
$ man git-push
```
`-u`オプションは

> -u, --set-upstream
> For every branch that is up to date or successfully pushed, add upstream (tracking) reference

さっき怒られたときに付いてたオプションと同じですね。
その意味は
`upstream (tracking) reference`を追加する、と。
さっきは`upstream`ブランチが設定されていなかったので、プッシュに失敗しましたが、
今は設定されているのでしょうか？

`.git/config`をのぞくと、

```
$ cat .git/config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = git@github-atchy:atchy/sandbox.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main
```

`branch`セクションが追加されています。

さらに、`.git/refs`まわりを見てみると、

```
$ tree -a .git/refs 
.git/refs
├── heads
│   └── main
├── remotes
│   └── origin
│       └── main
└── tags

4 directories, 2 files
```

`.git/refs/remotes/origin/main`ができてますね。

まとめると、

* `main`ブランチのリモートは`origin`とする
* リモートの参照として`.git/refs/origin/main`を配置する

と。

`merge`プロパティについては、いまはそっとしておきます・・・

### おわりに

知れば知るほどgitの奥深さと、じぶんがぜっんぜんgitのことを
理解していなかったのかが、身に沁みてわかります。
これからも精進していきます。

### 次回予告

とはいえ、リモートの存在意義とか、上流ブランチとか、リモート追跡ブランチとか、
腑に落ちない点がまだまだありますので、そのあたりを紐解いていきたいでっす