# Git

## はじめての

絵があって簡単で面白い

↓↓↓↓↓↓↓↓↓↓↓↓

<http://kinokoru.jp/archives/1017>

アニメーションで面白い

↓↓↓↓↓↓↓↓↓↓↓↓

<http://k.swd.cc/learnGitBranching-ja/>

コミット取り消し

<http://freak-da.hatenablog.com/entry/20111105/p1>

## コマンド

* commitする バージョンを1つ進める
* stageする 特定の変更内容をindexに登録する(次回commit分に含める)
* index 次回commit分のファイル一覧

|*.コマンド|*.意味|

|git init|カレントディレクトリ以下をgit管理下に置く|

|git add |をstageする。に.(ピリオド)を指定すると変更分全て|

|git commit -m "" | 1行メッセージ付きでcommitする|

|git status | stage(to be commited)と変更分(not updated)の状態を確認する|

|git reset \[]| (git logで表示されるハッシュ文字列部分)まで戻る。指定が無ければindexを全てunstageする|

|git log \[--format=oneline]|commitの履歴を見る。オプションでformatが幾つか指定出来る|

|git rm|ファイルを削除してgitに削除したことを伝える|

|git clean -fdxn ←確認 危険！必ず確認すること 実行→ git clean -fdx |管理対象外のファイルを削除する|

## 初期設定

```
git config --global user.name "your name"
git config --global user.email aaa@gmail.com
git config --global color.ui auto
```

## ヘッドの種類

* HEAD 現在作業しているワーキングツリーのコミットを指します。コミットが重なる度に先頭へ移動していきます。また git reset や git merge git rebase などのコマンドでも移動します。
* ORIG\_HEAD git reset や git merge など、通常のコミットとは違う極端な HEAD の移動が発生した時に、移動前の HEAD のコミットを指します。
* MERGE\_HEAD git merge を実行して、コンフリクトなどが発生してマージをしている最中にマージ元のブランチの先頭のコミットを指します。
* CHERRY\_PIKC\_HEAD MERGE\_HEAD とほぼ同じですが git cherry-pick を実行した場合のものです。
* FETCH\_HEAD git fetch した時、リモートから取得したコミットの先頭のコミットを指します。

<http://tkengo.github.io/blog/2014/02/10/how-to-track-git-history/>

## 違い

### resetオプションの違い

コミットメッセージだけ修正してコミットし直したい：git reset --soft

変更内容を追加してコミットし直したい：git reset --mixed

コミット自体なかったことにしたい：git reset --hard

## 世代のたどり方

* ^ について ^ を指定することで、親のコミットを取得することができます。よく聞く話かとは思いますが git のコミットには必ず 1 つは親が存在します(一番最初のコミットを除いて)。親というのは要するに直前のコミットのこと。そしてマージが発生すると、そのマージコミットについては 2 つの親を持つことになります
* \~ について 親のコミットを取得できるのは ^ と同じなのですが \~ の方はマージコミットがあっても本流の方だけをたどっていきます。

## Gitの中身

中の挙動が詳しく書いてある

<http://koseki.hatenablog.com/entry/2014/04/22/inside-git-1>

## コミットメッセージ規約

