Gitがv2.3.0になって、Push to Deployがしやすくなって便利


# 概要

割と便利だったので勢いで書いた。

開発用に用意しているサーバー（以下remote）でcloneしてあるリポジトリに対して、「masterとかfeature branchを更新したいなー」と思って、おもむろにpushしようとすると怒られる場合があるんですよね。

```bash:remote
git status
#=> On branch master
#=> Your branch is up-to-date with 'origin/master'.
#=> nothing to commit, working directory clean
```

```bash:client
git push remote master
#=> （中略）
#=> To takoba@123.45.67.89:/hoge/fuga
#=>  ! [remote rejected] master -> master (branch is currently checked out)
#=> error: failed to push some refs to 'takoba@123.45.67.89:/hoge/fuga'
```

それが、最近リリースされた[Git v2.3.0](https://github.com/blog/1957-git-2-3-has-been-released)だと、`receive.denyCurrentBranch = updateInstead`を設定しておけば怒られずに済むし、安全にPush to deployができるようになってて便利だよ、というエントリ。

# 通常の挙動

defaultでは、“（__non-bare__なリポジトリが）pushしたいbranchをremoteでcheckoutしている”状態だと、pushできないようになってます。
これはv2.3.0でも同様です。

```bash:client
git push remote master
#=> （中略）
#=> remote: error: refusing to update checked out branch: refs/heads/master
#=> remote: error: By default, updating the current branch in a non-bare repository
#=> remote: error: is denied, because it will make the index and work tree inconsistent
#=> remote: error: with what you pushed, and will require 'git reset --hard' to match
#=> remote: error: the work tree to HEAD.
#=> remote: error: 
#=> remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
#=> remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
#=> remote: error: its current branch; however, this is not recommended unless you
#=> remote: error: arranged to update its work tree to match what you pushed in some
#=> remote: error: other way.
#=> remote: error: 
#=> remote: error: To squelch this message and still keep the default behaviour, set
#=> remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
#=> To takoba@123.45.67.89:/hoge/fuga
#=>  ! [remote rejected] master -> master (branch is currently checked out)
#=> error: failed to push some refs to 'takoba@123.45.67.89:/hoge/fuga'
```

こんなかんじに拒否られます。
なので、ぼくは以下のような流れで“擬似的なmaster（tmp）にmasterをmerge”していました。

```bash:remote
git checkout -b tmp
#=> Switched to a new branch 'tmp'

git status
#=> On branch tmp
#=> nothing to commit, working directory clean
```

```bash:client
git push remote master
#=> （略）
```

```bash:server
 git merge master 
#=> Updating 1234567..abcdefg
#=> Fast-forward
#=>  a.php     | 16 ++++++++--------
#=>  b.html    | 20 ++++++++++++++++++++
#=>  c.js      | 28 +++++++++++++++++++++++++---
#=>  d.css     | 10 ----------
#=>  e.css     |  6 ++++++
#=>  README.md |  1 + 
#=>  5 files changed, 60 insertions(+), 21 deletions(-)
#=>  create mode 100644 e.css
#=>  delete mode 100644 d.css
```

## “bareなリポジトリ”とは？

ちなみに、`git clone`には`--bare`というオプションがあります。
bareなリポジトリ（bare repository）とは、簡単に言えば“GitHubのようにソースコードをホスティングするサーバーで用意するリポジトリ”です。

`git clone --bare https://github.com/takoba/fuga.git`というかんじでcloneしたリポジトリは、通常のリポジトリとディレクトリ構成が異なります。
通常ソースコードが配置されている“作業ディレクトリ”を持たず、commitデータやbranchのデータなどGitリポジトリとしての情報のみを主に保持しているリポジトリとなります。

```bash
git clone --bare https://github.com/takoba/fuga.git
#=> Cloning into bare repository 'fuga'...
#=> （中略）
#=> Checking connectivity... done.

# `.git/`がrepositoryのrootにそのまま展開されてるかんじ？
ls -al ./fuga
#=> total 32
#=> drwxr-xr-x  10 takoba  takoba   340  2 16 11:41 .
#=> drwxr-xr-x+ 56 takoba  takoba  1904  2 16 11:41 ..
#=> -rw-r--r--   1 takoba  takoba    23  2 16 11:41 HEAD
#=> -rw-r--r--   1 takoba  takoba   175  2 16 11:41 config
#=> -rw-r--r--   1 takoba  takoba    73  2 16 11:41 description
#=> drwxr-xr-x  11 takoba  takoba   374  2 16 11:41 hooks
#=> drwxr-xr-x   3 takoba  takoba   102  2 16 11:41 info
#=> drwxr-xr-x   4 takoba  takoba   136  2 16 11:41 objects
#=> -rw-r--r--   1 takoba  takoba   291  2 16 11:41 packed-refs
#=> drwxr-xr-x   4 takoba  takoba   136  2 16 11:41 refs
```

# v2.3.0でPush to deployするには

`git config`で、`receive.denyCurrentBranch`を`updateInstead`に設定しておきます。

```bash:remote
git config --get receive.denyCurrentBranch
#=> (empty)

git config receive.denyCurrentBranch updateInstead
git config --get receive.denyCurrentBranch
#=> updateInstead

git status
#=> On branch master
#=> Your branch is up-to-date with 'origin/master'.
#=> nothing to commit, working directory clean
```

```bash:client
git push remote master 
#=> （中略）
#=> To takoba@123.45.67.89:/hoge/fuga
#=>    25da1ec..c967392  master -> master
```

無事pushできました。

## `receive.denyCurrentBranch = ignore`はこわい

v2.3.0以前でも`receive.denyCurrentBranch`に`ignore`を設定しておくとpushできたりしました。
ですが、[割とリポジトリがぶっ壊れるようになる](http://d.hatena.ne.jp/nishiohirokazu/20120416/1334548800)ので微妙。

なので、なるべく利用しない方がよいです。
複数のclientから操作するケースでは__commitが闇に葬られる可能性がある__ので、使わないように...


# 参考

- [Git 2.3 has been released](https://github.com/blog/1957-git-2-3-has-been-released)
- [Git - サーバー用の Git の取得](http://git-scm.com/book/ja/v1/Git-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E7%94%A8%E3%81%AE-Git-%E3%81%AE%E5%8F%96%E5%BE%97)
  - bare repositoryについて
- [git config --add receive.denyCurrentBranch ignoreはどう危険なのか - 西尾泰和のはてなダイアリー](http://d.hatena.ne.jp/nishiohirokazu/20120416/1334548800)