-
1. Gitの始め方
- 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 スマートHTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 サードパーティのホスティングオプション
- 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 置き換え
- 7.14 認証情報の保存
- 7.15 まとめ
-
8. Gitのカスタマイズ
- 8.1 Git設定
- 8.2 Git Attributes
- 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 Packfiles
- 10.5 リフスペック
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータ復旧
- 10.8 環境変数
- 10.9 まとめ
-
A1. 付録A: 他の環境でのGit
- A1.1 グラフィカルインターフェース
- A1.2 Visual StudioでのGit
- A1.3 Visual Studio CodeでのGit
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMineでのGit
- A1.5 Sublime TextでのGit
- A1.6 BashでのGit
- A1.7 ZshでのGit
- A1.8 PowerShellでのGit
- A1.9 まとめ
-
A2. 付録B: アプリケーションへのGitの組み込み
- A2.1 コマンドラインGit
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
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 Plumbingコマンド
8.2 Gitのカスタマイズ - Git Attributes
Git Attributes
これらの設定の一部はパスごとに指定することも可能で、Gitはそれらの設定を特定のサブディレクトリやファイル群にのみ適用します。これらのパス固有の設定はGit属性と呼ばれ、いずれかのディレクトリ (通常はプロジェクトのルート) にある.gitattributes
ファイル、または属性ファイルをプロジェクトと一緒にコミットしたくない場合は.git/info/attributes
ファイルに設定します。
属性を使用すると、プロジェクト内の個々のファイルまたはディレクトリに対して異なるマージ戦略を指定したり、Gitに非テキストファイルをどのように差分比較するかを指示したり、Gitにコンテンツをチェックインまたはチェックアウトする前にフィルター処理させたりすることができます。このセクションでは、Gitプロジェクト内のパスに設定できるいくつかの属性について学び、この機能を実際に使用するいくつかの例を見ていきます。
バイナリファイル
Git属性を使用できるクールなトリックの一つは、どのファイルがバイナリであるかをGitに教えること(Gitがそれ以外では判別できない場合)と、それらのファイルの処理方法についてGitに特別な指示を与えることです。たとえば、一部のテキストファイルは機械生成されたもので差分比較できない場合がありますが、一部のバイナリファイルは差分比較可能です。どちらをどのように区別するかをGitに伝える方法を説明します。
バイナリファイルの識別
一部のファイルはテキストファイルのように見えますが、あらゆる目的においてバイナリデータとして扱われるべきです。たとえば、macOS上のXcodeプロジェクトには.pbxproj
で終わるファイルが含まれています。これは基本的に、IDEがディスクに書き出したJSON(プレーンテキストのJavaScriptデータ形式)データセットで、ビルド設定などが記録されています。技術的にはテキストファイル(すべてUTF-8であるため)ですが、実質的には軽量なデータベースであるため、そのように扱いたくはありません。2人が変更しても内容をマージすることはできませんし、差分比較も一般的に役に立ちません。このファイルは機械が消費することを意図しています。本質的には、バイナリファイルのように扱いたいのです。
すべてのpbxproj
ファイルをバイナリデータとして扱うようGitに指示するには、.gitattributes
ファイルに以下の行を追加します。
*.pbxproj binary
これでGitは、CRLFの問題を変換したり修正しようとせず、プロジェクトでgit show
やgit diff
を実行したときに、このファイルの変更について差分を計算したり表示したりすることもなくなります。
バイナリファイルの差分比較
Git属性機能を使用して、バイナリファイルを効果的に差分比較することもできます。これを行うには、バイナリデータを通常の差分比較で比較できるテキスト形式に変換する方法をGitに指示します。
まず、この手法を使用して、人類が知る最も厄介な問題の一つであるMicrosoft 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
これはGitに、このパターン(.docx
)に一致するファイルは、変更を含む差分を表示しようとするときに「word」フィルターを使用するべきだと伝えています。「word」フィルターとは何でしょうか?設定する必要があります。ここでは、Word文書を読み取り可能なテキストファイルに変換するためにdocx2txt
プログラムを使用するようにGitを設定し、その後、Gitが適切に差分比較できるようにします。
まず、docx2txt
をインストールする必要があります。https://sourceforge.net/projects/docx2txtからダウンロードできます。INSTALL
ファイルの手順に従って、シェルが見つけられる場所に配置してください。次に、Gitが期待する形式に出力を変換するラッパースクリプトを作成します。パス上のどこかにdocx2txt
というファイルを作成し、以下の内容を追加します。
#!/bin/bash
docx2txt.pl "$1" -
そのファイルをchmod a+x
するのを忘れないでください。最後に、Gitがこのスクリプトを使用するように設定できます。
$ git config diff.word.textconv docx2txt
これでGitは、2つのスナップショット間で差分を取ろうとし、いずれかのファイルが.docx
で終わる場合、それらのファイルを「word」フィルター(docx2txt
プログラムとして定義されている)に通すべきだと認識します。これにより、差分比較を試みる前に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
プログラムをダウンロードしてインストールすると、それを使用して画像をメタデータに関するテキストに変換できるため、少なくとも差分比較によって発生した変更のテキスト表現が表示されます。.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
ファイルサイズと画像寸法が両方とも変更されたことが簡単にわかります。
キーワード展開
SVNやCVSのようなキーワード展開は、これらのシステムに慣れた開発者からしばしば要求されます。Gitにおける主な問題は、コミット後にコミットに関する情報でファイルを変更できないことです。なぜなら、Gitはまずファイルのチェックサムを計算するからです。しかし、ファイルをチェックアウトするときにテキストをファイルに挿入し、コミットに追加する前に再度削除することができます。Git属性はこれを行う2つの方法を提供します。
まず、blobのSHA-1チェックサムをファイルの$Id$
フィールドに自動的に挿入できます。この属性を1つまたは複数のファイルに設定すると、次にそのブランチをチェックアウトするときに、GitはそのフィールドをblobのSHA-1に置き換えます。これはコミットのSHA-1ではなく、blob自体のSHA-1であることに注意することが重要です。.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はあまり役に立ちません。なぜなら、それはかなりランダムであり、SHA-1を見ただけではどちらが古いか新しいかを判断できないからです。
コミット/チェックアウト時にファイル内で置換を行う独自のフィルターを記述できることが判明しています。これらは「クリーン」フィルターと「スマッジ」フィルターと呼ばれます。.gitattributes
ファイルで、特定のパスに対してフィルターを設定し、ファイルがチェックアウトされる直前(「スマッジ」、「スマッジ」フィルターはチェックアウト時に実行されるを参照)とステージングされる直前(「クリーン」、「クリーン」フィルターはファイルがステージングされるときに実行されるを参照)にファイルを処理するスクリプトを設定できます。これらのフィルターは、あらゆる種類の楽しいことを行うように設定できます。