1. WhyやHowを書くこと。 WhereやWhatはいらん、diffを見ればわかる
2. 言語は英語にする（最初の１文は必ず英語。そのあと補足で日本語は可）
3. １文の場合にはピリオドを付けない
4. 主語は省き時制は現在の文章形式にする
5. 文頭の英単語を大文字にする
6. ファーストコミットは「Initial commit」 とする
7. issueから発生したコミットは先頭に「\[refs #"issue番号"]」を記述する。クローズの場合は\[close #"issue番号"]
8. 命令形とする <http://stackoverflow.com/questions/3580013/should-i-use-past-or-present-tense-in-git-commit-messages/8059167#8059167>

### [コミットメッセージでよく使う英語](https://yutakatay.gitbook.io/katapedia/doc/komittomessjideyokuu)

### Gitリポジトリブラウザ比較

* Gitlab
* Gitosis
* Gitolite
* Gitorious インストールが難しいらしい
* Gitblit インストール簡単だが、機能がまだそろっていないらしい

### Gitリモートリポジトリでgit init --bare --sharedを付け忘れた場合

```
git config core.sharedRepository group
chmod -R g+ws hooks
chmod -R g+ws info
chmod -R g+ws objects
chmod -R g+ws refs
```

で--shareと同じ効果

### ブランチの定義

issue-XXX/(大分類名)/ブランチ名で命名

issue-123/chat\_timeline/extract-url\_linkみたいな感じ

1. developブランチ 開発を行うためのブランチ。開発者は、主にこのブランチ上で作業を行う。次に紹介するfeatureブランチなど、他のブランチで行った作業は、ここにマージされる
2. featureブランチ 主要な機能を実装するためのブランチ。機能の実装やバグフィックスなど、タスクごとにfeatureブランチを作成し、作業を行う
3. releaseブランチ リリースの準備を行うためのブランチ。プロダクトをリリースする前に、このブランチを作成し、微調整を行う。releaseブランチを作成することで、リリース準備と次のバージョンに向けた開発のコードを分けることができる
4. masterブランチ リリースしたソースコードを管理するためのブランチ。リリース作業を行うと、releaseブランチはmasterブランチへマージされて、リリースタグが打たれる。開発者は、このブランチへのコミットは行わない
5. hotfixブランチ リリースされたソフトウェアに緊急の修正を行うためのブランチ。このブランチでの修正内容は、すぐにリリースされるので、hotfixブランチはリリースを管理するmasterブランチへマージされる

## ルール

### 原則

* masterで作業しない。ブランチを作って作業すること
* ブランチでは1機能もしくは1バグのみ作業すること
* メジャーバージョンは常に動くようにしておくこと
* チケットとリンクをとれるようにすること

### [Gitワークフロー](https://yutakatay.gitbook.io/katapedia/doc/gitwkufur)

## リポジトリ名規約

* 基本すべて小文字
* "\_"でなく"-"の方がよい（使っている人が多いかつかっこいい）

## 便利なコマンドとオプション

### リポジトリ名取得

`basename $(git remote show origin -n | grep "Fetch URL:" | sed 's/.*://;s/.git$//'`

<https://stackoverflow.com/questions/15715825/how-do-you-get-git-repos-name-in-some-git-repository>

### ブランチ表示ログ

`git log --graph --branches --pretty=format:"%d [%h] "%s""`

## 統計情報

### 変更行数

```
git diff <branch name> --shortstat *.cc

git log --author="Taro Tanaka" --oneline --shortstat

git log --numstat --pretty="%H" | awk 'NF==3 {plus+=$1; minus+=$2} END {printf("+%d, -%dn", plus, minus)}'
```

### 総追加行数

```
git log --since=2013-01-01 --until=2013-06-30 --oneline --numstat --no-merges --pretty=format:"" | cut -f1 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'
git log -- <branch name> --oneline --numstat --no-merges --pretty = format: "" | cut -f1 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'
git log --since=2015-09-01  --oneline --no-merges --numstat --pretty=""  *.cc *.h  | cut -f1  | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'
```

### 総削除行数

```
git log --since = 2013-01-01 --until = 2013-06-30 --oneline --numstat --no-merges --pretty = format: "" | cut -f2 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'
```

### コントリビューター一覧

git shortlog -se | awk -F't' '{print $2,$3}'

### 総コミット回数

git log --since = 2013-01-01 --until = 2013-06-30 --oneline --no-merges | wc -l

### よくマージしている人

git log --merges --format="%cn" | sort | uniq -c | sort -r | head

### よく編集しているファイル

git log --name-only --pretty="format:" | grep -ve '^$' | sort | uniq -c | sort -r | head

### よくfu\*kって書く人

git log --pretty="format:%cn:%s" | grep fu.k | cut -d":" -f1 | sort | uniq -c | sort -r

## submodule

### 一括更新

git submodule foreach --recursive 'git checkout master; git pull'

### ルートリポジトリからsubmoduleを更新してpushしたい

`git submodule foreach --recursive git add -A .`

`git submodule foreach --recursive git commit -m 'submodule commit message'`

`git push --recurse-submodules=on-demand`

孫もいるなら

`git submodule foreach --recursive git push origin master`

じゃないとだめっぽい

<https://ja.stackoverflow.com/questions/17501/git-submodule%E3%82%92%E8%A6%AA%E3%81%AE%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%A8%E5%90%8C%E6%99%82%E3%81%AB%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88-%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84>

2017 Q3にリリースされる予定のgitで孫がいてもpushできるようになる見込み

<https://stackoverflow.com/questions/29564257/git-push-recurse-submodules-on-demand-is-not-truly-recursive>

### submoduleの削除

```
git submodule deinit vendor/pelican-sober
git rm vendor/pelican-sober
rm -rf .git/modules/path/to/submodule
git commit -a
```

### 履歴を保持したままリポジトリを統合する

```
# Fetch the submodule commits into the main repository
git remote add submodule_origin git://url/to/submodule/origin
git fetch submodule_origin

# Start a fake merge (won't change any files, won't commit anything)
git merge --allow-unrelated-histories -s ours --no-commit submodule_origin/master

# ここでgit log しても表示されない

# Do the same as in the first solution
git rm --cached submodule_path # delete reference to submodule HEAD
git rm .gitmodules             # if you have more than one submodules,
# 他のsubmoduleがある場合はviで対象部分のみ削除
# you need to edit this file instead of deleting!
rm -rf submodule_path/.git     # make sure you have backup!!
git add submodule_path         # will add files instead of commit reference


# Commit and cleanup
git commit -m "removed submodule"
# ここでgit log したら表示される
git remote rm submodule_origin
```

<https://stackoverflow.com/questions/1759587/un-submodule-a-git-submodule>

## Tips

### 過去のコミットの名前を全書き換えする

`git filter-branch -f --env-filter "GIT_AUTHOR_NAME='sea_mountain'; GIT_AUTHOR_EMAIL='valid_email@example.com'; GIT_COMMITTER_NAME='sea_mountain'; GIT_COMMITTER_EMAIL='valid_email@example.com';" HEAD`

### No newline at end of fileを一括で修正する

```shell
for i in $(git ls-files | xargs -i file {} | grep --line-buffered "ASCII\|UTF-8" | cut -d ":" -f 1); do  echo $i; \
 if diff /dev/null "$i" | tail -1 | \
  grep '^\\ No newline' > /dev/null; then echo >> "$i"; \
fi; done
```

### 一括で改行コードを修正する

```shell
git ls-files | xargs -i file {} | grep --line-buffered "ASCII\|UTF-8" | cut -d ":" -f 1 | xargs -i nkf  -Lu --overwrite {}
```

### gitconfigにユーザー情報を入れたくない

GIT\_AUTHOR\_NAME="" GIT\_AUTHOR\_EMAIL=""

を定義しておけばよい

### 一部のファイルのみをstashする

stashしないファイルをgit addする `git stash -k` `git reset` # ステージからもとに戻す

### ノンパスワード設定

ssh-keygen -t rsa -C "<your.email@example.com>" -b 4096

cat \~/.ssh/id\_rsa.pub

確認

ssh -T <git@example.com>

あとは、

https\:// を <git@github.com>:に切り替える

これを使えばhttpsをsshに置き換えてくれる

`git config --global "url.git@github.com:.pushinsteadof" "https://github.com/"`

### push先とfetch先を変える

`git remote set-url --push origin git@github.com:User/forked.git`

`pushurl = http://192.168.1.20/Test/hubot-rocketchat.git`

<http://sleepycoders.blogspot.jp/2012/05/different-git-push-pullfetch-urls.html>

<http://stackoverflow.com/questions/948354/default-behavior-of-git-push-without-a-branch-specified>

### 複数のコミットをまとめる

git rebase -i xxx

pick->s

<http://iwb.jp/git-commit-rebase-squash/>

## Troubleshooting

### パスワードを入力するときに ( gnome-ssh-askpass:11826 ) : Gtk-WARNING \*\*: cannot open display: とか言われる

unset SSH\_ASKPASS

### 間違ってgit resetしてしまった

* コミットしているなら git reflog

git rest --hard xxx

* ステージングしている git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

git show xxx

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") xxx

<http://stackoverflow.com/questions/7374069/undo-git-reset-hard-with-uncommitted-files-in-the-staging-area>

* ステージングしていない 諦める

### EUCでコミットメッセージを書いてしまった

`git filter-branch ~~f --msg-filter 'nkf -w' -~~ --all`

<http://hiroom2.jimdo.com/2015/07/09/git-filter-branch%E3%81%A7%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E4%B8%80%E6%8B%AC%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B/>

### gitignoreでホワイトリスト化できない

階層構造になっていると工夫しないと認識されない

```
/*
/.*
/.*/*
/.*/*/*
```

という感じに階層分だけ最初に除外する。

ディレクトリがある場合は

/\*

!/src/

/src/\*

!/src/hello.c

というように上のディレクトリを一回対象外にして入れ直す

<http://seesaawiki.jp/aki/d/.gitignore%20%A5%D5%A5%A1%A5%A4%A5%EB%A4%CE%BD%F1%A4%AD%CA%FD>

### .gitignoreが反映されない

git rm -r --cached .

git add .

git commit -m "Update .gitignore"

### でかいファイルをコミットしてしまった場合

重いファイルをコミットした場合、ツリーから消さない限りリポジトリが重くなってしまう。

```
git filter-branch --force --index-filter  'git rm --cached --ignore-unmatch でかいファイル.tar.gz'  --prune-empty --tag-name-filter cat -- --all
```

これでツリー上から完全削除される

<http://www.walbrix.com/jp/blog/2013-10-github-large-files.html>

"軽量化の仕方":<http://techracho.bpsinc.jp/baba/2012\\_05\\_22/5594>

### でかくなったリポジトリの原因を特定する

これが一番良さそう

```
#my problem was that I had there a refs/remotes/origin/master line for a remote repository, delete it, otherwise git won't remove those files
check .git/packed-refs
#(optional)to check for the largest files
git verify-pack -v .git/objects/pack/#{pack-name}.idx | sort -k 3 -n | tail -5
#(optional)to check what are those files
git rev-list --objects --all | grep a0d770a97ff0fac0be1d777b32cc67fe69eb9a98
#to remove a file from all revisions
git filter-branch --index-filter 'git rm --cached --ignore-unmatch file_names' -- --all
#to remove git's backup
rm -rf .git/refs/original/
rm -rf .git/logs/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

# old
#to expire all the loose objects
#git reflog expire --all --expire='0 days'
#to check if there are any loose objects
#git fsck --full --unreachable
#repacking
#git repack -A -d
#to finally remove those objects
#git prune
```

リモートリポジトリにあげたら容量がもとに戻ってしまうので、リモートリポジトリを一旦削除して再度pushする必要がある。

<http://stackoverflow.com/questions/2164581/remove-file-from-git-repository-history>

[gitのリポジトリがでかくなったときの削減の昔のやり方](https://github.com/yutakatay/katapedia/blob/master/doc/git%E3%81%AE%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%8C%E3%81%A7%E3%81%8B%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%8D%E3%81%AE%E5%89%8A%E6%B8%9B%E3%81%AE%E6%98%94%E3%81%AE%E3%82%84%E3%82%8A%E6%96%B9.md)

### 一部のファイルだけ違うブランチのものを使う

`git checkout ブランチ名 ファイル名`

### コミットするユーザやメールアドレスを間違えた場合

コミット時のユーザやメールアドレスを変更するとき

git commit --amend --author="sea\_mountain <dummy_email_address@example.com>"

### 結構前のコミットするユーザやメールアドレスを間違えた場合

$ git rebase -i <コミット>

1. エディタが開くので以下のように変更して保存
2. (変更後)対象のコミットをeditに変更 $ git commit --amend -m "コミットメッセージ" --author="user.name \<user.email>"

$ git log

$ git rebase --continue

### 管理下のファイルの行末のスペース（trailing whitespace）を削除する

`git ls-files | sed -i 's/[ t]*$//'`

### refspecやremoteやrefsやらがわからなくなったとき

以下のサイトにわかりやすく解説されている。

<http://d.hatena.ne.jp/hokaccha/20120404/1333507076>

公式文書は難解なので。。。

### リモートリポジトリのコミットログを修正する

1. git checkout -b ローカルのブランチ名 origin/リモートのブランチ名 でブランチを切ります。
2. git rebase -iします。
3. git push origin +ローカルのブランチ名:リモートのブランチ名 して強制的にコミットツリーを変更します。 他のリポジトリから変更を取得する際は、git fetchしてgit rebase origin/リモートのブランチ名 としましょう。

### コミット日付をタイムスタンプに復元

```
for FILE in `git ls-files`; do
TIME=`git log --pretty=format:%ci -n1 $FILE`
echo $TIME't'$FILE
STAMP=`date -d "$TIME" +"%y%m%d%H%M.%S"`
touch -t $STAMP $FILE
done
```

### Gitのルートディレクトリに移動する

cd "$(git rev-parse --show-toplevel)"

### commitの際にオーナー情報を入れていなかった

```
git config --global user.name "your name"
git config --global user.email aaa@gmail.com
git commit --amend --reset-author
```

OR

.gitconfig

```
[user]
name = name
email = mail@gmail.com
```

### Gitでプロキシを通すURLと通さないURLを使い分ける

どうやらversion:1.8ではダメのよう

\~/.gitconfig

```
[http "http://192.168.1.30"]
postBuffer = 524288000
proxy =
[http "http://192.168.1.40"]
postBuffer = 524288000
proxy =
[http "http://192.168.1.50"]
postBuffer = 524288000
proxy =
#[http]
#    postBuffer = 524288000
#    proxy = http://192.168.1.30:10080
#[https]
#    postBuffer = 524288000
#    proxy = http://192.168.1.30:10080
[url "https://"]
insteadOf = git://
```

## Trivia

### objectsディレクトリの中

idxファイル：インデックスを保持

pack ファイル：データ実体

### おもしろいinitial commit

This is where it all begins...

Commit committed

Version control is awful

COMMIT ALL THE FILES!

The same thing we do every night, Pinky - try to take over the world!

Lock S-foils in attack position

This commit is a lie

I'll explain when you're older!

Here be Dragons

Reinventing the wheel. Again.

This is not the commit message you are looking for

Batman! (this commit has no parents)

## FAQ

### <git@github.com>形式でダウンロードできない場合

\~/.gitconfig

```
[url "http://github.com/"]
insteadOf = git@github.com:
```

### [Gitで大量のファイルの中から必要ファイルのみをaddする方法](https://yutakatay.gitbook.io/katapedia/doc/gitdenofairunokarafairunomiwoaddsuru)

### [CentOS6.x系でhttp認証に失敗する](https://github.com/yutakatay/katapedia/blob/master/doc/CentOS6.x%E7%B3%BB%E3%81%A7http%E8%AA%8D%E8%A8%BC%E3%81%AB%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B.md)

### [GitのGUI比較](https://yutakatay.gitbook.io/katapedia/doc/gitnogui)

### 現在のディレクトリでgit clone

`git clone http://xxx .`

※古いバージョンだとできない？v1.8系は無理だった。

### .gitignoreに設定しているファイルがリモートに存在するエラーの解決法

エラー文：

error:The following untracked workding tree files would be overwritten by merge:

解決法：

ようわからんけど、gitでリモートのブランチにローカルを強制一致させたい時

git fetch

git reset --hard FETCH\_HEAD
