-
1. 使い始める
- 1.1 バージョン管理に関して
- 1.2 Git略史
- 1.3 Gitの基本
- 1.4 コマンドライン
- 1.5 Gitのインストール
- 1.6 最初のGitの構成
- 1.7 ヘルプを見る
- 1.8 まとめ
-
2. Git の基本
- 2.1 Git リポジトリの取得
- 2.2 変更内容のリポジトリへの記録
- 2.3 コミット履歴の閲覧
- 2.4 作業のやり直し
- 2.5 リモートでの作業
- 2.6 タグ
- 2.7 Git エイリアス
- 2.8 まとめ
-
3. Git のブランチ機能
- 3.1 ブランチとは
- 3.2 ブランチとマージの基本
- 3.3 ブランチの管理
- 3.4 ブランチでの作業の流れ
- 3.5 リモートブランチ
- 3.6 リベース
- 3.7 まとめ
-
4. Gitサーバー
- 4.1 プロトコル
- 4.2 サーバー用の Git の取得
- 4.3 SSH 公開鍵の作成
- 4.4 サーバーのセットアップ
- 4.5 Git デーモン
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 サードパーティによる Git ホスティング
- 4.10 まとめ
-
5. Git での分散作業
- 5.1 分散作業の流れ
- 5.2 プロジェクトへの貢献
- 5.3 プロジェクトの運営
- 5.4 まとめ
-
6. GitHub
- 6.1 アカウントの準備と設定
- 6.2 プロジェクトへの貢献
- 6.3 プロジェクトのメンテナンス
- 6.4 組織の管理
- 6.5 スクリプトによる GitHub の操作
- 6.6 まとめ
-
7. Git のさまざまなツール
- 7.1 リビジョンの選択
- 7.2 対話的なステージング
- 7.3 作業の隠しかたと消しかた
- 7.4 作業内容への署名
- 7.5 検索
- 7.6 歴史の書き換え
- 7.7 リセットコマンド詳説
- 7.8 高度なマージ手法
- 7.9 Rerere
- 7.10 Git によるデバッグ
- 7.11 サブモジュール
- 7.12 バンドルファイルの作成
- 7.13 Git オブジェクトの置き換え
- 7.14 認証情報の保存
- 7.15 まとめ
-
8. Git のカスタマイズ
- 8.1 Git の設定
- 8.2 Git の属性
- 8.3 Git フック
- 8.4 Git ポリシーの実施例
- 8.5 まとめ
-
9. Gitとその他のシステムの連携
- 9.1 Git をクライアントとして使用する
- 9.2 Git へ移行する
- 9.3 まとめ
-
10. Gitの内側
- 10.1 配管(Plumbing)と磁器(Porcelain)
- 10.2 Gitオブジェクト
- 10.3 Gitの参照
- 10.4 Packfile
- 10.5 Refspec
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータリカバリ
- 10.8 環境変数
- 10.9 まとめ
-
A1. 付録 A: その他の環境でのGit
- A1.1 グラフィカルインタフェース
- A1.2 Visual StudioでGitを使う
- A1.3 EclipseでGitを使う
- A1.4 BashでGitを使う
- A1.5 ZshでGitを使う
- A1.6 PowershellでGitを使う
- A1.7 まとめ
-
A2. 付録 B: Gitをあなたのアプリケーションに組み込む
- A2.1 Gitのコマンドラインツールを使う方法
- A2.2 Libgit2を使う方法
- A2.3 JGit
-
A3. 付録 C: Gitのコマンド
- A3.1 セットアップと設定
- A3.2 プロジェクトの取得と作成
- A3.3 基本的なスナップショット
- A3.4 ブランチとマージ
- A3.5 プロジェクトの共有とアップデート
- A3.6 検査と比較
- A3.7 デバッグ
- A3.8 パッチの適用
- A3.9 メール
- A3.10 外部システム
- A3.11 システム管理
- A3.12 配管コマンド
8.2 Git のカスタマイズ - Git の属性
Git の属性
設定項目の中には、パスに対して指定できるものもあります。Git はこれらの設定を、指定したパスのサブディレクトリやファイルにのみ適用します。
これらパス固有の設定は、 Git の属性と呼ばれ、あるディレクトリ (通常はプロジェクトのルートディレクトリ)の直下の .gitattributes
か、あるいはそのファイルをプロジェクトとともにコミットしたくない場合は .git/info/attributes
に設定します。
属性を使うと、ファイルやディレクトリ単位で個別のマージ戦略を指定したり、テキストファイル以外の diff を取る方法を指示したり、あるいはチェックインやチェックアウトの前にその内容を Git にフィルタリングさせたりできます。 このセクションでは、Git プロジェクトでパスに対して設定できる属性のいくつかについて学び、実際にその機能を使う例を見ていきます。
バイナリファイル
Git の属性を使ってできるちょっとした技として、どのファイルがバイナリファイルなのかを (その他の方法で判別できない場合のために) 指定した上で、 Git に対してバイナリファイルの扱い方を指示するというものがあります。 たとえば、機械で生成したテキストファイルの中には diff が取得できないものがありますし、バイナリファイルであっても diff が取得できるものもあります。 それを Git に指示する方法を紹介します。
バイナリファイルの特定
テキストファイルのように見えるファイルであっても、何らかの目的のために意図的にバイナリデータとして扱いたいことがあります。
たとえば、Mac の Xcode プロジェクトの中には .pbxproj
で終わる名前のファイルがあります。これは JSON (プレーンテキスト形式の JavaScript のデータフォーマット) のデータセットで、IDE がビルドの設定などをディスクに書き出したものです。
このファイルの内容はすべて UTF-8 の文字なので、理論上はテキストファイルであると言えます。しかし、このファイルをテキストファイルとして扱いたくはありません。実際のところ、このファイルは軽量なデータベースとして使われているからです。他の人が変更した内容はマージできませんし、diff をとってもあまり意味がありません。
このファイルは、基本的に機械が処理するものなのです。
要するに、バイナリファイルと同じように扱いたいということです。
すべての pbxproj
ファイルをバイナリデータとして扱うよう Git に指定するには、次の行を .gitattributes
ファイルに追加します。
*.pbxproj binary
これで、Git が CRLF 問題の対応をすることもなくなりますし、git show
や git diff
を実行したときにもこのファイルの diff を調べることはなくなります。
バイナリファイルの差分
バイナリファイルに対して意味のある差分を取る際にも、Git の属性を使うことができます。 普通の diff でも比較できるよう、バイナリデータをテキストデータに変換する方法をGitに教えればいいのです。
このテクニックを使ってまず解決したいことといえば、人類にとって最も厄介な問題のひとつ、Wordで作成した文書のバージョン管理ではないでしょうか。
奇妙なことに、Wordは最悪のエディタだと全ての人が知っているにも係わらず、皆がWordを使っています。
Word文書をバージョン管理したいと思ったなら、Gitのリポジトリにそれらを追加して、まとめてコミットすればいいのです。しかし、それでいいのでしょうか?
あなたが git diff
をいつも通りに実行すると、次のように表示されるだけです。
$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 88839c4..4afcb7c 100644
Binary files a/chapter1.docx and b/chapter1.docx differ
これでは、2つのバージョンをチェックアウトして、目視で見比べなくては、比較はできませんよね? Gitの属性を使えば、これをうまく解決できます。 `.gitattributes`に次の行を追加して下さい。
*.docx diff=word
これは、指定したパターン (.docx
) にマッチした全てのファイルに対して、差分を表示する時には “word” というフィルタを使うよう Git に指示しているのです。
では、 “word” フィルタとは何でしょうか?
これは自分で用意しなければなりません。
ここでは、 docx2txt
を使ってWord文書をテキストファイルに変換した上で、正しく diff が取れるように設定してみましょう。
まず、 docx2txt
をインストールする必要があります。 http://docx2txt.sourceforge.net からダウンロードしたら、 INSTALL
ファイルの指示に従って、シェルから見える場所にファイルを置いてください。
次に、出力を Git に合わせて変換するラッパースクリプトを作成します。
パスの通った場所に、 `docx2txt`という名前のファイルを次の内容で作成してください。
#!/bin/bash
docx2txt.pl $1 -
作ったファイルに chmod a+x
するのを忘れないでください。
最後に、Git がこのファイルを使うように設定します。
$ git config diff.word.textconv docx2txt
これで、二つのスナップショットの diff を取る際に、ファイル名の末尾が .docx
だったら、 “word” フィルタを通す(この “word” フィルタは docx2txt
というプログラムとして定義されている)ということが Git に伝わりました。
こうすることで、Wordファイルの差分を取る際に、より効果的なテキストベースでの差分を取ることができるようになります。
例を示しましょう。この本の第1章をWord形式に変換し、Gitリポジトリに登録しました。
さらに、新しい段落を追加しました。
git diff
の出力は次のようになります。
$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 0b013ca..ba25db5 100644
--- a/chapter1.docx
+++ b/chapter1.docx
@@ -2,6 +2,7 @@
This chapter will be about getting started with Git. We will begin at the beginning by explaining some background on version control tools, then move on to how to get Git running on your system and finally how to get it setup to start working with. At the end of this chapter you should understand why Git is around, why you should use it and you should be all setup to do so.
1.1. About Version Control
What is "version control", and why should you care? Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. For the examples in this book you will use software source code as the files being version controlled, though in reality you can do this with nearly any type of file on a computer.
+Testing: 1, 2, 3.
If you are a graphic or web designer and want to keep every version of an image or layout (which you would most certainly want to), a Version Control System (VCS) is a very wise thing to use. It allows you to revert files back to a previous state, revert the entire project back to a previous state, compare changes over time, see who last modified something that might be causing a problem, who introduced an issue and when, and more. Using a VCS also generally means that if you screw things up or lose files, you can easily recover. In addition, you get all this for very little overhead.
1.1.1. Local Version Control Systems
Many people's version-control method of choice is to copy files into another directory (perhaps a time-stamped directory, if they're clever). This approach is very common because it is so simple, but it is also incredibly error prone. It is easy to forget which directory you're in and accidentally write to the wrong file or copy over files you don't mean to.
Gitは、追加した “Testing: 1, 2, 3.” という正しい文字列を首尾よく、かつ、簡潔に知らせてくれました。 これだけでは完璧ではありません(書式の変更はここでは表示されていません)が、確実に動作しています。
その他の興味深い問題としては、画像ファイルの差分があります。
ひとつの方法として、EXIF情報(多くのファイル形式で使用されているメタデータ)を抽出するフィルタを使う方法があります。
exiftool`をダウンロードしてインストールすれば、画像データを、メタデータを表すテキストデータへ変換できます。これによって、 diff では少なくとも、変更内容をテキスト形式で表示できるようになります。
ではここで、以下の行を
.gitattributes`に追加してみましょう。
*.png diff=exif
続いて、さきほどインストールしたツールを使うようGitの設定を変更します。
$ git config diff.exif.textconv exiftool
プロジェクト中の画像データを置き換えて git diff
を実行すると、次のように表示されるでしょう。
diff --git a/image.png b/image.png
index 88839c4..4afcb7c 100644
--- a/image.png
+++ b/image.png
@@ -1,12 +1,12 @@
ExifTool Version Number : 7.74
-File Size : 70 kB
-File Modification Date/Time : 2009:04:21 07:02:45-07:00
+File Size : 94 kB
+File Modification Date/Time : 2009:04:21 07:02:43-07:00
File Type : PNG
MIME Type : image/png
-Image Width : 1058
-Image Height : 889
+Image Width : 1056
+Image Height : 827
Bit Depth : 8
Color Type : RGB with Alpha
ファイルのサイズと画像のサイズが変更されたことが簡単に見て取れます。
キーワード展開
SubversionやCVSを使っていた開発者から、キーワード展開機能をリクエストされることがよくあります。 ここでの主な問題は、Git では、コミットの後に、コミットに関する情報を使ってファイルを変更することはできないということです。これは、Git がコミットの最初にファイルのチェックサムを生成するためです。 しかし、ファイルをチェックアウトする際にテキストを挿入し、コミットへ追加する際にそれを削除することは可能です。 Gitの属性はこれを行うための方法を2つ提供します。
ひとつめの方法として、ファイルの $Id$
フィールドへ、 blob の SHA-1 チェックサムを自動的に挿入できます。
あるファイル、もしくはいくつかのファイルに対してこの属性を設定すれば、次にそのブランチをチェックアウトする時、Gitはこの置き換えを行うようになります。
ただし、挿入されるチェックサムはコミットに対するものではなく、対象となるblobのものであるという点に注意して下さい。
ではここで、以下の行を`.gitattributes`に追加してみましょう。
*.txt ident
続いて、`$Id$`への参照をテスト用ファイルに追加します。
$ echo '$Id$' > test.txt
そうすると、次にこのファイルをチェックアウトする時、GitはblobのSHA-1チェックサムを挿入します。
$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt
$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
しかし、この結果はあまり役に立ちません。 CVSやSubversionのキーワード展開ではタイムスタンプを含めることができます。対して、SHA-1チェックサムは完全にランダムな値ですから、2つの値の新旧を知るための助けにはなりません。
これには、コミットおよびチェックアウトの時にキーワード展開を行うフィルタを書いてやれば対応できます。
このフィルタは “clean” および “smudge” フィルタと呼ばれます。
.gitattributes
ファイルで、特定のパスにフィルタを設定し、チェックアウトの直前( “smudge” 、 チェックアウトする時に “smudge” フィルタを実行する を参照)およびステージングの直前( “clean” 、 ステージングする時に “clean” フィルタを実行する を参照)に処理を行うスクリプトを設定できます。
これらのフィルタは、色々と面白いことに使えます。
この機能に対してオリジナルのコミットメッセージは簡単な例を与えてくれています。それはコミット前にCのソースコードを indent
プログラムに通すというものです。
*.c
ファイルに対してこのフィルタを実行するように、`.gitattributes`ファイルにfilter属性を設定できます。
*.c filter=indent
それから、smudgeとcleanで “indent” フィルタが何を行えばいいのかをGitに教えます。
$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat
このケースでは、 *.c
にマッチするファイルをコミットした時、Gitはステージング前にindentプログラムにファイルを通し、チェックアウトする前には cat
を通すようにします。
cat`は基本的に何もしません。入力されたデータと同じデータを吐き出すだけです。
この組み合わせを使えば、Cのソースコードのコミット前に、効果的に `indent
を通せます。
もうひとつの興味深い例として、RCSスタイルの $Date$
キーワード展開があります。
これを正しく行うには、ファイル名を受け取り、プロジェクトの最新のコミットの日付を見て、その日付をファイルに挿入するちょっとしたスクリプトが必要になります。
これを行うRubyスクリプトを以下に示します。
#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
このスクリプトは、git log
コマンドの出力から最新のコミットの日付を取得し、標準入力中のすべての $Date$
文字列にその日付を追加し、結果を出力します。お気に入りのどんな言語で書くにしても、簡単なスクリプトになるでしょう。
このスクリプトファイルに`expand_date`と名前をつけ、実行パスのどこかに置きます。
次に、Git にフィルタ(ここでは dater`とします)を設定し、チェックアウト時に smudge で `expand_date
フィルタを使うように指定します。
コミット時に日付を削除するのには、 Perl の正規表現が使えます。
$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
このPerlのスニペットは、 $Date$
文字列の内側にある内容を削除し、日付を挿入する前の状態に戻します。
さて、フィルタの準備ができました。このファイルが新しいフィルタに引っかかるように Git の属性を設定し、ファイルに $Date$
キーワードを追加した上で、テストしてみましょう。
date*.txt filter=dater
$ echo '# $Date$' > date_test.txt
これらの変更をコミットして、再度ファイルをチェックアウトすれば、キーワードが正しく置き換えられているのがわかります。
$ git add date_test.txt .gitattributes
$ git commit -m "Testing date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$
アプリケーションのカスタマイズにあたり、このテクニックがどれほど強力か、おわかりいただけたと思います。
しかし、注意してほしいのですが、 .gitattributes
ファイルはコミットされてプロジェクト内で共有されますが、ドライバ(このケースで言えば、dater
)そうはそうはいきません。そのため、この機能はどこででも働くわけではありません。
フィルタを設計する時には、たとえフィルタが正常に動作しなかったとしても、プロジェクトは適切に動き続けられるようにすべきです。
リポジトリをエクスポートする
あなたのプロジェクトのアーカイブをエクスポートする時には、Gitの属性データを使って興味深いことができます。
export-ignore
アーカイブを生成するとき、特定のファイルやディレクトリをエクスポートしないように設定できます。
プロジェクトにはチェックインしたいが、アーカイブファイルには含めたくないディレクトリやファイルがあるなら、それらに export-ignore
属性を設定することで、分別が行えます。
例えば、プロジェクトをエクスポートする際に tarball に含めたくないテストファイルが、 `test/`ディレクトリ以下に入っているとしましょう。 その場合、次の1行をGitの属性ファイルに追加します。
test/ export-ignore
これで、プロジェクトのtarballを作成するために git archive
を実行した時、アーカイブには test/
ディレクトリが含まれないようになります。
export-subst
デプロイ用にファイルをエクスポートする際に、export-subst
属性のついたファイルを指定して git log
のログ書式指定機能とキーワード展開機能で生成した内容をファイルに付与できます。
例えば、LAST_COMMIT`という名前のファイルをプロジェクトに追加し、`git archive`を実行した時にそのファイルのメタデータを最新コミットと同じ内容に変換したい場合、
.gitattributes`ファイルと`LAST_COMMIT`ファイルを
次のように設定します。
LAST_COMMIT export-subst
$ echo 'Last commit date: $Format:%cd by %aN$' > LAST_COMMIT
$ git add LAST_COMMIT .gitattributes
$ git commit -am 'adding LAST_COMMIT file for archives'
git archive
を実行すると、 LAST_COMMIT
は以下のような内容になっているはずです。
$ git archive HEAD | tar xCf ../deployment-testing -
$ cat ../deployment-testing/LAST_COMMIT
Last commit date: Tue Apr 21 08:38:48 2009 -0700 by Scott Chacon
このような置換に、コミットメッセージや git note を用いることもできます。その際、git log コマンドのワードラップ処理が適用されます。
$ echo '$Format:Last commit: %h by %aN at %cd%n%+w(76,6,9)%B$' > LAST_COMMIT
$ git commit -am 'export-subst uses git log's custom formatter
git archive uses git log's `pretty=format:` processor
directly, and strips the surrounding `$Format:` and `$`
markup from the output.
'
$ git archive @ | tar xfO - LAST_COMMIT
Last commit: 312ccc8 by Jim Hill at Fri May 8 09:14:04 2015 -0700
export-subst uses git log's custom formatter
git archive uses git log's `pretty=format:` processor directly, and
strips the surrounding `$Format:` and `$` markup from the output.
この結果作成されたアーカイブはデプロイするのにぴったりです。一方、いったんエクスポートされてしまったアーカイブで開発を続けるのはおすすめできません。
マージの戦略
Gitの属性を使えば、プロジェクト中の特定のファイルに対して、異なるマージ戦略を使うこともできます。 非常に有用なオプションのひとつに、指定したファイルで競合が発生した場合に、マージを行わずに、あなたの変更内容で他の誰かの変更を上書きするように設定するというものがあります。
これはプロジェクトにおいて、分岐したブランチや、特別版のブランチで作業をしている時、そのブランチでの変更をマージさせたいが、特定のファイルの変更はなかったことにしたいというような時に助けになります。
例えば、 database.xml
というデータベースの設定ファイルがあり、ふたつのブランチでその内容が異なっているとしましょう。そして、そのデータベースファイルを台無しにすることなしに、一方のブランチへとマージしたいとします。
これは、次のように属性を設定すれば実現できます。
database.xml merge=ours
その上で、ダミーのマージ戦略 ours
を次のように定義します。
$ git config --global merge.ours.driver true
もう一方のブランチでマージを実行すると、 database.xml
に関する競合は発生せず、次のような結果になります。
$ git merge topic
Auto-merging database.xml
Merge made by recursive.
この場合、 database.xml
は元々のバージョンのまま、書き変わりません。