-
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 スマート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 resetの解説
- 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属性
- 8.3 Gitフック
- 8.4 Gitによるポリシー適用例
- 8.5 まとめ
-
9. Gitと他のシステム
- 9.1 Gitクライアントとして
- 9.2 Gitへの移行
- 9.3 まとめ
-
10. Git内部構造
- 10.1 内部コマンドと外部コマンド
- 10.2 Gitオブジェクト
- 10.3 Git参照
- 10.4 パックファイル
- 10.5 リファレンス仕様
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータ復旧
- 10.8 環境変数
- 10.9 まとめ
-
付録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 まとめ
-
付録B. アプリケーションへのGitの埋め込み
- A2.1 コマンドラインGit
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
付録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`ファイルで設定されます。
属性を使用すると、プロジェクト内の個々のファイルやディレクトリに対して個別のマージ戦略を指定したり、Gitに非テキストファイルの差分作成方法を指示したり、GitがコンテンツをGitにチェックインまたはチェックアウトする前にコンテンツをフィルタリングするように指示したりできます。このセクションでは、Gitプロジェクトのパスに設定できる属性の一部について説明し、この機能を実際に使用した例をいくつか紹介します。
バイナリファイル
Git属性を使用できる便利な機能の1つは、Gitにどのファイルがバイナリファイルであるかを指示し(そうでない場合、Gitが判断できない場合)、Gitにそれらのファイルを処理する方法に関する特別な指示を与えることです。たとえば、一部のテキストファイルはマシン生成であり、差分作成ができない場合がありますが、一部のバイナリファイルは差分作成できます。Gitにどちらであるかを指示する方法を見ていきます。
バイナリファイルの識別
テキストファイルのように見えるファイルの中には、あらゆる意味でバイナリデータとして扱うべきものがあります。例えば、macOS上のXcodeプロジェクトには、.pbxproj
で終わるファイルが含まれています。これは基本的に、IDEによってディスクに書き出されたJSON(プレーンテキストのJavaScriptデータ形式)データセットで、ビルド設定などを記録しています。技術的にはテキストファイル(UTF-8でエンコードされているため)ですが、軽量データベースであるため、テキストファイルとして扱うべきではありません。複数の人が変更した場合、内容をマージすることはできず、差分も一般的に役に立ちません。このファイルはマシンが消費することを意図しています。つまり、バイナリファイルとして扱うべきです。
Gitに全てのpbxproj
ファイルをバイナリデータとして扱うように指示するには、.gitattributes
ファイルに次の行を追加します。
*.pbxproj binary
これで、GitはCRLFの問題を変換または修正しようとしなくなります。また、プロジェクトでgit show
またはgit diff
を実行したときに、このファイルの変更の差分を計算または表示しようとしなくなります。
バイナリファイルの差分表示
Git属性機能を使用して、バイナリファイルの差分を効果的に表示することもできます。そのためには、バイナリデータを通常の差分で比較できるテキスト形式に変換する方法をGitに指示します。
まず、この手法を使用して、人類が知る最も厄介な問題の1つを解決します。それは、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
これは、このパターン(.docx
)に一致するファイルは、変更を含む差分を表示しようとするときに「word」フィルターを使用する必要があることをGitに指示します。「word」フィルターとは何でしょうか?設定する必要があります。ここでは、Gitがdocx2txt
プログラムを使用してWord文書を可読なテキストファイルに変換するように設定し、それを適切に差分表示します。
まず、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
で終わる場合、docx2txt
プログラムとして定義されている「word」フィルターでそれらのファイルを実行する必要があることを認識します。これにより、差分を実行する前に、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.」という文字列を追加したと正しく簡潔に伝えています。完璧ではありません(書式変更はここで表示されません)が、確かに機能します。
この方法で解決できるもう1つの興味深い問題は、画像ファイルの差分表示です。これを行う1つの方法は、画像ファイルを実行して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$
フィールドに自動的に挿入できます。ファイルまたはファイルセットでこの属性を設定すると、次にそのブランチをチェックアウトしたときに、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
ファイルでフィルター属性を設定して、「indent」フィルターで\*.c
ファイルをフィルター処理することで設定できます。
*.c filter=indent
次に、Gitに「indent」フィルターがスマッジとクリーンでどのように動作するかを指示します。
$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat
この場合、*.c
に一致するファイルをコミットすると、Gitはステージング前にindent
プログラムで実行し、ディスクにチェックアウトする前にcat
プログラムで実行します。cat
プログラムは本質的に何も行いません。入力されたデータと同じデータを出力します。この組み合わせにより、コミット前にすべてのCソースコードファイルがindent
で効果的にフィルター処理されます。
もう1つの興味深い例は、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に指示できます。プロジェクトにチェックインする必要があるが、アーカイブファイルに含める必要がないサブディレクトリまたはファイルがある場合、export-ignore
属性を使用してそれらのファイルを決定できます。
たとえば、test/
サブディレクトリにいくつかのテストファイルがあり、プロジェクトのtarballエクスポートにそれらを含めることが理にかなわないとします。Git属性ファイルに次の行を追加できます。
test/ export-ignore
これで、git archive
を実行してプロジェクトのtarballを作成すると、そのディレクトリはアーカイブに含まれません。
export-subst
ファイルをデプロイするためにエクスポートする際には、git log
のフォーマットとキーワード展開処理を、export-subst
属性でマークされたファイルの選択部分に適用できます。
たとえば、プロジェクトに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
は元のバージョンどおりになります。