【Git】リモートからの取得とリモートへの反映で行っていること(fetch,pull,push)

fetchはリモートの「情報」を取ってくるだけ、
pullは情報を取ってきて、さらに実データをローカルに反映する。
という理解でした。

でも、fetchで何やら情報を取得している様子なのに、その後行ったpullでは何も情報が取得されない。
ということが有ったので、fetchって一体何をしているの？ということで調べてみた。

# まずはブランチについて
## ブランチの種類

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/48eada4e-da51-5bf9-56a3-99b8325c7ad2.png)

### ローカルブランチ
ローカルで開発するときにcheckoutで開いてコミットしていくお馴染みのもの。

```
master
develop
feature/topic
```

ローカルで作成したブランチは、pushすることでリモート追跡ブランチにも反映される。

### リモート追跡ブランチ
ローカルに保持している、クローン元リポジトリのブランチを取り込んだもの。
反映はfetchやpullコマンドを実行したタイミングであり自動取得はしないので、
いつの間にかリモートリポジトリ側と不一致になっていたということも有る。

```
git branch -a
```

とすると、

```
remotes/origin/master
remotes/origin/develop
remotes/origin/feature/topic
```

と表示されるもので、
コマンドのパラメータには、

```
origin/master
origin/develop
origin/feature/topic
```

と書く。

ローカルブランチとしてcheckoutするには、
origin/を外して、

```
git checkout feature/topic
```

とする。この時、ローカルブランチとリモート追跡ブランチのorigin/feature/topicが紐づけられる。

なお、リモート追跡ブランチは、あくまでも「リモートを"追跡する"ブランチ」なので、これをローカルでcheckoutしてコミットすることは出来ない。

```
git checkout origin/feature/topic
```

は不可。



### リモートのブランチ
クローン元リポジトリにおけるローカルブランチ。
間接的にリモート追跡ブランチを通じてしか知り得ない。


# リモートから取得する
手順としては、
1. リモートの情報を取得する
2. ローカルブランチにマージする
を行います。

この時、fetch, pull, merge, rebaseなどのコマンドを使います。

- fetchコマンドでリモートの情報を取得する

fetchコマンドは、前回取得して以降に更新された、全ブランチの全コミットデータを取ってきます。

この時、

「リモート追跡ブランチ」のみに反映します。ローカルブランチは変更しません。
コミットは、ローカルとリモート両方を共存する形で保持します。ローカルのコミットは変更しません。

なので、fetchではローカルのブランチとコミットには影響しない、ということになります。

- pullコマンド

fetch + mergeの動作を行います。
fetchで全ブランチの全コミットデータを取得してから、checkout中のカレントブランチに、対応するリモート追跡ブランチをマージします。


# パターン別のリモートからの取得方法
具体的にどういう動きをしているかパターン別に見てみましょう。

必要に応じて、
[[Git] 脱初級！誤コミットはもう怖くない](http://qiita.com/forest1/items/fcd2a2961a3faac27041)
の「ブランチは枝では無い」辺りも参考にして下さい。

## ローカルに無いリモートブランチ
リモートで作成されたブランチを初めて取得してくる時の動きです。

### fetchで情報を取得する

- リモートブランチ

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/e09702bc-c71b-19c2-d15b-243954658c7c.png)
リモートにdevelopブランチが新規に作成され、コミットA,B,Cが存在する状態。

```
git fetch
```

- fetch後のローカル環境

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/106f1e16-7969-c61f-f455-7499190e7df2.png)

のようにコミットA,B,Cとブランチ「origin/develop」がローカルに作成されます。

ただし、作成されるのは「リモート追跡ブランチ」(origin/develop)のみで、ローカルブランチは作成されません。

- ローカルブランチの作成

ブランチ名から origin/ を外した、

```
git checkout develop
```

でローカルブランチが作成されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/120192/47085f02-dada-3438-4c35-f1e6ef6f217e.png)

参考) リモート追跡ブランチに無いブランチをローカルで作成する場合は、branchコマンドを使う必要が有ります。

　
## ローカルでコミット無し、リモートでコミットされていた場合

### fetchでリモートの情報を取得する

- リモートブランチ

developブランチに新しくCがコミットされている状態。
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/5acbb724-fe8f-571c-28ba-63f695fb1653.png)

- fetch前のローカルブランチ

リモート追跡ブランチ origin/develop は、前回fetchで取得した時の状態。
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/9eca7dc0-2298-22ae-3401-3f34c7786484.png)
ここで、fetchを実行すると。

```
git fetch
```

- fetch後のローカルブランチ

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/c94d392e-cabd-216b-5262-a49e682fa827.png)

