Gitを使っていて困ったことを軒並みメモしていく

## git add を取り消す（indexから外す）
indexから外すだけで、ファイル自体は何も変更されない。

```bash
$ get reset HEAD ファイル名
```

## ファイル削除を取り消す
http://qiita.com/ongaeshi/items/6305e5c2fde5719c50a8

```bash
$ git checkout -f
```
※他の変更も取り消されるので注意。

## コミットを取り消す
★[危険性をよく理解した上で](http://qiita.com/shuntaro_tamura/items/db1aef9cf9d78db50ffe)実行すること！
★Windowsのコマンドプロンプトで実行する場合、HEAD^の部分を "" で囲んでおかないと正常に動作しない。

```bash
※commit だけ取り消し
$ git reset --soft HEAD^ 

※commit, add 取り消し （デフォルト）
$ git reset --mixed HEAD^ 

※commit, add を取り消して、ワーキングツリーも一つ前に戻す
$ git reset --hard HEAD^ 
```
コミットを2つ以上取り消す場合には ```HEAD^^```や```HEAD~2```のようにする。

但し、リポジトリの最初のコミットだけは上のやり方ではエラーになる。
そんなときは下のやり方で。

```bash
$ git update-ref -d HEAD
```

※参考: [gitで最初のコミットを取り消す](http://te2u.hatenablog.jp/entry/2014/09/01/230002)


## コミットの取り消しの取り消し(^^;)

```bash
# まずは過去のコミット一覧を見る
$ git reflog 

# そこからコミットを選択して復元する　
# 例）git reset --hard HEAD@{2}
$ git reset --hard <コミット>
```
※参考: [Gitでやらかした時に使える19個の奥義](http://qiita.com/muran001/items/dea2bbbaea1260098051)

## マージを中止する

```bash:マージを中止する（まだコンフリクトの編集をしていないとき）
$ git merge --abort
```

```bash:マージを中止する（いくつかコンフリクトの編集をしたあと）
$ git reset --hard HEAD
```

※参考: [【git】マージしたけどやっぱりやめたい時のやり方3種類 - Qiita](https://qiita.com/chihiro/items/5dd671aa6f1c332986a7)

## 完了したmerge/rebaseを取り消す
注: このやり方で取り消していいのは、まだ他者に取り込まれていない場合だけ。

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

※参考: [Gitでやらかした時に使える19個の奥義](http://qiita.com/muran001/items/dea2bbbaea1260098051)
※参考: [マージ後の git reset HEAD^ は危険だった - unpushの日記](http://d.hatena.ne.jp/unpush/20110107/1294411849)

## リポジトリからファイル/ディレクトリをその履歴もろとも完全に削除する【危険】
うっかりprivateな情報をコミットしてしまった場合や、巨大なファイルを入れてしまった場合など、どうしても完全削除しないといけない場合はある。コミットしたばかりなら `git reset` すれば事足りるが、やらかした後にいくつものコミットが積み重なってしまった場合、そんなときのために。

#### 準備
共同作業者がいる場合は、必ず事前に了解を取っておく。
 - あらかじめ全ての変更をcommit＆pushしてもらうこと。必要ならローカルのtag/branchもpushしてもらう。
 - 作業終了後にcloneし直しになることも伝えておく。

#### コマンド
```bash:コマンド実行例（GitBashにて。Windowsコマンドプロンプトではエラーが発生して止まる）
$ git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch send_mail/ word_gen/' --prune-empty -- --all
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
$ git push origin --all --force
```

#### 後処理
その後、他のローカルリポジトリをcloneし直す。
共同作業者がいる場合は、全員にcloneし直してもらう。

### 参考
[git最強のオプション filter-branch](http://qiita.com/Spring_MT/items/f60c391b5dbf569a1d12)
[git filter-branchで過去の全てのcommitから画像ファイルの追加/変更をなかったことにしてリポジトリを軽量化する](http://dskd.jp/archives/46.html)
[git repository size を削減する](https://www.d-wood.com/blog/2014/10/03_6965.html)

## コミットの打ち消し
http://d.hatena.ne.jp/miau/20100709/1278699637

「取り消し」とは違い、打ち消しコミットを入れる。
既にpushしていて、resetするわけにもいかないような場合にはこれで。

```bash
$ git revert dd85d5t
$ git revert 56dc6d9
```
打ち消したいコミットが複数ある場合は、コミットしたときの逆順で（＝最近のコミットから順に）revertしていけばOK。

## ブランチを作り忘れた時
http://qiita.com/kkabetani/items/edc69a806095e4fc489c

ブランチ(working)を作成してcheckoutするだけ。

```bash
$ git checkout -b working
```

既にコミットしてしまっている場合は、[もっといろんなことをやらないといけない](http://labs.timedia.co.jp/2010/12/git-moving-changes-with-cherry-pick.html)。

pushするときは -u を付ける。（まだremote側に同名ブランチがないので）

```bash
$ git push -u origin working
```

## ブランチを別の作業ディレクトリに取り出す - worktree

例えばfeatureブランチで作業中に、masterブランチをちょこっと修正したいというような時、かつstash&checkoutもメンドクサイ（事情によりやりたくない）というような時、リポジトリをcloneすることなく、現在のlocalリポジトリから**別の作業ディレクトリに**ブランチを取り出せる。


```bash
worktreeの作成（branchをworkdirに取り出し）
$ git worktree add <workdir> <branch>

※submoduleが含まれている場合は、addした後に `update -i` が必要
$ cd workdir
$ git submodule update -i

worktreeのリストを表示
$ git worktree list
F:/work/Src/App         d75d07d [feature-xxx]
F:/work/Src/AppInitial  ab268db [task_xxx]
F:/work/Src/AppMaster   1ada79f [master]
```

取り出したworktreeがいらなくなったら、まずはディレクトリを普通に削除し、次に以下のコマンドを実行。

```bash
$ git worktree prune
```

## 別名のブランチをpushする
ローカルの `develop-new-feature` ブランチを、リモート(heroku)の `master` にpushする例。

```bash
$ git push heroku develop-new-feature:master
```

## 不要なブランチ（ローカル＆リモート）を削除する

参考: [gitの不要なブランチを消すコマンド - Qiita](http://qiita.com/mather314/items/a1536c52a2eb0426b2b5)

#### ローカルブランチの削除

```bash
# マージ済みブランチを一覧
$ git branch --merged [ブランチ名] ※ブランチ名を省略した場合はカレントブランチ

# ブランチを削除 ※安全のため未マージのブランチは削除出来ないようになっている
$ git branch -d ブランチ名  ※ブランチ名はスペース区切りで複数可
```

#### ローカルでのブランチ削除をリモート側に反映

```bash
# リモートブランチの最新状況を取得: pオプションによりリモート側のブランチ削除をローカル側に反映させる
$ git fetch -p

# マージ済みリモートブランチ一覧
$ git for-each-ref --sort=committerdate --format='"%(committerdate:short)","%(authorname)","%(refname:short)"' refs/remotes --merged

# リモートブランチを削除
$ git push --delete リモート名 ブランチ名  ※ブランチ名はスペース区切りで複数可
```

## gitで管理しているファイルの一覧表示
```bash
$ git ls-files
```

## gitで無視したいファイルが既にリポジトリにあるかを調べる
http://te2u.hatenablog.jp/entry/2013/11/16/112629

> 無視したいファイルがリポジトリにあるか確認して、もしあるなら削除したいと思って調べたメモ。

> gitのls-filesサブコマンドで確認できる。

```bash
$ git ls-files -i --exclude-standard
```

> さらに削除まで行う。
> http://qiita.com/yuuAn/items/b1d1df2e810fd6b92574

```bash:GitBashなどで実行
$ git rm --cached `git ls-files -i --exclude-standard`
$ git st
$ git ci -m "xxxx"
```

特定のignoreファイルでignoreするファイルだけを対象にするならこれ。

```bash:GitBashなどで実行
$ git rm --cached `git ls-files -i --exclude-from=.gitignore`
```

## サブモジュールについて

#### リポジトリにサブモジュールを追加

```bash:親プロジェクトにsubmoduleを追加（指定ブランチのcommitを参照する）
$ git submodule add [-b submodule側のブランチ名] //server/share/repo/subdir.git [Path to submodule dir]
```

```bash:親プロジェクトをcloneしたらまずこれ
$ git submodule update -i
```

```bash:submodule側の更新を取り込み
# 親プロジェクトをpullした時にsubmoduleがmodifiedになっていたらsubmodule updateする
$ git submodule update
```

```bash:submoduleの状態を確認
$ git submodule status
 5509...6136 MaterialDesignInXamlToolkit (v1.0-1102-g55099eb)
+9d15...95bc WindowsUtil (xxxxx-g8fo4a8ab)
```
この例の2番目のsubmoduleのように、行頭に何かマークが付いていたら何らかの問題があるということ。`+` の場合は `git submodule update` すれば解消すると思う。

| プレフィックス | 意味 |
|:-:|:-:|
| - | 未初期化  |
| + | インデックスのSHA-1と合っていない  |
| U | コンフリクト発生  |

#### submoduleを解除（削除）
★VisualStudioでsubmoduleのプロジェクトを参照している場合はそれを外しておく。
★さらに、VisualStudioが起動していると git rm が失敗するので、あらかじめVisualStudioを終了しておく。

```bash:
# submoduleディレクトリの中身もキレイに消えてしまう点に注意
$ git submodule deinit <Path to submodule dir>
$ git rm <Path to submodule dir>
```
★上記サブモジュールディレクトリ`<Path to submodule dir>`自体が残っていたら、それも削除し、コミットする。
※これをやっておかないと、`git submoudle update -i` したときにエラーが発生する。

最後に `.git/modules/<submodule名>` ディレクトリを手動で削除する。（参考: [git submoduleについてのメモ 追加/削除/更新等](https://rcmdnk.com/blog/2013/10/18/computer-git/)）

#### 過去のコミットをチェックアウトしたらsubmoduleが見つからないというエラーが発生した場合
NAS名の変更やクラウドサービスの変更などにより、submoduleリポジトリパスが昔と変わってしまっている場合もままあると思われる。このような場合に古いコミットをチェックアウトすると submoduleが見つからない旨のエラーが発生し、正しくチェックアウトできないことがある。[^1]

そのような場合には、手作業で .git/config を編集し、urlの部分を正しい（現在の格納先）URLに変更し、再度チェックアウトする。

```
[submodule "MaterialDesignInXamlToolkit"]
	url = //server/repos/MaterialDesignInXamlToolkit.git
```

※この後、間違って `git submodule sync` してしまうと、.git/config の記述が書き換わってしまうので注意。

###### 参考: 関連するファイルは以下の2つ

| ファイル | ソース管理 | url記述 |
|:-----------------|:------------------|:---------------|
| .gitmodules | されている   | 当時のURLが記載されている |
| .git/config | されていない | 現在参照可能なURLを記述する |


[^1]: .git/configに正しいURLが記載されている場合は正しくチェックアウト出来る。


#### 参考
[git submodule は癖がすごいとの噂だったが素直につきあっていけそうという話](http://www.d-wood.com/blog/2014/05/22_6257.html)
[Git submoduleの抑えておきたい理解ポイントのまとめ - Qiita](http://qiita.com/kinpira/items/3309eb2e5a9a422199e9)

場合によっては（みんなのgitリテラシーが高ければ？） submodule よりも subtree の方が向いてるかも
[git-subtree移行メモ - Qiita](http://qiita.com/shogo82148/items/04b29b195dbbb373152f)

### HEAD detached at xxxxxxxx が発生したら…

VisualStudioでsubmodule側を編集しているといつの間にか `HEAD detached at xxxxxxxx` という状態になっていることがある。

```bash
> git st
HEAD detached at 6bc06705
nothing to commit, working tree clean
```

解消方法を手順だけ記載。

```bash
> git co -b tmp_branch   ※これで変更がちゃんとブランチに紐付く
> git co master   ※変更をmasterブランチに取り込む場合
> git merge tmp_branch   ※これで変更がmasterに取り込まれた
> git branch -d tmp_branch ※不要なブランチは削除しておく
```

参考: [detached HEAD状態から元に戻すコマンド](http://devlights.hatenablog.com/entry/20130417/p1)
関連コマンド: [detached HEAD から脱出する方法を git の内部構造から探る - Qiita](http://qiita.com/Kuniwak/items/4e5d55d6888e5f6411fe)

## リポジトリの特定ディレクトリを別リポジトリにする方法
もちろん、コミット履歴を残したまま。

```bash:GitBashでもWindowsコマンドプロンプトでも可
$ mkdir new_repo

# まず丸ごとclone
$ git clone org_repo new_repo
$ cd new_repo

# proj_a/subdir というディレクトリ（の中身）を最上位ディレクトリに引き上げて、
# それ以外は履歴もろとも全て削除。
# --allを付けることでBranchも上手いことやってくれるらしい
$ git filter-branch --subdirectory-filter proj_a/subdir -- --all
```

#### 参考
[gitの特定のディレクトリをブランチ毎別リポジトリにする方法]
(http://qiita.com/wataru420/items/f1eb7cf37fe5ba4980c3)
[git の特定ディレクトリをコミットログ等込みで別プロジェクトへ移動]
(http://subtech.g.hatena.ne.jp/secondlife/20101108/1289221814)

## bareリポジトリを共有ディスクに置いて中央リポジトリとして複数人で使う
GitHubのようなクラウドソースコード管理が使えるわけでもなく、かといって社内でGitBucketのようなオンプロミス型のGitサーバーを立てるのも面倒…
そんな時、お手軽にNASなどの共有ディスクにgitリポジトリ（bareリポジトリ）を置いて、みんなでpush/pullしても大丈夫か？リポジトリが壊れたりしないか？ということについて調べてみた。
探し方が悪かったのか、なかなかこれといった情報が見つからなかったが、やっと見つけた。

[Gitサーバー - プロトコル](https://git-scm.com/book/ja/v2/Git%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB)

結論としては、『複数人でもちゃんと使える』とのこと。

NAS等の共有ディスク上にbareリポジトリを生成する手順は次項参照。

## ローカルのリポジトリをcloneして、bareリポジトリを生成する手順

```bash:GitBashでもWindowsコマンドプロンプトでも可
$ cd ローカルリポジトリ

# リポジトリ名.git というディレクトリが作られて、その中にbareリポジトリができる
$ git clone --bare . //server/share/path/リポジトリ名.git

# originの宛先URLを変更する ※追加の場合は set-url ではなく add
$ git remote set-url origin //server/share/path/リポジトリ名.git

# 確認
$ git remote -v
$ git remote show origin

# 作成したbareリポジトリにのmasterとlocalのmasterを繋げておく ※upstream branch としてセットしておく
$ git push -u origin master
Branch master set up to track remote branch master from origin.
Everything up-to-date
```

#### 参考: remoteリポジトリを指定する文字列
以下のように `file://` を付けて記述してもいいらしいが、データ転送がとても非効率とのこと。

```
file:////server/share/repo
```
[参考: Gitサーバー - プロトコル](https://git-scm.com/book/ja/v2/Git%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB)

## リポジトリ（作業ディレクトリ）ごとにcommitユーザー(Author）を設定する

```bash
$ cd リポジトリ（作業ディレクトリ）のトップ
$ git config user.name "xxxx"
$ git config user.email "xxxx@xxxxx.xxx"
```
ローカルの .git/config に設定値が書き込まれる。

```:.git/config
[user]
	name = xxxx
	email = xxxx@xxxxx.xxx
```

確認コマンドはこちら。

```bash
$ git config --list
$ git config --list --global
$ git config --list --local
```

## .gitignore の生成
.gitignoreをコマンド一発で生成する。
[気付いたら.gitignoreはgiboで自動生成する時代になっていた - Qiita](http://qiita.com/tmknom/items/c4bcebe17d25381fa45d)

一度インストールすれば、あとはちょっとしたコマンドを叩くだけ。

```bash:コマンド例
# ローカルに保存しているgitignoreのテンプレートをupdate
$ gibo -u
# テンプレートを一覧表示
$ gibo -l
# 生成
$ gibo Ruby Rails JetBrains SublimeText VisualStudioCode Linux macOS Windows >> .gitignore
```

## SourceTreeにリポジトリを追加したら、不意に「Select a credential helper」というメッセージが表示された [Windows10]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36808/25512c92-73c3-5f53-a4ff-d786f2ec6abd.png)

「Always use this from now on」にチェックを付けて、2番目の **manager** を選択すれば、以降何も聞かれなくなった。

## その他

[Gitのリモートリポジトリの名前を付け替える](http://qiita.com/kon_yu/items/e9ebd7f778df430a4223)