今まで何となく使っていたGitだけど、自分でリポジトリを作ろうとしてハマったので今さらながら勉強してみた。
まず何にハマったかと言えば、ローカルでコミットした内容をサーバー側にプッシュしようとしたときに次のようなエラーが出て、何のことだかサッパリわからなかったということ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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'. |
まずnone-bareリポジトリって何? と思ったんだけど、わかってしまえば実に簡単なことだった。
none-bareリポジトリは普通にgit init
して作られるリポジトリで、例えば
1 2 3 |
> mkdir MyProject > cd MyProject > git init |
として作る。
そしてbareリポジトリを作る場合は
1 2 3 |
> mkdir MyProject.git > cd MyProject.git > git init --bare |
となる。違いはgit init
に--bare
オブションを付けるかどうかだけ。bareリポジトリの場合、ディレクトリ名に.gitを付けるのが慣習になっているらしい。
じゃあ実際にnon-bareとbareのリポジトリの違いは何なのかと言えば、次の図のように、non-bareリポジトリの場合はMyProject
ディレクトリの下に.git
ディレクトリが作られて、そこにリポジトリの本体がある。一方のbareリポジトリの場合はMyProject.git
がそのままリポジトリ本体となる。つまりリポジトリが裸のまま剥き出しになっているから「bare」というわけだ。そして.git
の代わりなので慣習としてディレクトリ名の最後に.git
を付けるのだろう。
次にギモンに思うのが、なんでbareとnon-bareのリポジトリがあるのか? ということ。これはGitとSVNの違いを考えるとよくわかる。
SVNの場合、上の図のようにリポジトリはサーバー側にしかない。そして、クライアント側はチェックアウトしてファイルをローカルの作業ディレクトリに取得する。そして作業ディレクトリに変更を加えたら、それをコミットしてサーバー上のリポジトリに反映させる。
これがGitになると次の図のように変わる。
サーバー上のリポジトリを取得するのは「クローン」であって「チェックアウト」ではないのがミソ。つまりローカルにもリポジトリを複製するのである。そしてローカルのリポジトリをチェックアウトして作業フォルダにファイルを展開する。そう、non-bareなリポジトリというのは、そのまま作業フォルダになっていて、リポジトリはそこにチェックアウトされた状態になる。
これが最初に出てきたエラーの原因。つまり、サーバー上にもnon-bareなリポジトリを作ってしまったから、そのリポジトリは作業フォルダにチェックアウトされた状態になっていたのである。そこに誰かがプッシュしてしまうと、チェックアウトしたときとリポジトリの状態が変わってしまう。もし、サーバー上で作業フォルダの変更をコミットしたら、プッシュされた変更は上書きされてしまうことになるので、エラーを出してプッシュを受け付けてくれなかったのだ。解決策は簡単で、サーバー上のリポジトリをbareなリポジトリにすればいい。bareなリポジトリは作業フォルダを持たないので、チェックアウトされることはなくなるのだ。エラーの中にごちゃごちゃ書いてある ‘receive.denyCurrentBranch’ は気にしなくて良い。