Gitで説明する機会が多かったのでまとめてみた

個々のコマンドの説明とか、Gitの記事はたくさんあるので、細かい話はしないです。僕がいつも説明するときに言っていることをまとめて、説明時間を短縮する目的の記事です。
（今更Git記事書くなよとかあると思いますが、そこはご容赦ください。）

# 対象者
ある程度、SVNなどをやっている人向けなので、説明が足らないところや省かれているところがあります。そこは適宜ググッて頂きたいです。（キーワードでググれば大丈夫かと）

# 僕のGit歴
説明するとか言いながら、Git初心者野郎だった。とかだと嫌だと思うので経歴です。

* Git歴2年以上
* Git移行委員会とかやったことある
* [BFG](https://rtyley.github.io/bfg-repo-cleaner/)とか使って、大きなバイナリファイル削除したりした
* SVNからGit移行とかもやったことある

# GitとGithubは違うもの

プルリクエストの機能はGitには無く、Githubの機能です。なので、gitコマンドではなく、hubコマンドを使わないとプルリクエストの作成などは出来ません。

# Gitの概念

Gitはバージョン管理ではなく、[変更管理なんだ](http://local.joelonsoftware.com/wiki/%E5%88%86%E6%95%A3%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%A7%E9%96%93%E9%81%95%E3%81%84%E3%81%AA%E3%81%84%E3%81%A3%E3%81%A6%E3%80%81%E3%83%99%E3%82%A4%E3%83%93%E3%83%BC)という記事があります。そう考えると後々の理解が若干楽になると思います。

# Gitの構成

[説明図](http://ndpsoftware.com/git-cheatsheet.html)

この図には、後述するローカルの**refs領域**が書いてないですが、他は書いてあるので引用です。

## サーバーにあるもの
* サーバーのGit領域（upstreamリポジトリ）

## ローカルにcloneした時に出来るもの
* サーバーのGitファイルを置く場所(refs領域)
* 自分のGitファイルが置いてある場所（ローカルリポジトリ）
* index領域（index）（stagingとも呼ばれたり）
* ワークスペース（workspace）（見えているファイルはこれ）
* STASH領域（僕はstash使ってないので、説明しないです。）

各コマンドがどこを更新するか？は、次から説明します。とりあえず、各領域があり用途により分かれているということを、覚えてほしいです。

# git initは使用しなくてもおｋ
他のサイトだと、git initすると書いてありますが、僕は使っていないです。Githubとかで作業するなら、cloneしかしないスタイルでも事足りると思います。Githubの画面からリポジトリを作成して、READMEと言語の選択してリポジトリを作成します。これをやると、副作用で.gitignoreとかもついてくるのでお得です。
（最初はスキップ出来るところはしたほうが良いというスタンスが良いと思います。）

# git cloneの種類
httpsとsshの2パターンあります。また、ローカルにあるGitクライアントが古い場合、httpsでクローンが出来なかったりするので注意です。その場合、バージョンを上げてください。

## https

```bash
git clone https://github.com/daijinload/hoge.git
```

こちらでcloneすると、pushするときに、IDとパスワードを聞かれます。
（IDEを使用している場合、IDとパスワードは保存出来ると思います。）

## ssh

```bash
git clone git@github.com:daijinload/hoge.git
```

こっちはパスワードとか聞かれないので良いのですが、sshkeyを作ったり、自分のgithubの設定に公開キーを置く必要があるので、コマンドでガンガンコミットする人以外は、シンプルに使いたいのであれば、パスしたほうが良いかと。

さらに、keyが1個しか無いなら、↑で良いのですが、複数設定したい場合、**~/.ssh/config**に設定書いて、

```bash
git clone git@ほげほげ:daijinload/hoge.git
```

という感じで、分岐名称を指定する必要があります。

[GitHubの複数アカウントを使い分けるならSSHよりhttpsの方がいいんじゃね？という話](http://qiita.com/zaki-yama/items/bfb0c2bef516af58c3fa)

ただし、何かしらノンパスで作業する必要のある人は、設定せねばならないと思います。
（jenkinsとかのCI環境で、Gitで複雑なことやったりする場合とか）

# statusとlog

```bash
git status
git log
```
状況確認コマンドです。hash値を調べたりするのにも良いので、使っていってください。

# gitでサーバに変更を送る(push)

```bash
git clone hoge.git
cd hoge
touch aaa.txt #(ファイル作成)（ワーキングディレクトリを更新）
git add --all #(index領域を更新)
git commit -m 'add aaa.txt' #(ローカルリポジトリを更新)
git push origin master #(ローカルリポジトリの変更分をサーバに送る)
```
IDEを使えば、addとcommitをセットで行えるので、僕はそうしています。手でやる時も、--allを使うなど可能な限り楽をします。

```bash
git commit -am 'update'
```

このコマンドなら、addとcommit同時にできるのですが、新規ファイルは出来ないため使ってません。

# gitでサーバーの差分を取ってくる

```bash
git fetch origin #(ローカルのサーバーのGitファイルが置く場所を更新(refs領域))
git rebase origin/master
```
git pullは使いません。pullだとfetch + mergeとなるので、fast-forward出来ない場合、意図せずコミットが作成されるからです。たまにプルリクエストにmasterにある変更がマージコミットとして差分として出たりするのですが、それはこれが原因です。
ここはつまづきポイントだと思います。

```bash
git pull --rebase
```
使うとしても、基本は、こんな感じで使ってほしいです。

# ともかくmasterに戻したい

```bash
git reset --hard origin/master
```

サーバー側が進んでいる場合、fetchしてからやると良いです。

> 豆知識　origin/masterの所を、コミットハッシュ指定出来るので特定コミットに戻りたい時などどうぞ。

# origin/masterとorigin masterの違い

* **origin/master**とスラッシュが付く場合、ローカルのrefs領域を指します。
* **origin master**とスペースが付く場合、サーバーを指します。

ここもつまづきポイント。pushする場合、ローカルに向けないと思うのでスペースと覚えると間違いないと思います。

# gitでmasterを最新にしてからブランチを切る

```bash
git checkout master
git fetch origin
git rebase origin/maseter 
git checkout -b feature/hoge
```

> 単純にcheckoutしてブランチを切る場合、ローカルリポジトリのmasterを最新にしておかないと古いやつで切ることになって、コンフリクトの危険性がマシます。。。

# git rebaseは2種類ある
* git rebaseは、マージっぽいやつ
* git rebase -iは、過去編集モード（コミットをまとめたり出来る）

僕のスタイルだとブランチ切った後は、小さい単位で、pushしまくります。コメントが適当でも、最後にコミットをまとめれば、無かったことにできます。また、OSSでプルリクエストを送る場合、コミットを綺麗にしないとダメなんだからね！！と言われる場所もあるので、**git rebase -i**はマスターしたほうが良いです。

```bash
git rebase -i 34ab39c4
```

また、上記のようにコミットハッシュを指定すれば、HEAD^3とか覚えなくて済むのでおすすめです。コミットをまとめることを、squashしてね。とか言ったりします。

# git戦略

git-flowとgithub-flowが良く知られています。いままで現場を見てきた感じ、そのまま採用するチームもあれば、足して2で割ったような戦略にしているチームもありました。SVNを使っていたチームなら、SVN時代と同じ感じでやるのが分かりやすいのでは？と、個人的には思います。僕の居たチームではタグコミットが頻繁にあったので、stable/hogeとかでブランチを切ることをタグ切り扱いにして、それをリリースしたり、stable/hogeに対してcommitをpushしたりしてました（gitのtagを使ってない）。シンプルで誰でも理解できる戦略であれば、なんでも良い気がします。

# stashを使わない理由

stashは、手元に編集中のファイルがある状態で、ブランチを切り替える時に便利な機能です。ですが、ローカルのPCに長い間ファイルを置いておくと、PCが突然死した時に作業分が失われるというリスクが高くなります。Gitは分散リポジトリなので、ローカルでコミット出来て便利と、ずっとpushしないで作業したりするのは危険だと思います。なので、僕は突然死の被害を抑えるために、ブランチを切って、途中でもファイルをサーバーに送っていくスタイルを取っています。そうしていたら、stashを使わない開発スタイルになっていた感じです。

# バイナリファイルは苦手

[性能観点からストレージ制限](http://qiita.com/t_yamatoya/items/fec5c5285f56eb4def99)
1Gbyteを超えた当たりでGitが重くなるようです。

僕の前に居た会社では、GitHub Enterpriseを使用していたのですが、1Gbyteを超えた当たりで、管理チームから小さくしろと勧告が来ました。その時に、[BFG](https://rtyley.github.io/bfg-repo-cleaner/)を使用して要らないバイナリファイルを削除したりしました。
(git filter-branchコマンドもあるのですが、遅かったのでBFG使いました。)

最近では、[大容量ファイルもGitで管理。 Git LFSの使い方](http://www.slideshare.net/hibiki443/git-git-lfs-60951449)も出てきているので、そんなことをしなくても良いのかなぁと思ったりするのですが、使用している方、経験談を頂けるとうれしいです。

# まとめ

ざっくりと以上となります。説明するときにこれを読んでもらって、フィードバックを得て、さらに改良していこうと思います。また、変なところや、分からないところがありましたら、コメントおねがいしますm(_ _)m