git 1.7.5がリリースされました。変更点はいろいろありますが、なかでも今回initとcloneに追加された--separate-git-dirオプションに注目してみます。
git initすると普通はカレントディレクトリの下に.gitディレクトリが作られ、そこにリポジトリ情報が格納されます。ワークスペースとリポジトリが単一ディレクトリ下にあるわけですが、以下のような処理をしてリポジトリを破壊してしまった人も居ることでしょう。
$ find -print0 | xargs -0 sed -i 's/foo/bar/g'
.. .git/objects/以下が壊滅
この事故はfind -print0
ではなくgit ls-files -z
を使うことで回避できますが、find以外にも色々と起こりえますし、そもそもの話でいえばワークスペースとリポジトリが同じ場所にある必要はないわけです。
そこで--separate-git-dirオプションを使えば、望み通りリポジトリを別の場所に作ることができます。
$ mkdir -p /tmp/working
$ cd /tmp/working
$ git init --separate-git-dir=/tmp/repo
$ pwd
/tmp/working
$ ls -a
. .. .git
$ cat .git
gitdir: /tmp/repo
$ git config core.worktree
/tmp/working
$ ls -a /tmp/repo
. .. HEAD branches config description hooks info objects refs
この/tmp/working/.gitはディレクトリではなくファイルで、最近流行りのYAML風スタイルでリポジトリの場所を指しているだけです。あとは普通にaddやcommitやlogなどすれば普通に使えます。
あとgit configにcore.worktreeというのが追加されています。この設定は何かというと、たとえば
$ mkdir -p /tmp/otherwork
$ cd /tmp/otherwork
$ cp /tmp/working/.git .
$ touch foo
$ git status
# On branch master
nothing to commit (working directory clean)
$ git add foo
fatal: pathspec 'foo' did not match any files
$ git config core.worktree
/tmp/working
$ git config core.worktree `pwd`
$ git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# a.txt
# foo
nothing added to commit but untracked files present (use "git add" to track)
$ git add foo
$ git commit -m "commit from otherwork"
のように、core.worktree以外の場所からリポジトリを操作しようとすると上手く動かないようになってます。git configでカレントディレクトリに上書きすると、カレントディレクトリがワークスペースとして認識されて通常通り使えるようになる感じです。たぶん1リポジトリ1ワークスペースっていうgitの都合か仕様かなんかでしょう。.gitファイルをコピペせず素直にcloneしましょう。
これもinitと同じです。/tmp/repoからcloneし、その際ワークスペースはwork2へ、work2のリポジトリはrepo2へ作るだけです。.gitファイルが作られたりcore.worktreeが設定されるのもinitと同じです。
$ mkdir ~/awesome_proj
$ cd ~/awesome_proj
$ git init --separate-git-dir=$HOME/Dropbox/awesome_proj_repo
$ echo "<?php phpinfo();" > miracle.php
$ git add .
$ git commit -m "オレはやるぜオレはやるぜ"
.. on another machine ..
$ mkdir ~/proj_on_another_machine
$ cd proj_on_another_machine
$ echo "gitdir: $HOME/Dropbox/awesome_proj" > .git
$ git config core.worktree `pwd`
$ git checkout HEAD .
$ echo "var_dump(trim('0') == false); // true" >> miracle.php
$ git add -u
$ git ci -m "注意書きを追加"
今までDropbox + gitといえば、Dropboxにbareリポジトリを置いてそこからcloneするというsvn時代の伝統を引きずったスタイルが主流でしたが、--separate-git-dir
オプションのおかげでもっとモダンかつ手軽に実現できます。
別マシンで作業するときは上のような感じでやります。余計ややこしいですが気のせいです。
まあ実際のところ、未pushのデータのロストを少しでも回避するためにリポジトリだけ別にするとか、冒頭に書いたfindの悪魔から身を守るとか、そういったカジュアルな使い方くらいしか思いつきませんが、そのうち誰かがクールな利用法を思いついてくれるでしょう。後生に期待です。