Table of Contents
(訳注:この資料は、http://www.kernel.org/pub/software/scm/git/docs/user-manual.html に掲載されている 内容を日本語訳したものです。 英語が得意でないので、誤訳があるかもしれません。 必要な場合は、原文を参照してください。)
git は高速な分散リビジョン管理システムです。
このマニュアルは、基本的な UNIX コマンドのスキルをもった人が読むことを想定していますが、 git に関する前提知識は必要ありません。
Chapter 1, リポジトリとブランチ と Chapter 2, 履歴の探索 では git を使用してプロジェクトを取得・調査する方法を説明します。 — これらの章を読むことで、ソフトウェアプロジェクトの特定のバージョンをビルドして テストしたり、回帰点を探し出す方法などを習得してください。
実際に開発する必要のある場合は、Chapter 3, git を使用した開発 と Chapter 4, 他のユーザと開発を共有する も読んでください。
さらに後ろの章では、より特化したトピックスを取り上げます。
包括的なリファレンスドキュメントは man ページ もしくはコマンドgit-help(1)で確認できます。 例えば、"git clone <repo>" のコマンドは次の どちらのようにしても確認できます。
$ man git-clone
$ git help clone
後者のやり方では、マニュアルビューワを選ぶことができます。 詳細はgit-help(1)をご覧ください。
git コマンドの概要を知るには Chapter 12, 付録A:Git Quick Reference を参照してください。
最後に、Chapter 13, 付録 B: このマニュアルの覚え書きとTODOリスト では、このマニュアルをより完全にする為の情報を説明しています。
Table of Contents
このマニュアルを読む際には、実験用のリポジトリを取得しておくと便利です。
実験用リポジトリを取得する一番良い方法は git-clone(1) コマンドを使用し、 既存のリポジトリのコピーをダウンロードすることです。
# git 自身 (ダウンロードサイズは約10MB):
$ git clone git://git.kernel.org/pub/scm/git/git.git
# linux カーネル (ダウンロードサイズは150MB):
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
初めてのclone(複製)では大きなプロジェクトの場合、時間をかなり消費するかもしれませんが、 clone は1回行なうだけで良いです。
clone コマンドはそのプロジェクトにちなんだ新しいディレクトリを作成します (上記例では "git" 又は "linux-2.6")。 このディレクトリ内にはプロジェクトファイルのコピーが存在し (これは 作業ツリー といいます)、 それと一緒に ".git" という特別なトップレベルディレクトリが存在します。 ".git" ディレクトリにはプロジェクトの履歴の全てが含まれています。
git はファイル集合の履歴を格納するツールとして非常によく考えられたツールです。 git は履歴を圧縮した集合として格納し、 プロジェクトの内容を相互に関連したスナップショットとして格納します。 git ではこれらのバージョンを コミット と呼びます。
それらのスナップショットは必ずしもすべてが古いものから最新のものに 単線で並んでいるとは限りません;その代り、開発ラインが並行して 進むことがあります (これは ブランチ といいます)。 ブランチは分岐し統合されます。
1つの git リポジトリは複数のブランチを追跡することができます。 Git は 各ブランチの最新のコミットの参照を ヘッド の 一覧に保管することで複数のブランチを追跡します; git-branch(1) コマンドはブランチの一覧を表示します:
$ git branch
* master
新しく clone したリポジトリは、1つのブランチヘッド(デフォルトではその名前は "master"です) を持っており、作業ディレクトリは、そのブランチ head が参照するプロジェクトの状態で 初期化されています。
多くのプロジェクトは tags も使用しています。 tag は head のようにプロジェクトの履歴を参照していて、 git-tag(1) コマンドを用いてリスト表示できます。
$ git tag -l
v2.6.11
v2.6.11-tree
v2.6.12
v2.6.12-rc2
v2.6.12-rc3
v2.6.12-rc4
v2.6.12-rc5
v2.6.12-rc6
v2.6.13
...
tag は常にプロジェクトの特定のバージョンを指し示し、 head は開発が進むに従い更新されていきます。
あるバージョンを指し示す新しいブランチの head を作成し、 そのブランチの head をチェックアウトするには git-checkout(1) を使用します:
$ git checkout -b new v2.6.13
この時、作業ディレクトリは v2.6.13 の tag を付けた時に プロジェクトのもっていた内容が反映されています。 そして、git-branch(1) すると2つのブランチが表示され、 現在チェックアウトしているブランチのマークがアスタリスク(*)で表示されます。
$ git branch
master
* new
もしバージョン 2.6.17 をむしろ参照したいと考えたなら、 現在のブランチを v2.6.17 を参照するように変更できます。次のようにして:
$ git reset --hard v2.6.17
ある特定の履歴がを参照しているのが、現在のブランチのみの場合、 ブランチをリセットすると、もやはその履歴を参照できなくなることに注意してください; ですから、このコマンドは注意して使用してください。
プロジェクトの全ての変更履歴は、commit として表現されます。 git-show(1) コマンドは現在のブランチ上で最後にコミットした 履歴を表示します:
$ git show
commit 17cf781661e6d38f737f15f53ab552f1e95960d7
Author: Linus Torvalds <[email protected].(none)>
Date: Tue Apr 19 14:11:06 2005 -0700
Remove duplicate getenv(DB_ENVIRONMENT) call
Noted by Tony Luck.
diff --git a/init-db.c b/init-db.c
index 65898fa..b002dc6 100644
--- a/init-db.c
+++ b/init-db.c
@@ -7,7 +7,7 @@
int main(int argc, char **argv)
{
- char *sha1_dir = getenv(DB_ENVIRONMENT), *path;
+ char *sha1_dir, *path;
int len, i;
if (mkdir(".git", 0755) < 0) {
このように、コミットは誰が最後に変更したか、何を、何故変更したかを表示します。
全てのコミットは 40桁の16進数の ID ("オブジェクト名" 又は "SHA-1 id" と呼ぶこともあります) を持ち、"git show" の出力の1行目にその ID が表示されます。 通常、コミットはタグやブランチ名のような短い名前で参照できますが、 この長い名前も役に立つことがあります。特に重要なのは、この ID がこのコミットを 大局的にユニークにしている点です:つまり、他のだれかにその ID を (例えば emailで)伝えた場合、その ID が彼らのリポジトリ内でもあなたのリポジトリ内でも 同じコミットを指し示すことを保障しています。 (彼らのリポジトリにそのコミットが完全に含まれている場合にはです)。 オブジェクト名はコミット対象のコンテンツのハッシュとして計算される為、 変更しない限りは、決して変わらないことが保障されています。
実際、gitのコンセプト では、git に格納されている全ての履歴が、 ファイルデータとディレクトリの中身も含めて、その中身のハッシュの名前で オブジェクトが格納されていることを見るでしょう。
全てのコミットは(プロジェクトの最初のコミットを除き)、そのコミットの前に 行われた変更を示す親のコミットを持っています。 親のつながりは最終的にはプロジェクトの開始点まで繋がっています。
しかし、コミットは単純なリストの形式にはなりません; git は分散開発とその統合を許可しており、2つの開発ラインが統合する点は、 "マージ" と呼ばれます。その為、マージを表現するコミットは1つ以上の親を持ち、 各親はその点につながる開発ラインの最新コミットを表現しています。
これがどのように作業するかを見る最良の方法は gitk(1) コマンドを 使用することです;git リポジトリ上で gitk を実行し、マージコミットを 探すことで、git が履歴をどのように整理しているかを理解することができます。
以下では、コミット X がコミット Y の祖先である場合に、 コミット X はコミット Y から "到達可能"(reachable) であると言うことにします。 同様に、Y は X の子孫である、あるいは、コミット Y から コミット X へ 繋がる親のチェーンがあると言うこともできます。
時々 git の履歴を以下のようなダイアグラムを使用して表現することがあります。 コミットは "o" で、コミット間のリンクは - / \ です。 時間は左から右に流れます:
o--o--o <-- Branch A / o--o--o <-- master \ o--o--o <-- Branch B
特定のコミットについて話をする必要がある時は。記号 "o" は 他の文字や数字に置き換えられることもあります。
ブランチの作成/削除/変更はとても簡単です; 以下にコマンドのサマリを載せます:
特別なシンボル "HEAD" 使用すると、常に現在のブランチを参照することができます。 実際 git は .git ディレクトリにある "HEAD" という名前のファイルを使用して 現在のブランチの場所を記憶しています。
$ cat .git/HEAD
ref: refs/heads/master
git checkout
コマンドは通常はブランチヘッドが引数で渡されることを期待していますが、
任意のコミットを指定することもできます;例えば、
タグによって参照されるコミットをチェックアウトすることができます。
$ git checkout v2.6.17
Note: moving to "v2.6.17" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
git checkout -b <new_branch_name>
HEAD is now at 427abfa... Linux v2.6.17
この時 HEAD はブランチの代わりにコミットの SHA-1 値を参照しており、 git branch を実行するとブランチにいないことが分かります。
$ cat .git/HEAD
427abfa28afedffadfca9dd8b067eb6d36bac53f
$ git branch
* (no branch)
master
この状態を HEAD が "切り離されている (detached)" と言います。
この方法は、新しいブランチを作成せずに特定のバージョンを チェックアウトする手軽な方法です。 こうした後でも、後からこのバージョンに対して新しいブランチ(またはタグ)を 作成することができます。
複製(clone)した時に作成される "master" ブランチは、 複製元リポジトリのヘッドのコピーです。 しかし複製元リポジトリにはそれ以外にもブランチがあるかもしれません。 ローカルリポジトリは、リモート(複製元)ブランチのそれぞれを追跡する為の ブランチを持っています。 それらのブランチは "-r" オプションを付けて git-branch(1) を実行すると 確認できます:
$ git branch -r
origin/HEAD
origin/html
origin/maint
origin/man
origin/master
origin/next
origin/pu
origin/todo
それらリモート追跡ブランチを直接チェックアウトすることはできませんが、 自身のブランチを作成しそこで調べることはできます。
$ git checkout -b my-todo-copy origin/todo
"origin" という名前は、複製(clone)したリポジトリを参照する為に git がデフォルトで使用する名前にすぎないことに注意してください。
ブランチ、リモート追跡ブランチ、タグは全てコミットを参照しています。 全ての参照は "refs" で始まるスラッシュ区切りのパス名が付けられています; これまで使用してきた名前は実のところ全て略記です。
フルネームは時折役に立つことがあります。例えば、 同じ名前のタグとブランチがあるような場合です。
(新しく作成された参照は .git/refs ディレクトリ内にその参照の 名前で格納されています。しかし、効率性の理由により、 1つのファイルに纏めて圧縮されることもあります;git-pack-refs(1) 参照)
もう一つ役に立つ略記として、あるリポジトリの "HEAD" は、単にそのリポジトリ名 を使うだけで参照できるというのがあります。 例えば "origin" は通常、リポジトリ "origin" の HEAD ブランチの略記を表わします。
参照先として git がチェックするパスの完全なリストと、 同じ略記をもつ複数の参照がある場合の選択規則については git-rev-parse(1) の "SPECIFYING REVISIONS" の節を 確認してください。
複製(clone)元の開発者はいずれ自身のリポジトリに変更を加えたり、 新しいコミットを作成したり、新しいコミットを参照したブランチを作成するでしょう。
"git fetch" コマンドは、引数なしの場合、全てのリモート追跡している ブランチを複製元リポジトリの最新バージョンの状態に更新します。 この動作は、自分自身のブランチについては何も変更しません。 — "master"ブランチも同じです、それは複製時にあなたの開発用に 作られたものです。
複製元以外のリポジトリにあるブランチを追跡することもできます。 そうするには git-remote(1) を使用します:
$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
$ git fetch linux-nfs
* refs/remotes/linux-nfs/master: storing branch 'master' ...
commit: bf81b46
新しいリモート追跡ブランチは "git remote add" で指定した 省略名で格納され、上記場合は linux-nfs です:
$ git branch -r
linux-nfs/master
origin/master
"git fetch <remote>" をその後に実行すると、<remote> という名前の 追跡ブランチが更新されます。
.git/config ファイルを見ると、git が新しい節を追加したことを 確認できます。
$ cat .git/config
...
[remote "linux-nfs"]
url = git://linux-nfs.org/pub/nfs-2.6.git
fetch = +refs/heads/*:refs/remotes/linux-nfs/*
...
これは、git がリモートブランチを追跡する為に作成したものです; テキストエディタで .git/config を編集し、これらの設定オプションを 変更、削除することもできます。 (詳細は git-config(1) の "CONFIGURATION FILE" を参照してください)
Table of Contents
git はファイルの集合の履歴を格納するツールとして とても良く考慮されたツールです。 ファイル階層の中身を圧縮したスナップショットと スナップショット間の関係を表す "commit" を格納することで これを実現しています。
git は非常に柔軟で高速に動作するプロジェクトの履歴探索ツールです。
プロジェクトにバグを入れ込んだコミットを見つける為の便利な 特殊ツールの説明からはじめましょう。
プロジェクトのバージョン 2.6.18 では動作するが、"master" ブランチの 最新バージョンではクラッシュするとしましょう。 そのようなリグレッションの原因を探し出す最良の方法は プロジェクトの履歴を総当たりで検索し、問題を引き起こす特定のコミットを見つけることです。 git-bisect(1) コマンドはこの作業の手伝いをしてくれます:
$ git bisect start
$ git bisect good v2.6.18
$ git bisect bad master
Bisecting: 3537 revisions left to test after this
[65934a9a028b88e83e2b0f8b36618fe503349f8e] BLOCK: Make USB storage depend on SCSI rather than selecting it [try #6]
この時点で "git branch" を実行すると、git は一時的に "(no branch)" に移動していることが確認できます。 HEADはいまやあらゆるブランチから分離されており、 "master" からは到達可能だが、v2.6.18 からは到達できない コミット(id が 65934… のコミット)を指しています。 コンパイルとテストをし、クラッシュするかを確認します。 もしクラッシュするのなら、以下のように:
$ git bisect bad
Bisecting: 1769 revisions left to test after this
[7eff82c8b1511017ae605f0c99ac275a7e21b867] i2c-core: Drop useless bitmaskings
として、より古いバージョンを確認します。このように git に各段階でそのバージョンが good か bad かを伝える作業を続け、 テストすべきリビジョンの残数は各回で約半分ずつに削られていきます。
(今回の場合では)約13回テストした後、罪を犯したコミットの id を 見つけることができます。そのコミットに対して git-show(1) を実行し、 誰がそのコミットを書いたかを見つけ出し、コミットの id を添えて、 バグレポートをメールします。最後に、
$ git bisect reset
を実行し、以前いたブランチに戻ます。
ここで注意すべきことはgit bisect
が各状態でチェックアウトする
バージョンが単なる提案にすぎないことです。
違うバージョンをテストしてもかまわないのです。
例えば、関係のない変更をしているコミット上にいるかもしれません;
その時は以下を実行してください。
$ git bisect visualize
こうすると gitk が起動して、選択されたコミットに "bisect" という マーカーをつけます。近くの安全そうなコミットを探し、コミットIDをメモして 次のコマンドでチェックアウトします。
$ git reset --hard fb47ddb2db...
そしてテストし、"bisect good" または "bisect bad" の適切な方を実行し、 作業を続けます。
"git bisect visualize"を実行して"git reset —hard fb47ddb2db…" を実行するかわりに、単に現在のコミットをスキップしたいかもしれません。 その場合は以下を実行してください。
$ git bisect skip
しかしこの場合にgitは結果的いくつかの最初のスキップされたコミットと その後の悪いコミットの間にある最初の悪いコミットが わからなくなる可能性があります。
もし良いコミットと悪いコミットを判断することが可能な テストスクリプトがあるなら、bisect作業の自動化には いくつかの方法があります。 詳細はgit-bisect(1)をご覧ください。
既にコミットの指定方法をいくつか紹介してきました:
他にもたくさんあります;リビジョンの呼び方の完全なリストは git-rev-parse(1) の man ページにある "SPECIFYING REVISIONS" の節で 確認できます。
$ git show fb47ddb2 # オブジェクト名の先頭の数文字は
# そのコミットを特定するのに通常は十分です。
$ git show HEAD^ # HEAD コミットの親
$ git show HEAD^^ # 祖父母
$ git show HEAD~4 # 祖父母の祖父母
マージコミットは1つ以上の親を持ちます;デフォルトでは ^ と ~ はコミットリストの1つ目の親を指しますが、 次のように指定することもできます;
$ git show HEAD^1 # HEAD の1つ目の親
$ git show HEAD^2 # HEAD の2つ目の親
HEAD の他にも、コミットを指す特殊な名前があります:
(後に説明する)マージは、git reset
のような操作と同じように、
現在チェックアウトしているコミットを変更し、
一般的には ORIG_HEAD に現在の操作以前にもっていた HEAD の値をセットします。
git fetch
の操作は、常に最後にフェッチしたブランチのヘッドを FETCH_HEAD に
格納します。例えば、操作対象であるローカルブランチを指定せずに git fetch
を
実行した場合、
$ git fetch git://example.com/proj.git theirbranch
フェッチされるコミットは FETCH_HEAD 取得されます。
マージについて議論するとき、MERGE_HEAD という特別な名前を目にします。 これは、現在のブランチにマージしようとしているもう一方のブランチを 参照しています。
git-rev-parse(1) コマンドは、低レベルのコマンドであり コミットに対する名前をコミットのオブジェクト名に変換するのに役立ちます。
$ git rev-parse origin
e05db0fd4f31dde7005f075a84f96b360d05984b
特定のコミットを参照する為にタグを作成することができます; 以下の操作を実行すると、
$ git tag stable-1 1b2e1d63ff
stable-1 という名前で 1b2e1d63ff のコミットを参照できるようになります。
これは "軽量" タグと呼ばれるものです。 タグにコメントを含めたい場合や、暗号化して署名したい場合には、 その代わりにタグオブジェクトを作成することができます;詳細は git-tag(1) の man ページを参照してください。
git-log(1) コマンドはコミットの一覧を表示します。 現在のブランチ上にある親コミットから到達可能な全てのコミットを表示します。; しかし、さらに特定のリクエストをすることもできます:
$ git log v2.5.. # v2.5以降のコミット(v2.5から到達不能なコミット)
$ git log test..master # master から到達可能だが、test からは到達可能でないコミット
$ git log master..test # test から到達可能だが、master からは到達可能でないコミット
$ git log master...test # test または master から到達可能だが、
# 両方からは到達可能でない...
$ git log --since="2 weeks ago" # 最近2週間のコミット
$ git log Makefile # Makefile を修正しているコミット
$ git log fs/ # fs/ 配下のファイルを修正している...
$ git log -S'foo()' # 文字列 'foo()' に一致する全てのファイルを
# 追加または削除しているコミット
そしてもちろん、これら全てを組み合わせることもできます; 以下は v2.5 以降のコミットで、Makefile 又は fs 配下のファイルを変更している コミットを検索します。
$ git log v2.5.. Makefile fs/
git log を使用し、パッチを表示することもできます:
$ git log -p
他の表示オプションについては git-log(1) の man ページにある "—pretty" オプション を参照してください。
git log は最新のコミットから開始し、親を辿って後方に検索します; しかし、git の履歴は複数の独立した開発ラインを含むことができる為、 一覧表示されるコミットの順番はいくらか任意になります。
git-diff(1) を使用すると2つのバージョン間の差分を 生成することができます:
$ git diff master..test
これは2つのブランチの先端の間の差分を表示します。 2つのブランチの共通の祖先から test までの差分を表示したい場合は ドットを2つではなく3つとし、次のようにします:
$ git diff master...test
時には差分ではなく各パッチの集合が必要な場合もあります;その際には git-format-patch(1): を使用します:
$ git format-patch master..test
このようにすると、test から到達可能だが、master からは到達できない各コミットの パッチを含むファイルを生成できます。
特定のリビジョンをチェックアウトすることで、ファイルの古いバージョンを表示させる ことができます。しかし時にはある1つのファイルの古いバージョンを チェックアウトせずに表示できると便利です; 次のコマンドでそれができます:
$ git show v2.5:fs/locks.c
コロン(:) の前はコミットを指す任意の名前で、その後ろは git が追跡しているファイルの任意のパスです。
"origin" から分岐した以降に "mybranch" 上で行ったコミットの数を知りたい とします:
$ git log --pretty=oneline origin..mybranch | wc -l
あるいは、下位レベルのコマンド git-rev-list(1) を使用し、 指定したコミットすべての SHA-1 をリスト表示することで行うこともできます:
$ git rev-list origin..mybranch | wc -l
2つのブランチが同じ履歴点にいるかどうかを確認したいとします。
$ git diff origin..master
この操作により、プロジェクトの中身が2つのブランチで同じであるか どうかを確認できます;しかし、理論的には同じプロジェクト内容が 2つの異なる履歴ルートによって作られることもありえます。 (訳注:コミットIDは違うが中身が一緒の場合もありえる) 従って、オブジェクト名を比較すべきです:
$ git rev-list origin
e05db0fd4f31dde7005f075a84f96b360d05984b
$ git rev-list master
e05db0fd4f31dde7005f075a84f96b360d05984b
あるいは、"…" のオペレータを使用し、一方からのみ到達可能な 全てのコミットを表示してみることです:つまり、
$ git log origin...master
を行い、2つのブランチが等しい時は、コミットが全く表示されません。
e05db0fd がある問題を解決したコミットであるとし、 その解決を含む最も早いタグ付けされたリリースを探したいとします。
もちろん、その答えは1つ以上あります—コミット e05db0fd 以降に 履歴が分岐しているなら、複数の "最も早い" タグ付けされたリリースが存在します。
e05db0fd 以降のコミットを視覚的に調査することで行えます:
$ gitk e05db0fd..
あるいは git-name-rev(1) を使用し、あるタグに基づいた そのコミットの子孫の1つを指し示す名前を表示することができます: (訳注:訳が不正確かな)
$ git name-rev --tags e05db0fd
e05db0fd tags/v1.5.0-rc1^0~23
git-describe(1) コマンドはこれとは反対のことをします。 指定したコミットのベースになるタグ名を使用してそのリビジョンの名前を 表示します。
$ git describe e05db0fd
v1.5.0-rc0-260-ge05db0f
しかし、それは時にはどのタグが指定したコミットの後に現れるかを 推測する手助けになります。
指定したタグ付けされたバージョンが特定のコミットを含むかどうかを 確認したい場合は、git-merge-base(1) を使用します:
$ git merge-base e05db0fd v1.5.0-rc1
e05db0fd4f31dde7005f075a84f96b360d05984b
merge-base コマンドは指定したコミットの共通の祖先を検索し、 一方が他方の子孫である場合にはそのどちらかを表示します; 従って上記出力は e05db0fd が実際に v1.5.0-rc1 の祖先であることを 示しています。
代わりに、
$ git log v1.5.0-rc1..e05db0fd
とすると、v1.5.0-rc1 が e05db0fd を含んでいる場合に限り何も出力をしません、 何故なら v1.5.0-rc1 から到達できないコミットだけが表示されるからです。
As yet another alternative, the git-show-branch(1) command lists the commits reachable from its arguments with a display on the left-hand side that indicates which arguments that commit is reachable from. So, you can run something like (訳注:訳せないので、原文のまま載せます)
$ git show-branch e05db0fd v1.5.0-rc0 v1.5.0-rc1 v1.5.0-rc2
! [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if
available
! [v1.5.0-rc0] GIT v1.5.0 preview
! [v1.5.0-rc1] GIT v1.5.0-rc1
! [v1.5.0-rc2] GIT v1.5.0-rc2
...
then search for a line that looks like
+ ++ [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if
available
Which shows that e05db0fd is reachable from itself, from v1.5.0-rc1, and from v1.5.0-rc2, but not from v1.5.0-rc0.
"master" という名前のブランチヘッドから到達可能だが自分のリポジトリ上の 他のヘッドからは到達できないコミットを全て参照したいとします。
git-show-ref(1) を使用するとこのリポジトリの全てのヘッドを 一覧表示できます:
$ git show-ref --heads
bf62196b5e363d73353a9dcf094c59595f3153b7 refs/heads/core-tutorial
db768d5504c1bb46f63ee9d6e1772bd047e05bf9 refs/heads/maint
a07157ac624b2524a059a3414e99f6f44bebc1e7 refs/heads/master
24dbc180ea14dc1aebe09f14c8ecf32010690627 refs/heads/tutorial-2
1e87486ae06626c2f31eaa63d26fc0fd646c8af2 refs/heads/tutorial-fixes
ブランチヘッドの名前を取得し、"master" の行を削除しすることができます。 標準ユーティリティである cut と grep の助けを使用して:
$ git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master'
refs/heads/core-tutorial
refs/heads/maint
refs/heads/tutorial-2
refs/heads/tutorial-fixes
そして、master から到達可能だがそれ以外のヘッドからは到達できない 全てのコミットをたずねることができます:
$ gitk master --not $( git show-ref --heads | cut -d' ' -f2 |
grep -v '^refs/heads/master' )
明らかに、絶え間ない変形もありえます;例えば、いくつかのヘッドからは到達可能だが、 リポジトリ内のどのタグからも到達できないコミットを全て表示するには:
$ gitk $( git show-ref --heads ) --not $( git show-ref --tags )
(—not
のようなコミットを選択する構文の説明は git-rev-parse(1)
を参照してください)
git-archive(1) コマンドはどのプロジェクトのバージョンからも tar 又は zip アーカイブを作成できます;例えば:
$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz
これは HEAD を使用し、各ファイルが "project/" が先行する tar アーカイブを 生成します。
ソフトウェアプロジェクトの新しいバージョンをリリースする場合、 リリースアナウンスを含める為、チェンジログを同時に作成したいかもしれません。
Linux Torvalds は例えば、それらにタグを付け、以下を実行することで 新しいカーネルリリースを作ります:
$ release-script 2.6.12 2.6.13-rc6 2.6.13-rc7
ここで、release-script はシェルスクリプトで、以下のような内容です:
#!/bin/sh
stable="$1"
last="$2"
new="$3"
echo "# git tag v$new"
echo "git archive --prefix=linux-$new/ v$new | gzip -9 > ../linux-$new.tar.gz"
echo "git diff v$stable v$new | gzip -9 > ../patch-$new.gz"
echo "git log --no-merges v$new ^v$last > ../ChangeLog-$new"
echo "git shortlog --no-merges v$new ^v$last > ../ShortLog"
echo "git diff --stat --summary -M v$last v$new > ../diffstat-$new"
そして、彼はそれらを確認してOKであることを見た後、単に出力されたコマンドを カット&ペーストします。
誰かがあなたにファイルのコピーを手渡し、どのコミットがそのように修正し、 コミットの前または後にそのような内容を含んだのかを問い合わせたとします。 その場合、次のようにしてそれを見つけ出します:
$ git log --raw --abbrev=40 --pretty=oneline |
grep -B 1 `git hash-object filename`
これが何故動作するかの説明は、(上級の)学生の演習として残しておきます。 git-log(1)、git-diff-tree(1) そして、git-hash-object(1) の man ページが理解の助けになります。
Table of Contents
コミットをする前に、git に自己紹介をすべきです。 一番簡単な方法はホームディレクトリ下にある .gitconfig というファイルに 次の行が表示されていることを確認することです。
[user]
name = Your Name Comes Here
email = [email protected]
(設定ファイルの詳細は git-config(1) の "CONFIGURATION FILE" 節を 参照してください)
ゼロから新規リポジトリを作成するのはとても簡単です:
$ mkdir project
$ cd project
$ git init
最初に登録したいものがある場合は (tarball の場合):
$ tar xzvf project.tar.gz
$ cd project
$ git init
$ git add . # ./ 以下にある全てを最初のコミットに含めます
$ git commit
新しいコミットを作成するには3つのステップが必要です:
実際には、ステップ1と2を相互に好きなだけ繰り返すことができます: ステップ3でコミットしたいものの追跡を保つ為、git は "索引(index)" と呼ばれる 特別なエリア内にツリーの中身のスナップショットを保管しています。
最初は索引の中身は HEAD の中身と同じです。 コマンド "git diff —cached" は HEAD と索引間の差分を表示する為、 この時点では何も出力しません。
索引を変更するのは容易です:
索引を新しく修正したファイルの中身で更新するには、以下のようにします。
$ git add path/to/file
新しいファイルの中身を索引に追加するにも、以下のようにします。
$ git add path/to/file
索引と作業ツリー上からファイルを削除するには、
$ git rm path/to/file
各ステップを行った後には、
$ git diff --cached
を行うことで HEAD と索引ファイル間の差分を確認することができます。— これはコミットした時に作成される内容です。— そして
$ git diff
は、作業ツリーと索引ファイル間の差分を表示します。
"git add" は常に現在のファイルの中身を索引に追加することに注意
してください;さらに同じファイルに変更を加えても再度 git add
をそのファイルに
行わない限りは無視されます。
準備ができたら、
$ git commit
を実行します。git はコミットのメッセージの入力を促してから 新しいコミットを作成します。意図した結果になっているかを確認するには、以下のようにします。
$ git show
特別なショートカットとして
$ git commit -a
というのがあります。これは変更又は削除した全てのファイルの索引を更新し コミットを作成する操作を、1回のステップで全て行います。
たくさんのコマンドがコミットしようとしているものの追跡を保つ為に 役に立ちます:
$ git diff --cached # HEAD と索引間の差分;
# つまり、"commit" を実行したときにコミットされる内容
$ git diff # 索引と作業ディレクトリ間の差分;
# つまり、"commit" を実行したときに含まれない
# 変更内容
$ git diff HEAD # HEAD と作業ツリー間の差分:
# つまり、"commit -a" を実行したときにコミットされる内容
$ git status # 上記のサマリをファイル毎に簡潔に表示
これらのことをするために git-gui(1) を使用することもできます。 git-gui は コミットの作成や、索引と作業ツリー間の差分の参照、 索引に含めるべき差分ハンクを個々に選択する(差分ハンクを右クリックして、 "Stage Hunk For Commit" を選択する) ことができます。
必須ではありませんが、格納メッセージを 次のようにするのは良い考えです。1行の短文(50文字未満)で変更のサマリを書き、 その後に空白行を挟んで、最後により綿密な記述をまとめる。 そうすることで、例えばコミットした内容を E-Mail に変更するツールにて、 Subjectに最初の行を使用し、残りの行を本文にすることができます。
プロジェクトはよく git に追跡してほしく'ない'ファイルを生成します。
典型的なものとしては、ビルドプロセッサーが生成するファイルや、
エディタが生成するバックアップファイルなどです。もちろん、
git が追跡しないファイルに対して git add`をしなければ良いだけの問題です。
しかし、これら追跡しないファイルがいることでイライラさせられることがあります;
例えば、それらファイルに対しての `git add .
は実際に不要であるにも、
関わらず、 git status
の出力でそれらが表示されてしまいます。
作業ディレクトリのトップレベルに .gitignore という名前のファイルを作成することで、 無視するファイルを git に伝えることができます。
# '#' で始まる行は無視されます
# foo.txt という名前の全てのファイルを無視する
foo.txt
# (生成された) html ファイルは無視する
*.html
# foo.html は例外とし、手でメンテナンスします
!foo.html
# object と archive ファイルは無視する
*.[oa]
記述形式の詳細は gitignore(5) を参照してください。
作業ツリーの他のディレクトリに .gitignore を置くこともできます。
その場合、そのディレクトリとサブディレクトリに適用されます。
.gitignore
ファイルは他のファイルと同様、リポジトリに追加することができます
(通常と同じで git add .gitignore
と git commit
を実行するだけです)。
(ビルド時の出力ファイルに一致するパターンのような) 除外パターンを
git で管理することは、
あなたのリポジトリを複製する他のユーザにとっても便利なことです。
(プロジェクトの全てのリポジトリの代わりに)ある特定のリポジトリでだけ
除外パターンを適用したい場合は、リポジトリ内の .git/info/exclude という場所に
それらを置くか、コンフィグレーション変数 core.excludesfile
によって
指定することができます。git コマンドによってはコマンドラインで除外するパターンを
直接指定することもできます。
詳細は gitignore(5) を参照してください。
2つの分散した開発ブランチは git-merge(1) を使用して マージできます:
$ git merge branchname
上記はブランチ "branchname" の開発を現在のブランチにマージします。 コンフリクトが発生した場合は — 例えば、リモートブランチとローカルブランチで 同じファイルが2つの異なる方法で変更された場合 — 警告が表示されます; 出力される内容は以下のようなものです:
$ git merge next
100% (4/4) done
Auto-merged file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
コンフリクトマーカーは問題のあるファイルに残り、コンフリクトを 手作業で解消した後は、索引をそのファイルの中身で更新し、 git commit を実行することができます。通常の新しいファイルを作成するときと 同じようにです。
gitk を使用しコミット結果を確認すると、それが2つの親を持っていて、 現在のブランチの先頭と、もうひとつのブランチの先頭とを位置している ことが分かります。
マージが自動的に解決されない場合、git は索引と作業ツリーを 特別な状態にし、マージの解決を手助けするのに必要な全ての情報を与えて くれます。
コンフリクトしたファイルは、索引内で特別なマージが付けられ、 その為問題を解決し索引を更新するまで、git-commit(1) は 失敗します:
$ git commit
file.txt: needs merge
また、git-status(1) はそれらのファイルを "unmerged" として表示し、 コンフリクトしたファイルには以下のようなコンフリクトマーカーが追加されています:
<<<<<<< HEAD:file.txt
Hello world
=======
Goodbye
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
すべきことは、ファイルを編集してコンフリクトを解決し以下のようにすることです。
$ git add file.txt
$ git commit
コミットメッセージは既にマージに関する情報が埋められていることに 注意してください。通常このデフォルトのメッセージは変更せずに使用できますが、 必要であれば自身のコメントを追加することもできます。
上記は単純なマージを解決する為に知る必要のある全てです。 しかし、git はコンフリクトの解決を手助けするさらなる情報を与えてくれます。
git が自動的にマージできた全ての変更は既に索引ファイルに 追加されています。そして git-diff(1) はコンフリクトだけを 表示します。そうするには通常の構文を使用します:
$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD:file.txt
+Hello world
++=======
+ Goodbye
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
このコンフリクトを解決した後、コミットされる予定の内容は 通常と違って2つの親を持っていることを思い出してください;一方は HEAD、つまり現在のブランチの先端;もう一方はもうひとつのブランチの先端で MERGE_HEAD に格納されています。
マージしている間、索引は各ファイルの3つのバージョンを持っています。 この3つはそれぞれファイルの異なる3つの "ファイルステージ" を表現しています:
$ git show :1:file.txt # 両方のブランチの共通祖先のファイル
$ git show :2:file.txt # HEAD にあるバージョン
$ git show :3:file.txt # MERGE_HEAD にあるバージョン
コンフリクトが起こっている箇所を表示するために git-diff(1)を実行すると、 コンフリクトが起こったマージ結果の間の 三方向の差分(three-way diff)が表示されます。 差分にはステージ2と3双方からきたコンテンツ が混ざった状態で含まれています。 (言い換えるとハンクのマージ結果が ステージ2だけからなされる場合その部分ではコンフリクトは起こらず 表示されません。ステージ3についても同じです)
上記の差分は file.txt の作業ツリーのバージョンとステージ2とステージ3の バージョン間の差分を表示します。その為、各行の先頭に1つの "+" または "-" が 付けられるかわりに、2つの列が使用されます:1つ目は 1つ目の親と作業ディレクトリコピーの間の差分を表すのに利用され、 2つ目は2つ目の親と作業ディレクトリコピーの間の差分を表示するのに利用されます。 (このフォーマットの詳細は git-diff-files(1) の "COMBINED DIFF FORMAT" の 節を参照してください)
コンフリクトを通常と同じ方法で解決した後、(indexの更新前に) diff を 実行すると次のように表示されます:
$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,1 @@@
- Hello world
-Goodbye
++Goodbye world
これは、解決済みのバージョンが 一つ目の親から "Hello world" を削除し、 2つ目の親から "Goodbye" を削除し、 両方の親に存在しない "Goodbye world" を追加したことを表しています。
他の特別な diff オプションを使用すると、これら任意のステージと 作業ディレクトリとの差分を表示することができます。
$ git diff -1 file.txt # ステージ1との diff
$ git diff --base file.txt # 上記と同じ
$ git diff -2 file.txt # ステージ2との diff
$ git diff --ours file.txt # 上記と同じ
$ git diff -3 file.txt # ステージ3との diff
$ git diff --theirs file.txt # 上記と同じ
git-log(1) と gitk(1) コマンドもまた merge の手助けをしてくれます。
$ git log --merge
$ gitk --merge
これらは、HEAD または MERGE_HEAD にだけ存在する全てのコミットを表示し、 マージされていないファイルを表示します。
linkgit:git-mergetool を利用することもできます。これを利用すると Emacs や kdiff3 のような外部ツールを使用してマージを行うことができます。
ファイルのコンフリクトを解決した後には、索引を更新してください:
$ git add file.txt
すると、そのファイルの各ステージは "崩壊" され、
git diff
はもはや (デフォルトでは) そのファイルに対する差分を表示しません。
マージ作業に行き詰まり、全ての処置を捨て去る場合には、 いつでもマージ前の状態に戻ることができます。次のようにします。
$ git reset --hard HEAD
あるいは、既に削除したいマージ結果をコミット済みの場合には、次のようにします。
$ git reset --hard ORIG_HEAD
しかし、最後のコマンドは、幾つかの場合に危険となりえます。— そのコミットが 他のブランチにマージされている場合は、決してそのコミットを削除しないでください。 もしそうしたなら、さらにマージする場合に混乱が起きます。
上記で説明してこなかった特別なケースがあります。 通常マージコミットにおけるマージ結果は2つの親を持ち、 各親はマージした2つの開発ラインのそれぞれを指し示しています。
そのため、現在のブランチが他方の子孫である場合には — つまり 全てのコミットが既に他方のコミットに含まれている場合には — git は "fast forward" を行います;現在のブランチの先頭はマージされるブランチの 先頭の位置に進められ、新しいコミットは作成されません。
作業ツリーに手を入れたが、間違いをまだコミットしていない場合は、 以下のようにして作業ツリーを最後にコミットした状態に戻すことができます。
$ git reset --hard HEAD
コミットした後ですべきではなかったと気が付いた時は、 2つの異なる解決方法があります:
前の変更を取り消す新しいコミットを作成するのはとても簡単です; 単に git-revert(1) コマンドに間違ったコミットへの参照を 渡すだけです;例えば、直前のコミットを元に戻すには:
$ git revert HEAD
この操作により、HEAD の変更を取り消す新しいコミットが作成されます。 また、新しいコミットに対するコミットメッセージが促されます。
より過去の変更を取り消すこともできます、例えば、2つ前の場合:
$ git revert HEAD^
この場合 git はそれ以前の変更はそのまま残し、指定したコミットの変更だけを 取り消そうとします。指定コミットより後の変更内容が取り消す変更内容とオーバーラップ している場合は、マージの解決 の場合と同じく、 コンフリクトを手動で解決するよう促されます。
問題のあるコミットが直前のコミットであり、まだ公開していない場合は、 単に`git reset`を使用した削除を行うと良いです。
また、代わりに、作業ディレクトリを編集し間違いを訂正した後、索引を更新 することもできます。新しいコミットの作成 で 示した手順で作業していたなら、次のようにします。
$ git commit --amend
これにより、古いコミットが変更内容が記録された新しいコミットに置き換わり、 過去にコミットしたメッセージを編集することもできます。
再注意となりますが、他のブランチに既にマージしているコミットに対しては 決してこの操作を行わないでください;その場合は、git-revert(1) を使用してください。
履歴内のさらに過去のコミットを置き換えることもできますが、 次章 の上級トピックスとして残しておきます。
以前の間違った変更を取消作業の中で、git-checkout(1) を使用して
特定ファイルの古いバージョンをチェックアウトすると便利な場合があるかもしれません。
これまで branch を切り替える際に git checkout
を使用してきましたが、
パス名が与えられた場合には全くことなる動作をします:
次のコマンド
$ git checkout HEAD^ path/to/file
は、path/to/file をコミット HEAD^ の時の内容で置き換え、 索引の更新も行ないます。ブランチは変更しません。
単にそのファイルの古いバージョンを参照したいだけの時は、 git-show(1) を使用すると、作業ディレクトリを修正せずに そのバージョンのファイルを表示できます:
$ git show HEAD^:path/to/file
あなたが何か複雑な作業をしている途中に、今の作業とは関係のない明らかなバグを みつけたとします。作業を中断してそのバグを処置したいとします。 git-stash(1) を使用すると、現在の作業状態を保存し、 バグ処置をした後 (あるいは、異なるブランチ上で処置を行い、元に戻り)、 作業中の状態に戻すことができます。
$ git stash save "work in progress for foo feature"
このコマンドはあなたの変更を stash
に保存し、
作業ディレクトリをリセットし、索引を現在のブランチの tip に一致
させます。通常の手順でバグの処置をしてください。
... edit and test ...
$ git commit -a -m "blorpl: typofix"
その後、git stash apply
を用いて作業していた時の状態に
戻ることができます。
$ git stash apply
大きなリポジトリでは、git はディスクとメモリの使用量を節約するため、 履歴の情報を圧縮して管理することができます。
この圧縮は自動的には行なわれません。従って 時々 git-gc(1) を実行する必要があります:
$ git gc
アーカイブを圧縮する処理はたくさんの時間がかかるため、
git gc
をするときは、他の作業をしない方が良いでしょう。
git-fsck(1) コマンドはリポジトリに対してたくさんの自己一貫性チェックを 実行し、あらゆる問題を報告します。この作業にはいくらかの時間が かかります。最も多い警告は "dangling" オブジェクトに関するものです:
$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
dangling blob 218761f9d90712d37a9c5e36f406f92202db07eb
dangling commit bf093535a34a4d35731aa2bd90fe6b176302f14f
dangling commit 8e4bec7f2ddaa268bef999853c25755452100f8e
dangling tree d50bb86186bf27b681d25af89d3b5b68382e4085
dangling tree b24c2473f1fd3d91352a624795be026d64c8841f
...
Dangling オブジェクトは無害です。悪くとも幾らかのディスクスペースを 余計に消費するだけです。これらは時に紛失した作業内容を復旧させる 最後の機会を与えてくれます — 詳細は the section called “Dangling オブジェクト” を参照してください。
git-reset(1) —hard
を使用してブランチを修正した後に
履歴上でそれを参照しているのがそのブランチであることに気がついたと
します。
幸運なことに、git は "reflog" と呼ばれるログを保持しており、 各ブランチの過去全ての値を保持しています。従ってこの場合 古い履歴を例えば次のようにして見つけ出すことができます:
$ git log master@{1}
このコマンドは "master" ブランチヘッドの1つ前のバージョンから到達可能なコミットの一覧を表示します。 この構文は git log 以外にもコミットを引数に持つ任意の git コマンドに利用 できます。以下は例です:
$ git show master@{2} # 2つ前のブランチの状態を表示
$ git show master@{3} # 3つ前のブランチの状態を表示
$ gitk master@{yesterday} # 昨日の状態を表示
$ gitk master@{"1 week ago"} # 1週間前の状態を表示
$ git log --walk-reflogs master # master に対する reflog エントリを表示します
分割された reflog は HEAD を保つため、
$ git show HEAD@{"1 week ago"}
は、1週間前に現在のブランチが指していた場所を指すのではなく、 1週間前に HEAD が指していた場所を表示します。 これにより、チェックアウトしていた場所の履歴を確認することができます。
reflog はデフォルトでは30日間保存され、その後削除されます。 git-reflog(1) と git-gc(1) を参照すると、 reflog がどのように削除されるかを学ぶことができます。 詳細は git-rev-parse(1) の "SPECIFYING REVISIONS" の節を参照してください。
reflog の履歴は通常の git の履歴と大きく違うことに注意してください。 通常の履歴は同じプロジェクトの各リポジトリ間で共有されますが、 reflog は共有されません:あなたのローカルリポジトリのブランチが時間と ともにどのように変更されたかを説明するだけです。
いくつかの場面で、reflog を用いても救済できない場合があります。例えば、
ブランチを削除した後に、そこにあった履歴が必要になったような場合です。
この時は reflog もまた削除されます;しかし、リポジトリをまだ削除していない場合は、
git fsck
がリポートする dangling オブジェクト内に削除したコミットを見つけられる
場合があります。詳細は the section called “Dangling オブジェクト” を参照してください。
$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
...
これら dangling コミットの一つを例えば以下のようにして見ることができます。
$ gitk 7281251ddd --not --all
これは見たままのことをします:つまり、dangling コミットが表示する コミット履歴のうち、存在する全てのブランチとタグに含まれていないコミットを 表示します。従って、紛失したそのコミットから到達可能な履歴を全て 得ることができます。 (それは単に1つのコミットではないかもしれない点に注意してください: "ラインの先端(tip)"を dangling として報告するだけであり、捨てられた深く複雑な コミットの全てであるかもしれないからです)
履歴を元に戻したい時は、それを参照する新しいブランチを作成してください。 例えば、次のようにします:
$ git branch recovered-branch 7281251ddd
dangling オブジェクトの他の型 (blob や tree)も存在します。 それらは他の状況で発生します。
Table of Contents
リポジトリを複製し、自分でソースを変更した後には、 元のリポジトリが更新されているかを確認し、自分の作業ディレクトリ上に マージしたいと思うでしょう。
既に git-fetch(1) を用いて 外部追跡ブランチを最新に保つ方法 と2つのブランチをマージする方法を見てきました。 従って、元のリポジトリのマスターブランチの変更をマージすることができます:
$ git fetch
$ git merge origin/master
しかし、git-pull(1) コマンドを使用すれば、1回のステップで この操作を行うことができます。
$ git pull origin master
実際のところ、あなたが "master" をチェックアウトしていたなら、"git pull" はデフォルトでは 元のリポジトリの HEAD ブランチからマージを行ないます。従って たいていは単に以下のようにするだけで、上記のことが出来ます。
$ git pull
より一般的には、リモートブランチで作成されたブランチは、
デフォルトではそのブランチから pull されます。
それらデフォルト値のコントロール方法を理解するには、
git-config(1) の branch.<name>.remote と branch.<name>.merge のオプション
の記述と、git-checkout(1) の —track
オプションの説明を参照してください。
さらに、キータイプを省略する為に "git pull" は pull 元のブランチとリポジトリを説明したデフォルトのコミットメッセージを 生成してくれます。
(しかし、fast forward の場合にはそのようなコミットは 作成されないことに注意してください;その代わり、あなたのブランチには 上流のブランチの最新のコミット位置に更新されます。)
git pull
コマンドは "." を "remote" のリポジトリとして扱い、
その場合、単に現在のリポジトリからブランチにマージを行います;
従って次のコマンド
$ git pull . branch
$ git merge branch
は、大雑把に言えば同じです。前者は実際に広く一般に使われています。
行なった変更を投稿する一番簡単な方法は email でパッチとして それらの変更を送信することです。
初めに、git-format-patch(1) を使用します。;例えば:
$ git format-patch origin
により、カレントディレクトリ内に番号付けされた一連のファイルが生成されます。 それらはカレントブランチには含まれるが origin/HEAD には含まれないパッチです。
これらをあなたのメールクライアントにインポートし、手作業でそれらを 送信できます。しかし、一度にたくさん送信したい時は、むしろ git-send-email(1) スクリプトを使用してこの作業を自動化させたいでしょう。 メーリングリストで助言を求め、あなたのプロジェクトがそのようなパッチを どのように扱うことを望むのか決定すると良いでしょう。
Git は git-am(1) (am は "apply mailbox(メールボックスを適用する" という意味です) と呼ばれるツールを提供しており、このようなメールされた一連のパッチを インポートすることができます。 パッチが含まれるメッセージ全部を順番に1つの mailbox ファイル、"patches.mbox" と言います、 に保存してください。そして、以下を実行します。
$ git am -3 patches.mbox
Git は各パッチを順番に適用します;もしコンフリクトが見つかった場合は、 適用は中止され、"マージの解決" で説明されているように コンフリクトを解決してください。( "-3" のオプションは git に マージすることを伝えます;もし単純に変更を取り消し、 ツリーと索引を変更したくない場合は、"-3" のオプションを省略してください。)
コンフリクトを解消して索引を更新した後は、 新しいコミットを作成する変わりに、
$ git am --resolved
を実行すると、コミットが生成され、mailbox にある残りのパッチの適用が 再開されます。
最終的には、一連のコミットとなり、一つ一つが元の mailbox の パッチに対応し、各パッチに含まれているメッセージから取得された 著者とコミットログメッセージが利用されます。
プロジェクトに変更を投稿するもう一つの方法はプロジェクトの管理者に
あなたのリポジトリから git-pull(1) を使用して変更を pull してもらうことです。
"git pull
を使用して更新する" のセクションで
我々は "main" リポジトリから更新を取得する方法を説明してきましたが、
逆の方向についても同じことができます。
あなたと管理者が同じマシン上にアカウントを持っている場合は、 互いのリポジトリから直接変更を pull することができます; リポジトリの URL を引数として受け取ることのできるコマンドは ローカルのディレクトリ名もまた受け取ることができます:
$ git clone /path/to/repository
$ git pull /path/to/other/repository
又は、ssh の URL :
$ git clone ssh://yourhost/~you/repository
開発者の少ないプロジェクトや、少ないプライベートなリポジトリを同期 するような場合、これらが必要な全てとなりえます。
しかしながら、より一般的にこれを行なうには、 他のユーザが変更を pull する為の独立した公開リポジトリを(通常は別のホスト上に) 準備する必要があります。このほうが通常はより便利で、こうすることで 個人の作業中の変更と公開する変更とをきれいに分けることができます。
日々の作業は自分の個人用リポジトリ上で行い、 定期的に個人用リポジトリから公開リポジトリに変更を "push" することで 他の開発者は公開リポジトリから変更を pull できるようになります。 従って、変更のフローは、公開リポジトリを持つ別の開発者が1人いるような場合には、 次のようになります:
あなたが push あなたの個人リポジトリ --------------------------> あなたの公開リポジトリ | | | | あなたが pull | 彼らが pull | | | | | 彼らが push V 彼らの公開リポジトリ <--------------------------- 彼らのリポジトリ
次のセクションでどのようにこれを行なうかを説明します。
~/proj ディレクトリにあなたの個人用リポジトリがあるとします。
初めにリポジトリのクローンを新規作成し、git daemon
にそれを公開することを
伝えます;
$ git clone --bare ~/proj proj.git
$ touch proj.git/git-daemon-export-ok
作成される ディレクトリ proj.git は "裸の(bare)" git リポジトリが含まれています。— すなわち、".git" ディレクトリの中身だけが含まれ、チェックアウトされたファイルは含みません。
次に、proj.git を公開リポジトリのホストとするサーバにコピーします。 scp, rsync その他使いやすいもの何を使っても良いです。
これは好ましい方法です。
他のだれかがサーバ管理をしている場合は、その人に どこのディレクトリにリポジトリを置くと、git:// URL のどこに現れるかを 教えてもらってください。その場合は以下の説明はスキップして "公開リポジトリへ変更を push する" のセクションに進んでください。
そうでない場合にあなたがすべき事は git-daemon(1) を開始することだけです;
このデーモンは 9418 ポートを使用します。デフォルトでは、git ディレクトリと思われ、
git-daemon-export-ok ファイルが存在する全てのディレクトリへのアクセスを許可します。
git daemon
の引数にディレクトリのパスを与えることで、
それらパスに対してさらに制限をかけることができます。
git daemon
は inetd サービスで動かすこともできます;
詳細は git-daemon(1) を参照してください。
(特に、例 のセクションを参照)
git プロトコルはパフォーマンスと信頼性の面でより良いですが、 web サーバが設定されているホストでは、http によるエクスポートの方がより簡単に設定 できるかもしれません。
その場合に行なうべきことは、web サーバが export できるディレクトリ内に 裸の git リポジトリを新規作成し、webクライアントがアクセスする際に必要となる いくつかの追加情報を設定することだけです。:
$ mv proj.git /home/you/public_html/proj.git
$ cd proj.git
$ git --bare update-server-info
$ mv hooks/post-update.sample hooks/post-update
(最後の2行の説明は、git-update-server-info(1) と、 githooks(5) のドキュメントを参照してください。)
proj.git の URL を通知してください。 他のユーザがそのURLから clone 又は pull できるようになっているはずです。 例えば、次のように実行します:
$ git clone http://yourserver.com/~you/proj.git
(WebDAV を使用することで http で push を行なえるようにすることもできます。 詳細は setup-git-server-over-http を参照してください)
上記で説明した技術(http または git 経由でのエクスポート) により、他の管理者があなたの最後の変更を取得できるようにはなりますが、 それらを編集することはできません。 個人用リポジトリで行なった最新の変更を公開リポジトリに反映するには編集操作が必要です。
編集を行なう一番簡単な方法は git-push(1) と ssh を使用する方法です; あなたのブランチ "master" の最新の状態で、リモートブランチ "master" を更新するには、
$ git push ssh://yourserver.com/~you/proj.git master:master
または単に
$ git push ssh://yourserver.com/~you/proj.git master
を実行します。
git fetch
と同じように git push
は fast forward されない場合に
文句を言います;このような場合の対応については次節を参照してください。
"push" のターゲットは通常は bare リポジトリであることに 注意してください。チェックアウトした作業ツリーを持つリポジトリに対しても push することはできますが、push しても作業ツリーは更新されません。 その為、push したブランチが現在チェックアウトしているブランチの場合、 予期しない結果となります!
git fetch
と同じように、タイピングを節約する為のオプションを
設定することができます;例えば、次のようにします。
$ cat >>.git/config <<EOF
[remote "public-repo"]
url = ssh://yourserver.com/~you/proj.git
EOF
こうすることで次のように上記場所に push できるようになります。
$ git push public-repo master
remote.<name>.url, branch.<name>.remote, remote.<name>.push の詳細は git-config(1) のオプションを参照してください。
push の結果がリモートブランチの fast forward とならない場合、 次のようなエラーが発生して push は失敗します:
error: remote 'refs/heads/master' is not an ancestor of
local 'refs/heads/master'.
Maybe you are not up-to-date and need to pull first?
error: failed to push to 'ssh://yourserver.com/~you/proj.git'
これは、例えば次のような場合におきます:
git reset —hard
を使用し、すでに発行していたコミットを削除した場合、または
git commit —amend
を使用し、すでに発行していたコミットを差し替えた場合
(履歴を再編集して間違いを訂正するの手順で)、または
git rebase
を使用し、すでに発行したコミットをリベースした場合
(git rebase を使用する の手順で)
ブランチ名の前に+記号を付けることで、強制的に git push
を実行
することができます:
$ git push ssh://yourserver.com/~you/proj.git +master
通常、公開したリポジトリ内のブランチヘッドを修正した時はいつでも、 以前指し示していた子孫のコミットを指し示すように変更されます。 このような場合に強制的に push を行うと、規約を壊すことになります) (履歴の書き換えによって生じる問題 を参照)
にもかかわらず、この方法は作業中の一連のパッチを簡単な方法で発行する必要がある場合に 一般に行われる方法であり、あなたがブランチを管理する方針を他の開発者に 伝えている限りは許容できるでしょう。
push が失敗するもう一つのケースとして、同じリポジトリの push 権限を他の人にも 与えている場合が考えられます。その場合、はじめに pull するか rebase によって変更を取得した後に再度 push を試みてください; 詳しくは 次の節 と gitcvs-migration(7) を参照してください。
共同作業をする為のもう一つの方法は CVSで一般に使用されているのと同じような モデルを使用するやり方で、特別な権限を持った複数の開発者が 1つの共有リポジトリに対する push と pull の全てを行なうやり方です。 この場合の設定方法については gitcvs-migration(7) を 参照してください。
しかし、git は共有リポジトリを使った運用で何も問題を起こすことはありませんが、 この運用モードは一般には推奨されません。 それは単に、git がサポートする共同開発のモード(つまり、パッチを交換し、 公開リポジトリからpull する方法)の方が、中央リポジトリよりも多くの点で 利点があるからです:
git pull
の機能により
寄せられる変更の任意レビューを行なう一方で、変更の受け入れ作業を
他の管理者に容易に委任することができます。
ここでは、Linux カーネルの IA64 アーキテクチャのメンテナンスを担当している Tony Luck の git の利用方法を紹介します。
彼は2つの公開リポジトリを使用します:
彼は他にも一時的なブランチ("topic branches")を使用します。 それぞれのブランチはパッチの論理的なグループを含んでいます。
この設定をする為には、最初に Linus の公開ツリーを複製することで 作業ツリーを作成します:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
$ cd work
Linus のツリーは origin/master という名前のリモートブランチに格納され、 git-fetch(1) を使用して更新できます;他の公開ツリーも git-remote(1) を使用して "remote" を設定し git-fetch(1) で それらを最新に保ちます;Chapter 1, リポジトリとブランチ を参照してください。
さて、あなたが作業する為のブランチを作成しました; このブランチは origin/master ブランチの現在の先端から開始していて、 (git-branch(1) の —track オプションを使用して) デフォルトでは Linus からの変更をマージする設定にすべきです。
$ git branch --track test origin/master
$ git branch --track release origin/master
これらは git-pull(1) を用いて簡単に最新に保つことができます。
$ git checkout test && git pull
$ git checkout release && git pull
重要な注意点! これらのブランチにローカルな変更を加えると、 このマージは履歴内にコミットオブジェクトを生成します (ローカルで 変更を加えていない時は単に "Fast forward" でマージされます)。 多くの人は Linux の履歴にこれが作成されることを "ノイズ" として嫌います。 その為 "release" ブランチにこの気まぐれが行なわれるのを避けるべきです。 これらノイズとなるコミットはあなたが Linus にリリースブランチへの pull を依頼するときに恒久的な履歴の一部になってしまいます。
幾つかの設定変数(git-config(1) 参照)は 両方のブランチをあなたの公開ツリーに push するのを容易にしてくれます。 (the section called “公開リポジトリの設定” 参照。)
$ cat >> .git/config <<EOF
[remote "mytree"]
url = master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
push = release
push = test
EOF
上記により test と release の両方のツリーに git-push(1) を 使用して push することができるようになります:
$ git push mytree
あるいは、test と release のブランチの一方にだけ push する場合は:
$ git push mytree test
あるいは、
$ git push mytree release
さて、コミュニティからの幾つかのパッチを適用することを考えます。 このパッチ(あるいは関連するパッチグループ)を保管するブランチに 短く分かり易い名前を付けます。
$ git checkout -b speed-up-spinlocks origin
パッチを適用し、幾つかのテストを実行し、変更をコミットします。 パッチが複数のパートから構成される場合は、それぞれを分割した コミットとしてこのブランチに適用すべきです。
$ ... patch ... test ... commit [ ... patch ... test ... commit ]*
この変更状態が良好なら、"test" ブランチに pull し 公開する準備をします。
$ git checkout test && git pull . speed-up-spinlocks
ここではコンフリクトは発生しそうにありません…しかし この段階までの間にしばらくの時間がかかり、上流から新しいバージョンを pull しているかもしれません。
しばらく後に十分な時間が経ちテストが完了したときに、 同じブランチを "release" ツリーに pull し、上流に向かう準備をします。 これは、それぞれのパッチ(あるいは一連のパッチ)をパッチ用のブランチ に留めておくありがたみを理解する場面です。この作業は一連のパッチが "release" ツリーへ任意の順番で移動できることを意味します。 (訳注: 先を読めばわかりますが、ブランチを作って、パッチを適用すると、パッチ群 のテストをブランチごとに並行して行なうことができます。そして、テストが完了したパッチ のブランチから順に release ツリーへ移動することができます。そして、各パッチ用のブラ ンチの状況も簡単に把握できます。)
$ git checkout release && git pull . speed-up-spinlocks
その後、たくさんのブランチが作成され、それらブランチの名前を 適切に指定していたとしても、それらが何であるか又は何が含まれているかを 忘れてしまうかもしれません。特定のブランチでどんな変更が行なわれているかを 思い出すには、次のようにします:
$ git log linux..branchname | git shortlog
test または release ブランチに既にマージされたかどうかは 次のようにして確認します:
$ git log test..branchname
あるいは
$ git log release..branchname
(このブランチがまだマージされていない場合、いくつかのログエントリが表示されます。 既にマージされている場合は、何も出力されません。)
パッチがこの大きなサイクル(test から release に移動し、 Linus に pull され、最終的に自身の "origin/master" ブランチに返却する流れ) を完遂すると変更に対するブランチは不要になります。 このことは次の出力が空であることを確認することで検出できます:
$ git log origin..branchname
この時点でブランチは削除することができます:
$ git branch -d branchname
幾つかの変更は自明で、分割したブランチを作成し test と release ブランチに それぞれマージする必要がない場合もあります。 そういった変更は直接 "release" ブランチに適用し、 "test" ブランチにマージします。
Linus に送る "pull してください" のリクエストに含める diff 状態と 変更の短いサマリを作成するには、次のようにします:
$ git diff --stat origin..release
そして
$ git log -p origin..release | git shortlog
以下はこれら全てをさらに単純化するスクリプトです。
==== update script ====
# GIT ツリーのブランチを更新する。更新すべきブランチが
# origin の場合、kernel.org から pull する。そうでない時は
# origin/master ブランチを test|release ブランチにマージします。
case "$1" in
test|release)
git checkout $1 && git pull . origin
;;
origin)
before=$(git rev-parse refs/remotes/origin/master)
git fetch origin
after=$(git rev-parse refs/remotes/origin/master)
if [ $before != $after ]
then
git log $before..$after | git shortlog
fi
;;
*)
echo "Usage: $0 origin|test|release" 1>&2
exit 1
;;
esac
==== merge script ====
# ブランチを test または release ブランチにマージ
pname=$0
usage()
{
echo "Usage: $pname branch test|release" 1>&2
exit 1
}
git show-ref -q --verify -- refs/heads/"$1" || {
echo "Can't see branch <$1>" 1>&2
usage
}
case "$2" in
test|release)
if [ $(git log $2..$1 | wc -c) -eq 0 ]
then
echo $1 already merged into $2 1>&2
exit 1
fi
git checkout $2 && git pull . $1
;;
*)
usage
;;
esac
==== status script ====
# ia64 GIT ツリーの状態をレポートする
gb=$(tput setab 2)
rb=$(tput setab 1)
restore=$(tput setab 9)
if [ `git rev-list test..release | wc -c` -gt 0 ]
then
echo $rb Warning: commits in release that are not in test $restore
git log test..release
fi
for branch in `git show-ref --heads | sed 's|^.*/||'`
do
if [ $branch = test -o $branch = release ]
then
continue
fi
echo -n $gb ======= $branch ====== $restore " "
status=
for ref in test release origin/master
do
if [ `git rev-list $ref..$branch | wc -c` -gt 0 ]
then
status=$status${ref:0:1}
fi
done
case $status in
trl)
echo $rb Need to pull into test $restore
;;
rl)
echo "In test"
;;
l)
echo "Waiting for linus"
;;
"")
echo $rb All done $restore
;;
*)
echo $rb "<$status>" $restore
;;
esac
git log origin/master..$branch | git shortlog
done
Table of Contents
通常コミットはプロジェクトに追加されるのみで、削除したり置き換えられる ことはありません。Git はこの仮定をもとにデザインされており、 この仮定を破ると git のマージ装置は(例えば)間違ったことをしてしまいます。
しかし、この仮定を破ると便利なシチュエーションもあります。
あなたが大きなプロジェクトのコントリビュータであったと仮定し、 複雑な変更を加えたとします。あなたはそれを他の開発者に公表する為、 その変更を読みやすい手順にし、それが正しいとわかることを証明し、 各変更を行なった理由がわかるようにしたいとします。
1つのパッチ(あるいはコミット)として変更全てを公表すると、 大き過ぎる為一度に全てを消化できません。
あなたの作業の完全な履歴を公表するとなると、間違いや訂正、意味無く終わったもの などが全て含まれ、冗長すぎてしまいます。
従って、通常は次のような一連のパッチを生成するのが理想的です:
これら作業の手助けをする幾つかのツールを紹介し、 それらの使い方を説明し、履歴を再編集することにより発生する問題の幾つかを 説明します
リモート追跡ブランチ "origin" の上にブランチ "mywork" を作成し、 幾つかコミットを作成したとします:
$ git checkout -b mywork origin
$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit
...
mywork にマージをしていないので、変更は "origin" から単純に並行に 行なわれています。
o--o--o <-- origin \ o--o--o <-- mywork
プロジェクトの上流では他の興味深い変更が行なわれ、 "origin" は発展します:
o--o--O--o--o--o <-- origin \ a--b--c <-- mywork
この時点で、"pull" を使用して変更をマージさせることができます; 結果として新しいマージコミットが生成されます、次のようにです:
o--o--O--o--o--o <-- origin \ \ a--b--c--m <-- mywork
しかし、自分の履歴をマージ操作の無い、単純な一連のコミットの状態で 保ちたいのであれば、その代わりに git-rebase(1) を使用すると 良いでしょう。
$ git checkout mywork
$ git rebase origin
これは、mywork からあなたの各コミットを削除し、一時的に (".git/rebase-apply" という名前のディレクトリ内に)パッチとして保存し、 mywork を origin の最新バージョンの位置に更新し、その後で保存した 各パッチを新しい mywork ブランチに適用します。結果は次のようになります:
o--o--O--o--o--o <-- origin \ a'--b'--c' <-- mywork
この作業中にコンフリクトが発生するかもしれません。その場合は
コンフリクトを解決してください;コンフリクトを解消した後に
git add
を使用してそれらの内容で索引を更新し、
git commit
を実行する代わりに、
$ git rebase --continue
を実行します。すると、残りのパッチを適用する作業が続けられます。
どの時点でも —abort
オプションを使用すると、この作業を取り消し、
rebase を開始する前の mywork の状態に戻ることができます:
$ git rebase --abort
履歴を再編集して間違いを訂正する で見てきたように直前のコミットを 以下のようにして修正することができます。
$ git commit --amend
この操作は、過去のコミットをあなたが変更を受け入れる新しいコミットに 置き換え、過去のコミットメッセージを編集する機会を与えてくれます。
これと git-rebase(1) を組み合わせることで、履歴内のさらに過去のコミットについても 置き換えをし、その先頭の変更に立ち入ることができます。 初めに、次のようにして問題のあるコミットにタグを付けます。
$ git tag bad mywork~5
(gitk または git log
が問題のあるコミットを見つけるのに役立ちます。)
そのコミットをチェックアウトして編集し、残りの一連の変更を その先頭にリベースします(ここでは 引き剥がされたhead を 使用する代わりに、一時的なブランチ上にコミットをチェックアウトします。):
$ git checkout bad
$ # ここで変更と索引の更新をします
$ git commit --amend
$ git rebase --onto HEAD bad mywork
これらを行なった後、チェックアウトした mywork が保たれ続け、mywork 上の 先頭のパッチは修正したコミットの先頭に再適用されます。 そして、これらを片付けることができます。
$ git tag -d bad
git の履歴が不変であるという性質は既存のコミットを実際に "変更" していない ことを意味していることに注意してください;代わりに、古いコミットを 新しいオブジェクト名を持つ新しいコミットで置き換えています。
存在するコミットを引数にして git-cherry-pick(1) コマンドを 実行すると、そのコミットが行なった変更を適用し、新しいコミットを 作成することができます。従って、例えば、"mywork" が "origin" の先頭の 一連のパッチを指しているなら、以下のようにすることができます:
$ git checkout -b mywork-new origin
$ gitk origin..mywork &
そして、gitk を使用して mywork ブランチのパッチの一覧を表示し、
cherry-pick を使用して mywork-new にそれらを(可能なら異なる順番で)適用し、
可能であるなら git commit —amend
を使用してそれらを修正します。
git-gui(1) コマンドは個々の選択した diff ハンクを索引に含めるかどうかを
選択するのに役に立つかもしれません。(diff ハンクを右クリックし、
"Stage Hunk for Commit" を選択します)
もう一つの技術は git format-patch
を使用して一連のパッチを作成し、
パッチの前の状態にリセットすることです:
$ git format-patch origin
$ git reset --hard origin
そして、修正し、順番を並び替え、取り除き git-am(1) を使用して 再びパッチを適用します。
ブランチの履歴を書き換えることによって生じる主な問題はマージに関する ことです。誰かがあなたのブランチをフェッチし、自分のブランチにマージ すると、結果は次のようになります:
o--o--O--o--o--o <-- origin \ \ t--t--t--m <-- their branch:
そして、最後の3つのコミットを修正したとします:
o--o--o <-- new head of origin / o--o--O--o--o--o <-- old head of origin
もしそれらが1つのリポジトリに入っていたとすると、 次のようになります:
o--o--o <-- new head of origin / o--o--O--o--o--o <-- old head of origin \ \ t--t--t--m <-- their branch:
Git は新しい head が古い head の更新されたバージョンであることを認識 しません;Git はこのような状態を2つの開発者が古い head と新しい head で 並行に作業したものとして扱います。 その為、だれかが当たらし head を自身のブランチにマージしようとすると、 git は old を new に置き換える代わりに、その2つの開発ライン(old と new) をいっしょにマージしようとします。 その結果は、期待したものとはことなります。
履歴が再編集されたブランチをまだ公開しようとするかもしれません。 そして、それらブランチをフェッチし順番にテストするのは役に立つことだと 思うかもしれません。しかし、そのようなブランチを自分の作業エリアに pull すべきではありません。
適切なマージをサポートする本来の分散開発では、 公開されたブランチは決して再編集されるべきではありません。
git-bisect(1) コマンドはマージコミットを含んだ履歴を正確に扱います。 しかし、コミットがマージコミットである時は、そのコミットが何故問題を起こしている かの原因を見つけ出すのに苦労することがあります。
次の履歴を考えてください:
---Z---o---X---...---o---A---C---D \ / o---o---Y---...---o---B
上側の開発ライン上のコミットXにて Zから存在する関数の意味が 変更されたとします。ZからAにつながるコミットは、関数の実装を変更し、 Zの時点で存在する全コール箇所と新しく追加したコール箇所の両方を変更して、 矛盾のない状態にしていたとします。 A の時点ではバグはありません。
それと同時に下側の開発ラインではコミットYにてその関数の新しい コール箇所を追加していたとします。 ZからBにつながるコミットの全ては、その関数の古い動作を想定していて コール元とコール先は互いに矛盾していません。Bにもバグはありません。
2つの開発ラインがきれいにマージできたとすると、 コンフリクトの解消要求はありません。
にもかかわらず、Cのソースは壊れています。なぜなら 下側の開発ラインは上側の開発ラインで行われた新しい動作に変換 されていないからです。 したがって、git-bisect(1) は D に問題があり、 Z には問題ないこと、そしてC が問題の原因であると伝えます。 どのようにしたら、問題が動作変更によるものだと見つけることが できるでしょうか?
git bisect
の結果が非マージコミットの場合、通常、単にそのコミットを
確認するだけで問題を見つけることができます。
開発者はコミットを自己完結したより小さいものに分割することで
これを容易に行うことができます。しかし、
上記場合には1つのコミットを確認することでは問題が明らかにならない為、
このような方法では解決の役に立ちません;その代わりに開発の大局的な視点が
必要となります。さらに悪いことに、問題となっている関数の動作変更が
上側の開発ラインの単なる小さな部分の変更であるかもしれません。
その一方で、C のマージをする代わりに、ZからBの履歴を Aの先頭 にリベースした場合、次のような1行の履歴を取得することができます:
---Z---o---X--...---o---A---o---o---Y*--...---o---B*--D*
Z と D* の間を git bisect すると、1つの問題の原因となるコミット Y* を見つけることができ、Y* が何故問題を引き起こしている理由を 容易に理解することができるでしょう。
部分的にですが、この理由により、たくさん経験を積んだ git ユーザは マージが頻繁に行われるプロジェクトで作業する場合でさえも 最新の上流バージョンに対するリベースを行うことによって 履歴を1ラインに保つようにしています。
Table of Contents
git-remote(1) を使用する代わりに、時には1つのブランチだけを 更新し、任意の名前でローカルに保存することもできます:
$ git fetch origin todo:my-todo-work
最初の引数 "origin" はクローン元のリポジトリからフェッチすることを git に伝えています。2つ目の引数はリモートのリポジトリの "todo" という 名前のブランチをフェッチし、refs/heads/my-todo-work という名前で ローカルに保存することを git に伝えています。
他のリポジトリにあるブランチをフェッチすることもできます;例えば、
$ git fetch git://example.com/proj.git master:example-master
は "example-master" という名前の新しいブランチを作成し、 指定した URL にあるリポジトリの "master" という名前のブランチの内容を 保存します。既に "example-master" という名前のブランチが存在する場合は、 example.com の master ブランチが与えるコミットの fast-forward を 試みます。詳細は次の節で説明します。
前節の例では、存在するブランチを更新する時に、"git fetch" は リモートブランチの最新のコミットがあなたのコピーしたブランチの最新の コミットの子孫であることを確認してから、新しいコミットの位置に 更新しようとします。 Git はこのプロセスを fast forward と呼びます。
fast forward は、以下のように見えます:
o--o--o--o <-- old head of the branch \ o--o--o <-- new head of the branch
時には、新しい head は古い head の子孫ではない可能性があります。 例えば、開発者が深刻な間違いをしたことに気がつき、変更を 元に戻すことにした場合です、その結果は次のようになります:
o--o--o--o--a--b <-- old head of the branch \ o--o--o <-- new head of the branch
この場合、"git fetch" は失敗し、警告が表示されます。
その場合にも、次の節で説明する方法で、強制的に新しい head に 更新することができます。しかし、上記の場合には それらへの参照を既に作成していない限りは "a" と "b" のコミットが紛失することを意味することに注意してください。
新しいブランチの head が古い head の子孫ではない為に、 git fetch 失敗した場合には、次のようにして強制的に 更新することができます:
$ git fetch git://example.com/proj.git +master:refs/remotes/example/master
"+" 記号を追加していることに注意してください。代わりに "-f" を のフラグを使用し、フェッチした全てのブランチを強制的に更新することもできます:
$ git fetch -f origin
この操作により、example/master の古いバージョンが指していたコミットは紛失して しまうことに注意してください。それは、前の節で説明したとおりです。
既に説明したように、"origin" はクローン元のリポジトリを参照する ショートカットです。この情報は git の構成ファイルに格納されており、 git-config(1) を使用して参照することができます:
$ git config -l
core.repositoryformatversion=0
core.filemode=true
core.logallrefupdates=true
remote.origin.url=git://git.kernel.org/pub/scm/git/git.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
他にも頻繁に参照するリポジトリがある場合は、タイピング数を節約するため、 同じように構成ファイルに登録することができます;例えば、
$ git config remote.example.url git://example.com/proj.git
とすると、次の2つのコマンドは同じことをするようになります:
$ git fetch git://example.com/proj.git master:refs/remotes/example/master
$ git fetch example master:refs/remotes/example/master
さらに、次のオプションを追加すると:
$ git config remote.example.fetch master:refs/remotes/example/master
次のコマンドは全て同じことをするようになります:
$ git fetch git://example.com/proj.git master:refs/remotes/example/master
$ git fetch example master:refs/remotes/example/master
$ git fetch example
"+" をつけて強制的に更新することもできます:
$ git config remote.example.fetch +master:ref/remotes/example/master
"git fetch" が example/master 上のコミットを捨て去る可能性があることを 嫌う場合は、この操作はしないでください。
また、上記の構成全ては、git-config(1) を使用する代わりに 直接 .git/config ファイルを編集して登録することもできます。
詳細は git-config(1) 内の 構成オプションについて触れられている 箇所を参照してください。
Table of Contents
Git は少ない数のシンプルだが強力なアイデアで成り立っています。 それらを理解しなくても git を利用することはできますが、 理解することで git をより直感的に理解できます。
最も重要なコンセプトである オブジェクトデータベース と 索引(index) の説明から開始しましょう、
既に the section called “履歴の理解:コミット” で見てきたように、全てのコミットは 40桁の "オブジェクト名" で格納されています。実際、プロジェクトの履歴を 表現するのに必要な全ての情報は、そのような名前のオブジェクトとして格納されています。 それぞれの名前はオブジェクト内容の SHA-1 ハッシュによって 計算されています。SHA-1ハッシュは暗号学的ハッシュ関数です。 それはつまり、同じ名前を持つ2つの異なるオブジェクトを見つけるのが 不可能であることを意味します。このことは多くの利点を持っています。 とりわけ:
(オブジェクトの形式と SHA1 計算の詳細は the section called “オブジェクトの保管形式” を参照してください)
Gitが扱う オブジェクトには4種類あります:"blob", "tree", "commit" そして "tag" です。
オブジェクトタイプの詳細:
"commit" オブジェクトはツリーの物理的な状態にリンクし、 また、どのようにしてその記述に至ったかの情報も一緒に含んでいます。 —pretty=raw オプション付きで git-show(1) または git-log(1) を 実行すると、特定のコミットの内容を確認できます:
$ git show -s --pretty=raw 2be7fcb476
commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4
tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf
parent 257a84d9d02e90447b149af58b271c19405edb6a
author Dave Watson <[email protected]> 1187576872 -0400
committer Junio C Hamano <[email protected]> 1187591163 -0700
Fix misspelling of 'suppress' in docs
Signed-off-by: Junio C Hamano <[email protected]>
このように、コミットは次のように定義されています:
注意:コミット自身は実際にどのような変更がされたかの情報を持っていません; 全ての変更はコミットが参照しているツリーとparents から連想されるツリーとの比較 によって計算されます。特に、git はファイル名の変更を明示的には記録しようと しません。しかし、同じデータをもつファイルが存在する場合に名前変更であると 認識する方法があります。(例えば、git-diff(1) の -M オプションを参照)
コミットは通常 git-commit(1) によって作成されます。 デフォルトではその親を現在のHEADとしたコミットが作成され、 そのツリーは現在の索引に格納されている内容が使用されます。
git-show(1) コマンドは tree オブジェクトに対しても 使用することができますが、git-ls-tree(1) の方が より詳細な情報を表示します:
$ git ls-tree fb3a8bdd0ce
100644 blob 63c918c667fa005ff12ad89437f2fdc80926e21c .gitignore
100644 blob 5529b198e8d14decbe4ad99db3f7fb632de0439d .mailmap
100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 COPYING
040000 tree 2fb783e477100ce076f6bf57e4a6f026013dc745 Documentation
100755 blob 3c0032cec592a765692234f1cba47dfdcc3a9200 GIT-VERSION-GEN
100644 blob 289b046a443c0647624607d471289b2c7dcd470b INSTALL
100644 blob 4eb463797adc693dc168b926b6932ff53f17d0b1 Makefile
100644 blob 548142c327a6790ff8821d67c2ee1eff7a656b52 README
...
このように、tree オブジェクトは名前順でソートされたエントリの一覧を含んでおり、 各エントリは mode, オブジェクトタイプ、SHA-1名、名前を持っています。 tree は 1つのディレクトリツリーの中身を表現します。
オブジェクトタイプが blob の場合はファイルデータであることを表し、 tree である場合は、サブディレクトリであることを表しています。 tree と blob は他のオブジェクトと同じようにその中身の SHA-1値によって 名前が付けられていて、その中身が(全てのサブディレクトリの内容も含めて)同じ場合 にのみ同じ SHA-1 名となります。この仕組みにより git は2つの関連する tree オブジェクト間の差分を高速に調べることができます。
(注意:サブモジュールが存在する場合、tree は commit をエントリに 持つことがあります。Chapter 8, サブモジュール のドキュメントを参照。)
注意:ファイルは全て 644 または 755 のモードとなります:実際、git は 実行パーミッションだけを管理しています。
git-show(1) を使用すると blob の内容を参照できます; 例として、上記ツリーの blob エントリ "COPYING" を確認します:
$ git show 6ff87c4664
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
v2.2 or v3.x or whatever), unless explicitly otherwise stated.
...
"blob" オブジェクトはバイナリの blob データであるにすぎません。 参照や属性といったものは持っていません。
blob はそれ自身のデータによって完全に定義されるため、 ディレクトリツリー内(またはリポジトリ内の異なるバージョン)に 2つのファイルがあり、それらが同じ内容であるなら、同じ blob オブジェクトを 共有します。オブジェクトはディレクトリツリーの位置に完全に独立しており、 ファイル名を変更してもそれに対応するオブジェクトは変更されません。
注意:全ての tree と blob オブジェクトは git-show(1) に <revision>:<path> の引数を付けて実行することができます。 これにより、現在チェックアウトしていないツリーの中身を ブラウズすることができます。
あるソースから blob の SHA-1値と(信頼できないかもしれない)ソースの中身 を受け取ったとします。この場合でも SHA-1値が一致する限りはその内容が 正しいと信頼することができます。何故なら SHA-1値は同じハッシュ値を生成 する異なるファイルを見つけることが困難なように設計されているからです。
同様に、トップレベルのツリーオブジェクトの SHA-1値を信頼するだけで それが参照する全ディレクトリの内容を信頼することができます。 また、信頼するソースからコミットのSHA-1値を受け取ったなら、 そのコミットの親から到達可能なコミットの全履歴とコミットが参照する ツリーの内容全てを容易に信頼することができます。
従ってトップレベルのコミット名を含む 一つの 特定のノートに デジタル署名するだけで、システムに信頼性を持たせることができます。 デジタル署名はあなたがそのコミットを信頼していることを示し、 コミット履歴の不変性は全ての履歴が信頼できることを示しています。
言い換えると、トップレベルのコミットのSHA-1値を伝えるメールを作成し、 GPG/PGPでデジタル署名するだけで、容易に全ての履歴の妥当性を示す ことができるということです。
この仕組みを支援するため、gitはtagオブジェクトも用意しています。
tag オブジェクトはオブジェクトとオブジェクトタイプ、タグ名、 タグの作成者、メッセージ(これには署名が付けられることがあります)を 含んでいます。このことは git-cat-file(1) で確認できます:
$ git cat-file tag v1.5.0
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v1.5.0
tagger Junio C Hamano <[email protected]> 1171411200 +0000
GIT 1.5.0
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQBF0lGqwMbZpPMRm5oRAuRiAJ9ohBLd7s2kqjkKlq1qqC57SbnmzQCdG4ui
nLE/L9aUXdWeTFPron96DLA=
=2E+0
-----END PGP SIGNATURE-----
タグの作成方法と検証方法は git-tag(1) コマンドを参照してください。 (注意: git-tag(1) は'軽量'タグを作成することもできます。 これは、tagオブジェクトとは全く異なるもので、'refs/tags/'で始まる 単なる参照です)
新規作成されたオブジェクトは最初はオブジェクトの SHA-1ハッシュ値の 名前でファイルとして保存されます(.git/objects 内に保管されます)。
残念なことに、プロジェクトに大量のオブジェクトが作成されると、 この仕組みでは不十分になります。古いプロジェクトで以下を実行 してみてください:
$ git count-objects
6930 objects, 47620 kilobytes
最初の数字はファイルとして保管されているオブジェクト数です。 二つ目の数字はこれらの "遊離した" オブジェクトによって消費される 容量の合計値です。
これら遊離したオブジェクトを "packファイル" に移動することで ディスク容量を節約し、またgitを高速化することができます。 pack ファイルはオブジェクトのグループを十分に圧縮した形式で保管します: packファイル形式の詳細は technical/pack-format.txt を参照してください。
遊離したオブジェクトを pack に移動するには、git repack を実行するだけです:
$ git repack
Generating pack...
Done counting 6020 objects.
Deltifying 6020 objects.
100% (6020/6020) done
Writing 6020 objects.
100% (6020/6020) done
Total 6020, written 6020 (delta 4070), reused 0 (delta 0)
Pack pack-3e54ad29d5b2e05838c75df582c65257b8d08e1c created.
その後、
$ git prune
を実行すると pack に含まれる全ての "遊離した" オブジェクトは削除されます。 この操作は参照されていないオブジェクト(例えば、"git reset" によって コミットを削除した場合などに作成される)も全て削除します 遊離したオブジェクトが削除されたことは .git/objects ディレクトリを 見るか、次のコマンドを実行することで確認できます。
$ git count-objects
0 objects, 0 kilobytes
オブジェクトファイルが削除されても、そのオブジェクトを参照する 全てのコマンドは以前と同じように動作します。
The git-gc(1) コマンドは repack、prune などを実行してくれるので 通常は高レベルであるこのコマンドのみ使用します。
git-fsck(1) コマンドは dangling オブジェクトに関する メッセージを表示することがありますが、これは問題ではありません。
dangling オブジェクトが作成される主な原因は ブランチをリベースした場合や、 他のユーザがリベースしたブランチを pull した場合です。 — Chapter 5, 履歴を再編集し、一連のパッチを管理する 参照。この場合、ブランチの古い head は まだ存在していて、head が参照していたオブジェクトも全て残っています。 ブランチのポインタは、他の場所に移しかえられているので、存在しませんが。
dangling オブジェクトが作成される他の例もあります。 例えば、ファイルを "git add" したが、そのファイルに別の変更を加えて コミットしたような場合です。 — この場合、もともと add していた内容は、どのコミットとツリーにも 参照されず、dangling blob オブジェクトとなります。
同様に、"再帰的に" マージを実行した際に、マージ内容が複雑で 複数のマージベースが存在するような場合(あまり発生することはありませんが)には、 中間状態のツリーが一時的に作成されます。これら中間状態の作成時に オブジェクトが作成されますが、それらは最終的なマージ結果では 参照されることがありません。従ってこれらも "dangling" となります。
一般に、dangling オブジェクトが存在しても心配する必要はありません。 どちらかといえば、それらは役に立つものです:何か操作間違いをした時に、 dangling オブジェクトを使用すると元の状態に戻すことができます。 (リベースした後に誤りに気が付いた時、 ある古い dangling の状態に head をリセットすることができます)
commit の場合は、次のようにします:
$ gitk <dangling-commit-sha-goes-here> --not --all
これは、指定したコミットから到達可能だが他のブランチやタグ、その他の参照からは 到達できない履歴すべてを表示します。そのうちのどれかが望むものであるなら、 新しい参照を作成します、例えば次のように。
$ git branch recovered-branch <dangling-commit-sha-goes-here>
blob と tree の場合は、同じようにはできませんが、 次のようにして確認することができます。
$ git show <dangling-blob/tree-sha-goes-here>
これは、blob の中身が何であるか(ツリーの場合、ディレクトリの "ls" の内容) を表示するので、その dangling オブジェクトを残すのにどのような操作が 必要となるかを教えてくれるでしょう。
通常、dangling blob と tree はあまり必要になることはありません。 大抵はコンフリクトマーカのついたマージの途中状態であるか、 "git fetch" を Ctrl+C や何かで中断した際に作成されたもので、 宙ぶらりんで役に立ちません。
いずれにしろ、dangling 状態に興味がないことを確認したのなら、 到達できないオブジェクト全てを破棄することができます:
$ git prune
"git prune" は必ず休止状態にあるリポジトリでだけ実行してください。 — これはファイルシステムの fsck によるリカバリのようなものです: ファイルシステムがマウントされている時には行なうべきではありません。
("git fsck" についてもこれと同じことが言えます。ところで、
git fsck
は実際に決してリポジトリを変更することはなく、
検出されたものを報告するだけで、実行しても危険なものではありません。
誰かがリポジトリを変更している最中に実行した場合、
紛らわしく恐ろしいメッセージが表示されますが、悪いことは行なわれません。
それとは反対に "git prune" を他のユーザがリポジトリを変更している最中に
実行するのは 悪い アイデアです。)
git は意図的にデータを慎重に扱います。しかし、git 自身にバグがなかったとしても ハードウェアやオペレーティングシステムのエラーによりデータが壊れることがあります。
そのような問題に対する第1の防御はバックアップです。 clone コマンドを使用するか cp, tar または他のバックアップメカニズムを 使用して git ディレクトリをバックアップすることができます。
最後の手段は、破損したオブジェクトを探し出して手作業で置き換えることです。 破損しかかっている最中であっても、作業をする前にリポジトリを バックアップしてください。
1つの blob が紛失または破損している場合を考えます。 そのような場合は時に解決できることがあります。 (ツリー、そして特にコミットの紛失から復旧する場合はより困難です)
開始する前に、破損している箇所を git-fsck(1) を使用して 確認します;これには多大な時間を必要とするかもしれません。
次のように出力されたとします:
$ git fsck --full
broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
to blob 4b9458b3786228369c63936db65827de3cc06200
missing blob 4b9458b3786228369c63936db65827de3cc06200
(通常、これらと一緒に "dangling オブジェクト" のメッセージも表示 されますが、これらは興味深いものではありません)
4b9458b3 の blob が紛失しており、2d9263c6 のツリーが それを参照していることがわかります。その紛失したblobのコピーを 他のリポジトリなどから見つけることができるなら、それを .git/objects/4b/9458b3… に移動すれば修復は完了です。もしそれが できない場合でも、git-ls-tree(1) を用いて、それが何を指し示して いるかを確認することができます:
$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore
100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap
100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING
...
100644 blob 4b9458b3786228369c63936db65827de3cc06200 myfile
...
これにより、紛失した blob が "myfile" という名前のファイルデータであることが わかります。そして、それがあるディレクトリも特定できたとします— "somedirectory" とします。運よくチェックアウトした作業ツリー内の "somedirectory/myfile" にそれと同じコピーがあるのなら、 git-hash-object(1) を用いてそれが正しいかどうかをテストできます。
$ git hash-object -w somedirectory/myfile
これにより、somedirectory/myfile の内容をもった blob オブジェクトを 作成して格納し、そのオブジェクトの SHA-1 を表示します。 その値が 4b9458b3786228369c63936db65827de3cc06200 であったなら あなたは非常に幸運です。この場合、あなたの推測は正しく、破損は 修復されました!
一致しなかった場合、より詳しい情報が必要になります。 そのファイルのどのバージョンを無くしたかを確認します。
最も簡単な方法は、次のとおりです:
$ git log --raw --all --full-history -- somedirectory/myfile
生データを出力しているため、次のような出力が得られます
commit abc
Author:
Date:
...
:100644 100644 4b9458b... newsha... M somedirectory/myfile
commit xyz
Author:
Date:
...
:100644 100644 oldsha... 4b9458b... M somedirectory/myfile
これはすぐ前のファイルのバージョンが "newsha" であり、すぐ後のバージョンが "oldsha1" であることを示しています。 また、oldsha から 4b9458b の変更点と 4b9458b から newsha での変更点 のコミットメッセージについても知ることができます。
変更内容が十分小さい場合、4b9458b の状態の内容を再作成することが できるかもしれません。
もしそれができたのなら、紛失したオブジェクトを次のようにして 再作成することができます。
$ git hash-object -w <recreated-file>
これにより、リポジトリは正常に戻ります!
(ところで、fsck を無視することもできます。次のようにして、
$ git log --raw --all
紛失したオブジェクト(4b9458b..)の sha を探し出します。 - git はたくさんの情報を持っていますが、紛失したのは1つの特定の blob バージョンであるにすぎません)
索引はバイナリファイル(通常 .git/index 内に保管)であり、 ソートされたパス名と、パーミッション、blob の SHA-1値の一覧を含んでいます; git-ls-files(1) を使用すると索引の中身を参照できます:
$ git ls-files --stage
100644 63c918c667fa005ff12ad89437f2fdc80926e21c 0 .gitignore
100644 5529b198e8d14decbe4ad99db3f7fb632de0439d 0 .mailmap
100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0 COPYING
100644 a37b2152bd26be2c2289e1f57a292534a51a93c7 0 Documentation/.gitignore
100644 fbefe9a45b00a54b58d94d06eca48b03d40a50e0 0 Documentation/Makefile
...
100644 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea 0 xdiff/xtypes.h
100644 2ade97b2574a9f77e7ae4002a4e07a6a38e46d07 0 xdiff/xutils.c
100644 d5de8292e05e7c36c4b68857c1cf9855e3d2f70a 0 xdiff/xutils.h
古いドキュメントでは、"現在のディレクトリキャッシュ" または単に "キャッシュ" と呼んでいることがあります。索引には3つの重要な 性質があります:
索引は1つの(ユニークに決定される)ツリーオブジェクトを 生成するのに必要な情報全てを含んでいます。
例えば、git-commit(1) を実行すると、索引から 新しいコミットが参照する tree オブジェクトを生成し、オブジェクトDBに 格納します。
索引は索引が定義する tree オブジェクトと 作業ツリーとを高速に比較することができます。
これは、各エントリに対する追加情報(最終更新日のようなもの)を 格納することで行なわれます。このデータは上記には表示されず、 tree オブジェクトを作成する時には格納されませんが、 変更されているファイルと索引に格納されているファイルを 高速に確認するために使用できます。この仕組みが git がデータの 全てを読み込み変更を探す作業を手助けします。
異なるツリーオブジェクト間のコンフリクトの情報を表現することができ、 各パス名は3-way マージを行なうときに作成されるツリーの情報を 持つことができます。
the section called “コンフリクトを解消する為の助けを得る” で見たように、マージしている間、 1つのファイルの("ステージ"と呼ばれる)複数のバージョンを格納する ことができます。git-ls-files(1) の出力の3つ目のカラムは ステージの番号で、コンフリクトしたファイルには0より大きい値が 付けられます。
このように索引は一時的な作業エリアの役割をし、 作業中のツリーの内容が詰められています。
索引を完全に消し去ると、それを記述していたツリーの名前を 持っていない限りは、全ての情報を失ってしまいます。
Table of Contents
大きなプロジェクトは自己完結したより小さいプロジェクトを含む場合があります。 例えば、組み込みLinuxディストリビュションのソースツリーは ディストリビュション内にローカルに変更を加えられたソフトウェアのピースが 含まれています;ムービープレーヤーは解凍ライブラリの特定のバージョンで ビルドできるようにする必要があるかもしれません;幾つかの独立したプログラムは 同じビルドスクリプトを共有しているかもしれません。
集中型のリビジョン管理システムでは、1つのリポジトリ内に各モジュールを含む ことによってこれを実現します。開発者は全てのモジュールあるいは必要なモジュール だけをチェックアウトすることができます。API や翻訳の移動や更新といった 幾つかのモジュールにまたがったファイルを1回のコミットで変更することができます。
Gitは部分的なチェックアウトを許可していない為、Git の複製アプローチでは 開発者は興味のないモジュールのコピーまで取得しなくてはなりません。 Git は各ディレクトリの変更をスキャンしなくてはならず、 莫大なコミットをチェックアウトすることで、想像以上に Git は遅くなります。
一方プラスの面として、分散型のリビジョン管理システムは外部ソースを より良い形で統合します。集中型モデルでは、外部プロジェクトの1つの任意の スナップショットをそれ自身のリビジョン管理ツールからエクスポートし、 ローカルのリビジョン管理ツールにベンダーブランチとしてインポートします。 変更履歴は全て隠れてしまいます。分散型のリビジョン管理システムでは 外部の履歴全てを複製することができ、よりいっそう容易に開発を進め、 ローカルの変更を再マージすることもできます。
Git のサブモジュール機能は外部のプロジェクトを サブディレクトリとしてリポジトリに含ませることができます。 サブモジュールはそれ自身で独自性をもってメンテナンスされます; これは、サブモジュールは自身のリポジトリとコミットIDとを持つということであり、 そのサブモジュールを含むプロジェクト("親プロジェクト")を複製した場合、 同じリビジョンのサブモジュールを全て容易に複製できます。 親プロジェクトの部分チェックアウトは可能です:サブモジュールを 複製する、しないをサブモジュールごとにしていすることができます。
git-submodule(1) コマンドは Git 1.5.3 から利用可能になりました。 Git 1.5.2 を使用しているユーザもリポジトリ内のサブモジュールのコミットを 参照し、それらをマニュアルでチェックアウトすることはできます; 初期のバージョンでは全てのサブモジュールを認識することはできないでしょう。
サブモジュールがどのように利用できるかを見るため、 例として4つのリポジトリを作成します:
$ mkdir ~/git
$ cd ~/git
$ for i in a b c d
do
mkdir $i
cd $i
git init
echo "module $i" > $i.txt
git add $i.txt
git commit -m "Initial commit, submodule $i"
cd ..
done
そして、親プロジェクトを作成し、全てのサブモジュールを追加します:
$ mkdir super
$ cd super
$ git init
$ for i in a b c d
do
git submodule add ~/git/$i $i
done
注意:親プロジェクトを公開する予定がある場合は、ローカルのURLは使用しないでください!
git submodule
がどのようなファイルを作成するか見ましょう:
$ ls -a
. .. .git .gitmodules a b c d
git submodule add <repo> <path>
コマンドは幾つかのことをしています:
親プロジェクトをコミットします:
$ git commit -m "Add submodules a, b, c and d."
そして、親プロジェクトを複製してみます:
$ cd ..
$ git clone super cloned
$ cd cloned
サブモジュールのディレクトリが作成されていますが、それらは空です:
$ ls -a a
. ..
$ git submodule status
-d266b9873ad50488163457f025db7cdd9683d88b a
-e81d457da15309b4fef4249aba9b50187999670d b
-c1536a972b9affea0f16e0680ba87332dc059146 c
-d96249ff5d57de5de093e6baff9e0aafa5276a74 d
注意:あなたの環境では、コミットオブジェクトの名前が上記のものとは
違っているかもしれません。しかし、それらはあなたのリポジトリの HEAD コミットの
オブジェクト名と一致しているはずです。このことは git ls-remote ../a
で
確認できます。
サブモジュールの取得は2つの手順で行ないます。まず git submodule init
を実行しサブモジュールのリポジトリURLを .git/config
に追加します:
$ git submodule init
そして git subumodule update
を実行すると、リポジトリの複製と
親プロジェクトにて指定されているコミットをチェックアウトが行われます:
$ git submodule update
$ cd a
$ ls -a
. .. .git a.txt
git submodule update
と git submodule add
の間の大きな違いは
git submodule update
が特定のコミットをチェックアウトするのに対し
git submodule add
はブランチの先端をチェックアウトするという点です。
それは、タグをチェックアウトするのに似ています:
つまり head から引き離され、ブランチ上に位置しません。
$ git branch
* (no branch)
master
head から引き離されているサブモジュール内で変更を加えたい場合は、 ブランチを作成またはチェックアウトし、変更を加え、サブモジュール内の変更を 公開し、新しいコミットを参照するように親プロジェクトを更新します:
$ git checkout master
または
$ git checkout -b fix-up
そして、
$ echo "adding a line again" >> a.txt
$ git commit -a -m "Updated the submodule from within the superproject."
$ git push
$ cd ..
$ git diff
diff --git a/a b/a
index d266b98..261dfac 160000
--- a/a
+++ b/a
@@ -1 +1 @@
-Subproject commit d266b9873ad50488163457f025db7cdd9683d88b
+Subproject commit 261dfac35cb99d380eb966e102c1197139f7fa24
$ git add a
$ git commit -m "Updated submodule a."
$ git push
サブモジュールも更新したい場合は、
git pull
をした後に git submodule update
を実行します。
サブモジュールを参照する親プロジェクトの変更を公開する前には 必ずサブモジュールの変更を公開してください。もしサブモジュールの変更を 公開し忘れた場合、リポジトリを複製できなくなるでしょう:
$ cd ~/git/super/a
$ echo i added another line to this file >> a.txt
$ git commit -a -m "doing it wrong this time"
$ cd ..
$ git add a
$ git commit -m "Updated submodule a again."
$ git push
$ cd ~/git/cloned
$ git pull
$ git submodule update
error: pathspec '261dfac35cb99d380eb966e102c1197139f7fa24' did not match any file(s) known to git.
Did you forget to 'git add'?
Unable to checkout '261dfac35cb99d380eb966e102c1197139f7fa24' in submodule path 'a'
親プロジェクトにより記録されたコミットよりも手前にサブモジュールのブランチを 巻き戻すべきではありません。
そうした場合、ブランチを最初にチェックアウトせずにサブモジュール内の変更を
コミットした時に git submodule update
は安全に動作しません。
何も言わずに変更を上書きしてしまいます。
$ cat a.txt
module a
$ echo line added from private2 >> a.txt
$ git commit -a -m "line added inside private2"
$ cd ..
$ git submodule update
Submodule path 'a': checked out 'd266b9873ad50488163457f025db7cdd9683d88b'
$ cd a
$ cat a.txt
module a
注意:変更はサブモジュールの reflog にまだ残っています。
ただし、変更をコミットしていない場合は残っていません。
Table of Contents
多くの上位レベルコマンドは、より小さくコアとなっている下位レベルの git コマンド を使用したシェルスクリプトで元々は実装されています。 下位レベルのコマンドは通常は行なわないようなことを git で行なう場合や 内部の仕組みを理解する手段として役に立つ場合があります。
git-cat-file(1) コマンドは任意のオブジェクトの中身を表示する ことができますが、上位の git-show(1) の方が通常は便利です。
git-commit-tree(1) コマンドを用いると任意の親とツリーで コミットを構成させることができます。
git-write-tree(1) を用いるとツリーを作成することができ、 その中身は git-ls-tree(1) でアクセスできます。2つのツリーは git-diff-tree(1) で比較することができます。
タグは git-mktag(1) で作成でき、その署名は git-verify-tag(1) で検証することができます。しかし、これら2つは git-tag(1) を 使用すると同じようなことができます。
git-commit(1)、git-checkout(1)、git-reset(1) のような高レベルの操作は作業ツリー、索引、オブジェクトDB間の データを移動することによって仕事をします。 Git はこれらの各ステップを独立に行なう下位レベルの操作を提供しています。
一般に、全ての "git" 操作は索引ファイル上で作用します。 いくつかの操作は 純粋に 索引ファイル(現在の索引の状態を表示) 上で作用しますが、ほとんどの操作は索引ファイルとデータベース又は 作業ディレクトリ間のデータを移動します。従って、主に4つの組み合わせがあります:
git-update-index(1) コマンドは作業ディレクトリの情報を 索引に更新します。一般には更新したいファイルを特定して 索引の情報を更新します、次のように:
$ git update-index filename
しかし、ファイル名を補完した際などの間違いを防ぐため、 このコマンドは新しいエントリや削除された古いエントリを 自動的に追加することはありません。即ち、存在するキャッシュエントリを単に更新するだけです。
あるファイルを削除する場合は —remove
フラグを、
新しいファイルを追加する場合は —add
フラグを使用します。
注意! —remove
フラグを使用しても必ずしもそのファイルが削除される
わけではありません:ファイルがディレクトリ内にまだ存在する場合、
索引は新しいステータスに更新されますが、ファイルは削除されません。
—remove
はファイルが削除されたのが正しいことであることが
索引の更新時に考慮されることを意味しているだけであり、
もしファイルが本当に存在しないのなら索引をそれに従い更新するだけです。
git update-index —refresh
という特別なオプションもあります。
これは現在の "stat" 情報に一致する各索引の "stat" 情報を
リフレッシュします。オブジェクトのステータス自身は更新せず、
オブジェクトが過去に格納されたオブジェクトとまだ一致している
かを素早くテストする為に使用されるフィールドだけを更新します。
以前紹介した git-add(1) は git-update-index(1) の 単なるラッパースクリプトです。
現在の索引ファイルを "tree" オブジェクトに書く為には、プログラム
$ git write-tree
を使用します。これにはオプションがありません。— これは その状態を記述する現在の索引をツリーオブジェクトの集合へと 書き出す役目をし、トップレベルのツリーの名前を返却します。 このツリーはいつでも索引を再生成する為に利用できます。
オブジェクトDBから "tree" ファイルを読み込み、現在の索引を そのツリーの状態と同じにするには次のようにします。 (索引が後で元に戻す可能性がある未保存の状態を含む場合にはこれをしないでください!)
$ git read-tree <SHA-1 of tree>
これにより索引ファイルは過去に保存したツリーと同じ状態になります。 しかし、それは index ファイルに対してだけです:作業ディレクトリは 何も変更されていません。
次のようにすると、作業ディレクトリを索引の状態に 更新することができます。
$ git checkout-index filename
あるいは、全ての索引をチェックアウトしたい場合は -a
を使用します。
注意! git checkout-index
は通常、古いファイルを上書きすることを居します。
従って、チェックアウト済みの古いバージョンのツリーを持っている場合、
("-a" フラグ又はファイル名の前に) "-f" フラグを使用し、強制的に
チェックアウトする必要があります。
最後に、一方から他方への純粋な移動ではないものを説明します:
"git write-tree" で作成したツリーをコミットするには、 そのツリーと背後にある履歴(つまり、その履歴とつながる"親の"コミット) を参照する "commit" オブジェクトを作成します。
通常 "commit" は一つの親を持ちます:これはある変更を行った一つ前の ツリー状態を指しています。しかしながら時には二つ以上の親を持つこと もあります。それは "マージ" と言われる場合であり、そのようなコミットは 二つ以上の状態を一つに纏めます。
言い換えると、ツリーがある作業ディレクトリの特定の状態を表す一方、 コミットはその状態の時刻とそこに至った経緯を表します。
コミットを作成するには、コミット時点の状態を表すツリーと親のリストを 渡すことで作成することができます:
$ git commit-tree <tree> -p <parent> [-p <parent2> ..]
コミット時のコメントは標準入力(あるいはパイプやファイルのリダイレクト) で指定します。
git commit-tree
は作成したコミットのオブジェクト名を返却します。
通常、この名前は .git/HEAD
ファイルに保存され、その結果、
最後のコミットが何であるかを確認できます。
以下は Jon Loeliger が作成したテキストのイラストで、 項目間の関係を示しています。
様々なヘルパーツールを用いることでオブジェクトDBと 索引の中身を確認することができます。 各オブジェクトの中身は git-cat-file(1) で確認します:
$ git cat-file -t <objectname>
これはオブジェクトのタイプを表示し、タイプを確認した後は 次のようにして、中身を確認することができます。
$ git cat-file blob|tree|commit|tag <objectname>
注意! Tree はバイナリであり、その中身を見るには git ls-tree
という
特別なツールを使用します。これは、バイナリの内容をより読みやすい形式に
変換してくれます。
"commit" オブジェクトの中身を見ることはとても教育的です。
それらは小さく、一目瞭然です。特に、
最新のコミット名が .git/HEAD
にあるという規約を使用し、
次のようにすると、最新のコミットの中身を確認できます。
$ git cat-file commit HEAD
Git は 3方向マージを行なう手助けをします。これは 任意の回数繰り返すことにより n方向マージに拡張することができます。 通常は3方向マージ(2つの親によるマージ)のみを行ないますが、 複数の親を1回でマージすることもできます。
3方向マージをするには、マージする2組の "commit" オブジェクトが 必要です。これを使用し共通の親(3番目の"commit"オブジェクト)を見つけ、 それら commit オブジェクトを使用してディレクトリ("tree" オブジェクト) の状態を探します。
マージの "ベース" を得るには、まず次のようにして 2つのコミットの共通の親を見つけます。
$ git merge-base <commit1> <commit2>
これは、共通のベースコミットを返却します。 これらコミットの "tree" オブジェクトは次のようにして 容易に見つけることができます。(例えば)
$ git cat-file commit <commitname> | head -1
tree オブジェクトの情報は、コミットオブジェクトの1行目に 常に記述されています。
マージしようとする3つのツリー(1つは "original"の tree いわゆる 共通の tree、 2つの "結果" tree、いわゆるマージしたいブランチ)を知った後は、 索引に読み込んでそれらを "マージ" します。 古い索引の中身を破棄しなくてはならない場合、エラーを表示します。 従ってそれらをコミットしたか確認してください。— 実際、通常は常に 最後にコミットした内容をもとにマージします(そのコミットは従って 現在の索引の内容と一致します)
マージをするには次のひょうにします。
$ git read-tree -m -u <origtree> <yourtree> <targettree>
これにより、自明のマージ操作は直接索引ファイルに行なわれ、
git write-tree
を用いてその結果を書き込むことができます。
残念なことに、多くのマージは自明ではありませんん。 追加や移動、削除されたファイルがある場合や、あるいは両方のブランチ で同じファイルが変更されている場合、"マージエントリ" を含んだ 索引ツリーが残されます。このような索引ツリーは tree オブジェクトには書き込むことができません。その結果を書き込む前に 他のツールを使用してコンフリクトを解消する必要があります。
このような索引状態は git ls-files —unmerged
を
用いると確認できます。例えば:
$ git read-tree -m $orig HEAD $target
$ git ls-files --unmerged
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello.c
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello.c
100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello.c
git ls-files —unmerged
の出力の各行は blob のモードビット、
blob の SHA-1、stage番号'、そしてファイル番号で始まります。
'stage番号 はそれがどの tree から来たかを表す git のやり方です:
stage 1 は $orig
ツリーに対応し、stege 2 は HEAD
の tree
stage 3 $target
tree です。
既に自明のマージは git read-tree -m
にて行なわれたことを
説明しました。例えば、ファイルが HEAD と
$target` のどちらでも
変更されていない場合や、HEAD
と $target
が同じ内容の場合は
明らかに最終的な結果は HEAD
のものと同じです。
上記の例では hello.c
は HEAD
で変更されていて $target`でも
別の変更が行なわれている場合です。
好きな3方向マージツールを実行してこれを解決することができます。
例えば `diff3`、`merge
あるいは git 自身の merge-file などを使用し
これら3つのstage 上のファイルを次のようにしてマージします。
$ git cat-file blob 263414f... >hello.c~1
$ git cat-file blob 06fa6a2... >hello.c~2
$ git cat-file blob cc44c73... >hello.c~3
$ git merge-file hello.c~2 hello.c~1 hello.c~3
これはマージ結果は hello.c~2
ファイルに保存し、
コンフリクトがあるときは、コンフリクトマーカが付けられます。
マージ結果を確認した後、このファイルの最終的なマージ結果を
git に次のようにして伝えます:
$ mv -f hello.c~2 hello.c
$ git update-index hello.c
ファイルにマージ済みになっていない時は、git update-index
は
ファイルに解決済みのマークを付けます。
上記は下位レベルにおける git のマージ動作の解説で、
水面下でどのようなことが起きているかの概念を理解する手助けになります。
実際、誰も、git 自身でさえ、git cat-file
を3回も実行しません。
git merge-index
プログラムがあり、これが stage を一時ファイルとして
抽出し、"merge" スクリプトをコールします:
$ git merge-index git-merge-one-file hello.c
そして、上位レベルとして git merge -s resolve
が実装されています。
Table of Contents
この章では git の実装内部の詳細について説明します。 これらの内容はおそらく git の開発者のみが必要となる内容です。
すべてのオブジェクトは静的に決定された "種別" を持っており、 それによりオブジェクトの形式が特定されます(例えば、どのように使用され どのように他のオブジェクトを参照するかなど)。現在のところ、オブジェクト 種別は4つ存在します: "blob", "tree", "commit", "tag" です。
オブジェクトの種別に関係なく、すべてのオブジェクトは次の特徴を持ちます:
オブジェクトは全て zlib で圧縮され、ヘッダにはオブジェクトの種別だけでなく、
オブジェクトデータのサイズの情報が含まれています。
なお、オブジェクトの名前に使用されている SHA-1 ハッシュ値は元データ+このヘッダの
ハッシュ値となっています。従ってファイルの sha1sum
の結果は
そのオブジェクトの名前とは一致しません。
その結果、オブジェクトの一般的な一貫性はその中身やオブジェクト種別とは
常に独立してテストすることができます:全ておオブジェクトは次のようにして
検証することができます。(a)それらのハッシュ値がファイルの中身と一致していること。そして、
(b)オブジェクトを解凍することができ、解凍したデータは <オブジェクトの種別> 空白
<サイズ> + <byte\0> + <オブジェクトのバイナリデータ> の順番となっていること。
構造化されたオブジェクトの場合は、さらにその構造と他のオブジェクトの
連結性によって懸賞することができます。これらは git fsck
プログラムが
通常行っている内容であり、全オブジェクトの完全な依存グラフを生成し、
それらの内部一貫性を検証します(さらに、単にハッシュを使用した表面的な
一貫性の検証も行います)
新しい開発者にとっては Git のソースコードの構成を理解するのは必ずしも 容易ではないでしょう。このセクションでは、どこから開始したら良いかを 示す小さなガイダンスです。
手始めとして良い場所は、初期コミットの内容を見ることです。次のように取得します:
$ git checkout e83c5163
初期コミットには現在の git のほとんど全ての基礎ができていますが、 ざっと読みとおすのには十分な小ささです。
現在ではこのリビジョンの頃から用語が変更されています。例えば、 このリビジョンの README では現在 コミット と呼んでいる ものを "チェンジセット" と呼んでいます。
また、現在は "キャッシュ" という言い方はせず それどころか"索引(index)" と呼んでいますが、
ファイルは今も cache.h
です。(注釈:Not much reason to change it now,
especially since there is no good single name for it anyway, because it is
basically the header file which is included by all of Git's C sources.)
初期コミットを眺めて概要を把握できたら、より新しいバージョンを
チェックアウトし、cache.h
, object.h
, commit.h
にざっと目を
通してください。
初期の頃には、Git は UNIX の伝統にならい、非常にシンプルなプログラムの 集まりとなっていて、それらをパイプで組み合わせ、スクリプト化したりして 利用するようなツールでした。新しいことを試すのが容易であったため、 初期の開発でこれは良いことでした。しかし、現在では、これらの大部分は 内部に組み込まれ、いくつかのコア部分は "ライブラリ化" されました。 つまり、パフォーマンスや移植性、コードの重複の回避などの理由で libgit.a に入れられました。
ここまでの確認で、索引が何かを理解し、そして、cashe.h`内の索引のデータ
構造を理解することができたはずです。また、4つのオブジェクトタイプ(blob, tree, commit, tag)が
`struct object
の共通構造を継承していていることや、
struct object
がそれらの最初のメンバであり
例えば (struct object *)commit
のようにキャストすることで
&commit->object
と同じ結果が得られること、同じようにオブジェクト名やフラグも
得られることを確認できたはずです。
ここまでで、オブジェクトデータに関する確認はひと段落です。
次のステップ:オブジェクトの指定方法を理解してください。
コミットの指定方法 に書かれているとおり、
オブジェクトの指定方法は数個です(そして、それらはコミットだけに限ったことではありません!)。
これらの指定部分の実装は sha1_name.c
で行われています。
その中の関数 get_sha1() をざっとみてください。
特徴的な部分は `get_sha1_basic()
などの関数部分でハンドリングされています。
この部分は、Git の最もライブラリ化された部分、すなわちリビジョンウォーカー に対するよい導入になっています。
基本的には、git log
の初期バージョンは次のようなシェルスクリプトでした:
$ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
LESS=-S ${PAGER:-less}
これは何を意味するでしょうか?
git rev-list
はリビジョンウォーカーの元になったバージョンであり、
常に リビジョンの一覧を標準出力に出力するコマンドです。
このコマンドは現在でも役に立っており、新しい Git プログラムを
スクリプトとして作成するような場合によく利用されます。
git rev-parse
はもはや重要ではありません;何故ならこれは、スクリプトにより
コールされる異なる配管コマンドに対して、関連のあるオプションをフィルタリング
する為だけに利用されているからです。
git rev-list
が行っていることのほとんどは、revision.c
と revision.h
に含まれています。rev_info
という名前の構造内のオプションを包み込み、
どのようにどのリビジョンにウォーク(アクセス)するかなどのコントロールをします。
git rev-parse
の元の役割は、現在は setup_revisions()
関数に
取って代わられました。setup_revisions()
はリビジョンウォーカーの為に
リビジョンと共通のコマンドラインの解析をします。解析結果は rev_info
に
保管されて利用されます。setup_revisions()
を呼び出した後に、自身の
コマンドラインオプションを解析することもできます。その後に
prepare_revision_walk()
をコールして初期化すると、get_revision()
関数にて
ここのコミットを取得することができます。
リビジョンウォーカーの処理をより詳しく知りたい場合は、始めに cmd_log()
の
実装を参照してください;git show v1.3.0~155^2~4
を実行し、その関数まで
スクロールしてください(もはや setup_pager()
を直接コールする必要がない
点に注意してください)。
現在では git log
はビルトイン化されています(つまり、git
コマンド内に含まれています)。
ビルトイン化されているソース部分は
cmd_<bla>
という名前の関数で、通常は builtin-<bla>.c
内で定義されていて、
宣言は builtin.h
内で行われています。
git.c
内の commands[]
配列のエントリと、
Makefile
内の BUILTIN_OBJECTS
内のエントリとがあります。
時には1つのソースファイルに複数のビルトインが含まれていることがあります。
例えば、cmd_whatchanged()
と cmd_log()
はコードのかなりを共有しているため、
両方とも builtin-log.c
内に存在します。このような場合、コマンドは
.c
ファイルの名前で呼ばれず、Makefile
内の BUILT_INS
内に
リストされている必要があります。
git log
は 元のスクリプトがしていた内容よりも C の実装の方がより
複雑に見えますが、より高い柔軟性とパフォーマンス性を持っています。
ここまでで再び一息つくのにちょうど良い箇所です。
レッスン3はコードの観察です。(基本的な概念を学んだ後に) Git の構成を学ぶには これが一番良い方法です
興味のある内容を考えてください。たとえば
"blob のオブジェクト名からどのように blob にアクセスするのだろう?"とか。
まず最初に、それを行う Git コマンドを見つけてください。この例では、
git show
または git cat-file
です。
明瞭にするため、git cat-file
にしましょう。何故なら
cat-file.c
のリビジョンが 20 個、
ビルトイン化されて builtin-cat-file.c
に変更されてからのリビジョンは 10個以下です)。
builtin-cat-file.c
を開き、cmd_cat_file()
を探し出して、
何が行われているかを見てください。
git_config(git_default_config);
if (argc != 3)
usage("git cat-file [-t|-s|-e|-p|<type>] <sha1>");
if (get_sha1(argv[2], sha1))
die("Not a valid object name %s", argv[2]);
明らかに細かな部分はスキップしましょう;ここで興味深い箇所は
get_sha1()
を読んでいる箇所です。ここではオブジェクト名である argv[2]
を読み込み、もしそれが現在のリポジトリに存在するオブジェクトを参照している場合は
その SHA-1 を変数 sha1
に代入します。
ここで興味深いのは次の2点です:
get_cha1()
は 成功 時に 0 を返却します。これは Git のハックを
初めて行う人にとっては驚くべきことですが、エラーの種別毎に異なる負数を返却し、
成功時には 0 を返却するのは UNIX の昔からの伝統です。
get_sha1()
関数の戻り値である変数 sha1
は unsigned char *
ですが、
実際は unsigned char[20]
を指し示すことが想定されています。
この変数は指定されたコミットの 160-bit の SHA-1 を含んでいます。
SHA-1 が unsigned char *
として渡される時はいつでも、
それがバイナリ表現であり、char *
で渡される 16進の ASCII表現とは違う
ことに注意してください。
これらの両方をソースを通して確認できます。
さて、ここが核心です:
case 0:
buf = read_object_with_reference(sha1, argv[1], &size, NULL);
これが blob (実際のところ、blob だけでなくすべてのオブジェクト)を読む方法です。
この関数 read_object_with_reference()
がどのように動くかを知る為には
その部分のソースコードを git grep read_object_with | grep ":[a-z]"
のように
して見つけ出し、ソースを読むことです。
結果がどのように使用されるかを見つけるには、cmd_cat_file()
内を見ます:
write_or_die(1, buf, size);
時には機能の実装箇所を探し出すことができない場合があるかもしれません。
そのような場合には、git log
の出力を探しだし、関連するコミットを git show
すると手助けになるかもしれません。
例: git bundle
に対するテストケースがあることは知っているが、
それがどこにあるかがわからない場合 (もちろん、git grep bundle t/
することも
できますが、それではポイントを示すことになりません!):
$ git log --no-merges t/
ページャ (less
) 内で "bundle" を検索し、数行戻ると、
commit 18449ab0… にあるのがわかります。このオブジェクト名をコピーし
コマンドラインにペーストします
$ git show 18449ab0
ビンゴ!
次の例:スクリプトをビルトイン化するために必要な作業を調べるには:
$ git log --no-merges --diff-filter=A builtin-*.c
ここまでに見てきたように、Git は Git 自身のソースを調べるときにも 最も役に立つツールなのです!
.git
の拡張子を持つ ディレクトリ で、
リビジョン管理下にあるチェックアウトしたファイルをローカルに持たないディレクトリです。
通常 .git
サブディレクトリ に隠れている git
の管理ファイル全てが
repository.git
ディレクトリに直接存在し、
他のファイルは存在せず、チェックアウトもされていません。
通常、公開リポジトリを出版する人は、裸のリポジトリを作成します。
名詞:git の履歴内のある1点です。プロジェクトの全履歴は 相互に関連したコミットの集合により表現されています。git では"コミット"という言葉を 他のリビジョン管理システムが使用する "リビジョン" または "バージョン"と同じ意味で 使用することがあります。また、コミットオブジェクト の略称として使われることもあります。
動詞:プロジェクト状態の新しいスナップショットを git の履歴に格納する操作のことで、 索引(index)の現在の状態を現わす新しいコミットを作成し、 新しいコミットを指し示すように HEAD の位置を進めます。
.git/info/grafts
ファイルにより設定します。
$GIT_DIR/refs/heads/
の下に保管されますが、
例外として参照が圧縮(pack)されることもあります。(git-pack-refs(1) 参照)
$GIT_DIR/hooks/
ディレクトリに保管されおり、
ファイル名から`.sample`を削除するだけで利用可能になります。
古いバージョンのgitではスクリプトを実行パーミッションを付与することで
利用可能になります。
$GIT_DIR/objects/
に存在します。
git branch -r
にて参照できます。
—pickaxe-all
オプションは指定したテキストラインを追加または削除した
チェンジセットを全て表示する為に用います。
git-diff(1) 参照。
$GIT_DIR/refs/
に格納されることがあります。
git fetch $URL refs/heads/master:refs/heads/origin
は
"$URL にある master ブランチのヘッド をつかみ、
それを自身のリポジトリ内の origin ブランチのヘッドに保管する" ことを意味します。
また git push $URL refs/heads/master:refs/heads/to-upstream
は
"自身のリポジトリ内の master ブランチのヘッドを $URL の to-upstream ブランチとして
発行する" ことを意味します。git-push(1) も参照してください。
—depth
オプションを付けることで
作成でき、git-fetch(1) により後から履歴をより深くすることもできます。
$GIT_DIR/refs/tags/
内に
保管されます。git の タグは List tag とは無関係です (Lisp tagは、
git の資料内では オブジェクトタイプ と呼んでいます)。
タグはコミットの祖先の 繋がり(chain) 内の特定の位置に印を
つけるために良く使用されます。
Table of Contents
この章は主要コマンドのクイックサマリです; 詳細はこれまでの章の説明を参照してください。
tar ボールから:
$ tar xzf project.tar.gz
$ cd project
$ git init
Initialized empty Git repository in .git/
$ git add .
$ git commit
リモートのリポジトリから:
$ git clone git://example.com/pub/project.git
$ cd project
$ git branch # リポジトリ内のすべてのローカルブランチを表示
$ git checkout test # 作業ディレクトリをブランチ "test" に切り替え
$ git branch new # 現在の HEAD から始まる "new" ブランチを作成
$ git branch -d new # "new" ブランチの削除
現在の HEAD (デフォルト)以外の場所から始まるブランチを作成するには、次のようにします:
$ git branch new test # "test" ブランチのHEADから開始するブランチ "new" を作成
$ git branch new v2.6.15 # タグ v2.6.15 から開始するブランチ "new" を作成
$ git branch new HEAD^ # 最新のコミットの1つ前の位置から分岐するブランチを作成
$ git branch new HEAD^^ # 最新のコミットの2つ前の位置から分岐するブランチを作成
$ git branch new test~10 # ブランチ "test" の10個前の位置から分岐するブランチを作成
新しいブランチの作成と切り替えを同時に行うには:
$ git checkout -b new v2.6.15
複製(clone)元のリポジトリのブランチを更新しテストするには:
$ git fetch # 更新
$ git branch -r # リモートブランチの一覧を表示
origin/master
origin/next
...
$ git checkout -b masterwork origin/master
他のリポジトリ上にあるブランチを取得(fetch)し、 そのブランチに対して名前を付けるには:
$ git fetch git://example.com/project.git theirbranch:mybranch
$ git fetch git://example.com/project.git v2.6.15:mybranch
リポジトリの一覧を管理し、定期的にそこから変更を取得するには:
$ git remote add example git://example.com/project.git
$ git remote # リモートリポジトリの一覧を表示
example
origin
$ git remote show example # 詳細を表示
* remote example
URL: git://example.com/project.git
Tracked remote branches
master
next
...
$ git fetch example # example のブランチを更新
$ git branch -r # すべてのリモートブランチの一覧を表示
$ gitk # 履歴を視覚的にブラウズする
$ git log # すべてのコミットを一覧表示する
$ git log src/ # src/ 配下のファイルを変更している・・・
$ git log v2.6.15..v2.6.16 # v2.6.16 には含まれているが、v2.6.15 には含まれていない・・・
$ git log master..test # test ブランチに含まれているが、master ブランチには含まれていない・・・
$ git log test..master # master ブランチに含まれているが、test ブランチには含まれていない・・・
$ git log test...master # 片方のブランチには含まれているが、両方のブランチには含まれていない・・・
$ git log -S'foo()' # 差分に "foo()" を含んでいる・・・
$ git log --since="2 weeks ago"
$ git log -p # パッチも表示する
$ git show # 最新のコミットを表示
$ git diff v2.6.15..v2.6.16 # 2つのタグ付されたバージョン間の差分
$ git diff v2.6.15..HEAD # 現在の head との差分
$ git grep "foo()" # 作業ディレクトリ内で "foo()" を検索する
$ git grep v2.6.15 "foo()" # 過去のツリー内で "foo()" を検索する
$ git show v2.6.15:a.txt # a.txt の過去のバージョンを表示する
回帰点を探す:
$ git bisect start
$ git bisect bad # 現在のバージョンは bad
$ git bisect good v2.6.13-rc2 # 最新の good なリビジョンを知らせる
Bisecting: 675 revisions left to test after this
# ここからテストを行う
$ git bisect good # このリビジョンが good の場合
$ git bisect bad # このリビジョンが bad の場合
# 回帰点を見つけるまで続ける
誰の変更であるかを git に知らせる:
$ cat >>~/.gitconfig <<\EOF
[user]
name = Your Name Comes Here
email = [email protected]
EOF
次のコミットに含めるファイルの内容を指定し、 コミットする:
$ git add a.txt # 更新したファイル
$ git add b.txt # 新しいファイル
$ git rm c.txt # 削除したファイル
$ git commit
コミットの準備と実行を一度に行う:
$ git commit d.txt # d.txt の最後の内容のみをコミット
$ git commit -a # 管理対象の全ファイルの最新の状態をコミット
$ git merge test # "test" ブランチを現在のブランチにマージ
$ git pull git://example.com/project.git master
# リモートブランチの変更を取得し、マージ
$ git pull . test # git merge test と同じ
パッチをインポートし、エクスポートするには:
$ git format-patch origin..HEAD # HEAD に存在するが origin には存在しない
# 各コミットのパッチを整形する
$ git am mbox # メールボックス "mbox" からパッチをインポートする
他の git リポジトリ内のブランチから変更を取得し、 現在のブランチにマージする:
$ git pull git://example.com/project.git theirbranch
現在のブランチにマージする前にローカルブランチに変更を格納する:
$ git pull git://example.com/project.git theirbranch:mybranch
ローカルブランチ上でコミットした後に、リモートブランチに その内容を反映させる:
$ git push ssh://example.com/project.git mybranch:theirbranch
リモートブランチとローカルブランチの名前が両方とも "test" である場合:
$ git push ssh://example.com/project.git test
よく使用するリモートリポジトリに対して略称を付ける:
$ git remote add example ssh://example.com/project.git
$ git push example test
この仕事は作業中です。
基本要件:
git am
コマンド"
ではなく、"パッチをプロジェクトにインポートする" とする。
全内容を読まなくても重要なトピックスの理解ができるよう、 各章の依存関係を明確にしたグラフの作成方法を考えるべきです。
Documentation/ ディレクトリをスキャンし、他の残項目を探す;特に:
howto に関するもの technical/ の下のいくつか? フック linkgit:git[1] 内のコマンドの一覧
E-mail のアーカイブをスキャンし、他の残項目を探す。
man ページをスキャンし、to see if any assume more background than this manual provides.
Simplify beginning by suggesting disconnected head instead of temporary branch creation?
より良い例を追加する。全てのセクションに使用例を追加するのは 良いアイデアかもしれない;"高度な例" を章末のセクションに追加する?
用語集への相互参照を適切な場所に盛り込む。
shallow clone のドキュメントは? ドラフト版の 1.5.0 のリリースノート に幾らかの説明があります。
他のバージョン管理システムと作業する場合に関するセクションを追加する。 CVS, Subversion も含めて。リリースする tarball にそれらをインポートする。
gitweb に関するより詳しい説明?
配管スクリプトの使用と、スクリプトの書き方に関する章を追加。
交互の、clone -reference, など
リポジトリの破損からの復旧についてより詳しく。参照: http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2 http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2