ステージ(インデックスに変更を追加すること)について前回までざっくりと見てきましたが、第1回で紹介したコマンド git-add のマニュアルを日本語訳したので紹介します。
git-add(1)
git-add はステージを理解する上での要とも言えるコマンドなので、ひととおりオプションを眺めてみると、何か悟れるかも?
なお、原本は git-1.6.4.1 の時点でのマニュアルです。お使いのバージョンによっては存在しないオプションもあるかもしれません。新しいバージョンほどより便利になっているので、これを期に git://git.kernel.org/pub/scm/git/git.git から最新版を clone して使うというのも良い選択だと思います。
ステージというテーマでは今回が最終回になります。
gitに関する次回以降の記事は "easy branch easy commit" をテーマに何回かにわけてお送りしたいと思います。
ではでは。
2009年9月12日土曜日
2009年9月2日水曜日
hello wrold!その2(Apacheモジュール編)
hello world!の続編です。
前回は後半inetdを利用したインチキWebサーバを作りましたが、今回は由緒正しいWebサーバであるApache httpdのモジュールでhello worldをやってみたいと思います。
「apacheモジュールを書くなんて難しそう」と思ったあなた、大丈夫です。死ぬほど簡単です。
●準備: apxs のインストール
まずhttpdがインストールされたマシンにapxsコマンドをインストールしましょう。
FreeBSD を使っている場合は、ports/packagesでhttpdをインストールしていれば、apxsコマンドもすでにインストールされているはずです。
Linux を使っている場合は、httpdがインストールされていても
などで見つからなければ、インストールする必要があります。REHL系であれば、httpd-develというRPMをインストールしましょう。
●雛形のモジュールを生成
apxsはapacheモジュール開発者向けのビルド&インストールツールですが、モジュールの雛形を生成する機能があります。
上記の -g オプションが雛形生成の指示、-nの引数が新しいモジュールの名前になります。
生成された hello/mod_hello.c の中程に ap_rputs() を呼び出している箇所がありますので、その中の第一引数を書き換えます。
例えば以下のように編集します。
●コンパイル
編集が終わったら makeでコンパイルしましょう。
エラーになってしまった場合は、apxs -cを手動で実行しましょう。
(私の環境ではFreeBSD上で生成してmakeするとエラーになってしまいました)
●インストール
コンパイルできたら root で make install します。
エラーになってしまう場合は、やはりapxs -i を実行してインストールを試してみてください。
●設定&動作確認
インストールが終わったらhttpdの設定ファイルにmod_helloの設定を加えます。
設定すべき内容は、mod_hello.cの冒頭のコメント部分に記載されています。
RHEL系Linux であれば /etc/httpd/conf.d/hello.conf などに新しくmod_hello用の設定ファイルを置くのが良いでしょう。
FreeBSD の www/apache22 であれば、/usr/local/etc/apache22/Includes/hello.conf になります。
httpd をリスタートします。
これで作業は終了です。お好みのブラウザで設定したURLへアクセスしてみてください。
このように「hello world!」と表示されたら成功です。
ね?簡単でしょ?
●おわりに
前回の記事を読んで、「いまさらWebサーバをスクラッチから書くやつなんていねーよ」と思ったあなた、今回のような apacheモジュールなどはいかがでしょうか?
これさえマスターしておけば、IT企業への就職活動などの場において「特技はプログラミング?ていうかapacheモジュール書いてました」と言い張れますね!
ただし、相手によっては「apacheモジュール?何それおいしいの?」となるので注意が必要です!
つづく!
前回は後半inetdを利用したインチキWebサーバを作りましたが、今回は由緒正しいWebサーバであるApache httpdのモジュールでhello worldをやってみたいと思います。
「apacheモジュールを書くなんて難しそう」と思ったあなた、大丈夫です。死ぬほど簡単です。
●準備: apxs のインストール
まずhttpdがインストールされたマシンにapxsコマンドをインストールしましょう。
FreeBSD を使っている場合は、ports/packagesでhttpdをインストールしていれば、apxsコマンドもすでにインストールされているはずです。
Linux を使っている場合は、httpdがインストールされていても
kt@ume% which apxs
などで見つからなければ、インストールする必要があります。REHL系であれば、httpd-develというRPMをインストールしましょう。
root@ume# yum install -y httpd-devel
Loaded plugins: refresh-packagekit
(略)
●雛形のモジュールを生成
apxsはapacheモジュール開発者向けのビルド&インストールツールですが、モジュールの雛形を生成する機能があります。
kt@ume% apxs -g -n hello
Creating [DIR] hello
Creating [FILE] hello/Makefile
Creating [FILE] hello/modules.mk
Creating [FILE] hello/mod_hello.c
Creating [FILE] hello/.deps
上記の -g オプションが雛形生成の指示、-nの引数が新しいモジュールの名前になります。
生成された hello/mod_hello.c の中程に ap_rputs() を呼び出している箇所がありますので、その中の第一引数を書き換えます。
例えば以下のように編集します。
kt@ume% cd hello
kt@ume% vi mod_hello.c
kt@ume% diff -u mod_hello.c.orig mod_hello.c
--- mod_hello.c.orig 2009-08-23 03:37:25.000000000 +0900
+++ mod_hello.c 2009-08-23 03:39:34.000000000 +0900
@@ -51,7 +51,7 @@
r->content_type = "text/html";
if (!r->header_only)
- ap_rputs("The sample page from mod_hello.c\n", r);
+ ap_rputs("hello world!\n", r);
return OK;
}
●コンパイル
編集が終わったら makeでコンパイルしましょう。
kt@ume% make
/usr/lib64/apr-1/build/libtool --silent --mode=compile gcc -pthread -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -I/usr/include/httpd -I. -I/usr/include/apr-1 -I/usr/kerberos/include -prefer-pic -c mod_hello.c && touch mod_hello.slo
/usr/lib64/apr-1/build/libtool --silent --mode=link gcc -pthread -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -L/usr/kerberos/lib64 -Wl,-z,relro -o mod_hello.la -rpath /usr/lib64/httpd/modules -module -avoid-version mod_hello.lo
エラーになってしまった場合は、apxs -cを手動で実行しましょう。
(私の環境ではFreeBSD上で生成してmakeするとエラーになってしまいました)
kt@matsu% apxs -c mod_hello.c
/usr/local/build-1/libtool --silent --mode=compile cc -prefer-pic -O2 -fno-strict-aliasing -pipe -I/usr/local/include/mysql -DHAVE_MYSQL_H -I/usr/include -DLDAP_DEPRECATED -I/usr/local/include/apache22 -I/usr/local/include/apr-1 -I/usr/local/include/apr-1 -I/usr/local/include -c -o mod_hello.lo mod_hello.c && touch mod_hello.slo
/usr/local/build-1/libtool --silent --mode=link cc -o mod_hello.la -rpath /usr/local/libexec/apache22 -module -avoid-version mod_hello.lo
●インストール
コンパイルできたら root で make install します。
kt@ume% su -
=====================================================================================================================
>> Welcome to ume.local
=====================================================================================================================
There are 6 user(s) logged in.
root@ume# cd /home/kt/tmp/hello
root@ume# make install
make[1]: Entering directory `/a/fuji/home/kt/tmp/hello'
/usr/lib64/apr-1/build/libtool --silent --mode=install cp mod_hello.la /usr/lib64/httpd/modules/
make[1]: Leaving directory `/a/fuji/home/kt/tmp/hello'
/usr/lib64/apr-1/build/libtool --silent --mode=install cp mod_hello.la /usr/lib64/httpd/modules/
エラーになってしまう場合は、やはりapxs -i を実行してインストールを試してみてください。
kt@matsu% su -
=====================================================================================================================
>> Welcome to matsu.local
=====================================================================================================================
There are 2 user(s) logged in.
root@matsu# cd /home/kt/hello
root@matsu# apxs -i -a mod_hello.la
/usr/local/share/apache22/build/instdso.sh SH_LIBTOOL='/usr/local/build-1/libtool' mod_hello.la /usr/local/libexec/apache22
/usr/local/build-1/libtool --mode=install cp mod_hello.la /usr/local/libexec/apache22/
cp .libs/mod_hello.so /usr/local/libexec/apache22/mod_hello.so
cp .libs/mod_hello.lai /usr/local/libexec/apache22/mod_hello.la
cp .libs/mod_hello.a /usr/local/libexec/apache22/mod_hello.a
chmod 644 /usr/local/libexec/apache22/mod_hello.a
ranlib /usr/local/libexec/apache22/mod_hello.a
----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/libexec/apache22
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the `LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the `LD_RUN_PATH' environment variable
during linking
- use the `-Wl,--rpath -Wl,LIBDIR' linker flag
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
chmod 755 /usr/local/libexec/apache22/mod_hello.so
[activating module `hello' in /usr/local/etc/apache22/httpd.conf]
●設定&動作確認
インストールが終わったらhttpdの設定ファイルにmod_helloの設定を加えます。
設定すべき内容は、mod_hello.cの冒頭のコメント部分に記載されています。
root@ume# head -n 20 mod_hello.c
(中略)
** Then activate it in Apache's httpd.conf file for instance
** for the URL /hello in as follows:
**
** # httpd.conf
** LoadModule hello_module modules/mod_hello.so
** <Location /hello>
** SetHandler hello
** </Location>
**
** Then after restarting Apache via
RHEL系Linux であれば /etc/httpd/conf.d/hello.conf などに新しくmod_hello用の設定ファイルを置くのが良いでしょう。
FreeBSD の www/apache22 であれば、/usr/local/etc/apache22/Includes/hello.conf になります。
root@ume# cat > /etc/httpd/conf.d/hello.conf
LoadModule hello_module modules/mod_hello.so
<Location /hello>
SetHandler hello
</Location>
httpd をリスタートします。
root@ume# /etc/init.d/httpd restart
Stopping httpd: [ OK ]
Starting httpd: [ OK ]
これで作業は終了です。お好みのブラウザで設定したURLへアクセスしてみてください。
kt@ume% wget -q -O - http://ume.local/hello/
hello world!
このように「hello world!」と表示されたら成功です。
ね?簡単でしょ?
●おわりに
前回の記事を読んで、「いまさらWebサーバをスクラッチから書くやつなんていねーよ」と思ったあなた、今回のような apacheモジュールなどはいかがでしょうか?
これさえマスターしておけば、IT企業への就職活動などの場において「特技はプログラミング?ていうかapacheモジュール書いてました」と言い張れますね!
ただし、相手によっては「apacheモジュール?何それおいしいの?」となるので注意が必要です!
つづく!
2009年8月29日土曜日
ステージを理解して git をもっと便利に使う(その3)
前回から引き続き、「ステージを理解して git をもっと便利に使う」というテーマでお送りする、git解説記事の第3弾です。
まずは、前回までのおさらいです。
git にはリポジトリに格納された「最新のコミット」と「ワーキングコピー」の間に「index」と呼ばれる緩衝地帯が存在しています。チェックアウト直後は
ですが、ワーキングコピーを編集した後、git addにより編集内容がstage(indexに登録)され、
となります。
を積極的に使うことで、編集内容を部分的にstageする/しないをコントロールし、1コミットの内容を洗練しましょう、というのが第1回の内容でした。
第2回はgit diffとgit diff --cachedの違いについて解説しました。
は「index」と「ワーキングコピー」の差分を表示し、
は「最新のコミット」と「index」の差分を表示する、
という内容でした。
第3回はgit resetになります。
●git reset
実行例
git resetを使うと、一度git addでstageした変更内容をindexから削除(unstage)することができます。
例として、
「ワーキングコピーの README と src/abc.c を編集し、git add -uで両方ともstageしたが、よく考えてみると別々にコミットすべき内容だった」
という状況を考えます。git statusで確認すると、以下のように表示されます。
今回はREADMEだけをコミットし、src/abc.cはその後のコミットに含めることにしたとします。
この場合、git reset src/abc.cを実行することでsrc/abc.cをunstageできます。
このとおり、src/abc.cは「Changes to be committed:」から「Changed but not updated:」に移りました。
これでgit commitしても変更が反映されるのはREADMEだけです。
git add -pによりファイルの内容を部分的にstageできますが、unstageはファイル単位でしかできないようです。
git add -pを間違えた場合は、一旦git resetして再度git add -pでstageしなおしましょう。
なお、上の実行例の「Changes to be committed:」の下の行にこんなコメントが書かれているのに気づいたでしょうか?
unstageのやりかたがそのものズバリ書いてあります。
もしgit resetを忘れてしまった場合にも「git statusの出力を見れば書いてある」ということさえ覚えておけば、どうすればよいか思い出せるでしょう。
●おまけ
前回紹介したgit diff --cached ってコマンドを打つのが面倒じゃありませんか?こんなときは git configで別名を定義しましょう。
上記の例では difc を "diff --cached" の別名として定義しています。
CVSやSubversionからの乗り換えユーザには以下のようなaliasもおすすめです。
まずは、前回までのおさらいです。
git にはリポジトリに格納された「最新のコミット」と「ワーキングコピー」の間に「index」と呼ばれる緩衝地帯が存在しています。チェックアウト直後は
「最新のコミット」=「index」=「ワーキングコピー」
ですが、ワーキングコピーを編集した後、git addにより編集内容がstage(indexに登録)され、
「最新のコミット」≠「index(+α)」=「ワーキングコピー(+α)」
となります。
% git add
や
% git add -p
を積極的に使うことで、編集内容を部分的にstageする/しないをコントロールし、1コミットの内容を洗練しましょう、というのが第1回の内容でした。
第2回はgit diffとgit diff --cachedの違いについて解説しました。
% git diff
は「index」と「ワーキングコピー」の差分を表示し、
% git diff --cached
は「最新のコミット」と「index」の差分を表示する、
という内容でした。
第3回はgit resetになります。
●git reset
実行例
% git reset
git resetを使うと、一度git addでstageした変更内容をindexから削除(unstage)することができます。
例として、
「ワーキングコピーの README と src/abc.c を編集し、git add -uで両方ともstageしたが、よく考えてみると別々にコミットすべき内容だった」
という状況を考えます。git statusで確認すると、以下のように表示されます。
kt@ume% git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD..." to unstage)
#
# modified: README
# modified: src/abc.c
#
今回はREADMEだけをコミットし、src/abc.cはその後のコミットに含めることにしたとします。
この場合、git reset src/abc.cを実行することでsrc/abc.cをunstageできます。
kt@ume% git reset src/abc.c
src/abc.c: locally modified
kt@ume% git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD..." to unstage)
#
# modified: README
#
# Changed but not updated:
# (use "git add..." to update what will be committed)
#
# modified: src/abc.c
#
このとおり、src/abc.cは「Changes to be committed:」から「Changed but not updated:」に移りました。
これでgit commitしても変更が反映されるのはREADMEだけです。
git add -pによりファイルの内容を部分的にstageできますが、unstageはファイル単位でしかできないようです。
git add -pを間違えた場合は、一旦git resetして再度git add -pでstageしなおしましょう。
なお、上の実行例の「Changes to be committed:」の下の行にこんなコメントが書かれているのに気づいたでしょうか?
# (use "git reset HEAD..." to unstage)
unstageのやりかたがそのものズバリ書いてあります。
もしgit resetを忘れてしまった場合にも「git statusの出力を見れば書いてある」ということさえ覚えておけば、どうすればよいか思い出せるでしょう。
●おまけ
前回紹介したgit diff --cached ってコマンドを打つのが面倒じゃありませんか?こんなときは git configで別名を定義しましょう。
kt@ume% git config --global alias.difc "diff --cached"
kt@ume% git help difc
`git difc' is aliased to `diff --cached'
上記の例では difc を "diff --cached" の別名として定義しています。
CVSやSubversionからの乗り換えユーザには以下のようなaliasもおすすめです。
git config --global alias.ad add
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.co checkout
git config --global alias.dif diff
git config --global alias.di diff
git config --global alias.st status
2009年8月23日日曜日
ステージを理解して git をもっと便利に使う(その2)
前回から引き続き、「ステージを理解して git をもっと便利に使う」というテーマでコマンドを紹介します。
今回は git diff --cached です。
●git diff --cached
使用例
前回覚えた git add でstageする/しないを使い分けると、困ったことがひとつ出てきます。
それは git diff が「index」と「ワーキングコピー」の差分だけを表示すること、つまり一旦git addでstageした内容はgit diffで表示されなくなってしまうことです。
前回の復習として、git add直後の「最新のコミット」と「index」、「ワーキングコピー」の関係を思い出してみましょう。
git diffは「index」と「ワーキングコピー」の差分だけを表示するので、「最新のコミット」と「index」の差分や「最新のコミット」と「ワーキングコピー」の差分は表示できません。
もちろんgit add -uで「ワーキングコピー」の変更点をすべてstageすると、git diffは何も表示しなくなります。
ここで git diff --cached を使えば「最新のコミット」と「index」の差分(これは次回のコミットに含まれる変更点でもあります)を表示できます。
それでは具体的な例を見ていきましょう。
前回使った src/abc.c と README のシチュエーションを考えます。
git addする前であれば「index」は「最新のコミット」と同一なので、以下のようにgit diffはcheckoutしてから編集した内容すべてを示してくれますが・・・
以下の用にgit addでstageすると、その変更内容はgit diffでは表示されなくなります。
ここでgit diff --cachedを実行することで、「index(stageされた変更内容)」と「最新のコミット」との差分、つまり次回のコミットに含まれる変更内容を表示することができます。
いかがですか?
git diff --cachedを使うことで次回のコミットに含まれる内容を簡単に確認できるようになりました。
今まで使っていたgit diffと合わせて使えばstage管理は簡単です。
次回は間違えてaddしてしまった内容を取り消すgit resetを紹介します(ヒントはgit statusの出力にあります)。
つづく!
今回は git diff --cached です。
●git diff --cached
使用例
git diff --cached
前回覚えた git add でstageする/しないを使い分けると、困ったことがひとつ出てきます。
それは git diff が「index」と「ワーキングコピー」の差分だけを表示すること、つまり一旦git addでstageした内容はgit diffで表示されなくなってしまうことです。
前回の復習として、git add直後の「最新のコミット」と「index」、「ワーキングコピー」の関係を思い出してみましょう。
「最新のコミット」≠「index」=「ワーキングコピー」
git diffは「index」と「ワーキングコピー」の差分だけを表示するので、「最新のコミット」と「index」の差分や「最新のコミット」と「ワーキングコピー」の差分は表示できません。
もちろんgit add -uで「ワーキングコピー」の変更点をすべてstageすると、git diffは何も表示しなくなります。
ここで git diff --cached を使えば「最新のコミット」と「index」の差分(これは次回のコミットに含まれる変更点でもあります)を表示できます。
それでは具体的な例を見ていきましょう。
前回使った src/abc.c と README のシチュエーションを考えます。
git addする前であれば「index」は「最新のコミット」と同一なので、以下のようにgit diffはcheckoutしてから編集した内容すべてを示してくれますが・・・
kt@ume% git status # On branch master # Changed but not updated: # (use "git add..." to update what will be committed) # # modified: README # modified: src/abc.c # no changes added to commit (use "git add" and/or "git commit -a") kt@ume% git diff diff --git a/README b/README index f6419fa..59fbc52 100644 --- a/README +++ b/README @@ -9,4 +9,4 @@ This is test project. % make % make install -3. Have fan. +3. Have fun. diff --git a/src/abc.c b/src/abc.c index 869a851..31c175f 100644 --- a/src/abc.c +++ b/src/abc.c @@ -10,7 +10,7 @@ int main(int argc, char **argv) while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { - case 'V': + case 'v': printf("%s %s\n", "hello", "0.1"); return 0; break;
以下の用にgit addでstageすると、その変更内容はgit diffでは表示されなくなります。
kt@ume% git add src/abc.c kt@ume% git status # On branch master # Changes to be committed: # (use "git reset HEAD..." to unstage) # # modified: src/abc.c # # Changed but not updated: # (use "git add ..." to update what will be committed) # # modified: README # kt@ume% git diff diff --git a/README b/README index f6419fa..59fbc52 100644 --- a/README +++ b/README @@ -9,4 +9,4 @@ This is test project. % make % make install -3. Have fan. +3. Have fun.
ここでgit diff --cachedを実行することで、「index(stageされた変更内容)」と「最新のコミット」との差分、つまり次回のコミットに含まれる変更内容を表示することができます。
kt@ume% git status # On branch master # Changes to be committed: # (use "git reset HEAD..." to unstage) # # modified: src/abc.c # # Changed but not updated: # (use "git add ..." to update what will be committed) # # modified: README # kt@ume% git diff --cached diff --git a/src/abc.c b/src/abc.c index 869a851..31c175f 100644 --- a/src/abc.c +++ b/src/abc.c @@ -10,7 +10,7 @@ int main(int argc, char **argv) while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { - case 'V': + case 'v': printf("%s %s\n", "hello", "0.1"); return 0; break;
いかがですか?
git diff --cachedを使うことで次回のコミットに含まれる内容を簡単に確認できるようになりました。
今まで使っていたgit diffと合わせて使えばstage管理は簡単です。
次回は間違えてaddしてしまった内容を取り消すgit resetを紹介します(ヒントはgit statusの出力にあります)。
つづく!
2009年8月22日土曜日
ステージを理解して git をもっと便利に使う
git には「stage(ステージ)する」という概念があります。あるいは「index」と言い換えてもいいかもしれません。
簡単にいうと「stageする」=「特定の変更内容をindexに登録する」=「次回コミットに含めるようgitに指示する」ということなのですが、この概念は今まで主流だった CVS や Subversion といったバージョン管理システムにはありませんでいした。
長年CVSを使っていて、その考え方に凝り固まっていた私は、gitを使い始めてしばらくはstageやindexの概念を理解できなかったので、今回ここで紹介することにしました。
このstageとindexを覚えると「ひとつのコミットには、その主題となる変更と無関係な変更を含めない」という「バージョン管理システムを使う上で重要なはずなのに、つい疎かにしてしまいがち」なポリシーを簡単に実践できるようになります。
今回stageおよびindexについて、その管理に必要ないくつかのコマンドとあわせて紹介しますので、よさそうだなと感じたら是非使って見てください。
●git add
使用例
最初に紹介するコマンドはgit addです。
これは編集したファイルをstageする(=次回のcommit対象にする)コマンドです。
「そんなの知ってるし」と思った人も多いかもしれませんが、待ってください。あなたは add を使いこなしていますか?
心当たりのある人は、addの存在理由を理解して使いこなせるようになればgitが格段に便利になります。
●stageとindex
git addの存在理由を理解するためには、まずstageとindexという概念を理解しましょう。
git はチェックアウトされたファイルを編集した直後にcommitを実行しても、どのファイルも新しいバージョンとして登録されません。
新しいバージョンとして登録するにはこのgit addをつかって、「このファイルは次回のコミットに含める」と宣言してやる必要があります。
このようにある変更点を次回のコミットに含めるようgitに指示することをgit用語で 「stageする」とかstagingと言います。
また、ワーキングコピーからstageした内容を保持する領域のことをindexと呼びます。
indexは、コミットを永遠に記録するリポジトリと、ワーキングコピーとの間にある緩衝地帯であると考えると理解しやすいと思います。
* リポジトリに格納された「最新のコミット」
* stageした内容を保持する「index」
* ユーザが通常のファイルとして編集する「ワーキングコピー」
の関係を図示すると、checkout直後は
となっています。ワーキングコピーを編集すると
となりますね。次にaddの操作をを図示すると、
であり、その結果、
となります。
なお、「index」の内容を「最新のコミット」として反映するリポジトリに反映するコマンドはgit commitです。
commitの操作を図示すると、
であり、その結果
となります。
このstage機構を利用することで「ひとつのコミットには主題となる変更とは無関係な変更を含めない」というポリシーを実現しやすくなります。
例えば「あー、調子に乗って途中で別のバグも修正しちゃったけど、まぁいいかcommitしたれ」と、複数の意味を持った変更を、ひとつのチェンジセットとしてcommitしてしまった経験がないでしょうか?
このようなコミットは後から変更点のdiffを見直したとき別々の意味を持った変更がごっちゃになって表示されたり、commit logには片方の変更にしか言及してなかったり、と理解の妨げになることが多く、あまり行儀の良いコミットとは言えません。
このように変更を加えすぎてしまったときは次回コミットに含めたいファイルだけをgit addしてやれば良いので、行儀の良いコミットを実践しやすくなります。
●操作例
それでは具体的な操作例を見てみましょう。
今、あなたは自分が管理しているプロジェクトのsrc/abc.cというファイルのバグ修正を終えたところで、READMEにスペルミスがあることを見つけ、これも修正したとします。
ここでgit statusを実行すると、以下のように出力されます。
この状態から
や、または
などを実行すると、「src/abc.cのバグ修正」と「READMEのスペルミス修正」という二つの主題をひとつのチェンジセットとしてコミットしてしまうことになります。
このような場合は、本来「src/abc.cのバグ修正」と「READMEのスペルミス修正」は別々のコミットに分けるべきですので、正しい操作を git add していない状態から実行する例を見てみます。
まずは src/abc.c だけを add することで、次のコミットでは src/abc.c の変更だけを登録できるようになります。
ここで git status を確認してみましょう。
このように"Changes to be committed:"、つまり「コミットされる変更点」として src/abc.c だけが登録されていることが分かります。
ここで1回めのgit commitを実行します。
と commit を実行してやれば、「src/abc.cのバグ修正」と「READMEのスペルミス修正」のうち、
「src/abc.cのバグ修正」だけをコミットできました。
続いて、「READMEのスペルミス修正」をgit addしてgit commitします。
このように、git addを使えば、ひとつのコミットに含めるべきでない複数の変更をしてしまっても、簡単に複数のコミットに分割できるようになります。
●git add -p
続いて使用例に挙げた -p オプションについて解説します。
先ほど挙げた「あー、調子に乗って途中で別のバグも修正しちゃったけど、まぁいいかcommitしたれ」というシチュエーションとして、1ファイル内で複数の箇所を編集してしまった場合も考えられます。
そんなときは、このgit add -pを実行すれば、1ファイル内の差分ごとにstageする/しないを対話的に選択できます。(1ファイル内の変更点から一部だけstageできます)
git add -p は対話的な処理であり、プロンプトに?と打てば使用方法が表示されますので、詳細は省略します。
●おまけ
なお、すでに見たように、git statusは編集されているファイルを「stageされているファイル」と「stageされていないファイル」に分けて表示してくれます。(git status 時に表示されるコメントをよく読んでみましょう)
さらに、git add を使いこなせるようになったら次回紹介する git diff --cached も覚えると良いでしょう。
つづく!
簡単にいうと「stageする」=「特定の変更内容をindexに登録する」=「次回コミットに含めるようgitに指示する」ということなのですが、この概念は今まで主流だった CVS や Subversion といったバージョン管理システムにはありませんでいした。
長年CVSを使っていて、その考え方に凝り固まっていた私は、gitを使い始めてしばらくはstageやindexの概念を理解できなかったので、今回ここで紹介することにしました。
このstageとindexを覚えると「ひとつのコミットには、その主題となる変更と無関係な変更を含めない」という「バージョン管理システムを使う上で重要なはずなのに、つい疎かにしてしまいがち」なポリシーを簡単に実践できるようになります。
今回stageおよびindexについて、その管理に必要ないくつかのコマンドとあわせて紹介しますので、よさそうだなと感じたら是非使って見てください。
●git add
使用例
git add [-p] <filename>
最初に紹介するコマンドはgit addです。
これは編集したファイルをstageする(=次回のcommit対象にする)コマンドです。
「そんなの知ってるし」と思った人も多いかもしれませんが、待ってください。あなたは add を使いこなしていますか?
- 「いつもadd -uで何も考えずに全部addしている」とか、
- 「commit -aでコミットするときに一括でaddしてばかり」だったり、
- 「いちいちaddすんの面倒だなー」と思ってたりしませんか?
心当たりのある人は、addの存在理由を理解して使いこなせるようになればgitが格段に便利になります。
●stageとindex
git addの存在理由を理解するためには、まずstageとindexという概念を理解しましょう。
git はチェックアウトされたファイルを編集した直後にcommitを実行しても、どのファイルも新しいバージョンとして登録されません。
新しいバージョンとして登録するにはこのgit addをつかって、「このファイルは次回のコミットに含める」と宣言してやる必要があります。
このようにある変更点を次回のコミットに含めるようgitに指示することをgit用語で 「stageする」とかstagingと言います。
また、ワーキングコピーからstageした内容を保持する領域のことをindexと呼びます。
indexは、コミットを永遠に記録するリポジトリと、ワーキングコピーとの間にある緩衝地帯であると考えると理解しやすいと思います。
* リポジトリに格納された「最新のコミット」
* stageした内容を保持する「index」
* ユーザが通常のファイルとして編集する「ワーキングコピー」
の関係を図示すると、checkout直後は
「(リポジトリに格納された)最新のコミット」=「index」=「ワーキングコピー」
となっています。ワーキングコピーを編集すると
「最新のコミット」=「index」≠「ワーキングコピー」
となりますね。次にaddの操作をを図示すると、
「最新のコミット」=「index」←(git add)←「ワーキングコピー」
であり、その結果、
「最新のコミット」≠「index」=「ワーキングコピー」
となります。
なお、「index」の内容を「最新のコミット」として反映するリポジトリに反映するコマンドはgit commitです。
commitの操作を図示すると、
「最新のコミット」←(git commit)←「index」=「ワーキングコピー」
であり、その結果
「最新のコミット」=「index」=「ワーキングコピー」
となります。
このstage機構を利用することで「ひとつのコミットには主題となる変更とは無関係な変更を含めない」というポリシーを実現しやすくなります。
例えば「あー、調子に乗って途中で別のバグも修正しちゃったけど、まぁいいかcommitしたれ」と、複数の意味を持った変更を、ひとつのチェンジセットとしてcommitしてしまった経験がないでしょうか?
このようなコミットは後から変更点のdiffを見直したとき別々の意味を持った変更がごっちゃになって表示されたり、commit logには片方の変更にしか言及してなかったり、と理解の妨げになることが多く、あまり行儀の良いコミットとは言えません。
このように変更を加えすぎてしまったときは次回コミットに含めたいファイルだけをgit addしてやれば良いので、行儀の良いコミットを実践しやすくなります。
●操作例
それでは具体的な操作例を見てみましょう。
今、あなたは自分が管理しているプロジェクトのsrc/abc.cというファイルのバグ修正を終えたところで、READMEにスペルミスがあることを見つけ、これも修正したとします。
ここでgit statusを実行すると、以下のように出力されます。
kt@ume% git status# On branch master # Changed but not updated: # (use "git add <file>..." to update what will be committed) # # modified: README # modified: src/abc.c #no changes added to commit (use "git add" and/or "git commit -a")
この状態から
% git add -u % git commit -m "Fix bug in src/abc.c and Fix typo in README."
や、または
% git commit -a -m "Fix bug in src/abc.c and Fix typo in README."
などを実行すると、「src/abc.cのバグ修正」と「READMEのスペルミス修正」という二つの主題をひとつのチェンジセットとしてコミットしてしまうことになります。
このような場合は、本来「src/abc.cのバグ修正」と「READMEのスペルミス修正」は別々のコミットに分けるべきですので、正しい操作を git add していない状態から実行する例を見てみます。
まずは src/abc.c だけを add することで、次のコミットでは src/abc.c の変更だけを登録できるようになります。
kt@ume% git add src/abc.c
ここで git status を確認してみましょう。
kt@ume% git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: src/abc.c # # Changed but not updated: # (use "git add <file>..." to update what will be committed) # # modified: README#
このように"Changes to be committed:"、つまり「コミットされる変更点」として src/abc.c だけが登録されていることが分かります。
ここで1回めのgit commitを実行します。
kt@ume% git commit -m "Fix bug in src/abc.c."
と commit を実行してやれば、「src/abc.cのバグ修正」と「READMEのスペルミス修正」のうち、
「src/abc.cのバグ修正」だけをコミットできました。
続いて、「READMEのスペルミス修正」をgit addしてgit commitします。
kt@ume% git add README kt@ume% git commit -m "Fix typo in README."
このように、git addを使えば、ひとつのコミットに含めるべきでない複数の変更をしてしまっても、簡単に複数のコミットに分割できるようになります。
●git add -p
続いて使用例に挙げた -p オプションについて解説します。
先ほど挙げた「あー、調子に乗って途中で別のバグも修正しちゃったけど、まぁいいかcommitしたれ」というシチュエーションとして、1ファイル内で複数の箇所を編集してしまった場合も考えられます。
そんなときは、このgit add -p
git add -p は対話的な処理であり、プロンプトに?と打てば使用方法が表示されますので、詳細は省略します。
●おまけ
なお、すでに見たように、git statusは編集されているファイルを「stageされているファイル」と「stageされていないファイル」に分けて表示してくれます。(git status 時に表示されるコメントをよく読んでみましょう)
さらに、git add を使いこなせるようになったら次回紹介する git diff --cached も覚えると良いでしょう。
つづく!
2009年8月20日木曜日
hello world!
最初はみんなこれでしょ、ということで hello world の書き方です。
まずはドノーマルなC言語から
ポイントがいくつかあります。
zsh では precmd という関数を定義することでプロンプト表示前に関数を実行してくれます。
bash の場合は PROMPT_COMMAND という変数に実行したいコマンドを定義しましょう。
いつまでコマンドを叩いても hello world の表示が止まることはありません。良かったですね!
お次はネットワークを利用した hello world です。まずは FreeBSD 編
inetd から起動するプログラムは「標準入力=クライアントからの入力」「標準出力=クライアントへの出力」という前提で書かれています。逆に言うと普通に標準入出力でやりとりするプログラムを書けば、inetd から呼び出すことであっというまにサーバを書けます。
もちろんIPv6対応です。
すごいぞinetd!だてにインターネットスーパーサーバなんて大層な名前を背負ってませんね。
これを応用すればWebサーバだって楽勝です。
ここまでできたらお好みのWebブラウザで設定したホストにアクセスしてみましょう。hello world! と表示されましたか?
うまく行ったら、就職活動で「得意科目はぁープログラミング?ていうかぁ、Webサーバとか書いてましたぁ」と言い張ることができます。すごいですね!(本当にやって過度な期待を持たれても私は知りません)
続いてLinux編を・・・と思ったのですが、もう疲れたのでやめときます。Linuxの場合はxinetdでググれ。
それではここまで読んでいただいてありがとうございました。
気が向いたらまた書くかもしれません。ブログが続くかどうかもわかりませんが・・・
つづく!
まずはドノーマルなC言語から
kt@ume% cat > hello.c #include <stdio.h> int main() { printf("hello world!\n"); return 0; } kt@ume% make hello cc hello.c -o hello kt@ume% PATH=.:$PATH hello hello world
ポイントがいくつかあります。
- cat を使うことで気分はBill Joy。でも間違ってEnterを押すと前の行には戻れません。行内でも間違いを見つけたらひたすらバックスペースで戻る以外に修正する術はなし。
- *.c ファイルのファイル名から .c を除いた文字列を make することで Makefile いらず。
- 私が小さい頃はカレントディレクトリをPATHに入れてるとグーで殴られました。他のPATHの前に入れるなんて、もってのほか。でも今はこうして安全な方法を編み出すことができました。./hello は邪道です。sh系限定ですcsh系とは違うんです。
kt@ume% precmd () { echo 'hello world!' } hello world! kt@ume% cd / hello world! kt@ume% ls a/ boot/ etc/ lib/ lost+found/ mnt/ proc/ sbin/ srv/ tmp/ var/ bin/ dev/ home@ lib64/ media/ opt/ root/ selinux/ sys/ usr/ hello world!
zsh では precmd という関数を定義することでプロンプト表示前に関数を実行してくれます。
bash の場合は PROMPT_COMMAND という変数に実行したいコマンドを定義しましょう。
[kt@susuki ~]$ PROMPT_COMMAND='echo hello world!' [kt@susuki ~]$ cd / hello world! [kt@susuki /]$ ls COPYRIGHT cdrom entropy lib proc sys a compat etc libexec rescue tmp bin dev home media root usr boot dist jail mnt sbin var hello world!
いつまでコマンドを叩いても hello world の表示が止まることはありません。良かったですね!
お次はネットワークを利用した hello world です。まずは FreeBSD 編
root@susuki# vi /etc/inetd.conf # ファイルの最後に以下の2行を加えます。 最後の hello は最初に作ってコンパイルしたコマンドを指定してください。 hello-port stream tcp nowait root /home/kt/misc/blog/hello/hello hello-port stream tcp6 nowait root /home/kt/misc/blog/hello/hello root@susuki# /etc/rc.d/inetd forcestart Starting inetd. root@susuki# telnet localhost hello-port Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello world! Connection closed by foreign host.
inetd から起動するプログラムは「標準入力=クライアントからの入力」「標準出力=クライアントへの出力」という前提で書かれています。逆に言うと普通に標準入出力でやりとりするプログラムを書けば、inetd から呼び出すことであっというまにサーバを書けます。
もちろんIPv6対応です。
root@susuki# telnet ::1 hello-port Trying ::1... Connected to localhost. Escape character is '^]'. hello world! Connection closed by foreign host.
すごいぞinetd!だてにインターネットスーパーサーバなんて大層な名前を背負ってませんね。
これを応用すればWebサーバだって楽勝です。
kt@susuki% cat > hello.c #include <stdio.h> int main() { printf("HTTP/1.0 200 OK\r\n"); printf("ContentType: text/plain;\r\n\r\n"); printf("hello world!\n"); return 0; } kt@susuki% make hello cc -O2 -fno-strict-aliasing -pipe hello.c -o hello kt@susuki% su - root@susuki# perl -pi.bak -e 's/^hello-port/http/;' /etc/inetd.conf root@susuki# /etc/rc.d/inetd forcerestart Stopping inetd. Starting inetd root@susuki# telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. HTTP/1.0 200 OK ContentType: text/plain; hello world! Connection closed by foreign host.
ここまでできたらお好みのWebブラウザで設定したホストにアクセスしてみましょう。hello world! と表示されましたか?
うまく行ったら、就職活動で「得意科目はぁープログラミング?ていうかぁ、Webサーバとか書いてましたぁ」と言い張ることができます。すごいですね!(本当にやって過度な期待を持たれても私は知りません)
続いてLinux編を・・・と思ったのですが、もう疲れたのでやめときます。Linuxの場合はxinetdでググれ。
それではここまで読んでいただいてありがとうございました。
気が向いたらまた書くかもしれません。ブログが続くかどうかもわかりませんが・・・
つづく!
登録:
投稿 (Atom)