Git総集編

# Git

## Gitとは
* 分散型バージョン管理システム
* 開発者はリーナス・トーバルズ(Linuxカーネル開発者)

### Gitの特徴
* ユーザーのワーキングディレクトリに全履歴を含んだリポジトリの完全な複製が作られる
* ネットワークにアクセスできないなどの理由で中心リポジトリにアクセスできない環境でも、履歴の調査や変更の記録といったほとんどの作業を行うことができる
* 設計はBitKeeperとMonotoneが元

### Gitの概念
* [概念に関する詳しい説明はこちら](https://open-groove.net/git/git-summary/)

## Git運用フロー
* [有名なフローやリリーススパンに合わせたフローの選定についてはこちら](https://qiita.com/arai-ta/items/7350d1053d14078a2a97)
* [A successful Git branching model (有志翻訳版)](http://keijinsonyaban.blogspot.com/2010/10/a-successful-git-branching-model.html)

## Git

### リリースノート

* [「Git 2.23」リリース、「git switch」や「git restore」コマンドを実験的に導入](https://mag.osdn.jp/19/08/19/183000)

## Gitコマンド

* [【Git】一覧](https://qiita.com/sunstripe/items/0b611f6e8a480024dd33)
* [Gitコマンド一覧](https://qiita.com/fukumone/items/73e1a9a62c5e4454263b)
* [Git チートシート](https://qiita.com/Fendo181/items/ebeaf68374f788ec1413)


### 設定

```sh:Terminal
# メールアドレスの設定
git config --global user.email "登録メールアドレス"

# メッセージの色分け
git config --global color.ui true

# 設定値の一覧
git config -l

# ヘルプ参照
git config --help

# ヘルプ参照
git help
```

### コミットユーザーを指定、パスワードを省略する場合

```sh:.git/config
...途中省略...
[remote "origin"]
	url = https://webmaster-patche:%password%@github.com/webmaster-patche/capistrano_sample_app_v1.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
```

### リポジトリ操作

#### リポジトリの複製
```sh
git clone %url%
```

#### リポジトリの操作
```sh
# リモートリポジトリへの接続一覧を表示
git remote

# リモートリポジトリへステージング
git remote add origin/%remoteRepositoryName% git@%address%

# リモートリポジトリ削除（ローカルリポジトリ同名）
git remote rm origin
```

### ブランチ操作

#### ブランチ確認
```sh
# ローカルの一覧表示
git branch

# ローカル＆リモートの一覧表示
git branch -a

# リモートの一覧表示
git branch -r
```

#### ブランチの操作
* [git push の取り消し方法](http://www-creators.com/archives/2020)
* [Gitでマージコミットを取り消す方法](https://qiita.com/t-vinn/items/537c2236e56ef389133b)
* [gitのコミットの歴史を改変する(git rebase) 1 / 2](http://tkengo.github.io/blog/2013/05/16/git-rebase-reference/)
* [Git 1ファイルだけ別のブランチから持ってくる](https://qiita.com/oret/items/b646fcada9d89ed308c4)

```sh
# 指定のブランチのファイルを確認(view)
git show %branche%:%filePath%

# リモートブランチからローカルブランチへの同期
git fetch origin

# 指定のファイルをリモートブランチから取得・上書き
git checkout %branche% -- %filePath%

# ディレクトリ毎上書き
git checkout %branche% -- %directoryPath%

# リモートブランチからローカルブランチ同期
git pull origin %brancheName%

# ローカルブランチからリモートブランチに同期
git push origin %brancheName%

# ローカルブランチから強制的にリモートブランチに同期（上書き）
git push -f origin %brancheName%

# 現在のブランチから指定ブランチにリベース
git rebase -i %brancheName%

# リベース中断
git rebase -abort

# リベース再開
git rebase -continue

# 指定のブランチ（n個前のコミット）に対してリベース
git rebase -i HEAD~%n%
```
#### リベースについて
* [プルリクエストでコンフリクトが発生した場合の対処法](https://webbibouroku.com/Blog/Article/git-conflict-rebase)
* [gitのマージとコンフリクトを理解する](https://qiita.com/te2u/items/c23f82ec84cf65564554)

```sh
# masterブランチを最新の状態にし、リベースしたいブランチをチェックアウトする
git checkout master
git fetch origin
git pull origin master
git checkout hoge

# 作業ブランチにmasterブランチをリベース
git pull --rebase origin master # masterの最新コミットからブランチを派生させる

# コンフリクトの解消(ここから)
Failed to merge in the changes.

#　コンフリクト解消したファイルのステージング
git add .

# リベースの再開(ここまで)
git rebase --continue

# 以降、ここからここまでを繰り返す
# 尚、リベースしたいブランチ側の変更を破棄する場合
git push origin %brancheName% -f
```

* 一時的な変更だった場合

```sh:Terminal
 git checkout rebase_from
M	README.md
Switched to branch 'rebase_from'
Your branch is up to date with 'origin/rebase_from'.

 git pull
remote: Enumerating objects: 102, done.
remote: Counting objects: 100% (102/102), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 89 (delta 73), reused 88 (delta 72), pack-reused 0
Unpacking objects: 100% (89/89), done.
From https://github.com/webmaster-patche/sample
 + c435d3787...1c7f4857 rebase_from -> origin/rebase_from  (forced update)
Already up to date.

 git checkout feature/rebase_to
M	README.md
Switched to branch 'feature/rebase_to'
Your branch is up to date with 'origin/feature/rebase_to'.

 git rebase rebase_from
error: cannot rebase: You have unstaged changes.
error: Please commit or stash them.

$ git status
On branch feature/rebase_to
Your branch is up to date with 'origin/feature/rebase_to'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

 git checkout README.md
Updated 1 path from the index

 git rebase rebase_from
First, rewinding head to replay your work on top of it...
Applying: [#999] [schema] ユーザー

 git status
On branch feature/rebase_to
Your branch and 'origin/feature/rebase_to' have diverged,
and have 2 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

nothing to commit, working tree clean
```


#### ブランチの作成
```sh
# 作成のみ
git branch %branchName%

 # カレントブランチ切り替えを同時に行う
git branch -b %branchName%
```

#### ブランチの削除
```sh
# ブランチの削除
git branch -d %ブランチ名%
```

#### ブランチの比較
* [git diffで、異なるブランチの異なるファイルを比較する方法](http://dqn.sakusakutto.jp/2013/06/git_diff_branch_file.html)

```sh
# ブランチAとブランチBのファイル比較
git diff %brancheA%:%filePath% %brancheB%:%filePath%

# ファイルパスが同じものを比較する場合は以下の方が便利かつ安全
git diff %brancheA% %brancheB% %filePath%

# コミットの差分比較
git diff --cached

```

#### ブランチの追跡

```shell:Terminal
git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> feature/hogehoge
```

この様なエラーメッセージが表示された場合は、 `set-upstream` でブランチを追跡する

```shell:Terminal
git branch --set-upstream-to=origin/feature/hogehoge
```


### ブランチ退避
* [【git stash】コミットはせずに変更を退避したいとき](https://qiita.com/chihiro/items/f373873d5c2dfbd03250)
* [変更を一時的に退避！キメろgit stash](https://qiita.com/fukajun/items/41288806e4733cb9c342)

#### ワーキングディレクトリのスタック領域退避
```sh
# スタッシュの保存
git stash save

# スタッシュ一覧
git stash list

# スタッシュ一覧（変更内容をgit log形式で確認）
git stash list -p

# スタッシュの変更確認
git stash show %stashName%

```

#### スタック領域からワーキングディレクトリに反映
```sh
# ワーキングディレクトリに戻すだけ
git stash apply %stashName%

# ステージング状態を保持して反映
git stash apply %stashName% --index

# スタック領域

# ワーキングディレクトリに戻し、スタック領域から削除
git stash pop %stashName%

# スタッシュ削除
git stash drop %stashName%
```


#### ブランチの併合

```sh
# feature/xxxxのマージ
git merge feature/xxxx

# CONFLICT
Auto-merging lib/hogehoge.js
CONFLICT (content): Merge conflict in lib/hogehoge.js
Automatic merge failed; fix conflicts and then commit the result.

## 状態の確認
git status

On branch feature/develop_2020xxxx
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   lib/hogehoge.js

no changes added to commit (use "git add" and/or "git commit -a")

## 衝突の解消
vi lib/hogehoge.js

<<<<<<< HEAD
This sentence is added by master branch.
=======
This sentence is added by feature branch.
>>>>>>> feature/xxxx

## 解消のマーク
git add lib/hogehoge.js

## コミット
git commit
```

### コミット操作

#### コミットの打ち消し
* [git rebaseを元に戻す方法](http://tobysoft.net/wiki/index.php?git%2Fgit%20rebase%A4%F2%B8%B5%A4%CB%CC%E1%A4%B9%CA%FD%CB%A1)
* [[Git]コミットの取り消し、打ち消し、上書き
](https://qiita.com/shuntaro_tamura/items/06281261d893acf049ed)

```sh
# rebaseを無かったことにする
git reset --hard ORIG_HEAD

# ORIG_HEAD リモートブランチのHEAD？

# 直前のコミットをなかった事にする
git reset --hard HEAD^

# --hard コミット取り消した上でワークディレクトリの内容も書き換えたい
# --soft ワークディレクトリの内容はそのままでコミットだけを取り消したい
# HEAD^ 直前のコミット
# HEAD~{%n%} n個前のコミット
# @^ HEAD^と同じ
# @~%n% HEAD~{%n%}と同じ
# HEAD^^^ HEAD~{3}と同じ
# HEAD~~~ HEAD^^^とHEAD~{3}と@^^^は同じ
```

* --softの場合、直前のコミットのみを取り消し、ワーキングコピーはadd indexした状態に戻す

```text:log
$ git status
On branch %branch_name%
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   app/**/*****.rb
	modified:   app/**/*****.rb
	modified:   app/**/*****.rb
	new file:   db/migrate/****.rb

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   ****
	modified:   app/**/*****.rb
  modified:   app/**/*****.rb
  modified:   app/**/*****.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	hogehoge.txt
```

#### コミットの打ち消し（履歴を残す）
* 正確には指定のコミットを修正するので、元々のコミットを無かったことにした上で、新しいコミットとする

* [Revert to a commit by a SHA hash in Git?](https://stackoverflow.com/questions/1895059/revert-to-a-commit-by-a-sha-hash-in-git)
* 取り消したいコミットを打ち消すようなコミットを新しく作成する処理
    * [【gitコマンド】いまさらのrevert](https://qiita.com/chihiro/items/2fa827d0eac98109e7ee)
* [【git】revretしたcommitをまたrevertして戻した話](http://tmicha.hateblo.jp/entry/2017/04/26/140548)

```sh
# 元々のコミットの取り消し
git revert %コミットのハッシュ値%

# ※editorが起動する（quitすると指定のコミットが無かったことになる（打ち消し用の）コミットがされる）
# 指定のコミット分、全てワーキングディレクトリに戻る
# ***作業***
# インデックス、コミット
# pushする
# 打ち消し用のコミットとは別に、元々のコミットを変更したコミットができる

# 元々のコミットの取り消しをしたコミットを取り消す
# 要はrevertをrevertする
git revert %コミットのハッシュ値%
# 〜上記繰り返し〜
git revert %revertコミットのハッシュ値%
# 〜上記繰り返し〜
```

#### コミットの上書き

* [.4 Git の基本 - 作業のやり直し](https://git-scm.com/book/ja/v1/Git-%E3%81%AE%E5%9F%BA%E6%9C%AC-%E4%BD%9C%E6%A5%AD%E3%81%AE%E3%82%84%E3%82%8A%E7%9B%B4%E3%81%97)
* [[Git]コミットの取り消し、打ち消し、上書き](https://qiita.com/shuntaro_tamura/items/06281261d893acf049ed)
* [git commit --amendでコミットメッセージを修正](https://qiita.com/piruty/items/2fbfd26fd8dcbfed592a)

```sh
# 直前コミットの修正
git commit --amend
# ※editorが起動する（quitするとコミットされる）

# エディタなしでコミットの修正
git commit --amend --no-edit
# ※git add .の後などに使うと便利
```
コミットメッセージを修正したり、コミットし忘れたファイルをインデックス追加してコミットするのをまとめたりする

**一度pushしたり、rebaseした場合はamendするとpush出来ないため、強制オプションが必要になるので注意**

```shell:Terminal
git push origin %branch_name% -f
```

-fをつける事でpushできます

#### コミットの操作
```sh
# コミットを直接ローカルに上書き
git cherry-pick %commitID%
```
commitIDはgit logで確認できる


### ワーキングディレクトリ操作

#### ワーキングディレクトリにGit適用
```sh
git init
```

#### ワーキングディレクトリの状態
```sh
# 差分、状態の確認
git status
```

#### ログ

* 自分がコミットした物を日付をy/m/d形式で出力する

```sh:Terminal
git log --date=short --pretty=format:"%h - %ae, %ad : %s" | grep "%name%"
```

### トラブルシューティング

#### git push
* You have not concluded your merge"というエラーが発生する
  * [gitでpullかpushすると"You have not concluded your merge"というエラーが発生する](https://blog.otsukasatoshi.com/entry/2018/03/01/023743)
  * これはコンフリクトが起こっているために、マージが上手くできず完了していない状態

```sh
# 直前のマージコミットを取り消し
git reset --merge

# merge + fetch
git pull origin %remoteBranche%

# するとvim(またはデフォルトで指定しているテキストエディタ)が立ち上がる
# コミットメッセージを編集

# コミット
git commit -m "競合解決"

# リモートブランチにpush
git push
```

* Git, fatal: The remote end hung up unexpectedly
    * [Git, fatal: The remote end hung up unexpectedly](https://stackoverflow.com/questions/15240815/git-fatal-the-remote-end-hung-up-unexpectedly/32329453#32329453)

```sh
git config http.postBuffer 524288000
```


### ノウハウ

#### ブランチ運用のKPI

- １コミットはストーリー・施策・機能単位で行う
  > コミットを作業単位で作成すれば、コミットログが積み重なってしまう為、例えば、Githubの様にスカッシュマージを運用するならともかく、スカッシュマージをしないのであれば、インデックスの追加を行うのが好ましい

* [コミットは作業ログではない！](https://qiita.com/suzuki-hoge/items/cc91877ce69527ced692)

#### orphanブランチ

##### orphanブランチとは

* 親のいないと言う意味の orphanブランチを作成し、そこに切り替えた後、このブランチ上に対して作られるコミットは、親コミットがなく、その他のすべてのブランチおよびコミットから切り離された、新しいヒストリーを持つこととなる

* [既存のブランチをgitで孤児にする方法](https://codeday.me/jp/qa/20190323/461501.html)
* [Git の履歴を削除して新しいブランチを作成する方法 ](http://tanakahx.github.io/blog/2014/06/07/git-create-new-branch-without-logs/)
* [Gitで親を持たない空ブランチを作成する](https://qiita.com/ciloholic/items/769fba1c178a897cf42e)

```text:log
$ git checkout --orphan %orphan branch name%
# この状態で、いつものように Git 管理下におきたいファイルをステージングして commit する。何も commit していない状態だと git branch を実行しても tmp ブランチは表示されないようだ。

$ git add .
$ git commit -m "first commmit"
# この時点で master と履歴が 1 つの tmp というブランチが存在することになる。あとは master ブランチを tmp ブランチで push して履歴をすべて削除した master ブランチに改変する。

$ push -f . %orphan branch name%:master
# 別のリモートリポジトリに push
# GitHub から clone したリポジトリを履歴を削除した上で BitBucket で新たに管理を開始したい場合などは、まず上記の手順で履歴を削除したリポジトリを作成した後、以下のように origin の url を変更する。

$ git checkout master # カレントブランチを master ブランチに戻す
$ git remote set_url origin git@github.com:username/repository.git
# あとはリモートリポジトリに対して push すればよい。

$ git push origin master
# 最初に作成した orphan なリポジトリはもはや不要なので削除してよい。

$ git branch -d %orphan branch name%
```

#### Bitbucket
* コンフリクト解消はrevertが楽
    * [bitbucketでのプルリクエストの競合について](https://teratail.com/questions/57962)
    * masterチェックアウトしてfeatureブランチを作成
    * プルリク中のブランチにマージ
    * 上記の手順よりrevertした方が楽

## 最後に

* Gitのバージョンアップデートや拡張機能で変化が激しい為、このまとめは随時編集されます