gitの使い方がいまいちわからない


2013-05-08 この記事は古い上に間違ったオペレーションで迷走した記録なので参考にしないでください。
あえていうなら、非bareなリポジトリでpushしあうと危険であるという例になるかと。


なにやら時代はgitらしいのでとりあえず使ってみた。が、使い方がよくわからない。

想定利用状況

たぶん分散管理型バージョン管理システムの想定からは外れているんだろうけど、リポジトリは中央集権管理としたい。
個人利用やオープンソースなんかだと分散のほうがいいんだと思うけど、企業内でしかも受託開発みたいなクローズドなプロジェクトだと分散されていては困る。必ずマスターとなるリポジトリがあって、最終的にはそこで全てが管理されているようにしたい。


とはいえ、開発中はローカルにリポジトリのコピーが持てたり、チームに分かれてそれぞれのリポジトリで作業するなんていうのもいいと思うので、その辺は分散されていてもいいと思う。いやむしろ気軽に作業できるローカルリポジトリは待ち望んでいた。ただし、いずれにしても最終的にはマスターリポジトリに集約されなければ困る。納品するのに派生版がいくつもあるとか最新版がどこにあるのかわからないなんて状況では困る。


なお、ユーザマニュアルのリポジトリ公開のところに書いてあるような、お互いでpullし合うような運用は考えたくない。中央に向かってみんなで更新していくスタイルとしたい。

使ってみる(間違った使い方っぽい)

ベストプラクティスの模索どころか、そもそもgit自体を触ったことがないけど、チュートリアルや巷のブログを見ながらやってみた。


1.リポジトリの作成。

mkdir test-repo
cd test-repo
git init
Initialized empty Git repository in .git/

リポジトリ管理領域の.gitディレクトリが作成される。


2.ファイルの追加&コミット。

echo "one" >test.txt
git add test.txt
git commit -m "first commit"
Created initial commit 263dd6c: first commit
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 test.txt


3.忙しくなってきたのでサトウさんに手伝ってもらうことにした。

[サトウさん]
git clone ssh://paselan_host/paselan/test-repo sato-repo
Initialized empty Git repository in /home/sato/sato-repo/.git/
0 blocks


2013-05-08追記
中央集権的にするならこの時点でbareリポジトリを作成して、お互いがそこを参照するべき。


4.サトウさんがファイルを更新。

[サトウさん]
cd sato-repo
echo "2" >>test.txt
git add test.txt
git commit -m "Added 2 by sato"
Created commit 02e82a2: Added 2 by sato
 1 files changed, 1 insertions(+), 0 deletions(-)


5.ちょうどキリがよいところまで作業が進んだので、マスターにpushするサトウさん。

git push origin master
Counting objects: 5, done.
Writing objects: 100% (3/3), 267 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To ssh://paselan_host/paselan/test-repo/.git
   263dd6c..02e82a2  master -> master


2013-05-08追記
現在はreceive.denyCurrentBranchのデフォルトがrefuseとなっており、非bareリポジトリで相手がチェックアウト中のブランチにはpushできないようになっている(下記のようなエラーになる)。
この設定をignoreに変更すると上記と同様にpushできるようになる(危険なのでしないほうがよい)。

Counting objects: 5, done.
Writing objects: 100% (3/3), 258 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
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 ssh://paselan_host/paselan/test-repo
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'ssh://paselan_host/paselan/test-repo'


6.そして自分もtest.txtを更新する(サトウさんが更新してpushしてくれたのには気づいていない)。

[自分]
cat test.txt
one

echo "two" >>test.txt
git add test.txt
git commit -m "Added two by paselan"
Created commit 1c57319: Added two by paselan
 1 files changed, 1 insertions(+), 1 deletions(-)

えっ、コミットできちゃうの!? コンフリクトじゃないの!?
サトウさんはpushしたわけだから、自分のリポジトリにはサトウさんの更新が入ってるよね!?
え、なにこれ、上書き?


2013-05-08追記
注意して見ると"1 deletions(-)"というのに気づく。これはサトウさんの追加した"2"のこと。
コミットする時点ではサトウさんのpushしたコミットはできているのだが、ワークツリーとインデックスはサトウさんがpushしてくる前の状態であり、その内容でコミットが作成される。


7.サトウさんpullしてみる。

[サトウさん]
git pull
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From ssh://paselan_host/paselan/test-repo/
   02e82a2..1c57319  master     -> origin/master
Updating 02e82a2..1c57319
Fast forward
 test.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
cat test.txt
one
two

上書きされてる\(^o^)/

よくわからないけど

中央管理すると決めたリポジトリの中で作業してはいけない?


2013-05-08追記
そもそも上記のは中央管理とは言えない。中央管理するならばどこかにbareリポジトリを作成するべき。


6の作業をする前にgit statusとかgit logするとサトウさんのpushが反映されていることは確認できる。
作業コピーよりも新しいものがリポジトリに入っているのに何の警告も無しにコミットできちゃうの?


2013-05-08追記
単に(pushされてきた)最新のコミットへの変更として扱われる。


そして、git checkoutなんて打っても変更があるっぽい表示が出るだけで更新はされない。
つかこれって作業コピーに変更があるっていう表示じゃね?


git checkout -fとやるか、git reset --hardとやると更新される。
なにこれ毎回こんなことやるの? 一回でも忘れたらアウト? タイミングが重なったらどっちにしろアウト?


2013-05-08追記
そんなことをしたら今度は自分の作業コピーの変更が失われるだけ。

作業コピーを作らないソリューション

面倒なので実行内容は省くが、誰かがpushしたやつに別の誰かがpushしようとしてコンフリクトした場合はしっかりエラーにしてくれるようだ。
つまり、マスターリポジトリは作業コピーを作らずにgit initして終了(2013-05-08追記 くどいようだがgit init --bare --shared)。あとは各自がcloneしてpush/pullという運用にすればいいっぽい。


うーん、いやしかしやっぱり納得できない……。

git-svnを使うソリューション

マスターリポジトリSubversionで運用して、git-svnで作業するソリューション。