Gitの概観とコマンド（初級レベル）

Gitについて人に説明する機会があったので、概観をまとめました。

既にGitの文献やサイトでたくさん解説されていますが、詳細（正確さ）追求しすぎると、かえってわかりにくい説明になりがちなので、大きくはずさない程度にざっくりかいてみます。


# ■■■全体■■■

Gitは自分のPCの中にリポジトリを作って変更管理ができるところが嬉しいところ。

ある程度、自分のPCの中でソースコードを育てたらリモート（クラウドなどにある）リポジトリにアップロードするというのがGit流のやりかた。

![git_repo_arch00.png](https://qiita-image-store.s3.amazonaws.com/0/170905/e4fd6841-bab5-1e94-ec48-7eddc2139ad8.png)

## ざっくりとした作業のながれ

↓こんな感じ

**全体図**
![repo_arch_anim.gif](https://qiita-image-store.s3.amazonaws.com/0/170905/16c19ce9-adf9-7126-9c02-2d5f3cacff2d.gif)

# ■■リモートリポジトリとローカルリポジトリ間のやりとり■■

全体図の右側から2つに注目したのが下の図。リモートリポジトリとローカルリポジトリの間のやりとりはこんな感じです。
**<font color="CornflowerBlue">青字</font>**はGitコマンド。


![git_local_remote_repo_01.png](https://qiita-image-store.s3.amazonaws.com/0/170905/874425f4-4087-5b23-b38e-ce8aedc788a1.png)

まず良く出てくるキーワードの整理から。

### **origin**とは
**origin**とはリモートリポジトリの名前、デフォルトでこの名前がつく。それだけ知っていれば何も難しいことはない。

### **master**とは
これも最初にリポジトリを作るとできるデフォルトのブランチ名。これも何も難しいことはない。

**master**には常に安定版に入れようとか、「ふだんの開発はトピックブランチなる別ブランチを作って品質確認した後でmasterブランチにマージしよう」のような**運用**のルールや慣例はある。
運用の考え方的にはSVNでいうところのtrunkに近いイメージ。

### **fetch**とは
リモートリポジトリにあるブランチをリモート追跡ブランチまでダウンロードしてくる。
（ダウンロード＝最新の履歴を取得する）

```bash
git fetch
```
- 上コマンドでは、リモートリポジトリにある**すべてのブランチ**をダウンロードしてくる。
- 勘違いしやすいのは、**git fetch --all**で、こちらは**すべてのリモートリポジトリ**をダウンロードしてくる。

特定のブランチだけあえてfetchしたければブランチ名を指定する

```bash
git fetch origin master
```

- 上のコマンドでは、originというリモートリポジトリにあるmasterブランチをローカルのリモート追跡ブランチにダウンロードしてくる。

### **merge**とは
リモート追跡ブランチからローカルブランチに差分を取り込む。

```bash
git merge origin/master
```
- 上のコマンドでは、origin/masterをmasterに取り込む。

### **origin/master**とは
**origin**という名前のリモートリポジトリの**master**ブランチをローカル（つまり自分のPC)にコピーしたものを指す呼び名。このブランチは、リモート側にある最新を追いかけているから、**リモート追跡ブランチ**。

このブランチの指定の方法はいくつかある。
**origin/master**のかわりに**remotes/origin/master**としてもOK。
フルネームだと**refs/remotes/origin/master**となる、これを指定してもOK。

### **pull**とは
git fetchとgit mergeを同時に行うコマンド。
（git fetchとgit mergeを別々にやりたがる人が多い。私はpull使い派。）

```bash
git pull
```

git fetchとgit mergeを別にやったときと同様、内部的にfetchしているときはリモートのすべてのブランチをダウンロードした後に、現在のブランチとのマージとなる。

下図は再掲

![git_local_remote_repo_01.png](https://qiita-image-store.s3.amazonaws.com/0/170905/874425f4-4087-5b23-b38e-ce8aedc788a1.png)

### **push**とは
ローカルブランチの変更をリモートブランチに反映（アップロードする）する。

```bash
git push
```

### **diff**とは
図の各所に示したが、２つの対象間の差分をみる。

# ■■ローカルリポジトリと作業ディレクトリの間のやりとり■■ 

![git_local_remote_repo_02.png](https://qiita-image-store.s3.amazonaws.com/0/170905/4cf7af0c-1b83-4923-5d16-1aacba8759fb.png)

作業用のディレクトリで編集したファイルをコミット（ローカルリポジトリに反映）するには、まず **git add**でコミット候補のファイルを指定する。その後に**git commit**でコミットする。

addとcommitを同時にやりたいときは**git commit -am**というコマンドを使う。

以上、概観はこんな感じです。

# ■■■シーン別Gitコマンド■■■

# ■■**あるフォルダをgit管理下にする**■■

## あるフォルダをgit管理下にする

そのフォルダに移動して以下を実行

```bash
git init
```
## ファイルをgitでトラックする

そのフォルダにあるすべてのファイルをgit管理下におく

```bash
git add -A
```

## 改行コードを自動変換しない(autoCRLF) 
Windows版gitをデフォルトインストールしている場合は autoCRLFなので注意

*.git*フォルダを開いて、configというファイルに以下を追記する
```bash
autoCRLF = false
```


# ■■**GITの状態・設定**■■

## 現在のステータス表示

- 前回のcommitからの差分表示

```bash
git status
```

- config情報の表示

```bash
git config --list
```

## GITエディタ設定

- エディタをterapadにする(vi系苦手なwinユーザー向け)

```bash
git config --global core.editor "'C:\Program Files (x86)\TeraPad\TeraPad.exe' //cu8" 
```

# ■■**ブランチ**■■

## ブランチの表示

- 現在のブランチ（＝自分が今編集しているブランチ）を表示する。＊印が現在のブランチ

```bash
git branch

* master
```

- リモートも含めた現在のブランチを表示

```bash
git branch -a

* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

```

## ブランチ作成、ブランチ選択（チェックアウト）

- リモートリポジトリのorigin/topic2ブランチをtopic2という名前のローカルブランチとして作成する。

```bash
git branch topic2 origin/topic2

Branch topic2 set up to track remote branch topic2 from origin.
```

- topic2というローカルブランチを選択する（作業するブランチを切り替える）。topic2をチェックアウトする、という。

```bash
git checkout topic2
```

- 上の2つを一度にやるのが↓のコマンド。つまり、リモートリポジトリのorigin/topic2をローカル側にダウンロードしてきて、かつ、そのブランチの選択する、ところまでを以下のコマンドで一発でできる。

```bash
git checkout -b topic2 origin/topic2
```

## ブランチの新規作成/削除
- topic1というローカルブランチを作成して、そのブランチを選択

```bash
git checkout -b topic1
Switched to a new branch 'topic1'
```

- topic1というブランチを作成

```bash
git branch topic1
```

- topic1というブランチを削除

```bash
git branch -d topic1

Deleted branch topic1 (was a4e4a05).
```

- topic1というブランチを強制削除

```bash
git branch -D topic1
```

- topic1というブランチを選択

```bash
git checkout topic1

Switched to branch 'topic1'

```

## ブランチをマージ

- まずmasterブランチにもどる

```bash
git checkout master
```

- topic1をmasterにマージする –no-ff オプションを
つけてマージコミットをのこす。fast-forwardできる場合はコミット履歴が残らないが、このオプションをつけるとfast-forwardできる場合でもあえてコミット履歴を残す動作となる。

```bash
git merge --no-ff topic1
```

## ブランチをPUSH

- 編集したブランチをリモートリポジトリに新たにPUSHする。
リモートリポジトリにも同名のブランチtopic1でpushする（名前を変えてももちろんOK)

```bash
git push –u origin topic1
```

# ■■**コミット**■■


## コミット

- コミットをする前に、どのファイルをコミットするか指定するために**git add**する。addされたファイルは**ステージ領域**（インデックス）という**コミット待ち**の領域に追加される


```bash
git add -u
```

**-u**オプションで、変更があったファイルやフォルダと、削除されたファイルやフォルダをすべてステージ領域に追加する。詳しくは後述。

- (ステージしたファイルを) topic1ブランチにコミットする

```bash
git commit -m “change something”
```


- git addとgit commitを1コマンドでやる。
commit対象ファイルはgit管理されているファイル

```bash
git commit -am “change something”
```

### **add**のコマンドバリエーション

```bash
git add -A
```

**git add -A**は以下をステージ領域に追加
- 新規追加されたファイルやフォルダ
- 変更があったファイルやフォルダ
- 削除されたファイルやフォルダ

----

```bash
git add -u
```

**git add -u**は以下をステージ領域に追加
- 変更があったファイルやフォルダ
- 削除されたファイルやフォルダ



# ■■**reset ～コミットを無かったことに～**■■

## ある時点のcommitに戻る
- commitログを表示する

```bash
git log --graph

| |
| * commit 2c4d8f2dc95b4fa50dac35d03f9facfb77d336b8
|/  Author: John Doe<jj@example.com>>
|   Date:   Fri Oct 27 22:48:29 2017 +0900
|
|       Add osaka
|
* commit a4e4a054dfe9322a84b8d075db4b751038b9ba1a
| Author: John Doe<jj@example.com>>
| Date:   Fri Oct 27 22:08:22 2017 +0900
|
```


- commitログをonelineで表示する

```bash
git log --oneline --graph
```

- あるcommitまで戻る

```bash
git reset --hard a4e4a054dfe9322a84b8d075db4b751038b9ba1a

HEAD is now at a4e4a05 Add tokyo
```

- 今編集している内容を無かったことにする
 (前回コミット以降に変更した分を、前回コミット相当の状態に戻す）

```bash
git reset --hard
```


- 直前のcommitを取り消す（HEADを1つ前に移動し、そのほかの（addとワークツリー）変更をすべて戻す）

```bash
git reset --hard HEAD^
```

- 直前のcommitを取り消す（HEADだけを1つ前に移動し、ステージとワークツリーはそんまま）

```bash
git reset --soft HEAD^
```

## これまでHEADが辿った位置の変遷

- HEAD位置の変遷をみる

```bash
git reflog

9e84eb9 HEAD@{0}: commit (amend): Fix conflict and merge
adb8e69 HEAD@{1}: commit (merge): Fix conflict
1aed162 HEAD@{2}: reset: moving to HEAD@{5}
a4e4a05 HEAD@{3}: checkout: moving from topic2 to master

```

- あるcommitまで戻る

```bash
git reset --hard HEAD@{3}
```

## リセットを間違えた

- 直前のreset操作を取り消す

```bash
git reset --hard ORIG_HEAD
```

## Gitでトラックしていないファイルも消す

```bash
git clean -f -d
```

# ■■**revert ～コミットを打ち消す**■■

## 直前のcommitを打ち消す
- 直前のcommit打消しするcommitを実行する

```bash
git revert HEAD
```

- 複数のcommitを打ち消すcommitをつくる。
-n オプションをつけるとcommitを実行しないでstageまでの巻き戻しになる。
つまり、以下のように複数の-nつきrevertをしたあとに、commitすれば複数巻き戻しが1回のcommitで済む

```bash
git revert –n <commitId>
git revert –n <commitId>
git revert –n <commitId>
git commit
```

# ■■**コンフリクト解消**■■

## PUSH時のエラー
- ローカルブランチをPUSHしようとしたら既にリモートブランチ側が更新されていた場合
以下のようになったら

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/54f5669b-ebb4-52cf-6fb4-c607a7526ad0.png)
**⇒git fetch をして、まずリモートの最新をfetchする**

- ローカルブランチをPUSHしようとしたら、non-fast-forward　だった場合、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/84915984-32d2-0e9d-3b96-1c08b7013d1e.png)
**⇒git mergeをする**

## マージ時のエラー
- リモートブランチとローカルブランチをmergeしようとしたら CONFLICTを起こしていた場合

```bash
git merge

Auto-merging src/main/java/com/example/quick/App.java
CONFLICT (content): Merge conflict in src/main/java/com/example/quick/App.java
Automatic merge failed; fix conflicts and then commit the result.
```

### 選択肢その１
コンフリクトしている部分を適宜修正した後、commit

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/e45486f3-a121-5f79-d6c8-2000fe704114.png)

### 選択肢その２
（とりあえず）mergeコマンド実行前の状態にもどす

```bash
git reset --hard HEAD
```

# ■■**PUSH**■■

## ローカルブランチをPUSH
- 現在の編集中のブランチをリモートリポジトリにPUSHする
（現在設定されているこのローカルブランチの upstreamブランチにpushされる）

```bash
git push
```

- pushするまえにまず、今自分がどのブランチで作業しているか確認する

```bash
git status

On branch topic3
Your branch is ahead of 'origin/topic3' by 1 commit.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean
```

- 現在のブランチがどのupstreamブランチに設定されているか

```bash
git branch -vv
```

## Upstreamブランチが設定されていないときのPUSH
- まだローカルブランチしかなくて、Upstreamブランチが設定されていない場合はUpstreamブランチを設定

```bash
git push --set-upstream origin topic2

Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (9/9), 581 bytes | 0 bytes/s, done.
Total 9 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/xxxxxx/mytest.git
 * [new branch]      topic2 -> topic2
Branch topic2 set up to track remote branch topic2 from origin.

```

## (類似コマンド再掲）ブランチをPUSH

- 編集したブランチを新たなリモートリポジトリにPUSHする
リモートリポジトリにも同名のブランチtopic1でpushする
（名前を変えてももちろんOK)

