Git超絶まとめ

# ファイル保存領域
- ワーク（ワーキング）ツリー
- インデックス（ステージング）
- リポジトリ

## ワーク（ワーキング）ツリー
- ユーザーが作業しているディレクトリ領域

## インデックス（ステージング）
- ワークツリーとリポジトリの中間領域（一時領域）
- コミット対象のファイルを登録する領域

## リポジトリ
- ファイルやディレクトリの状態を管理する領域

# ブランチ
履歴の流れを分岐して保存していくための機能
- masterブランチ
- 追跡ブランチ
- HEAD

## masterブランチ
gitリポジトリに最初にコミットすると作成されるブランチ

## 追跡ブランチ
- リモートブランチの状態を監視するためのブランチで**ローカルブランチの一種**

```bash
git branch -a
* master //作業ブランチ
  remotes/origin/HEAD -> origin/master //HEADの位置
  remotes/origin/master //追跡ブランチ

```

- `git branch -a`で表示されるブランチは作業ブランチと追跡ブランチ（`remotes/<remote>/<branch>`）で、共に**ローカル**に存在する。（"作業ブランチ"という名前は仮。追跡ブランチと区別するため）
- `remotes/<remote>/<branch>`はリモート先のブランチを指しているのではなく、「リモートにある`<remote>/<branch>`を追跡するためのローカルブランチ」という意味
- `remotes/origin/HEAD -> origin/master`は現在のリポジトリのHEADが存在するブランチを指す。（変更したい場合は`git remote set-head <remote> <branch>`）
- ~~`remotes/origin/HEAD -> origin/master`は「リモートの`origin/HEAD`から追跡ブランチ`origin/master`を**ローカル**に作成した」という意味~~
- ~~`git fetch`することでリモートブランチの内容を追跡ブランチに取得~~
- ~~`git branch <branch> <repository>/<branch>`で追跡ブランチ`<repository>/<branch>`を元に新規ブランチ`<branch>`を作成~~

### 追跡ブランチの生成と作業ブランチとの紐付きについて
```bash
git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

# ローカルにブランチを作成しただけでは追跡ブランチは作成されない
git branch hoge
git branch -a
* master
  hoge
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

# pushすると自動的に作成される
git push origin hoge
git branch -a
* master
  hoge
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/hoge

# ====================================================

# ブランチをフェッチしてきた場合、追跡ブランチが作成される
git fetch origin fuga
...
 * branch            fuga -> FETCH_HEAD
 * [new branch]      fuga -> origin/fuga

# フェッチしただけでは作業ブランチは作成されていない
git branch -a
* master
  hoge
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/hoge
  remotes/origin/fuga

# ブランチ作成時に追跡ブランチを指定
git branch fuga origin/fuga
git branch -a
* master
  hoge
  fuga
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/hoge
  remotes/origin/fuga

# 作業ブランチと追跡ブランチの関連を確認
git branch -vv
  master     23a2b23 [origin/master]
  hoge       23a2b23 [origin/hoge]
  fuga       23a2b23 [origin/fuga]

# 作業ブランチと追跡ブランチの紐付けができていない場合、--set-upstreamを使用
git branch --set-upstream hoge
```

### 追跡ブランチの更新/削除

```bash
# 更新
git remote update -p
# まとめて削除
git branch -a | grep remotes | sed -e 's/remotes\///g' | xargs -L 1 git branch -rd
```

## HEAD
- 今いるブランチの最新コミット
- ブランチを移動（チェックアウト）すればHEADの位置も変わる
- HEAD^、HEAD~はHEADから一つ前のコミットという意味
- HEAD^^^、HEAD~~~、HEAD~3のような方法でいくつ前のコミットか指定

# コマンド

## status

#### 現在のコミット状況や変更ファイルの一覧などを確認

```bash
git status
```

## add

#### ワークツリーからインデックスに登録（ステージング）するファイルについて操作

