gitの基本的な使い方 === * 概要 gitの説明は多々あるが、複数のリモートレポジトリを使った操作について触れられているものは少ない。 そのため、ドキュメント化する。 * シナリオ(こじつけ) ほげ社ではgitを使ってhogewareというアプリを開発をしていた。 ある日、とある機能の開発を外注で頼んだ。 つまり、自社でもともとレポジトリを持っていたが、 あるタイミングでそのソースコードを外に出す。 そのあと、完成品を自分のレポジトリにマージしたい。 この差異、中間のcommit(変更履歴)はもらう必要がない。(もらったっていい) * 前提 ``` $ git config --global push.default matching ``` * 親プロジェクトをつくる ``` ### ここからプロジェクトが始まる(レポジトリを作ります) $ mkdir -p repos/hogeware $ cd repos/hogeware $ git --bare init Initialized empty Git repository in /home/kanai/repos/hogeware/ ### 作業用レポジトリを作りチェックアウト $ mkdir ~/workdir/ $ cd ~/workdir/ $ git clone file:///home/kanai/repos/hogeware/ Cloning into 'hogeware'... $ cd hogeware/ ### 最初のcommitをします(Changelogとsource1というファイルをcommit) $ echo "init" > Changelog $ echo "#include" > source1.c $ git add . $ git commit -m "init" ### ここまででMasterができた。 ### Branchを切ってticket1というパッチをつくる $ git branch ticket1 $ git checkout ticket1 Switched to branch 'ticket1' $ cat "#include" >> source1.c $ echo "#include" >> source1.c $ echo "#include" >> source2.c $ git commit -m "working" On branch ticket1 $ git add . $ git commit -m "working" [ticket1 ff36d86] working ### ticket1はまだ完成していなくて、もう少し変更を加えたとします $ echo "ticket1 done" >> Changelog $ git add . $ git commit -m "finish #1" [ticket1 3cac89f] finish #1 ### ticket1の変更が終わったのでmasterにmerge $ git checkout master $ git diff ticket1 > あてるパッチを比較 $ git merge --no-ff ticket1 -m "merge ticket1" > master Branchに対して(=今のBranch)にticket1をmergeします。 ### 以上では、masterブランチを開発用にしてきた。しかし、 ### 今後、Developmentで開発する気になってきた $ git branch development $ git checkout development > このBranchはmasterからbranchしたものなので、 > この時点ではmasterである ### せっかくなので、commit logをもう一個作る。 ### Ticket2というBnrachを切ってdevelopmentにcommit $ git branch ticket2 $ git checkout ticket2 $ echo "main(){printf("hoge");}" >> source2.c $ echo "ticket2 finish" >> Changelog $ git add . $ git commit -m "finish #2" ### 終わったのでdevelopmentにmerge $ git checkout development $ git merge --no-ff ticket2 -m "merge #2" ### ここで、自分のリモートレポジトリに最新をpushする ### tagをきってpush $ git tag 0.0 $ git push origin master $ git push origin development $ git push --tag $ git branch -a * development master ticket1 ticket2 remotes/origin/development remotes/origin/masterxx ``` * 開発先へのデプロイ ここで、ほげ社は開発先もげ社に資材を渡す。 こちらではこちらでの管理をしたい。 ``` @mieru2 ### もげ社では、自社でレポジトリサーバを立てる。 ### 通常のcloneでは、non-bareになるので、bareで作成する(push可能) $ git clone --bare 192.168.9.99:~/workdir/hogeware hogeware Cloning into bare repository 'hogeware'... ### mieru2はレポジトリサーバであり、workdirも持つ。 ### checkout用のディレクトリをつくり、cloneする。(これはnon-bare) $ mkdir ~/workdir $ cd ~/workdir $ git clone file:///home/kanai/repos/hogeware $ git branch -a * development ``` 次に、もげ社は、宗教的にmasterを開発用にしているので、自分のブランチは development = masterにしたいとする。 ``` $ git push origin :master # developmentに入って、ここから派生を行う $ git checkout development $ git remote set-head origin development > masterを一度消したいので、headをdevelopmentに向ける $ git branch -a * development $ git branch -D master > masterを消す Deleted branch master (was 9d126d7). $ git push origin :master > originからmasterを削除する To file:///home/kanai/repos/hogeware - [deleted] master $ git branch -m development master > masterをdevelopmentにrenameする $ git branch -a * master remotes/origin/HEAD -> origin/development remotes/origin/development remotes/origin/ticket1 remotes/origin/ticket2 ### この時点では、localのmasterは(rename前の)origin/developmentをむいている $ git push origin master Total 0 (delta 0), reused 0 (delta 0) To file:///home/kanai/repos/hogeware * [new branch] master -> master ### HEAD(remote)の向き先を変える $ git remote set-head origin master $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/development remotes/origin/master remotes/origin/ticket1 remotes/origin/ticket2 #$ git pull origin master # From file:///home/kanai/repos/hogeware # * branch master -> FETCH_HEAD # Already up-to-date. ### ちゃんとdevelopmentの内容が反映されている $ tail Changelog init ticket1 done ticket2 finish ### このままだと、push先はorigin/developmentになる $ git branch -vv issue101 6b0b1ad solve issue #101 issue102 0ec50bc #102 * master d8bcc0e [origin/development: ahead 2] merge #101 release e5f1387 merge #2 $ $ git push -u origin master Branch master set up to track remote branch master from origin. Everything up-to-date $ git branch -vv issue101 6b0b1ad solve issue #101 issue102 0ec50bc #102 * master d8bcc0e [origin/master] merge #101 release e5f1387 merge #2 ``` これで、一応、developmentをmasterにすることができる。(やる必要があるのかは別の話) 開発側で少しいじってみる。開発側はreleaseというブランチで開発元にコードを引き渡したい。 とりあえず、release branchだけつくる。 ついでに、開発元でやってたpatchブランチはいらないので消す。 ``` @mieru2 ### いらないの消す $ git branch release $ git push origin release Total 0 (delta 0), reused 0 (delta 0) To file:///home/kanai/repos/hogeware * [new branch] release -> release $ git push origin :ticket1 To file:///home/kanai/repos/hogeware - [deleted] ticket1 $ git push origin :ticket2 To file:///home/kanai/repos/hogeware - [deleted] ticket2 $ git branch -a * master release remotes/origin/HEAD -> origin/master remotes/origin/development remotes/origin/master remotes/origin/release ``` ここで、remoteのHEADをかえる ``` ### developmentを消そうとするとエラーとなる ### これは、remoteのorigin/HEADがdevelopmentをむいているため $ git push origin :development remote: error: By default, deleting the current branch is denied, because the next remote: error: 'git clone' won't result in any file checked out, causing confusion. remote: error: remote: error: You can set 'receive.denyDeleteCurrent' configuration variable to remote: error: 'warn' or 'ignore' in the remote repository to allow deleting the remote: error: current branch, with or without a warning message. remote: error: remote: error: To squelch this message, you can set it to 'refuse'. remote: error: refusing to delete the current branch: refs/heads/development To file:///home/kanai/repos/hogeware ! [remote rejected] development (deletion of the current branch prohibited) error: failed to push some refs to 'file:///home/kanai/repos/hogeware' ### この様子を確認する $ git remote show origin * remote origin Fetch URL: file:///home/kanai/repos/hogeware Push URL: file:///home/kanai/repos/hogeware HEAD branch: development Remote branches: development tracked master tracked release tracked Local branch configured for 'git pull': master merges with remote master Local refs configured for 'git push': master pushes to master (up to date) release pushes to release (up to date) ``` いくつかやり方はあるが、repos側でtrack先を変える。 ``` @mieru2: これはレポジトリ内での操作 $ cd ~/repos/hogeware/ $ git symbolic-ref HEAD refs/heads/master @mieru2: 作業ディレクトリでremoteを確認する $ cd ~/workdir/hogeware/ $ git remote show origin * remote origin Fetch URL: file:///home/kanai/repos/hogeware Push URL: file:///home/kanai/repos/hogeware HEAD branch: master ★track先がmasterになっている Remote branches: development tracked master tracked release tracked Local branch configured for 'git pull': master merges with remote master Local refs configured for 'git push': master pushes to master (up to date) release pushes to release (up to date) ### これで、remote origin/masterはmasterなので、deleteする $ git push origin :development To file:///home/kanai/repos/hogeware - [deleted] development ``` 折角なので、masterで開発してみる。 ``` @mieru2 $ git branch issue101 $ git checkout issue101 $ echo "issue101 finish" >> Changelog $ echo "int main(){}" >> source1.c $ git add . $ git commit Aborting commit due to empty commit message. $ git checkout master $ git commit -m "solve issue #101" $ git merge --no-ff issue101 -m "merge #101" Merge made by the 'recursive' strategy. Changelog | 1 + source1.c | 1 + 2 files changed, 2 insertions(+) $ git branch issue 102 $ git checkout issue102 $ echo "102" >> Changelog $ git add . $ git checkout master $ git commit -m "#102" $ git merge --no-ff issue101 -m "merge #101" $ git branch -D issue102 Deleted branch issue102 (was 0ec50bc). $ git branch -D issue101 Deleted branch issue101 (was 6b0b1ad). $ git push e5f1387..d8bcc0e master -> master ``` ここで、開発先は、もう一台のPCに開発先のレポジトリを落とす。 ``` @mieru3 ### こちらは開発用なので、non-bare $ git clone ssh://192.168.9.98//home/kanai/repos/hogeware $ cd hogeware $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/release ``` それぞれで競合しないパッチをあてて、masterツリーにpushしてみる。 ここでの開発方法はmaster=開発先に対して、localでmergeしてからpushする方法を考える。 ``` ### mieru2側での作業 @mieru2 $ git branch mieru2-101 $ git checkout mieru2-101 $ echo "/* 101 */" >> source1.c $ git add . $ git commit -m '101' $ git checkout master $ git merge --no-ff mieru2-101 $ git push 5bc1c15..92e56e3 master -> master ### ここまでで、mieru2の変更は ### mieru3側での作業 @mieru3 $ git branch mieru3-102 $ git checkout mieru3-102 $ echo "/* 102 */" >> source2.c $ git add . $ git commit -m "102" $ git merge --no-ff mieru3-102 $ git push kanai@192.168.9.98's password: To ssh://192.168.9.98//home/kanai/repos/hogeware ! [rejected] master -> master (fetch first) ### これは、単に、最新とぶつかっただけ ### mieru3がわでmerge作業する ### というものの、conflictしていないので、単にmergeするだけ。 @mieru3 $ git fetch -p 5bc1c15..92e56e3 master -> origin/master $ git merge --no-ff origin/master Merge made by the 'recursive' strategy. $ git push 92e56e3..3a79f95 master -> master ``` このように開発を行ったら、releaseブランチを切る。(受け渡し用) ``` @mieru2 $ git checkout release $ git log | grep "commit " | wc -l 6 $ git merge --no-ff master -m "RELEASE-0.1" $ git log | grep "commit " | wc -l 16 > masterのlogツリーが取り込まれたことが分かる $ git push e5f1387..137453c release -> release e$ git tag 0.1 $ git push --tags Total 0 (delta 0), reused 0 (delta 0) To file:///home/kanai/repos/hogeware * [new tag] 0.1 -> 0.1 ``` ここで、開発先は開発が終わった。こんどは、開発元がこの変更を取り込む。 ``` ### mieru1 @mieru1 $ git fetch -p # remoteのレポジトリを登録する $ git remote add mieru2 ssh://192.168.9.98//home/kanai/workdir/hogeware # mieru1側でmieru2のブランチをpullする $ git fetch mieru2 release * branch release -> FETCH_HEAD * [new branch] release -> mieru2/release $git fetch --tags mieru2 release kanai@192.168.9.98's password: From ssh://192.168.9.98//home/kanai/workdir/hogeware * branch release -> FETCH_HEAD # mieru1でreleaseと $ git merge --no-ff 0.1 Merge made by the 'recursive' strategy. $ git push e5f1387..92a3e59 development -> development ```