Version Control for Designers

このドキュメントは Version Control for Designers の日本語訳であり、元のドキュメントと同じくソースコード管理に予備知識がまったくない人を想定している

Published on: 2009-06-23

はじめに

What have you done for me lately?

バージョン管理、ソース管理やリビジョン管理とも呼ばれているものはあらゆる開発に必須である。なぜなら基本的にはメールやインスタント・メッセンジャーと同じようなコミュニケーションをとることができるツールでありながら、人々の会話ではなくソースコードを元にして機能することができるからだ。

バージョン管理

他のプログラマと簡単に意思の疎通を図ることができる

開発チームでコードを共有することができる

デプロイしている「製品」バージョンを別個にメンテナンスできる

同じコードをベースにして同時に別々の機能を開発できる

全ての古いバージョンのファイルを追跡できる

作業が上書き (変更ではなく) されることがない

バージョン管理とは何か

バージョン管理、リビジョン管理またはソース・コード管理としても知られているもの、は累積する開発の各段階におけるファイルの変更を保存して維持するシステムである。バージョン管理システムはファイルをバックアップするシステムと同じ原理であるが、もっと賢い。システムの管理下にある全てのファイルは完全な変更の履歴を持ち、その履歴の任意のバージョンへ簡単に戻すことができる。その各バージョンはアルファベットと数字で構成されたユニークな識別子 (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 のコードにブランチを簡単に更新することができる。

作業の流れ

Attack of the clones

作業コピーをベースになるコードから取得するには、リモート・リポジトリを自身のマシンへクローンする必要がある。クローンすることによってリポジトリが作成され 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 user@yourserver.com: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

Commit to something in your life

好きなように変更を加えた後にファイルをステージに追加したなら、次にその変更を自身のローカルなリポジトリへコミットすることになる。そうするには 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 行目は必須ではない。

コミット・メッセージを書き終わったなら、保存しテキスト・エディタを終了させる。すると自身のローカル・リポジトリにコミットされ、開発作業に戻ることができる。

Push back

ローカル・リポジトリに変更をコミットしたなら、次はリモート・リポジトリ、誰でも作業コピーを取得することができる基準になるリポジトリ、にプッシュする (変更を反映させる) ことになる。そのためには 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 (リポジトリのデフォルトのブランチ) を更新して、今コミットしたリビジョンを参照するようにしている。これで誰もがローカルの作業コピーを更新して、コミットした変更が反映されたコードへ同期させることができるようになる。

Get updates from afar

自身のローカル・リポジトリと作業コピーをリモート・リポジトリへコミットされた最新のものへ更新するには、 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 git@yourco.com: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 <joe@example.com> Date: Sun Feb 17 22:19:21 2008 -0800 build new html files commit 3607253d20c7a295965f798109f9d4af0fbeedd8 Author: fred <fred@example.com> 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