```bash
# ワークツリーに新規作成、もしくは変更されたファイルをステージング
#（git rmで削除されたファイルはステージングされない）
git add ./
git add .

# ワークツリーで削除(git rm)、変更されたファイルをステージング
# （新規作成されたファイルはステージングされない）
git add -u

# ワークツリーに新規作成されたファイル、変更されたファイル、削除（git rm）されたファイルをステージング
# （git add . と git add -u を足した機能）
git add -A

# 変更の内容を分割してステージング
git add -p

# オプション
...
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?
# y：このハンクをステージします。
# n：このハンクをステージしません。
# q：終了します。このハンクおよび以降のハンクはステージしません。
# a：このハンクおよびそのファイルのそれ以降のハンクを全てステージします。
# d：このハンクおよびそのファイルのそれ以降のハンクはステージしません。
# g：処理対象となる(別の)ハンクを選択します。
# /：入力した正規表現にマッチするハンクを検索します。
# j：このハンクを保留にし、次の保留されているハンクを表示します。
# J：このハンクを保留にし、次のハンクを表示します。
# k：このハンクを保留にし、ひとつ前の保留されているハンクを表示します。
# K：このハンクを保留にし、ひとつ前のハンクを表示します。
# s：現在のハンクを小さいハンクに分割します。
# e：現在のハンクを手動で編集します。
# ?：ヘルプを表示します。

# ステージングされるファイルを表示
git add -n ./ # 新規作成、変更したファイル一覧を表示
git add -nu # 変更、削除したファイル一覧を表示
git add -nA # 新規作成、削除、変更したファイル一覧を表示
```

## commit

#### インデックスからローカルリポジトリに変更内容を登録

```bash
# コミットメッセージをつけてコミット
git commit -m 'fast commit'

# 直前のコミットを取り消してコミットし直す
git commit --amend -m 'first commit'
# 注意！リモートにプッシュした後に使うと、リモートとのコミットポイントに差異が生じ別途マージしなければならないため使用しない。
```

## push

#### ローカルリポジトリの変更内容をリモートリポジトリに登録

```bash
# origin（リモートリポジトリ）のmasterブランチにローカルのmasterブランチの変更内容を登録
git push origin master

# より詳細な指定
git push origin master:master
# -> git push <リモートリポジトリ> <ローカルブランチ>:<リモートブランチ>
# git push origin master:b_100の場合
# ローカルのmasterブランチをリモートのb_100ブランチにプッシュしている
# 理論的には可能だが、基本的に使用しない
```

## diff

#### 各領域間での差分を表示

```bash
# ワークツリーとインデックスの差分
git diff

# ワークツリーとHEAD（現在ブランチの最新コミット）との差分
git diff HEAD

# インデックスとHEADとの差分
git diff --staged(--cached)

# 差分の統計情報を表示
git diff --stat

# 差分のあるファイル一覧を表示
git diff --name-only
```

## reset

#### HEAD、インデックス、ワークツリーの内容を変更

- `--soft`：HEADの位置を変更
- `--mixed（または指定なし）`：HEADの位置、インデックスの内容を変更
- `--hard`：HEADの位置、インデックス、ワークツリーの内容を変更

```bash
# addを取り消したい場合
git reset (--mixed) HEAD

# ワークツリーで修正した内容をなかったことにしたい場合
git reset --hard HEAD

# コミットを取り消したい場合に使用
git reset --hard HEAD^

# 間違えてresetしてしまい、reset前の状態（ORIG_HEAD）に戻したい場合に使用
git reset --hard ORIG_HEAD
```

## clone

#### リモートリポジトリをローカルにコピーする

```bash
# リモートリポジトリを指定してコピー
git clone <url + repository>

# <path>にリモートリポジトリを指定してコピー
git clone <url + repository> <path>
```

## fetch

#### リモートリポジトリから変更をローカルリポジトリに取得

```bash
# 指定したリモートリポジトリの変更を取得
git fetch origin

# 指定したリモートリポジトリ、ブランチの変更を取得
git fetch origin master

# リモートリポジトリの変更をすべて取得
git fetch --all
```
 
## branch

#### ブランチを操作

