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の弊害となる以下の点について認識しておかなければならない。
- マージに伴う新しいコミットが生成されない。
- 従って、「ブランチをマージした」という事実が歴史(コミットグラフ)に残らない
- 結果、「ブランチのマージ」を特定しづらく、取り消しづらい。
- 上記の理由により、通常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ブランチにマージする前に 別のエンジニアのコードレビューを通してからマージする仕組みのこと。
これによって、 変更に伴うバグを事前に発見しやすく なったり、
関わっているエンジニアが 他のエンジニアが書いたコードの内容もある程度把握しておける ので、
あとからその場所に手を加えることになった場合にもスムーズに入れるなどのメリットがある。
実際のフローとしては一般的に以下の様になっている。
- コードに修正を加えるトピック単位で、ローカルに新規ブランチを切る
- コードを修正し、コミットする。
- 良きタイミングでリモートにpushし、別のエンジニアに変更内容のレビュー依頼を投げる (これがプルリクエスト)
- 依頼されたエンジニアは変更内容をチェックし、問題があればフィードバックを、問題がなければOKサインを返す。 (エンジニア界ではLGTM(Looks Good To Me)という単語がよく使われる)
- OKサインがもらえるところまでいけたら、変更を加えたエンジニアがマージ作業を行う。
細かいやりとりのルールはプロジェクトによって違うものの、おおまかな流れはだいたい同じ。
プルリクエストの仕組みは業務だけでなく、オープンソースの世界でも広く利用されている。
ちなみにこのプルリクエストはGit自身の機能ではなく、Github社が最初に提供した機能。
オープンソースでコードに改良を加えていくにあたって、コードの品質を高く保ち続けるためにはなくてはならない仕組み。
その他 チーム開発時に気をつける事項
- 使い終わったブランチは消しておくと吉。Githubの場合だとweb上のUIからボタンクリックで消すことも可能。
$ git branch -d unused_branch
$ git push origin :unused_branch
- 常にリモートとローカルの状態は同じにしておくことを心がける!
ちなみにこんな方法もあります。ローカルでごにょごにょやってて収集がつかなくなったときは、大人しくリモートと強制同期させましょう。
- git push -f は絶対ダメ!
参考サイト
-
いつやるの?git入門
http://www.slideshare.net/matsukaz/git-17499005 -
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/