Githubを使ったチーム開発の備忘録


Githubを使ったチーム開発を3ヶ月ほど行ったので、その知見を備忘録的にメモ。

# Gitのデータ領域
Gitには3つのデータ領域があり、以下のような関係になっている。
データ領域を理解することはGitの基本。

### ワークツリー
ユーザが編集するファイル / ディレクトリのこと。
作業ディレクトリとも呼ばれる。

### ステージングエリア
ワークツリーの変更点を登録する一時領域のこと。
インデックスとも呼ばれる。
git addするとここに登録される。

### Gitリポジトリ
.gitディレクトリ内に格納されるデータ。
git commitするとここに登録される。

**[ワークツリー] - git add -> [ステージングエリア] - git commit -> [Gitリポジトリ]**

# 基本的なコマンドについて軽くおさらい
### add
ワークツリーにあるファイルをステージングエリアへと移動させる。
ステージングエリアとは、コミットするための準備領域のようなもの。
ネクストバッターズサークル的な。

### rm
ステージングエリアに置いてあるファイルを削除する。

### commit
更新内容をリポジトリに登録する。
新しいコミットが生成され、HEADに格納される。

### checkout
変更を加えたワークツリーをステージングエリアの状態に戻す。
またはブランチを切り替えるときにも使う。

### reset
変更を加えたステージングエリアをGitリポジトリの状態に戻す。
コミット名を指定することで、特定のコミットにまで戻すことも可能。

### push
ローカルで変更を加えたGitリポジトリの状態を、リモートリポジトリに反映させる。

### merge
```
git merge {topic名}
```

* 早送り(fast forward)できる場合はそうする。できなければ普通のマージをする。
* ここで、fast forwardの弊害となる以下の点について認識しておかなければならない。

1. マージに伴う新しいコミットが生成されない。
2. 従って、「ブランチをマージした」という事実が歴史(コミットグラフ)に残らない
3. 結果、「ブランチのマージ」を特定しづらく、取り消しづらい。

* 上記の理由により、通常mergeを行う場合は--no-ffをつけるのが望ましい。これで、fast-forwardは行われず、上記の問題を避けることができる。

```
git merge --no-ff {topic名}
```
 
* マージをおこなうと、conflictする場合がある。 
* conflictとは、マージを実行したとき「同じファイルの同じ行に違う変更をしていた」ブランチ同士をマージした場合に起きる。
* コンフリクトが起こった時は、どちらの変更(ブランチ)を採用するかgitはわからないので利用者の判断が必要になる。
* コンフリクトが起こった場合、gitはコンフリクトが解決するまで処理を停止する。
* なのでコンフリクトが発生したら、git statusコマンドでマージ失敗したソースコードを確認し、手作業で修正する必要がある。
* 修正が完了したら、ソースコードをaddして再度ステージングエリアに登録する必要がある。
* 最後にコミットを実行してコンフリクトは解決。

### rebase
* クセモノ。注意が必要。
* rebaseで作られたコミットは元のコミットと内容は同じだが、別のコミット(コピー)になる。
* 元のコミットとは1つ前のコミットが異なる。コミットのコピーを作って、rebase先のブランチに適用するイメージ。
* 従って、既にpushされているブランチをrebaseするとpushできなくなる。歴史が変わるから。最新コミットの1つ前のコミットが変わっているため、整合性がとれなくなる。
* git push -f で強制的にpushすることもできるが、これはタブーであり大変なことになるので絶対にやめたほうがいい。
* コンフリクトが発生した場合、コミットごとにコンフリクト解消しないといけないため面倒。マージだと、どんなにコンフリクトしていても解消は1回のコミットだけで良い。
* 基本的にローカルにのみ存在するブランチに対してのみrebaseは行うほうが良い。

### stash
* 作業途中にブランチを切り替えたくなった。でもこのタイミングでコミットするのは中途半端・・。そんなときにはstashを使う。
* stashを使えば現在の作業状態を一時退避させておくことができる。stash popすることで、再度作業状態を復活させることができる。
* 結構便利でよく使う。

### revert
* git revert {commit}

{commit} (リビジョン)のコミットを取り消すためのコミットを作る

ちなみにresetとは全く挙動が異なる。

* git reset --hard {commit}

の場合は、{commit}(リビジョン)時点の状態まで **完全に巻き戻す**。

### fetch
リモートリポジトリから最新情報をローカルリポジトリに持ってくる

### pull
* pull = fetch + merge
* pull --rebase で fetch + rebaseもできる

# プルリクとは？
プルリクとは、変更を加えたコードをMasterブランチにマージする前に **別のエンジニアのコードレビューを通してからマージする仕組み**のこと。

これによって、 **変更に伴うバグを事前に発見しやすく** なったり、
関わっているエンジニアが **他のエンジニアが書いたコードの内容もある程度把握しておける** ので、
あとからその場所に手を加えることになった場合にもスムーズに入れるなどのメリットがある。

実際のフローとしては一般的に以下の様になっている。

1. コードに修正を加えるトピック単位で、ローカルに新規ブランチを切る
2. コードを修正し、コミットする。
3. 良きタイミングでリモートにpushし、別のエンジニアに変更内容のレビュー依頼を投げる **(これがプルリクエスト)**
4. 依頼されたエンジニアは変更内容をチェックし、問題があればフィードバックを、問題がなければOKサインを返す。 (エンジニア界ではLGTM(Looks Good To Me)という単語がよく使われる)
5. OKサインがもらえるところまでいけたら、変更を加えたエンジニアがマージ作業を行う。

細かいやりとりのルールはプロジェクトによって違うものの、おおまかな流れはだいたい同じ。
プルリクエストの仕組みは業務だけでなく、オープンソースの世界でも広く利用されている。

ちなみにこのプルリクエストはGit自身の機能ではなく、Github社が最初に提供した機能。
オープンソースでコードに改良を加えていくにあたって、コードの品質を高く保ち続けるためにはなくてはならない仕組み。

# その他 チーム開発時に気をつける事項
* 使い終わったブランチは消しておくと吉。Githubの場合だとweb上のUIからボタンクリックで消すことも可能。

```
$ git branch -d unused_branch
$ git push origin :unused_branch
```

* 常にリモートとローカルの状態は同じにしておくことを心がける！

ちなみにこんな方法もあります。ローカルでごにょごにょやってて収集がつかなくなったときは、大人しくリモートと強制同期させましょう。

[gitでリモートのブランチにローカルを強制一致させたい時](http://qiita.com/ms2sato/items/72b48c1b1923beb1e186)

* git push -f は絶対ダメ！

# 参考サイト
* こわくないgit
http://www.slideshare.net/kotas/git-15276118?ref=http://morizyun.github.io/blog/how-to-git-review-book/

* いつやるの？git入門
http://www.slideshare.net/matsukaz/git-17499005

* 逆引きGit
http://www.backlog.jp/git-guide/reference/

* Gitの基礎勉強
http://tracpath.com/bootcamp/learning_git_firststep.html

* Git 7つの心構え
https://speakerdeck.com/sotarok/the-7-habits-of-git

* LearnGitBranching
http://k.swd.cc/learnGitBranching-ja/