このドキュメントは Version Control for Designers の日本語訳であり、元のドキュメントと同じくソースコード管理に予備知識がまったくない人を想定している
バージョン管理、ソース管理やリビジョン管理とも呼ばれているものはあらゆる開発に必須である。なぜなら基本的にはメールやインスタント・メッセンジャーと同じようなコミュニケーションをとることができるツールでありながら、人々の会話ではなくソースコードを元にして機能することができるからだ。
バージョン管理
バージョン管理、リビジョン管理またはソース・コード管理としても知られているもの、は累積する開発の各段階におけるファイルの変更を保存して維持するシステムである。バージョン管理システムはファイルをバックアップするシステムと同じ原理であるが、もっと賢い。システムの管理下にある全てのファイルは完全な変更の履歴を持ち、その履歴の任意のバージョンへ簡単に戻すことができる。その各バージョンはアルファベットと数字で構成されたユニークな識別子 (443e63e6..) を持っている。
バージョン管理には様々なソフトウェアがある。この文書では Git を使って解説しているが、他にも Subversion や CVS 、 Darcs 、 Mercurial など色々ある。それぞれの違いはわずかな操作法の違いでしかない。
最も単純なバージョン管理システムは、全てのファイルとそのバージョンが格納されているリポジトリで構成されている。リポジトリとは簡単に言えばデータベースのようなもので、その管理下にあるあらゆるファイルを任意のバージョンに戻すことができたり、任意のファイルの変更履歴であったり、プロジェクト全体の変更履歴であったりする。
#25 Joe Adjust user profile information
#24 Fred Add login box
#23 Mary Allow user photo uploads
#22 Joe Change the color of the header to yellow
#21 Mary Change the header to blue
リポジトリのユーザーは、変更を加えることができる最新のファイル群のコピーである作業コピーをチェックアウトすることができる。いくつかの変更を加えた後、チェックインまたはコミットすることによって変更をリポジトリに反映させると、変更されたファイルや変更を加えたユーザーなどについてのメタ・データと共に新しいバージョンが作成される。
基準になるリポジトリがあることは単純明快ではあるが、必須というわけではない。各ユーザーがリポジトリの完全なコピーを自身のマシンに持っていても良い。一般的には、自身のローカル・リポジトリに変更をコミットし、一段落したら開発チームが共有するリポジトリにプッシュする。他のリポジトリから変更をプルすることもできる。
ブランチはメールを書くときの下書きのような役目を果たす。下書きを書いている時、完成するまでこまめに保存するだろう。そして完成したら、メールを送信し下書きを削除する。こういった場合、メールを送信するまでに下書きを頻繁に更新してもメーラーの送信トレイが下書きであふれてしまうなどということはない。
ブランチを切ることは新しい機能を開発する時に役に立つ。なぜなら master ブランチ (メーラーの送信トレイにあたる) は常にきちんと機能するままなので、デプロイできる状態を維持できるからだ。いくつもの下書き (実験的なブランチ) を必要なだけ開発中に作成することもできるだろう。ブランチは簡単に作成でき、かつ簡単に切り替えることができる。
ブランチでのコーディングが完了し、そのブランチがテストを通ったなら、変更を master ブランチにマージし、メールの下書きのようにブランチを削除することができる。また、もし誰かが master ブランチにコードをコミットした時も、最新の master のコードにブランチを簡単に更新することができる。
作業コピーをベースになるコードから取得するには、リモート・リポジトリを自身のマシンへクローンする必要がある。クローンすることによってリポジトリが作成され HEAD
として参照されている最新のバージョンがチェックアウトされる。
まず、オープン・ソースのプロジェクトをクローンしてみよう。
$ git clone git://github.com/wycats/jspec.git
Initialized empty Git repository
これで最初のリポジトリをクローンできた。 clone
コマンドはいくつかの便利な機能を提供する。元になったリポジトリのアドレスを保存したり、それに対して origin という名前でエイリアスを張ったりなどだ。これらの便利な機能によって (適切な権限を持っているのならば) 変更をリモートのリポジトリに反映させることが簡単にできるようになっている。
カレント・ディレクトリに jspec
という名前のディレクトリが作成されているだろう。 cd
でそのディレクトリに移動すれば、 JSpec のソース・コード (いくつかのファイルだけだが) の中身を見ることができる。
Git は先ほど利用した "git://" プロトコル (多くの公開プロジェクトは git:// を使用している) を初めとして、様々なプロトコルをサポートしている。デフォルトで Git はリモート・リポジトリに安全にアクセスしてもらうために ssh プロトコルを使用する。
$ git clone [email protected]:thing.git
上記のようにして ssh 認証情報を設定してアクセスすることができる。
以上で作業コピーは手に入ったので、ファイルに変更を加えることができるようになった。ファイルの編集に特別なことは何もなく、ファイルを編集し保存するだけだ。一旦ファイルを保存したなら、現在のリビジョンに変更したことを追加する必要がある (または、大抵はそうするが、いくつかのファイルに変更を加えた後にまとめて追加する) 。そうするには git add
で変更したファイルを指定する。これを「ステージに追加する (staging) 」と言う。
$ git add index.html
あるディレクトリ全体をステージに追加するには以下のように指定する。
$ git add public/
これは public/
にある全てのファイルをステージに追加する。カレント・ディレクトリをステージに追加するには以下のように指定する。
$ git add .
もしステージに追加した後 (コミットする前) にそのファイルに何かしらの変更を加えた場合、もう一度 git add
する必要がある
git status
コマンドはリポジトリの現在の状態を表示する。
ninja-owl:public courtenay$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: public/index.html
#
git diff
コマンドは差分を表示する。デフォルトではまだステージに追加されていないファイルの変更の差分も表示するが、 --cached
オプションを付けると、ステージに追加されたファイルの変更の差分のみを表示する。
ninja-owl:public courtenay$ git diff --cached
diff --git a/public/index.html. b/public/index.html
index a04759f..754492a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -8,7 +8,6 @@ revision control or source code management, is a system that maintains versions
+ This a line that I added to the file
- This is a line I removed from the file
このような出力は diff
や patch
と呼ばれており、これを共同開発者にメールで送信して、その人のローカルのコードに変更を適用してもらうこともできる。ファイル名や行番号、 +
と -
の記号による変更の表示をしてくれるので、人間が読んで理解できるものでもある。また、この差分をファイルにパイプで出力することもできる。
ninja-owl:public courtenay$ git diff --cached > line_modify.patch
好きなように変更を加えた後にファイルをステージに追加したなら、次にその変更を自身のローカルなリポジトリへコミットすることになる。そうするには git commit
コマンドを実行する。このコマンドを実行すると、変更を加えたファイルの一覧に先頭に空白行を加えたテキスト・ファイルがテキスト・エディタで開かれる。その空白行には、共同開発者が一目で自分が変更した内容を把握できるように具体的に変更した内容を記述する必要がある。「何か」というような漠然としたものよりは詳しく書く必要はあるが、以下のように極端に詳しく書く必要はない。
Changed line 434 in index.html to use spaces rather than tabs.
Changed line 800 in products.html.erb to have two spaces between the tags.
Changed line 343, 133, 203, 59, and 121 to have two spaces at the start rather than 9.
変更した内容の短い概要で十分なので、簡潔なコミット・メッセージを書くのがコツである。
Minor formatting changes in the code.
1 行目に 80 文字以下で概要を書き、 2 行目に空白行、 3 行目に詳しい内容を書いたりもできる。 2 行目と 3 行目は必須ではない。
コミット・メッセージを書き終わったなら、保存しテキスト・エディタを終了させる。すると自身のローカル・リポジトリにコミットされ、開発作業に戻ることができる。
ローカル・リポジトリに変更をコミットしたなら、次はリモート・リポジトリ、誰でも作業コピーを取得することができる基準になるリポジトリ、にプッシュする (変更を反映させる) ことになる。そのためには git push
コマンドを実行する。このコマンドは自身のローカル・リポジトリにコミットされた変更を全てリモート・リポジトリに反映させる。
Git の push
コマンドは git push <repository> <branch>
というように複数のパラメータを取る。クローンした場合、元になったリポジトリには既に origin
という名前でエイリアスが張られている。それに対して master ブランチをプッシュするので以下のように実行する。
$ git push origin master
git push
コマンド (と git pull
コマンド) はデフォルトでローカル・リポジトリと元になったリポジトリの双方に存在する全てのブランチをプッシュする (またはプルする) ので、パラメータは省略することもできる。
git push
コマンドを実行すると以下のように表示される。
your-computer:git_project yourusername$ git push
updating 'refs/heads/master'
from fdbdfe28397738d0d42eaca59c6866a87a0336e2
to 1c9ec11f757c099680336875b825f817a992333e
Also local refs/remotes/origin/master
Generating pack...
Done counting 2 objects.
Deltifying 2 objects...
100% (2/2) done
Writing 2 objects...
100% (2/2) done
Total 2 (delta 3), reused 0 (delta 0)
refs/heads/master: fdbdfe28397738d0d42eaca59c6866a87a0336e2 -> 1c9ec11f757c099680336875b825f817a992333e
この出力は、プッシュする準備が整ったこと (Generating pack
) や、リモート・リポジトリがファイルを受け取ったこと (Writing 2 objects
) を教えてくれる。そしてリモート・リポジトリの head/master
(リポジトリのデフォルトのブランチ) を更新して、今コミットしたリビジョンを参照するようにしている。これで誰もがローカルの作業コピーを更新して、コミットした変更が反映されたコードへ同期させることができるようになる。
自身のローカル・リポジトリと作業コピーをリモート・リポジトリへコミットされた最新のものへ更新するには、 git pull
コマンドを実行する。このコマンドはリモート・リポジトリから全ての変更点をダウンロードして、自身のローカル・リポジトリに加えた変更とマージする (変更点があるなら) 。
git pull
コマンドを実行すると、以下のように表示される。
remote: Generating pack...
remote: Done counting 12 objects.
remote: Result has 8 objects.
remote: Deltifying 8 objects...
remote: 100% (8/8) done
Unpacking 8 objects...
remote: Total 8 (delta 4), reused 0 (delta 0)
100% (8/8) done
* refs/remotes/origin/master: fast forward to branch 'master' of [email protected]:git_project
old..new: 0c793fd..fdbdfe2
Auto-merged file.cpp
Merge made by recursive.
.gitignore | 2 ++
file.cpp | 8 ++++++--
src/things.html | 5 +++--
your_file.txt | 18 ++++++++++++++++++
4 files changed, 19 insertions(+), 4 deletions(-)
create mode 100644 .gitignore
create mode 100644 your_file.txt
基本的には push
コマンドと反対の動作をする。リモート・リポジトリの準備が整ったら (Generating pack
) 、自身のローカル・リポジトリへ変更された点を転送する (Unpacking 8 objects
) 。これでローカル・リポジトリに自分でコミットした変更と同じように変更が反映される (ローカルで例のように file.cpp
をマージした時や、 .gitignore
や your_file.txt
を作成した時のように) 。
.gitignore
: .gitignore
ファイルは Git に特定のファイルやディレクトリを無視させる時に利用する。コンパイルされたバイナリやログ・ファイル、パスワードを含むファイルに対して使うと良い。
必ず作業を開始する前にブランチを切るべきである。そうすれば master は常にきちんと動作する状態を維持でき、他の開発者たちが行う変更とは独立して作業することができる。ブランチを作成すると、 master
ブランチをクローンし、そのクローンしたブランチにコミットできるようになる。そして作業が完了したら、そのブランチでの変更点を master
ブランチにマージすることもできる。もし master
ブランチに何かしらの変更を加えていた場合は、その変更も含めてまとめてマージすることができる。プッシュやプルと同じような形ではあるが、ブランチは同じディレクトリの中で完結する。以下は以上のプロセスを図解したものである。
ブランチを切ることは、同じコードに対して複数の人間が同時に別々に作業する必要がある時などに威力を発揮する。大幅なコードのリファクタリングやウェブ・サイトのリデザインといったような恒久的な結果をもたらすようなことから、パフォーマンスのテストといったような一時的な変更まで、様々なケースで利用することができる。
Git でブランチを作成するには、 git checkout -b <branch name>
を実行する。すると更新されているファイルがリストアップされ、 redesign
ブランチがチェックアウトされる。
$ git checkout -b redesign
M public/index.html
Switched to a new branch "redesign"
master
ブランチに戻るには、パラメータで master だけを指定する。
$ git checkout master
M public/index.html
Switched to a new branch "master"
他の開発者にそのブランチでの変更をプルできるように、リモート・リポジトリにもブランチを作成することができる。
$ git push origin redesign
また、現在のブランチを名前を変えてリモートへプッシュすることもできる。
$ git push origin redesign:master
これは現在の作業コピーをローカルの redesign
ブランチとリモートのリポジトリへ全ての変更をコミットしてプッシュする。全ての追加点や変更点は master
ブランチではなくこのブランチに残ることになる。
現在のブランチは?: 現在のブランチを調べたり、全てのローカル・ブランチを一覧するには git branch
コマンドを実行する。
もしリモートの master
ブランチから変更 (例えば重要なコードの変更やセキュリティ関連の更新など) をプルして現在のブランチにその変更を反映させるには git pull
コマンドを使って以下のように実行する。
$ git pull origin
$ git merge master
このコマンドは Git に全ブランチを含んだ origin
リポジトリ (Git デフォルトのリモート・リポジトリのエイリアス) から全ての変更点を取得するように伝え、現在のブランチへ master
ブランチからマージしている。作業が完了し master
ブランチへマージさせる場合は、以下のようにまず master
をチェックアウトし、それから作業ブランチをマージする必要がある。
$ git checkout master
$ git merge redesign
これで redesign
ブランチでの変更点は master
ブランチにマージされる。作成したブランチでの作業が完全に終了したのなら、 -d
オプションを使ってそのブランチを削除できる。
$ git branch -d redesign
リモート・リポジトリのブランチを削除するには、 push
コマンド (git push <remote> <local branch>:<remote branch>
でローカルのブランチをリモートの違う名前のブランチにプッシュすることができる) をうまく利用して、空のブランチでリモートのブランチを上書きする。
$ git push origin :redesign
ステージに追加したファイルをステージから削除するには git reset HEAD <filename>
を実行する (HEAD
は省略しても構わない)。
ファイルに加えた変更をなかったことにしてリポジトリのコピーに戻すには git checkout <filename>
を実行して、リポジトリからファイルをチェックアウトし直す。
ファイルを古いリビジョンに戻す場合にも git checkout
を実行するが、リビジョンの ID を指定する必要があるので、まず git log
を実行してコミット・ログを参照する。
$ git log index.html
commit 86429cd28708e22b643593b7081229017b7f0f8d
Author: joe <[email protected]>
Date: Sun Feb 17 22:19:21 2008 -0800
build new html files
commit 3607253d20c7a295965f798109f9d4af0fbeedd8
Author: fred <[email protected]>
Date: Sun Feb 17 21:32:00 2008 -0500
Oops.
古いリビジョン (360725...) に戻すには、その ID を指定してチェックアウトする。すると Git はその古いバージョンのファイルを確認してからコミットできるようにステージに追加してくれる。
$ git checkout 3607253d20c7a295965f798109f9d4af0fbeedd8 index.html
もしその巻き戻した古いバージョンを必要としない場合は、ステージから削除してチェックアウトし直せば良い。
$ git reset HEAD index.html
$ git checkout index.html
これは以下のようにまとめて実行することもできる。
$ git checkout HEAD index.html
HEAD
をリビジョンの ID と入れ替えても大丈夫なことに気が付いただろうか。それは Git においてリビジョンとブランチが事実上同じものであるからだ。
git blame <file>
を実行すると誰が何時そのファイルのどこに変更を加えたのか見ることができる。
gitk
を使うと作業コピーの詳細な履歴を見ることができる。
gitk はツリー状の変更履歴 (ブランチをまたがった変更履歴) のブラウズや差分の閲覧、古いリビジョンの検索など様々な機能を持っている。
バージョン管理システムを使って開発を続ける上でのちょっとしたヒントやアドバイスをこのセクションにまとめた。
ワープロ・ソフトを使う時は「頻繁に保存しないと後悔する」とよく言われるように、ローカル・リポジトリにできる限り頻繁にコミットするべきである。作業内容が消えてしまうことがないだけではなく、いつでも必要な時に古いバージョンに戻すことができるという安心感も得られるだろう。もちろん単語や文字を入力するたびにコミットしたり、コミットするごとにコミットしたことをコミットするなどというのは極端すぎる。重要な変更 (全部または一部が重要だと思われる変更) の度にコミットするべきである。
また、こまめにプルするべきでもある。こまめにプルすることによって、常に最新のコードであることを維持でき、運が良ければ重複した作業を避けることができる。既に共同開発者によって実装済みでかつリポジトリに反映されている機能を、3週間に一度しかプルしないために知らずに何時間もかけて実装した時はとてもショックを受けるだろう。
checkout
と reset
は気を付けて使う最後のコミットから特定のファイルに加えた変更を元に戻す場合、 git checkout <filename>
か git reset
を使って直前のコミット以降の変更点をなかったことにできる。元に戻すという機能はとても重要な機能だ (特に完全に間違ったアプローチで作業をしていたことに気づいた場合) が、諸刃の剣でもある。一旦 checkout
や reset
で変更点を削除してしまうと、それらを元に戻すことはできないので気を付ける必要がある。何も考えずに実行した reset
によって数時間に渡る作業が無に帰すこともある。
ローカルの小さなプロジェクト (つまり巨大なリモート・リポジトリなどがまったくないプロジェクト) をバージョン管理したい場合、単に git init
を実行して独立したローカル・リポジトリを作成すれば良い。例えば、新しいアプリケーションの構想を練るというような場合、以下のようにするだけで良い。
$ mkdir design_concepts
$ git init
これで Git の本当のリモート・リポジトリと同じようにファイルを追加したり、ブランチを作成したりすることができるようになる。もしプッシュやプルも行いたくなった時は、リモート・リポジトリを設定してやる必要がある。
$ git remote add <alias> <url>
$ git pull <alias> master