のように、ローカルブランチにリモートのコミットCが継ぎ足されて、
リモート追跡ブランチorigin/developが更新されます。
ローカルブランチdevelopは変更されません。

注意点として、この状態からローカルブランチdevelopにコミットしてしまうと、
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/1a23cd67-5a23-903b-fdf4-b77d44453332.png)
のように枝分かれしてしまいます。

対処法としては、fetchしたら速やかにマージするというところでしょうか。
そういう意味では、fetchとmergeがセットになっているpullを使うのがいいのかもしれません。

### ローカルブランチにマージする 
fetchにより以下図の状態になっています。

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/c0d67e58-a03d-76b4-44a4-f2c5282a17e5.png)

リモートのコミットをそのまま受け入れるだけでいいので、いわゆるFast-Forwardマージのケースです。
動作としては、developブランチがorigin/developのところに移動するだけです。

- merge コマンド

```
git checkout develop
git merge origin/develop
```

- pull コマンド

fetch + mergeと同じ動作です。

```
git checkout develop
git pull
```

pullの引数無しの場合、カレントブランチのみが対象となります。

- rebase コマンド

```
git checkout develop
git rebase origin/develop
```

これらのコマンドはどれでも、以下状態になります。
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/3fc099ea-6645-b97d-7882-fbbe4ef15967.png)

(オプション指定によっては変わってきますが。とりあえず標準動作として）

## ローカルでコミット有り、リモートでコミット無しの場合

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/826400fd-5803-d705-c295-5026cf46d02b.png)

リモートに更新情報が無いのでfetchやpullを行っても何も変化は有りません。
むしろ、Dコミットをリモートに反映させる必要が有ります。


## ローカルとリモート両方でコミット有りの場合

### fetchでリモートの情報を取得する

- リモートブランチ

developブランチに新しくCがコミットされている状態
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/ca7aaa06-456f-1b80-477a-b0c5fc6231b3.png)

- fetch前のローカルブランチ

前回fetchをしたコミットBから新しくDがコミットされている状態
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/a3c3cf73-9262-756c-3611-cc56022eeb45.png)

```
git fetch
```

以下図のように、リモートのコミットCを別の枝として取り込むので、
ローカルブランチには影響が出ません。

- fetch後のローカルブランチ

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/adc3118b-795f-bfce-8e96-375d90ccc85b.png)


### ローカルブランチにマージする 

developにorigin/developを取り込みます。
ある程度はGitが自動マージしてくれるのですが、同じファイルの同じ場所を編集していた場合は、
手で編集する必要が有ります。

- merge コマンド

```
git checkout develop
git merge origin/develop
```

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/4e6627bf-7990-e530-1de3-7b2849f91949.png)

ローカルにマージコミットEが新しく作られdevelopブランチが更新されます。
リモートより先に進むので、リモートに反映させる(pushコマンド)必要が有ります。

- pull コマンド

```
git checkout develop
git pull
```

fetch + mergeコマンドと動作、結果は同じです。

- rebase コマンド

origin/developが指すコミットCの後ろにdevelopブランチのコミットDを移動します。

```
git checkout develop
git rebase origin/develop
```

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/44ea159b-a012-c2b1-b8fc-6f425db07d07.png)


# リモートへの反映

originよりローカルブランチの方がコミットが進んでいるケースです。

- ローカルブランチ

前回fetchしたコミットBから、ローカルに新しくCがコミットされた状態
![image](https://qiita-image-store.s3.amazonaws.com/0/120192/fc009183-9a59-c7e2-473c-1cf678cb1588.png)

pushでカレントブランチのコミットをリモートに反映します。

```
git checkout develop
git push
```

pushは、checkoutで開いたブランチのみをリモートに反映します。
（注意：gitのversion 1.x系は、リモートと紐づいている全てのブランチをリモートに反映するのがデフォルト動作なのでご注意下さい。必要に応じて、ブランチ名を指定するかオプションでデフォルト動作を変える必要が有ります）

これにより、リモートリポジトリ及び自リポジトリ内のリモート追跡ブランチorigin/developに、
ローカルブランチdevelopが反映されます。

![image](https://qiita-image-store.s3.amazonaws.com/0/120192/67ae5819-aa51-c857-434e-1871af41e1cc.png)

あと、ローカルでブランチを新規作成してコミットしたものをリモートに反映する場合も同じです。


# まとめ
- fetchは全ブランチの全コミットの情報を取ってきている
- fetchはローカルコミットとローカルブランチを変更しない
- pullはfetch+カレントブランチのみマージする動作
- pushもカレントブランチのみ対象