```bash
# 現在のリポジトリのブランチ一覧を表示
git branch

# 新規ブランチ<branch>を作成
git branch <branch>

# 追跡ブランチを作成
git branch <branch> <repository>/<branch>

# ブランチ<branch>を削除、マージされていない内容が残っている場合はエラーになる。
git branch -d <branch>

# ブランチ<branch>を削除、マージされていない内容が残っていても強制的に削除
git branch -D <branch>

# 現在のブランチ名を<branch>に変更
git branch -m <branch>

# 各ブランチの直近のコミットを一覧表示
git branch -v

# ローカルのブランチとリモート追跡ブランチの両方のブランチを表示
git branch -a

# リモート追跡ブランチを表示
git branch -r

# 追跡ブランチとローカルブランチ（作業ブランチ）を紐付ける
git branch --set-upstream <branch>

# 追跡ブランチとローカルブランチ（作業ブランチ）を紐付けを表示
git branch -vv

```

## checkout

#### ファイルやブランチの状態を指定の状態に変更する操作

```bash
#<branch>に切り替える
git checkout <branch>

#<commit>時の状態にする
git checkout <commit>

#<file>を<commit>時の状態にする
git checkout <commit> <file>

#<branch>を作成して切り替える
git checkout -b <branch>

# <commit>から<branch>を作成して切り替える
git checkout -b <branch> <commit>

# 履歴のないブランチを作成
git checkout --orphan ORPHAN-BRANCH

```
## merge

#### ブランチを統合する操作

```bash
# 現在のブランチに<branch>をマージする
git merge <branch>

# マージメッセージをつけてマージする
git merge -m "message" <branch>

# マージ後にコミットしない
git merge --no-commit <branch>

# マージ対象のブランチの変更をひとつのコミットにまとめる
git merge --squash <branch>

# マージした時に必ずコミットする
git merge --no-ff other-branch

# マージを中止して元の状態に戻す
git merge --abort
```

### fast forward
あるブランチにいくつか変更があり、その親ブランチの状態が、あるブランチが作成されてから変更されていない状態

- fast forward な関係

```
親ブランチ：A -> B
子ブランチ：A -> B -> C

# Bコミットから子ブランチが作成
# 子ブランチにCコミットがあるが、親ブランチは変更されていない
```

- fast forward でない関係

```
親ブランチ：A -> B -> D
子ブランチ：A -> B -> C

# Bコミットから子ブランチが作成
# 子ブランチにCコミットがあり、親ブランチにもDコミットがある
```

