SVN経験者向けGit再入門

# はじめに
　職場でSVNを使ったことあるがGitは未経験という人が多い。そういう人たちは、最低限のGitのコマンドの使い方を覚えるのは早い。ただ大抵の人は、GitをSVNと同じノリで使用しており、SVNに対するGitの良さを活かしきれていないことが多い(そしてそこから進歩しないことも多い)。たとえば、日付ごとにGitのリポジトリフォルダを作成する、複数の機能開発をしていて機能ごとにGitリポジトリフォルダを作成する、といった使い方がしばしば目につく。
　本記事では、SVNのノリで未だにGitを使用している人向けに、Gitの基本、良さ、ブランチ戦略、コマンドを説明する。

# Gitの基本
　Gitでは、まずリモートリポジトリの複製を自分のローカル環境に作成する。その後は、ローカルリポジトリ上で作業をする。なので、コミットをしても修正はローカルリポジトリのみに入り、リモートリポジトリには入らない。修正を公開するときは、コミット後にプッシュが必要となる。ブランチを作成した時もコミットと同様に、プッシュをしなければリモート上にはブランチは作成されない。
　このローカルの作業がリモートに即反映されないという点がSVNとの最大の違いであり、Gitを使いこなすためのポイントである。

![Git構成.png](https://qiita-image-store.s3.amazonaws.com/0/196890/9509e4dc-c4b7-2f7b-5e52-5d753e4397d4.png)![SVN構成.png](https://qiita-image-store.s3.amazonaws.com/0/196890/81dd6d78-1e8f-6e78-6338-922dd61a4cae.png)

## Gitの基本用語
- プッシュ(push)
    - ローカルリポジトリの内容をリモートリポジトリに反映させること。
    - 後述のコミットと組み合わせてSVNのcommitに相当
- プル(pull)
    - リモートリポジトリの内容をローカルリポジトリに取り入れること
    - SVNのupdateに相当
- コミット(commit)
    - 修正内容をローカルリポジトリに保存すること。リモートリポジトリには修正は入らない
    - 前述のプッシュと組み合わせてSVNのcommitに相当
- masterブランチ
    - 開発本線。単純な運用のプロジェクトならリリース用ブランチ
    - SVNでいえばtrunkに相当

# Gitの良さ
　SVN経験者としては、ローカルのコミットやブランチを反映するためにプッシュするのが面倒に感じるだろう。ただ、Gitの良さは、プッシュするまで自分の作業が公開されない点に尽きる。以下、Gitの良さを2つ挙げる。

## 細かい単位でコミットできる
　Gitでは細かい単位でのコミットができる。プッシュしなければ、コミットがリモートに反映されないからだ。したがって、コミットの単位が細かすぎてコミットログが見づらくなっても、他の人に迷惑をかけることはない。細かい単位のコミットが役に立つケースを示す。

- 作業の手戻りがしやすい
- 修正の意図を明確にできる
- 上手くコミットを分ければ、レビューがしやすくなる(レビュー1回あたりのコード量を抑えられるため)
- 日付ごとにリポジトリフォルダを作成することから卒業できる

　最後に注意点。最終的にはローカルの作業をプッシュすることになるが、遅くともその直前にはコミットの整理を検討すべきだ。細かすぎる単位のコミットをリモートリポジトリにそのまま入れてしまうと、上述したようにコミットログが見づらくなるからだ。

## ブランチを気軽に作成できる
　Gitでは、短期の作業用のブランチを気軽に作成できる。具体的には、1つの不具合ごとに修正ブランチを作成するのもOKだ。ブランチもプッシュしなければ、リモートに反映されないからだ。ブランチを積極的に作成することのメリットは以下。

- 複数の異なる修正作業を並行で進められる
- 複数の修正案がある時に、案ごとにフォルダを作成することから卒業できる
- 修正の意図を明確にできる

　最後に補足。GitHubやGitLabなどのサービスを使用している場合は、修正完了時点で、作業用のブランチをプッシュすることをおすすめする。サービス上でオンラインレビューができるからだ。

## まとめ - Gitの良さを活かすには
- こまめにコミットする
- ブランチは積極的に作成する

# Gitの運用戦略(ブランチ戦略)
　Gitにはいろいろなブランチ戦略がある。ここでは、GitHub Flowをおすすめする。単純かつSVNのノリから卒業するとっかりとして良いからだ。

## GitHub Flowの基本
　GitHub Flowを一言で説明すると、リポジトリを修正する場合は、その作業用にブランチを作成する戦略である。もう少し具体的にいえば、1つの不具合修正や1つの機能開発ごとに作業ブランチを作成するということ。以下、GitHub Flowの概念図(丸は1つのコミットを意味する)。
![GitHubFlow.png](https://qiita-image-store.s3.amazonaws.com/0/196890/5b7cf0e9-b97a-6057-3f2a-0a4e1337b739.png)

Git Hub Flowの詳細はこちら。
https://gist.github.com/Gab-km/3705015

## GitHub Flowのポイント
- masterブランチを常に動作可能な状態に保てる
- 説明的な名前の作業ブランチを作成することで修正の意図を明確にできる
- 作業ブランチをプッシュできる。つまり、オンラインレビューができる

# Gitコマンド
　以下にGitHub Flowに沿った基本的な開発の流れを示す。これをベースによく使用するGitコマンドを説明する。

1. 修正作業発生
1. リポジトリを取得
1. 他人の修正を取得
1. 作業ブランチ作成と作業ブランチへの切り替え
1. 修正とコミットを繰り返す
1. コミットを整理する
1. 作業ブランチをリモートにpushする(修正を公開する)
1. レビューをする
1. 修正が必要なレビュー指摘があれば、上4つの手順を繰り返す
1. レビューが完成したら作業ブランチをmasterにマージする
1. 作業ブランチを削除

## リポジトリを取得
```
git clone <リポジトリのURL>
```
　リポジトリのURLには、「https://～」や「ssh://～」が入る。1回実施すれば、次回以降はリポジトリ取得実施は不要。

## 他人の修正を取得
```
git checkout master
git pull
```
　(最初のコマンドは不要かも)

## 作業ブランチ作成と作業ブランチへの切り替え
```
git checkout master
git branch <作業ブランチの名前>
git checkout <作業ブランチの名前>
```
　または

```
git checkout master
git checkout -b <作業ブランチの名前>
```

　最初の「git checkout master」は作業ブランチの元ブランチをmasterにするために実行。master以外を元ブランチにしたい場合は、そのブランチ名を指定すること。

## 修正とコミット
### コミットされていない修正の確認
```
git status
```

### 修正のコミット
```
git add <修正したファイル>
git commit
```
　git addは指定したファイルを索引に追加するという意味。git addでフォルダを指定した場合は、そのフォルダ配下の修正は全て索引に追加される。<修正したファイル>には、新規に追加したファイルや削除したファイルも該当する。
　git commitは索引に追加されたファイルをコミットするコマンド。当然、git addしておかないとコミット対象にならない。commitを実行すると、コメントの入力画面が表示される。コメントのルールはプロジェクトごとに異なると思うが、Gitの基本的な作法としては、1行目は修正の要約、2行目は空行、3行目以降は修正の詳細とする模様。

### 修正履歴の表示
```
git log
```

### コミットされていない修正の取り消し
全て取り消し

~~~
git reset --hard
~~~

指定したファイルだけ取り消し

~~~
git checkout <ファイル>
~~~

Untracked file(大抵、*.oファイルなどのビルド生成物が該当)を削除する

~~~
git clean -xdf
~~~

### コミットの取り消し
```
git reset [ --soft | --mixed | --hard ] <コミットID>
```
　一番最新のコミットから<コミットID>で指定したコミットより前のコミットを無かったことにする。コミットIDは、git logで確認できる。
オプションによって挙動が大きく異なるので注意。

- mixed
    - 索引、コミットログを全て消す(デフォルトオプション)
- soft
    - コミットログを取り消す
- hard
    - ファイルの内容、索引、コミットログを全て消す

　mixedとsoftの場合、コミットログは消えるがファイルの内容自体はコマンド実行前と変化しない。resetの実行後に再度コミットすることで、直前のコミットのコメントを修正したり、コミットログを整理できる。hardは指定したコミットより後の修正が全く不要になった場合などに使用する。

## コミットを整理する
### 最新からいくつかのコミットを1つにまとめる
　コミットの取り消し(git reset --soft <コミットID>)の実行後に、再度コミットする。

### masterの修正を作業ブランチに取り入れる
~~~
git pull origin master
git rebase master
~~~
　作業ブランチ作成後にmasterに入った修正(コミット)を取り込むことができる。後述のマージと異なる点は、作業ブランチの起点がコマンド実行時の最新のmasterになること(以下の図を参照)。

![git_rebase_master.png](https://qiita-image-store.s3.amazonaws.com/0/196890/5035da9e-e5b7-8ad0-4829-8c18a60b06ee.png)

　このコマンドが有用となるのは、作業ブランチ作成後に他の人の修正がmasterに入ったケース。早い段階で最新のmasterの修正を取り込むことで、早い内にコンフリクトに気づける(コンフリクトへの対処もレビューできる)、コミットログがきれいになるなどの点が良い。
　なお、このコマンドは作業ブランチを1度もプッシュしていない場合のみ使用することを推奨。

### 最新からいくつかのコミットを編集する
~~~
git rebase -i HEAD~<n>
~~~
　最新からある範囲のコミットを編集するコマンド(コメントの修正、コミットの順番の入れ替え、複数コミットを1つにまとめるなど)。<n>には3などの数字を指定する。3を指定した場合、最新～2つ前のコミットが編集対象となる。
　詳細は割愛する。コマンド実行時にコミット編集エディタが開かれるが、そのエディタのコメントに説明があるので、それを見れば分かるはず。
　最後に使用時の注意点を1つ。既にリモートに公開されているコミットを編集範囲に含めないこと。問題が発生する可能性があるため。

## 作業ブランチをリモートにpushする
```
git push origin <作業ブランチの名前>
```

## 修正をmasterに入れる & masterのpush
```
git checkout master
git pull
git merge --no-ff <作業ブランチの名前>
git push origin master
```
　mergeの"--no-ff"は必ずマージコミットを作るためのオプション。マージコミットがあると、masterに対するコミットと作業ブランチに対するコミットを分けて表示することができる(なので、コミットログが見やすい)。mergeの詳細は以下のページが参考になる。
http://d.hatena.ne.jp/sinsoku/20111025/1319497900

## 作業ブランチを消す
~~~
git push origin :<作業ブランチの名前>
git branch -D <作業ブランチの名前>
~~~
　最初のコマンドはリモート上の作業ブランチを消している。
　2番目のコマンドは、ローカルの作業ブランチを消している。

# 最後に
　この記事では、Gitを使いこなすための最低限のことを説明した。ここに書いていないことで有用なことはかなりある(コンフリクトの解決、stash, revert, mergeのオプションなど)。この記事に書いてあることを実践できるようになったら、書籍を読むなりググるなりして、調べることを推奨する。