Gitを使ってPull Requestを投げるまで

# はじめに

チームメンバにGitを普及させるために、gitを使ってGitHub/GitLab/Bitbucket等のGitホスティングサービスにPull Requestを投げるまでの一連の流れを練習するための手順をまとめてみました。

Gitのコマンド単位で流れを覚えてもらうために、CUIでの手順になっています。

Windowsであれば[git for windows](https://git-for-windows.github.io/)に内包されているgit bashかgitが使えるシェル環境、macであればターミナル等のgitが動くシェル環境で実行してください。

# 手順

## 0. レポジトリをfolkする（任意）

以下の説明では https://github.com/takami228/git-sandbox のレポジトリを使います。

実際に試したい方はfolkして使ってみてください。

## 1. レポジトリをクローンする

作業用のディレクトリに移動し、以下のコマンドでgitのプロジェクトをクローンします。
id、pass or api token、urlは各環境のものに置き換えてください。
urlは各gitのホスティングサービスのレポジトリのトップページを見れば載っています（clone or downloadなどのボタンから参照できます）。

コラボレータやレポジトリのグループに所属していない場合は、事前に自分のアカウントに対象のレポジトリをforkしておく必要があります。forkはWeb上で実行できます。

```sh
# 作業用ディレクトリに移動する（クローンした資材を置く任意の場所）
cd /path/to/git

# レポジトリをクローンする
git clone https://<id>@<url>

# パスワードが聞かれるので入力する

# GitHubの場合
git clone https://<id>@github.com/<id>/git-sandbox.git
```

パスワード or APIトークンを使う場合は以下のやり方をすれば省略できます。

- [GitHubでユーザ名・パスワード省略（push, pull）](https://qiita.com/azusanakano/items/8dc1d7e384b00239d4d9)


忘れずにgit configを設定しておきましょう。

```
git config user.name <id>
git config user.email <mail>
```

`--global`オプションをつけるとそのマシン全体に適用されます。何もつけないか、`--local`をつけるとそのレポジトリのみに適用されます。

## 2. 作業用ブランチを作成する

以下のコマンドでクローンしたプロジェクトのディレクトリに移動します。lsでファイルの内容が確認できます。

```sh
# プロジェクトのディレクトリに移動する
cd git-sandbox

# ファイルの一覧を確認する
ls
```
Windowsのgit bashのプロンプトの右端のカッコをみると``(master)``とあり、これが現在いるブランチを示しています。

Macのターミナルのデフォルトだと何も表示されません。以下のコマンドでカレントブランチが確認できます。

```sh
# カレントブランチを確認する
git branch --contains=HEAD
```

次に作業用のブランチを以下のコマンドで作成します。

```sh
# ブランチの作成・移動をする
git checkout -b feature/{branchname}
```

ブランチの命名規則はプロジェクトによりますが、本手順では``feature/{branchname}``の形をとります。
``{branchname}``にはチケット番号や開発機能名などをつけることが多いです。

コマンドを実行するとブランチの作成と移動が同時に実行され、``master``ブランチから``feature/{branchname}``ブランチに移動します。

ブランチの作成と移動は別々にやってもできます

```sh
# ブランチを作る
git branch feature/{branchname}

# ブランチを移動する
git checkout feature/{branchname}
```

## 3. ファイルの編集・追加を行う

featureブランチで追加したい機能のプログラムを作成したり、既存のファイルを修正します。

本チュートリアルでは以下の二つを実施してみましょう。

1. sandbox.mdに加筆する
2. ``/contributors`` フォルダに``<username>.md``を追加する

適当なmarkdownエディタでファイルの編集、新規作成を行ってください。変更の内容はなんでもよいです。

## 4. ファイルの編集・追加をローカルレポジトリに反映させる

``git add``コマンドを使って変更対象のファイルをローカルレポジトリに反映させます。

```sh
# 変更対象のファイルをすべてaddする
git add sandbox.md
git add contributors/<username>.md
```

もし間違えたファイルをaddしてしまった場合は`git reset`でaddする前の状態に戻せます。

```sh
# addを取り消す
git reset
```

特定のファイルを指定してaddを取り消すこともできます。

```sh
# 特定のファイルのaddを取り消す
git reset /path/to/xxx.yyy
```

resetは`--hard`、`--mixed`、`--soft`の3つのオプションがあり、オプションを指定しないと`--mixed`が指定されます。
詳細は [git reset (--hard/--soft)ワーキングツリー、インデックス、HEADを使いこなす方法](https://qiita.com/shuntaro_tamura/items/db1aef9cf9d78db50ffe) に記載されています。

git addはフォルダ単位でも実行できます。
以下のコマンドだとカレントフォルダ配下のすべての変更をaddすることができますが、意図しない変更をaddしてしまう可能性があるのでやめましょう。

```sh
# 楽なコマンドですが事故が起きるのでやめましょう
git add .
```

次に変更のコミットを作成します。

```sh
# コミットする
git commit
# viが立ち上がるので、「i」を押しインサートモードにして、コメントを記入後に「エスケープ」+「:wq」でEnterを押す
```

コミットコメントの書き方ですが、以下のルールで書くとよいです。

- 一行目に変更のサマリを書く
- 一行開けて三行目から変更の詳細を書く

コミットコメントは変更内容ではなく``「なぜ」この変更を加えたのか``を書くとよいです。
またトレーサビリティのためにチケット番号などを書くプロジェクトもあるかもしれません。

- [Gitのコミットメッセージの書き方](https://postd.cc/how-to-write-a-git-commit-message/)


コミットは-mオプションを使うとviを経由せずにコメントを記入できます。

```sh
# git commit -m "ここにコメントを書く"でもよい
git commit -m "xxxxx"
```

もし間違ってcommitした場合はgit commit --amendコマンドで直前のコミットの修正ができます。

```sh
# 直前のコミットを修正する
git commit --amend
```

git statusコマンドで現在のレポジトリの状態が確認できます。

```sh
# レポジトリの状態を確認する
git status
```

## 5.リモートの変更を取り込む

自分の変更をmasterに取り込む前に、他人の変更が入る場合があるためリモートの変更を取り込む必要があります。

取り込み方はrebaseとmergeの2種類があり、思想やプロジェクトの方針によって変わります。
今回はrebaseを採用します。rebaseだとmergeコミットができないのでコミットフロー図がきれいになるという利点があります。

```sh
# リモートの変更をローカルに取り込む
git fetch

# リモートのmasterの変更をrebaseでローカルのfeatureブランチに取り込む
# ※git rebase masterではローカルのmasterで更新されてしまうので必ずoriginを指定すること
git rebase origin/master
```

- [git mergeとrebaseの違い](https://qiita.com/Delayman0920/items/4e5a2be5b9ed8fd281b5)


rebaseの実行時に、競合が発生する場合があります。

もし競合が発生した場合は、競合が発生したファイルを確認して競合の解消を行ってください。

```sh
# 競合が発生したファイルの編集結果をaddする。xxxはファイル名
git add xxx
 
# rebaseを実行する
git rebase --continue
 
# rebaseが完了するまでgit add / git rebase --continueを繰り返す
```

## 6. featureブランチをリモートレポジトリにPushする

以下のコマンドでfeatureブランチをリモートレポジトリにPushします。

```sh
# gitサーバへに変更をpushする
git push origin feature/{branchname}
```

## 7. Pull Requestを出す

Pushが成功したら、Gitのホスティングサービスのサイトの画面に行ってPull Requestを作成してください。

もし、Pull Request画面でコンフリクトが起きていることが分かったら、最新のマスターを取り込む必要があります。ホスティングサービスによってはブラウザ上でもできるようです。

ローカルで実行する場合は再度fetchしてorigin/masterの内容をrebaseします。

```sh

# 最新のマスターを取得
git fetch

# 現在のブランチにマスターの変更を取り込む
git rebase origin/master

# 競合が起きた場合は解消する
git add sandbox.md
git rebase --continue

# 競合が解消されたら再度pushする
git push origin feature/xxx
```

rebaseではなくてmasterブランチに対するmergeコミットを作ってもよいです。

```sh
# 最新のマスターを取得
git fetch

# 現在のブランチにマスターの変更を取り込む
git merge origin/master

# 競合を解消する
git add sandbox.md
git commit

# 再度pushする
git push origin feature/xxx
```

## 8. Pull Requestをマージする

レビュワーはPull Requestの内容を確認し、もしおかしなところがあったら指摘をしてあげてください。

レビューで受けた指摘を修正したい場合は、手順の3,4,5,6を再度実行すればよいです。

ホスティングサービス上でPull Request対象のブランチが更新された場合、リモートブランチの変更がローカルに反映されていないためPushに失敗する場合があります。

このときはrebaseでリモートブランチの変更を取り込んでください。

```sh
# リモートの変更をローカルに取り込む
git fetch

# リモートの変更をローカルのfeatureブランチに取り込む
git rebase origin/feature/{branchname}
```

レビュワーはPull Requestに問題がなければその旨のコメントを残し、マージを選択します。

## 9. Pull Requestのマージ結果をローカルに反映させる。

以下のコマンドでリモートのmasterの変更を取り込みます。

```sh 
# matserブランチに移動する
git checkout master

# リモートの変更をローカルに取り込む
git pull
```

git pullはgit fetch/git rebaseでもよいです。

```sh 
# リモートの変更をローカルに取り込む
git fetch
git rebase origin/master
```

以上でチュートリアルは終了です。

# 補足

### 改行コードで消耗しないために(Windows用)

WindowsとMacで改行コードか異なる場合があるので設定しておくとあとで消耗しなくて済みます。

- [windows環境の git で改行コードの自動変換に注意](https://qiita.com/yokoh9/items/1ec8099696ade0c1f36e)
- [git での改行コード](https://qiita.com/shuhei/items/2da839de8803cb335f86)

### パスワードに「@」が入っている場合

パスワードに「@」が入っている場合はそのままだとURLがおかしくなってcloneに失敗するため、URLエンコード文字である「%40」に置き換える必要があります。

- [使い始める Git](https://qiita.com/icoxfog417/items/617094c6f9018149f41f)

### 407エラーが発生した場合

プロキシの設定が必要です。

- [gitでプロキシを設定](https://qiita.com/hidetzu/items/c2db95613ba594a2ef25)