```bash
git push –u origin topic1
```

## Upstreamブランチを確認する
- ローカルブランチがトラッキングしているorigin側のブランチ（=upstream)
を確認する

```bash
git branch –vv

 master 9aecd10 [origin/master] Add third
  topic1 d0a5383 Add 1-chome
* topic2 0b04311 Add test3.txt
  topic3 310ee29 [origin/topic3: behind 1] Fix conflict

```

# ■■**topicブランチで開発したコードをmasterにpushする手順**■■

## トピックブランチで作業した後、masterにマージする

１．(リモートリポジトリ側にもtopicブランチがあるような場合)
リモートリポジトリのブランチを取得し、ローカルリポジトリにブランチを作る

```bash
git fetch
git checkout -b topic3 origin/topic3
```

２．topic3ブランチにmasterをマージする

```bash
git merge master
```

３．各種変更を加えコミットする

```bash
git commit –am “something”
```

４．（念のため）変更しているあいだにmasterが変わったかもしれないので、もういちどmergeしてもいい。

```bash
git fetch
git merge master
```

５．ローカルリポジトリの変更をリモートリポジトリのtopic3ブランチに反映してもいい。
（PUSHの後、何も指定していないが、その場合は設定されているupstreamブランチにpushされる）

```bash
git push
```

６．ブランチをmasterに切り替える(ねんのためpullしてもいい)

```bash
git checkout master
```

７．masterにtopic3をマージする –no-ff オプションをつけてfast-forwardでもcommitのログをのこす
(これを省略すると、HEADが移動するだけとなり、どこでマージしたかわからない）

```bash
git merge topic3 --no-ff
```