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<stdio.h>" > source1.c
$ git add .
$ git commit -m "init"
### ここまででMasterができた。
### Branchを切ってticket1というパッチをつくる
$ git branch ticket1
$ git checkout ticket1
Switched to branch 'ticket1'
$ cat "#include<stdlib.h>" >> source1.c
$ echo "#include<stdlib.h>" >> source1.c
$ echo "#include<stdio.h>" >> 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