- `git merge`は二つのブランチがfast forward でない関係の場合、マージ後に自動的にコミットする(first forwardな関係の場合コミットされない)
- `--no-ff`オプションはfirst forwardな関係であっても強制的にコミットする
- [参考](http://d.hatena.ne.jp/sinsoku/20111025/1319497900)

## remote

#### リモートリポジトリの情報を確認

```bash
# リモートリポジトリの一覧を表示
git remote -v

# 接続するリモートリポジトリを追加
git remote add <remote> <url>

# 接続しているリモートリポジトリを解除
git remote rm <remote>

# リモートリポジトリの情報を表示
git remote show <remote>

# ローカルの追跡ブランチの情報を更新（リモートブランチの情報と同期）
git remote update -p
```

## rebase

#### ベースブランチに対象ブランチのコミットをまとめる

```bash
# <branch>に現在のブランチのコミットを適用する
# mergeとはブランチの指定が逆なので注意!
git rebase <branch>

# <commit>以降のコミット、または<branch>のコミットの内容を変更
git rebase -i <commit>
git rebase -i <branch>

## 対話コマンドの内容
# p, pick：コミットを採用
# r, reword：コミットを採用するが、コミットメッセージを変更
# e, edit：コミットを採用するが、ファイルを修正する
# s, squash：一個前のコミットと合体させる
# f, fixup：コミットメッセージを変更しない点以外、squashと同じ
# x, exec：shellでコマンドを実行する

```
- [参考1](http://liginc.co.jp/web/tool/79390)
- [参考2](http://kik.xii.jp/archives/117)

## cherry-pick

#### 現在のブランチに特定のコミットだけを適用する

```bash
# <commit>の内容を現在のブランチに適用
git cherry-pick <commit>

# 現在のブランチのワークツリーとインデックスに<commit>を適用する（コミットしない）
git cherry-pick -n <commit>
```
- 指定のコミットが他のコミット（それより前のコミット）に依存している場合、競合する

## reflog

#### 操作履歴の一覧を表示

```bash
# 特定の操作時点まで戻したい場合
git reflog
git reset --hard <reflog_point>
```

## rm

#### 指定ファイルを各領域から削除

```bash
# ワークツリーとインデックスから削除
git rm hoge/

# 再帰的にディレクトリを削除
git rm -r hoge/

# インデックスからのみ削除
git rm --cached fuga.txt

# 削除対象ファイルを表示
git rm -n hoge/

# 管理済みディレクトリ（ファイル）を.gitignoreで無視するが
# ワークツリーには残しておきたい場合に使用
# 削除後、コミット・プッシュ
git rm -r --cached hoge/(fuga.txt)
```

## clean

#### 追跡していないファイルを削除

```bash
# 削除対象を確認
git clean -n

# 削除
git clean -f

# ディレクトリも合わせて削除
git clean -d -f
```

## ls-files

#### gitで管理しているファイルの一覧を表示

```bash
# 全ての管理済ファイルを表示
git ls-files -c

# ワークツリーで変更済のファイルを表示
git ls-files -m

# ワークツリーで削除済のファイルを表示
git ls-files -d

# 無視リストに載っているが管理済のファイルを表示
git ls-files -i --exclude-standard

# 全ての未管理ファイルを表示
git ls-files -o

# ワークツリーで新規作成されたファイルを表示
git ls-files -o --exclude-standard

# 無視リストに載っていて未管理のファイルを表示
git ls-files -io --exclude-standard
```

## update-index

#### 監視対象のファイルを無視する

### assume-unchanged
- 作業ツリー上で変更されていても無視される
- マージ時はマージ先の内容が取り込まれる

```bash
# 登録
git update-index --assume-unchanged [ファイル名]
# 解除
git update-index --no-assume-unchanged [ファイル名]
```
### skip-worktree
- 作業ツリー上の変更が維持される
- マージ時はマージ先の内容を取り込まない

```bash
# 登録
git update-index --skip-worktree [ファイル名]
# 解除
git update-index --no-skip-worktree [ファイル名]
```

## config

```bash
# グローバルconfigファイルのパス
~/.gitconfig

#各リポジトリのconfigファイルのパス
.git/.gitconfig

# config一覧を表示
git config -l

# <item>を表示
git config --get <item>

# .gitconfigファイルを編集
git config -e

# メールアドレス
git config --global user.email "Your email"

# ユーザー名
git config --global user.name "Your name"

# 編集用エディタ
git config --global core.editor vim

# ページャー
git config --global core.pager "lv -c"

# ターミナルの出力に色をつける
git config --global color.ui auto

# エイリアス
git config --global alias.co "checkout" # -> git co
git config --global alias.ci "commit" # -> git ci

# 改行コードの自動変換 CRLF(windows) <-> LF(linux系)
git config --global core.autocrlf true
```

- [その他の項目](http://git-scm.com/book/ja/v1/Git-のカスタマイズ-Git-の設定)
- [エイリアス例1](http://yuroyoro.hatenablog.com/entry/20101008/1286531851)
- [エイリアス例2](http://qiita.com/Kuniwak/items/b711d6c3e402dfd9356b)

# その他

## .gitignore

- Git管理から除外したいディレクトリ・ファイルを指定
- `ls-files`コマンドで管理対象を確認できる
- すでに管理済みのファイルを無視する場合は、`git rm --cached`コマンドを使用

```bash
# ルートディレクトリ（.gitignoreファイルが設置されているディレクトリ）直下のhogeディレクトリ配下をすべて無視
/hoge/*

# ルートディレクトリ配下にあるすべてのhogeディレクトリ配下を無視
hoge/*

# ただし、/hoge/fuga配下だけは無視しない
!/hoge/fuga/*

# 空のディレクトリ管理用に.gitkeepを使用する場合、すべての.gitkeepファイルを無視
!.gitkeep

```

# 主な参考
- [Git](http://git-scm.com/book/ja/v1)
- [サルでもわかるGit入門](http://www.backlog.jp/git-guide/)
- [Git 使い方 見出し一覧](http://transitive.info/article/git/)
- [Gitチュートリアル](https://www.atlassian.com/ja/git/tutorial)