git で不必要に複雑なコミット家系図にしてしまっていた原因が少しわかった
git 初心者。メンテナンス用の maint と新規開発用の master、二つの統合ブランチがあるだけなのに、なぜかコミット家系図が込み入ったものになってしまう、しかも望まないマージが行われたりしてしまう、という現象に悩んでいた。ひとつずつ手順を踏んで、毎回 gitk でコミット家系図を確認していて、原因が少しわかった。
# 最初は 1 から 9 までの数字が一行ごとに並んだファイル prime を作る。 # git 管理。最初のコミットを A とする。 mkdir first; cd first; seq 9 > prime git init; git add .; git commit -m 'first commit'; cd .. # 上流リポジトリ repos を作って、そこに作業用ディレクトリから push する。 # push できれば作業用ディレクトリ first は要らないから消しちゃう。 mkdir repos; cd repos; git --bare init --shared; cd .. cd first; git push ../repos master; cd .. rm -fr first # プロジェクトを進行する人 a さんと b さん、それぞれのリポジトリを # sidea と sideb に作る。 git clone repos sidea; git clone repos sideb # a さんの作業。10 までの素数表を作っていく統合ブランチ p10 を作る。 cd sidea git branch p10 origin/master # master ブランチはとりあえず 20 までの素数表を作っていくことにする。 git checkout master seq 19 > prime; git commit -a -m 'seq 19' # ここで作られたコミットを B とする。 # ブランチ master, p10 を push しておく。 git push origin master git push origin p10 # 奇数を抜き出す変更を行う。変更はリモート追跡ブランチ origin/p10 から # 起こしたトピックブランチ topic 上で行う。 git checkout -b topic origin/p10 mv prime tmp; awk 'NR%2' tmp > prime; rm tmp git commit -a -m 'odd' # ここでできたコミットを C とする。 # 各統合ブランチ master, p10 で topic を併合して push する。 git checkout master git pull origin master # ... (1) git merge topic git checkout p10 git pull origin p10 # ... (2) git merge topic git push # topic を master に併合したときに作られたコミットを D とする。
(2) の直前、
p10,origin/p10 | topic | | v v A----C \ \ \ \ B----D ^ ^ | | | master origin/master
となっている。ここで (2) を実行しても変化はない。けれど、(2) のところで引数なしの git pull を実行してしまうと次のようになる。
origin/p10 | topic | | v v A----C \ \ \ \ B----D ^ ^ | | | master p10,origin/master
ブランチ p10 の先頭が B になってしまう。A->B の変更はブランチ p10 に含めたくないにも関らず。この状態で topic を併合すると B と C を親とした新しいマージコミット E が作られてしまう。
origin/p10
| topic p10
| | |
v v v
A----C----E
\ _\__/
\ / \
B----D
^ ^
| |
| master
origin/master
git pull は git fetch + git merge だから、無闇に pull すると現在のチェックアウトしてあるブランチに思いもよらぬブランチ (origin/master かな) が併合されてしまう、ってことだろうか。とりあえずはブランチを明示して git pull する、ということでこのような現象は防げそうな気がしている。けど、何かまだ根本的に間違って理解しているかもしれない。
追記
引数なしで git pull したときに、どのリポジトリのどのブランチをマージするかは、構成変数に記述されていることがわかった。
[takeyuki@sunya sidea]$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = /home/takeyuki/gitest/2500/repos [branch "master"] remote = origin merge = refs/heads/master [branch "p10"] remote = origin merge = refs/heads/master [branch "topic"] remote = origin merge = refs/heads/p10
ブランチ p10 を作ったときには origin/master の先頭コミットを起点にしたから、branch.p10.merge は refs/heads/master、すなわち origin/master になっているわけだ。だから p10 をチェックアウトした状態で git pull すると origin/master がマージされる、と。
ブランチを明示して git pull するようにする、というのはひとつの解決策だけれど、引数なしの git pull で大丈夫なように構成変数を書き換えておく、というのも手だろう。
[takeyuki@sunya sidea]$ git config --unset branch.p10.merge master [takeyuki@sunya sidea]$ git config --add branch.p10.merge refs/heads/p10 [takeyuki@sunya sidea]$ git config --local -l core.repositoryformatversion=0 core.filemode=true core.bare=false core.logallrefupdates=true remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* remote.origin.url=/home/takeyuki/gitest/2500/repos branch.master.remote=origin branch.master.merge=refs/heads/master branch.p10.remote=origin branch.p10.merge=refs/heads/p10 branch.topic.remote=origin branch.topic.merge=refs/heads/p10