この機能の最初のコミットメッセージには、コミット前にすべてのCソースコードをindent
プログラムに通す簡単な例が示されています。.gitattributes
ファイルで、\*.c
ファイルを「indent」フィルターでフィルター処理するようにフィルター属性を設定することで設定できます。
*.c filter=indent
次に、「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
と呼びます)を設定し、チェックアウト時にファイルをスマッジするために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 "Test 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
アーカイブを生成する際に、特定のファイルやディレクトリをエクスポートしないようGitに指示できます。プロジェクトにチェックインしたいが、tarballエクスポートには含めたくないサブディレクトリやファイルがある場合、export-ignore
属性を使用してそれらのファイルを指定できます。
たとえば、test/
サブディレクトリにいくつかのテストファイルがあり、それらをプロジェクトのtarballエクスポートに含めるのは意味がないとします。Git属性ファイルに以下の行を追加できます。
test/ export-ignore
これで、プロジェクトのtarballを作成するためにgit archive
を実行しても、そのディレクトリはアーカイブに含まれなくなります。
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
を実行すると、アーカイブされたファイルの内容は次のようになります。
$ 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 notes
などが含まれ、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属性を使用して、プロジェクト内の特定のファイルに対して異なるマージ戦略を使用するようGitに指示することもできます。非常に便利なオプションの1つは、競合が発生した場合に特定のファイルをマージしようとせず、相手側の変更ではなく自分の側の変更を使用するようGitに指示することです。
これは、プロジェクト内のブランチが分岐しているか、特殊化されているが、そこから変更をマージし直したい場合で、特定のファイルを無視したい場合に役立ちます。2つのブランチで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
は元々持っていたバージョンに留まります。