svnの場合、一つのレポジトリに対して認証のあるユーザが変更を報告していくユースケースをとっています。gitの場合は、個々のローカルマシンにリポジトリが分散されて配置され、お互いに変更を報告しあうユースケース。これはLinuxの伝統的なバザール方式の開発を想定しています。そのため例えばカフェや電車で開発したり、マスターはgithubやgitfarm(Git Hosting参照)にしておいて時々ローカルの変更を報告することも可能です。
目次
- インストール
- 基本操作
- 共同作業 - 2人
- 履歴の検索
- 共同作業 - 2人 + ルート
- githubを使う
- Git + Apache + Basic認証
インストール
インストール方法はOSに応じて行ってください。
Mac
Ubuntu
Windows
基本操作
Gitリポジトリの作成
$ mkdir ~/workspace/git $ cd ~/workspace/git # 初期化 $ git init $ ls .git branches config description HEAD hooks info objects refs $ echo trial > test.txt # 索引の追加 # 索引はコミットする前のステージのようなもの。 # 索引はgit add によって繰り返し更新可能 # git add .はカレントディレクトリ以下のディレクトリ/ファイルを再帰的にスナップショットに追加します $ git add . # 削除したファイルを索引に追加する場合は -uが必要。例, git add -u path/to/files/no/longer/necessary # メッセージを指定してコミット $ git commit -m "initial commitment" # ブランチの一覧を表示 $ git branch * master #=> masterというブランチは初期状態で作成されます $ git log commit e1bf45812b9eedb8aa578af3c0e87a96d84cf3b6 Author: bobDate: Fri Mar 12 00:28:59 2010 +0000 initial commitment
場所を移動してもgitは動作します。レポジトリは作業ディレクトリと同じ場所にあります。
$ mkdir trial $ mv test.txt trial/ $ mv .git trial/ $ cd trial # gitが動作している事を確認 $ git show #最後のコミット情報が表示される
こまめにコミットすることは個人で開発を進めている時にも非常に有効です。subversionと大きく違うところは個人でリポジトリを所有しているところです。マスターになるリポジトリにpush(後述)しない限り未試験のコードをローカルのリポジトリにコミットすることができます。例えば、複数の実装方法を思いついたときに現在の内容を一時的なソース保管庫としてリポジトリにコミットしておいて、ある方法が失敗したと思ったらすぐ元の位置に戻ればよいわけです。
こまめなコミットによってローカルのlogが汚れるのが嫌な場合は、tag(後述)を使用したり、公開用のリポジトリをもう一つ用意することによって対処できます。Gitを採用するのならこのアドバンテージを利用すべきでしょう。折角subversionから切り替えるのなら気持ちも一緒に切り替えると使い方の幅も広がるのではないでしょうか。
ブランチの作成
$ git branch new $ git branch * master #=> *は現在どのブランチを参照しているかを示しています new # svnのcheckoutはリポジトリから最新を取得しますが、 # gitのcheckoutはブランチを切り替えてその最新を取得します。 $ git checkout new Switched to branch 'new' $ git branch master * new $ git checkout master # ブランチの削除 $ git branch -d new
ブランチの作成(2)
# 新しくブランチを作成してチェックアウトします。= git branch new + git checkout new $ git checkout -b new $ echo ' of git' >> test.txt # 変更をリポジトリに報告 $ git add . # 索引と最後のコミットを比較 $ git diff --cached diff --git a/test.txt b/test.txt index 10b4e1b..48215e2 100644 --- a/test.txt +++ b/test.txt @@ -1 +1,2 @@ trial + of git # 今どんな状態? $ git status # On branch new # newブランチ上に # Changes to be committed: # コミットすべき変更有り # (use "git reset HEAD..." to unstage) # # modified: test.txt #更新: test.txt # $ cat .git/COMMIT_EDITMSG modified test.txt # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch new # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: test.txt # # -mオプションを省略するとgit diffと同様に # commitによってどのような変更がなされるか#付きのコメント行で示される。 $ git commit modified test.txt # On branch new # Changes to be committed: # (use "git reset HEAD ..." to unstage) #unstage(索引を空に)したい場合はgit rest HEADしてね # # modified: test.txt #
svnのようにbranch用のディレクトリをこしらえる必要は無い。
gitではブランチを切り替えることで作業ディレクトリの状態を切り替えます。
現在のブランチは.git/HEADで管理されています。
$ cat .git/HEAD ref: refs/heads/new $ cat .git/refs/heads/new bdfb3c51bba2c6d60e7bc326ea686702849c21ab $ git show commit bdfb3c51bba2c6d60e7bc326ea686702849c21ab $ cat test.txt trial of git $ git checkout master $ cat .git/HEAD ref: refs/heads/master $ cat test.txt trial # 作業ディレクトリにnewブランチの変更を引き込む $ git pull . new $ cat test.txt trial of git $ gitk
タグ
$ git tag v0.10.00 # タグの一覧表示 $ git tag -l v0.10.00 vx.xx.xx : vx.xx.xx # タグはいろんな箇所で使えます $ git diff v0.10.00 ORIG_HEAD # v0.10.0の位置に "stable" という名前の新しいブランチを作成 $ git branch stable v0.10.00
ファイルを無視する
$ mkdir bin $ touch bin/gabage $ git status # On branch master # Untracked files: # (use "git add..." to include in what will be committed) # # bin/ => binが追跡されてないのでgit addして と言ってる nothing added to commit but untracked files present (use "git add" to track) $ vi .gitignore bin/* */ $ git status # On branch master # Untracked files: # (use "git add ..." to include in what will be committed) # # .gitignore => .gitignoreもgitの管理対象 nothing added to commit but untracked files present (use "git add" to track) $ git add .gitignore $ git commit Added new file .gitignore $ git status # On branch master nothing to commit (working directory clean)
索引の理解
コミット - Gitでは履歴内にある1点のことを指す。プロジェクトの全履歴は 相互に関連したコミットの集合により表現されています。git では"コミット"という言葉を 他のリビジョン管理システムが使用する "リビジョン" または "バージョン"と同じ意味で 使用することがあります。また、コミットオブジェクト の略称として使われることもあります。
索引 - コミットしたいものの追跡を保つ為、git は "索引(index)" と呼ばれる 特別なエリア内にツリーの中身のスナップショットを保管しています。
コミット[HEAD]
↑
↑ git commit
↑
索引[index]
↑
↑ git add
↑
作業ディレクトリ[working tree]
$ echo 1 > diff.txt $ git add diff.txt $ echo 2 > diff.txt # 索引 と 最後のコミットを比較 $ git diff --cached diff --git a/diff.txt b/diff.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/diff.txt @@ -0,0 +1 @@ +1 # 作業ディレクトリ と 索引 を比較 $ git diff diff --git a/diff.txt b/diff.txt index d00491f..0cfbf08 100644 --- a/diff.txt +++ b/diff.txt @@ -1 +1 @@ -1 +2 # 作業ディレクトリ と 最後のコミット を比較 $ git diff HEAD diff --git a/diff.txt b/diff.txt new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/diff.txt @@ -0,0 +1 @@ +2
取り消し
導入
一口に変更を元に戻すといってもシングルリポジトリなSVNと異なり以下にあげたようにいくつかの場面があります。これは索引やコミットそしてリモートなどの仕組み上の違いによるものです。- 一個のファイルの変更を取り消して最後のコミットの状態にする
git reset -- filename
- 最後のコミットの内容に索引と作業ディレクトリを戻す
git reset --hard HEAD
- 最後のコミットと索引と作業ディレクトリをその前のコミットに置き換える
git reset --hard ORIG_HEAD
- 最後のコミットの後に、その前のコミットの状態をコミットする
git revert HEAD
以下に違いを解説します。でもその前に --hardっていうオプションはなんなんでしょうか?--hardがあるなら--softもあるのか?
--hardと--softの違い
$ git reset -h --mixed reset HEAD and index --soft reset only HEAD --hard reset HEAD, index and working tree --merge reset HEAD, index and working tree
一個のファイルの変更を取り消して最後のコミットの状態にする
きっと一番よく使うのがこれ。コミットしていない変更がある場合は単にgit checkout filenameとしてもファイルの内容は最後のコミットの状態には戻らない。git reset --hardは作業ディレクトリ以下を全て最後のコミットの状態に戻してしまう。そんな時は '--'オプションをつけると一個のファイルだけを戻すことができる。$ git checkout -- filename参考
このコマンドで削除したファイルをリポジトリから復活するもできます。
$ git status # On branch master # Changed but not updated: # (use "git add/rm..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # deleted: rerun.txt $ git checkout -- rerun.txt $ ls rerun.txt
間違ったコミットを無かったことにする
$ touch abigmistake $ git add . $ git commit -m 'a big mistake' $ git log commit b37c941bfe9364b681c8b760397b14e846b8fc7d Author: suzukimilanpaakDate: Fri Mar 19 17:05:55 2010 +0000 a big mistake commit bdfb3c51bba2c6d60e7bc326ea686702849c21ab Author: bob Date: Fri Mar 12 01:08:36 2010 +0000 modified test.txt $ git reset --hard ORIG_HEAD # 以下のように特定のタグを指定することもできる $ git reset --hard v0.10.00 #注意!: 作業ディレクトリの変更も削除されてしまう $ ls bin test.txt $ git log commit bdfb3c51bba2c6d60e7bc326ea686702849c21ab Author: bob Date: Fri Mar 12 01:08:36 2010 +0000 modified test.txt commit e1bf45812b9eedb8aa578af3c0e87a96d84cf3b6 Author: bob Date: Fri Mar 12 00:28:59 2010 +0000 initial commitment
注意! git reset --hard ORIG_HEADはリポジトリを直接変更します。誤ったコミットの履歴HEAD(この場合 'a big mistake')を取り除きます。変更を取り消した履歴を残すことはありません。そのため他の作業者がそのコミットを既に取り寄せていた場合親族関係が崩れてしまい、他の作業者の変更がどこにも到達不可能になってしまうというケースが起こりうります。そのためgit reset --hard ORIG_HEADを行う場合は他の作業者に事前の確認が必要です。
間違ったコミットを無かったことにするが、ファイルへの変更をリセットしない
$ git reset --soft HEAD^
コミットに含んで欲しくない変更があった場合や、タイポを後で見つけてしまった場合、新たな変更をコミットに含みたい場合などに便利
参照
最後のコミットを取り消して指定したコミットの状態に戻す。そして、その取消しをコミットとして履歴に残す
他の作業者がすでに間違ったコミットを取り寄せ済みの場合、間違ったコミットの変更を取り消すコミットを上書きする方法が望ましいようです。単に git revertに間違ったコミットへの参照を渡すことによって変更を取り消す新しいコミットが作成されます。 また、新しいコミットに対するコミットメッセージが促されます。
$ echo foo > test.txt $ git commit -am 'Modified test.txt' # HEADをORIG_HEADの内容に戻す。コミットも同時に行われます $ git revert HEAD Revert "Modified test.txt" This reverts commit 07abe15e79f4c1429fb1d22247723e29dc50293a. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD..." to unstage) # # modified: test.txt # $ git log commit 42b30ec986d7fdb8d76c7a5b7c89f17114e8039c Author: bob Date: Fri Mar 12 01:45:28 2010 +0000 Revert "Modified test.txt" This reverts commit 07abe15e79f4c1429fb1d22247723e29dc50293a. commit 07abe15e79f4c1429fb1d22247723e29dc50293a Author: bob Date: Fri Mar 12 01:45:04 2010 +0000 Modified test.txt #ここまでの変更をGUIで表示 $ gitk &
コミット前の変更を取り消す - git reset HEAD
索引を空にする
$ touch abigmistake $ git add . $ git status # On branch master # Changes to be committed: # (use "git reset HEAD..." to unstage) #unstageする場合はgit reset HEADしてね # # new file: abigmistake # $ git reset HEAD abigmistake # 索引から履歴は削除されるがファイル自体は残っている $ ls abigmistake test.txt # もう一度索引に追加することもできます $ git add abigmistake $ git reset --hard HEAD $ ls test.txt
共同作業 - 2人
準備
# aliceの署名情報を設定 $ git config --global user.name "alice" $ git config --global user.email "engineerflies+alice@gmail.com" $ cat ~/.gitconfig [user] name = alice email = engineerflies+alice@gmail.com $ cd .. $ mkdir alice && mv trial alice $ git clone ./alice bob bob$ cd bob # bobの署名情報を設定(この場合同じユーザで書名情報を設定しているので~/.configを上書きする) bob$ git config --global user.name "bob" bob$ git config --global user.email "engineerflies+bob@gmail.com" bob$ echo bob edited >test.txt bob$ git commit -am "bob edited" alice$ echo alice edited > test.txt alice$ git add . # bobのmasterブランチを現在のaliceのmasterブランチに取り寄せる # 変更情報のみがリモート追跡用ブランチに格納されます。test.txtは変更されない alice$ git fetch ../bob master alice$ cat test.txt alice edited # aliceのHEADとbobからpullしたFETCH_HEADの差異を表示 alice$ git diff HEAD..FETCH_HEAD diff --git a/test.txt b/test.txt index 48215e2..4cba843 100644 --- a/test.txt +++ b/test.txt @@ -1,2 +1 @@ -trial - of git +bob edited alice$ cat .git/FETCH_HEAD b61671548ad431d9d865f0dd7eaeded68ec23711 branch 'master' of ../bob alice$ git merge FETCH_HEAD Updating 7d62c24..b616715 error: Entry 'test.txt' would be overwritten by merge. Cannot merge. #このコミットはコンフリクトを起こさない(HEADと作業ディレクトリを比較しているから?) alice$ git commit -am "alice edited" alice$ git log commit d04f56226dde3ac67839a5695ab6fb5644a34bc9 Author: bobDate: Thu Mar 18 13:21:13 2010 +0000 alice edited commit 7d62c24953010a7628b29e891405e973b24a4239 Author: bob Date: Fri Mar 12 01:44:29 2010 +0000 ignore bin/ : : # 再度merge - コンフリクトを起こします alice$ git merge FETCH_HEAD Auto-merging test.txt CONFLICT (content): Merge conflict in test.txt Automatic merge failed; fix conflicts and then commit the result. このようにマージは常に直前にコミットが済んでいることを想定して行われる様です。 dirtyな状態(現在のブランチにコミットされていない変更が作業ディレクトリ内に含まれていること)ではマージは行われません。 #コンフリクトを手動で回収します alice$ vi test.txt bob edited then alice edited 結果の表示 # bobが最後にpullしてから、aliceがbobの変更をpullするまでの変更を表示する $ gitk HEAD..FETCH_HEAD # aliceとbobのそれぞれの変更のうちお互いに'到達不可能な'変更を表示する $ gitk HEAD...FETCH_HEAD alice$ git commit -a Merge branch 'master' of ../bob #マージ用のログを自動生成してくれます Conflicts: #コンフリクトが test.txtにあった test.txt # #あなたはマージをコミットしている様です(とgitは認識しています) # It looks like you may be committing a MERGE. #これが正しくなかったら、.git/MERGE_HEADを削除してからもう一回トライしてね # If this is not correct, please remove the file # .git/MERGE_HEAD # and try again. # #以下はいつもコミット時に表示されるメッセージと同じ # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: test.txt # #最後にaliceの変更をbobにも取り寄せておきます #pull は fetch + mergeと等価です bob$ git pull ../alice master
自分の変更を隠す
bob$ echo love letter from canada >> testto.txt bob$ git commit -am "love letter" alice$ echo affair in us>>test.txt alice$ git pull ../bob # stash: 隠しもの alice$ git stash save affair # 隠しものを一覧する alice$ git stash list stash@{0}: On master: affair alice$ git pull ../bob alice$ vi test.txt love letter from canada love letter from us alice$ git commit -am "in love" alice$ git stash clear
リモートブランチ
今までの作業でaliceがgit pull ../bobした場合はbobをリモートとして登録することなくbobの変更をaliceにマージしていた。bobとaliceの共同作業が恒久的に行われる場合はリモートブランチとしてbobを登録してしまった方が便利だ。この方法によってリモートにあるブランチをローカルのブランチであるかの様にマージができます。
bob$ echo bob edited > test.txt bob$ git commit -am "bob eidted" alice$ git remote add bob ~/workspace/git/bob alice$ git branch -r bob/master alice$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "bob"] url = /home/suzukimilanpaak/workspace/git/bob fetch = +refs/heads/*:refs/remotes/bob/* # 変更情報のみがリモート追跡用ブランチに格納されます。test.txtは変更されない */ alice$ git fetch bob # 自分のmasterとbobのmasterを比較 # -p はpatchの略、patch提出用のログメッセージを出力してくれます alice$ git log -p master..bob/master commit 460f2c8928b7f1b1065766c6c2652f79001a054c Author: bobDate: Thu Mar 18 14:01:11 2010 +0000 bob eidted # -pによって出力される変更ログ diff --git a/test.txt b/test.txt index 843ad85..4cba843 100644 --- a/test.txt +++ b/test.txt @@ -1 +1 @@ -love letter from canada +bob edited alice$ git merge master bob/master Already up-to-date with ed79b27cc53885e29b2770a9f20d8fb2174e48b6 Trying simple merge with 460f2c8928b7f1b1065766c6c2652f79001a054c Simple merge did not work, trying automatic merge. Auto-merging test.txt ERROR: content conflict in test.txt fatal: merge program failed Automatic merge failed; fix conflicts and then commit the result. # コンフリクトを解消します alice$ vi test.txt love letter from canada love letter from us bob edited alice$ git commit -a Merge branch 'master'; commit 'bob/master' Conflicts: test.txt # bobのリポジトリはaliceをcloneして作られたためoriginにaliceのリポジトリが指定されている。 bob$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* */ url = /home/suzukimilanpaak/workspace/git/./alice [branch "master"] remote = origin merge = refs/heads/master bob$ git config -l user.name=bob user.email=sin.wave808+bob@gmail.com core.repositoryformatversion=0 core.filemode=true core.bare=false core.logallrefupdates=true remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* */ remote.origin.url=/home/suzukimilanpaak/workspace/git/./alice branch.master.remote=origin branch.master.merge=refs/heads/master bob$ git branch -r origin/HEAD -> origin/master origin/master origin/new # git pullで引数を省略すると remote "origin"に登録されている変更を取り寄せる bob$ git pull # ちゃんと変更されてる bob$ cat test.txt love letter from canada love letter from us bob edited
履歴の検索
履歴の検索に強くなっておくと今後の作業に便利です。
$ git log commit 74f846a1bde25ef729b1742bfcd9fc693da1dbda # ある時点のコミットの詳細を表示 # SHA1値の全てを指定する必要は無い $ git show 74f846a1bde25ef commit 74f846a1bde25ef729b1742bfcd9fc693da1dbda Merge: 17b2308 a01e7b7 Author: suzukimilanpaakDate: Fri Mar 5 17:25:15 2010 +0000 tip of collaboration $ git show HEAD^ # HEAD の親を表示 $ git show HEAD^^ # HEAD の祖父を表示 $ git show HEAD~4 # HEAD の4つ前を表示 # "love"を検索 $ git grep love test.txt:love letter from canada test.txt:love letter from us # "love"をタグv0.00.00から検索 $ git grep love v0.10.00 #=> 何も見つからない $ git log v0.10.00 # v0.10.00以降のコミット $ git log --since=yesterday # 昨日からのコミット $ git log --since="3 hours ago" # 3時間前からのコミット # 最近2週間に "drivers" ディレクトリを修正したコミットでgitkで表示 $ gitk --since="2 weeks ago" drivers/ # ファイル名を指定 $ git diff v0.10.00:test.txt HEAD:test.txt # 変更したファイル名を検索 $ git log --stat|grep -A 5 haml changed the view rendering engine html to haml. app/views/feeds/new.html.haml | 48 ++++++++++++++++------------------------ 1 files changed, 19 insertions(+), 29 deletions(-)
差分の表示
$ git checkout -b a $ echo a > a.txt $ git commit -am "a" # aにあってmasterにないコミットを表示 $ git log master..a commit b090846acd519c2066a8975ed4c76de700d66fc3 Author: bobDate: Thu Mar 18 14:44:04 2010 +0000 a #masterにあってaにないコミットを表示 => 何も表示されない $ git log a..master
共同作業 - 2人 + ルート
今まではbobがaliceをoriginとして参照する方法で行って来ました。実際の開発ではルートとなるレポジトリを設けてアリスとボブがそれを参照する方が管理を行いやすいです。
/opt$ mkdir git # rootというリポジトリをaliceからコピーして生成します。 # この作業の前にaliceが開発の先端になっているべきであることに注意してください。 # bare"裸の"とは作業ファイルがなく、管理情報だけを持つリポジトリという意味 # sharedを付けると共用のユーザ権限がリポジトリに与えられます /opt$ git clone --bare --shared ~/workspace/git/alice/ root alice$ git remote rm bob # -t: master ブランチをtrack"追跡"します alice$ git remote add -t master origin /opt/git/root alice$ git config -l remote.origin.url=/opt/git/root remote.origin.fetch=+refs/heads/master:refs/remotes/origin/master # fetchがrefs/heads/masterを参照する様に設定されています # -tオプションが指定されていない場合はrefs/heads/*が設定されます */ # 引数なしでpullをするとremotes/origin/masterから変更を取り寄せます # git remote add に-tを使用したためです。 alice$ git pull # これまでこの記事にそってリポジトリを作成してきた場合originはaliceを参照しているはずです bob$ git config -l remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* */ remote.origin.url=/home/suzukimilanpaak/workspace/git/./alice bob$ git remote rm origin bob$ git remote add -t master origin /opt/git/root bob$ git pull bob$ echo love from vancouver > test.txt bob$ git commit -am "love from vancouver" bob$ sudo git push origin master alice$ echo love from san francisco > test.txt alice$ git commit -am "love from san francisco" alice$ sudo git push origin master To /opt/git/root ! [rejected] master -> master (non-fast forward) error: failed to push some refs to '/opt/git/root'
fast forwardじゃないというメッセージがでてpushが拒絶されます。
fast forward - 今行おうとしているコミットが既に他方のコミットに含まれている場合、gitは現在のブランチの先頭をマージされるブランチの先頭の位置に進め、新しいコミットは作成されません。これをfast forwardといいます。
この場合、aliceの変更はbobが先に行った変更と衝突するため拒絶されています。別の言い方をすると、共有リポジトリを置いて開発を行う場合fast forwardでない限りpushを受け付けてもらえないようです。
alice$ git pull CONFLICT (content): Merge conflict in test.txt alice$ vi test.txt love from vancouver love from san francisco alice$ git commit -a alice$ git push origin master
今度はpushを受け付けてもらえました
root$ git log commit 40c4ded3df30d696a16552d928b3a46515cfb516 Merge: f180d66 a2bcc0d Author: bobDate: Thu Mar 18 17:00:51 2010 +0000 Merge branch 'master' of /opt/git/root Conflicts: test.txt commit f180d664d2943ccc83075e7dc1300f7dca999d72 Author: bob Date: Thu Mar 18 16:40:37 2010 +0000 love from san francisco commit a2bcc0d98f0e2c98d37df233aecb30caacb028f5 Author: bob Date: Thu Mar 18 16:39:49 2010 +0000 love from vancouver
- 3つ以上のマージ
http://www8.atwiki.jp/git_jp/pub/Documentation.ja/user-manual.html#merging-multiple-trees
githubを使う
githubをマスターレポジトリとしてそこにローカルの変更をpushする環境を構築しましょう。まずはgithubでユーザアカウントを作成してください。githubでレポジトリを作成するとリポジトリのURLを教えてくれるのでそれを今まで通りgit remoteで登録します。そしてgithubにsshの公開鍵を渡し、ローカルの変更がうまく反映されるまでの流れを説明します。
- こちらにアクセスしてください。http://github.com/repositories/new
- 必要事項を書き込みます
- ローカル環境のセットアップの説明が表示されます
説明ページの手順にそって作業を進めていきます。
$ cd ~/.ssh 既存の鍵ペアがある場合はバックアップをとります $ mkdir key_backup $ cp id_rsa* key_backup $ rm id_rsa* 鍵ペアを生成します $ ssh-keygen -t rsa -C "tekkub@gmail.com"
鍵のファイル名はレポジトリごとに指定するのが後々管理しやすいです。ここではid_rsa_githubを指定しました。
ホスト毎にsshが読み込む秘密鍵が分かるように以下の設定を~/.ssh/configに書き込みます。
$ vi config Host github.com HostName github.com User git IdentityFile /home/suzukimilanpaak/.ssh/id_rsa_github
接続可能か試します。Permission denied (publickey).が表示されず、パスフレーズを求められれば上記の設定が成功しているはずです。
ssh -v git@github.com
公開鍵をコピーします。改行が入らないようにxclipを使用します
$ sudo apt-get install xclip $ cat ~/.ssh/id_rsa.pub | xclip -sel clip xclipでクリップボードに公開鍵の内容が保存されているので、それを以下のページのKeyという項目に張り付けます https://github.com/account#ssh_bucket titleの項目は任意です。 ~/workspace/git$ git clone alice snippets ~/workspace/git$ cd snippets リポジトリのアドレスは作成したリポジトリのページからコピーしてください snippets$ git remote add -t master origin git@github.com:yourusername/code-snippets.git snippets$ git pull
READMEを作ってみましょう
snippets$ vi README This repo is for my private use only. thank you 何だかgithubの意に反したことを言っていますが。。。 snippets$ git add . snippets$ git commit -am "added README" snippets$ git push origin master
- Network Graphを覗いてみましょう。今までの変更がグラフィカルに表示されているはずです。
http://github.com/yourusername/code-snippets/network
すばらしい
このセクションを読んだあとに山形浩生さんが公開されているEric Raymondの伽藍とバザールの日本語訳をご覧になられると良いかもしれません。このページの読者にはこれからオープンソースの開発に携わってみたいと思っている人も少なくないでしょう(私もその一人です)。githubの空気、ノリをつかむにはちょうど良い資材だと思います。
Git + Apache + Basic認証
エンタープライズな開発の現場でもgitが使われるといいですね。Apacheと組み合わせて使う方法をご紹介します。svnと基本はあまり変わらないです。2009年末にapacheの連携を解消するsmart HTTPが発表されました。Git > v1.6.6、Apache 2.x という環境で恩恵をうけることができますが、下位互換がありますのでこの記事を書くに当たってそちらの構成を採用しました(これについては書き掛け)。
Ubuntuでの設定をご説明します。まずはApacheが参照するリポジトリをDAV越しにローカルリポジトリが操作できるようにするところまで説明します。Smart HTTPについては後述します。
認証方式について
Gitは認証方式の指定ができません。そしてBasic認証を行おうとします。http.authanyで全てのHTTP認証方式に対応できるようですが都度で認証方式を決定するまでリトライするので重いようです。これではSmart HTTPを採用した意味がないので使用は避けた方がいいでしょう。
# DocumentRootの場所を探します $ grep -r DocumentRoot /etc/apache2/* */ # DocumentRootに移動 $ cd /var/www $ sudo mkdir dev.git # 裸のgitリポジトリを作成します。apacheユーザでアクセスするので--sharedオプションは使用しません。 $ sudo git --bare init $ git update-server-info # apacheユーザを検索(apacheが動作している前提) $ ps aux |grep apache www-data 1862 0.0 0.0 40064 52 ? S 09:10 0:00 /usr/sbin/apache2 -k start $ sudo chown -R www-data:www-data dev.git $ cd /etc/apache2
Ubuntuでapache2をapptitudeやapt-getからインストールした場合、/etc/apache2/mods-available/dav.loadがあるはずです。特に設定の変更は必要ありません。
次にDAVLockDBディレクティブがあるか確認します。ms_dav_fsはユーザの操作をロックするためにSDBMを使用します。そのためこのディレクティブによってデータベースファイルが指定されている必要があります。
$ grep -r DAVLockDB mods-available/* */ mods-available/dav_fs.conf:DAVLockDB /var/lock/apache2/DAVLock モジュールを有効にします。 $ sudo a2enmod dav dav_fs
バーチャルホストの作成
$ sudo vi sites-available/git NameVirtualHost * <VirtualHost *> ServerName <servername> ServerAdmin a.hi.tech.hippie@@googlemail.com DocumentRoot /var/www/dev.git ErrorLog /var/log/apache2/error.dev.git.log <Location /> DAV on AuthType Basic AuthName 'developer' AuthUserFile /etc/apache2/.htbasic Require valid-user </Location> </VirtualHost> $ sudo vi /etc/hosts 127.0.0.1 <servername> 127.0.1.1 <servername> $ htpasswd -c /etc/apache2/.htbasic <user> $ /etc/init.d/apache2 restart
ブラウザでhttp://<servername>/にアクセスして認証が通るか確認してください。失敗した場合はErrorLogディレクティブで指定したログファイルを参照しながら修正してください。
gitがapacheにアクセスできるように ~/.netrcファイルに次の記述をして作成してください。~/netrcはオーナーだけが読み書きできるように権限に600を設定してください。
machine <servername> login <user> password <password>
それではApache上のリポジトリからローカル作業用のcloneを作りましょう。
~/workspace/git$ git clone http://<servername>/ dev
もしこれが動作しない場合、curlを使って認証が通っているか確かめてみましょう。
curl --netrc --location -v http://<username>@<servername>/HEAD
初めてのプッシュ
~/workspace/git/dev$ touch work? ~/workspace/git/dev$ git add . ~/workspace/git/dev$ git commit -m "created work?" # masterブランチの内容をoriginにpush ~/workspace/git/dev$ git push origin master Fetching remote heads... refs/ refs/tags/ refs/heads/ updating 'refs/heads/master' from 0000000000000000000000000000000000000000 to bd4637936d5b8978b82ef06f8a8b6f75b716552f sending 2 objects done Updating remote server info
うまくいかない場合はApacheのリポジトリの所有者やwriteの権限、それからgit update-server-infoを実施したか再チェックしてみてください。
ここまででGit + Apache + Basic認証の設定は完了ですが、この構成で開発を続けていくときっとpushやfetchが遅いことに気づくと思います。それはGitがHTTP越しに動作するときにPackfile(Gitのストレージファイル)をまるごと転送しないといけないためです。Smart HTTPではupload-packとreceive-packというApache上で動くCGIを用意しており、それらがおあつらえのpackfileを作成し、一連のpostを行うようです。GETパラメータにSmart HTTPを実施するかどうかが渡されるため、対応していない場合単純に無視され旧バージョンの動作をします。この設定に関しては後日追記するつもりです。それまでこちらの記事を参考にしてください。
この文章で扱ってないこと
- ダンプ
この文章は著者の理解が甘いため時々修正したり、書き足したりして縦に伸びていく予定です。ツッコミ、文の分かりづらいなどの指摘お待ちしております。
.