gitの使い方がいまいちわからない
2013-05-08 この記事は古い上に間違ったオペレーションで迷走した記録なので参考にしないでください。
あえていうなら、非bareなリポジトリでpushしあうと危険であるという例になるかと。
なにやら時代はgitらしいのでとりあえず使ってみた。が、使い方がよくわからない。
想定利用状況
たぶん分散管理型バージョン管理システムの想定からは外れているんだろうけど、リポジトリは中央集権管理としたい。
個人利用やオープンソースなんかだと分散のほうがいいんだと思うけど、企業内でしかも受託開発みたいなクローズドなプロジェクトだと分散されていては困る。必ずマスターとなるリポジトリがあって、最終的にはそこで全てが管理されているようにしたい。
とはいえ、開発中はローカルにリポジトリのコピーが持てたり、チームに分かれてそれぞれのリポジトリで作業するなんていうのもいいと思うので、その辺は分散されていてもいいと思う。いやむしろ気軽に作業できるローカルリポジトリは待ち望んでいた。ただし、いずれにしても最終的にはマスターリポジトリに集約されなければ困る。納品するのに派生版がいくつもあるとか最新版がどこにあるのかわからないなんて状況では困る。
なお、ユーザマニュアルのリポジトリ公開のところに書いてあるような、お互いでpullし合うような運用は考えたくない。中央に向かってみんなで更新していくスタイルとしたい。
使ってみる(間違った使い方っぽい)
ベストプラクティスの模索どころか、そもそもgit自体を触ったことがないけど、チュートリアルや巷のブログを見ながらやってみた。
1.リポジトリの作成。
mkdir test-repo cd test-repo git init
Initialized empty Git repository in .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
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^)/
よくわからないけど
中央管理すると決めたリポジトリの中で作業してはいけない?
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で作業するソリューション。