Git User Manual

Revision History
2024-05-12

Table of Contents

Introduction
1. Repositories and Branches
How to get a Git repository
プロジェクトの別のバージョンをチェックアウトする方法
Understanding History: Commits
Understanding history: commits, parents, and reachability
Understanding history: History diagrams
Understanding history: What is a branch?
Manipulating branches
新しいブランチを作成せずに古いバージョンを調べる
Examining branches from a remote repository
Naming branches, tags, and other references
Updating a repository with git fetch
Fetching branches from other repositories
2. Exploring Git history
How to use bisect to find a regression
Naming commits
Creating tags
Browsing revisions
Generating diffs
Viewing old file versions
Examples
Counting the number of commits on a branch
2つのブランチが同じ履歴を指しているかどうかを確認します
指定の修正を含む最初のタグ付きバージョンを検索する
指定のブランチに固有のコミットを表示する
Creating a changelog and tarball for a software release
指定の内容を含むファイルを参照するコミットを検索する
3. Gitでの開発作業
あなたの名前をGitに教える
Creating a new repository
How to make a commit
適切なコミットメッセージの作成
Ignoring files
How to merge
Resolving a merge
マージ作業中に競合解決の助けを得る
マージを元に戻す
Fast-forward merges
作業ミスを修正する
新しいコミットによって作業ミスを修正する
履歴を巻き戻す事によって作業ミスを修正する
Checking out an old version of a file
作業中の作業を一時的に保存しておく
良好なパフォーマンスの確保
信頼性の確保
リポジトリの破損をチェックする
失われた変更の回復
4. Sharing development with others
Getting updates with git pull
Submitting patches to a project
Importing patches to a project
Public Git repositories
Setting up a public repository
Gitプロトコルを介したGitリポジトリのエクスポート
Exporting a git repository via HTTP
Pushing changes to a public repository
プッシュが失敗した場合の対処方法
Setting up a shared repository
Allowing web browsing of a repository
最小限の履歴でGitリポジトリを取得する方法
Examples
Maintaining topic branches for a Linux subsystem maintainer
5. Rewriting history and maintaining patch series
Creating the perfect patch series
Keeping a patch series up to date using git rebase
Rewriting a single commit
Reordering or selecting from a patch series
Using interactive rebases
Other tools
履歴の書き換えに関する問題
なぜマージコミットをbisectすることが線形履歴をbisectするよりも難しい場合があるのか
6. Advanced branch management
Fetching individual branches
git fetch and fast-forwards
Forcing git fetch to do non-fast-forward updates
Configuring remote-tracking branches
7. Gitの概念
The Object Database
Commit Object
Tree Object
Blob Object
Trust(信頼性)
Tag Object
Gitがオブジェクトを効率的に保存する方法: pack files
Dangling objects(宙ぶらりんオブジェクト)
破損したリポジトリの回復
The index
8. Submodules
サブモジュールの落とし穴
9. Low-level Git operations
Object access and manipulation
The Workflow
working directory → index
index → object database
object database → index
index → working directory
全てを結び付ける
Examining the data
Merging multiple trees
Merging multiple trees, continued
10. Hacking Git
Object storage format
Gitのソースコードを俯瞰する
11. Git用語集
Git解説
A. Git Quick Reference
Creating a new repository
Managing branches
Exploring history
Making changes
Merging
Sharing your changes
Repository maintenance
B. Notes and todo list for this manual
Todo list

Introduction

Gitは高速分散型リビジョン管理システムです。

この説明書は、基本的なUNIXコマンドラインスキルを持っているが、Gitの予備知識がない人でも読めるように設計されています。

Chapter 1, Repositories and BranchesChapter 2, Exploring Git history は、gitを使用してプロジェクトをフェッチして調査する方法を説明しています。これらの章を読んで、ソフトウェアプロジェクトの特定のバージョンをビルド、テスト、デグレを探す方法を学んで下さい。

実際の開発を行う必要がある人は、 Chapter 3, Gitでの開発作業Chapter 4, Sharing development with others も読みたいと思うでしょう。

それ以降の章では、より専門的なトピックについて説明します。

包括的なリファレンスドキュメントは、マニュアルページまたは git-help(1) コマンドから入手できます。 たとえば、コマンド git clone <repo> の場合、次のいずれかを使用できます:

$ man git-clone

または:

$ git help clone

後者では、選択したマニュアルビューアを使用できます。 詳細については git-help(1) を参照してください。

詳しい説明なしでのGitコマンドの概要については、 Appendix A, Git Quick Reference も参照してください。

最後に、このマニュアルをより完全にするためにあなたが貢献する方法については、 Appendix B, Notes and todo list for this manual を参照してください。

Chapter 1. Repositories and Branches

How to get a Git repository

このマニュアルを読むときに、Gitリポジトリを試してみると便利です。

これを取得する最良の方法は、 git-clone(1) コマンドを使用して既存のリポジトリのコピーをダウンロードすることです。あなたがプロジェクトをまだ考えていない場合は、いくつかの興味深い例を以下に示します:

        # Git itself (approx. 40MB download):
$ git clone git://git.kernel.org/pub/scm/git/git.git
        # the Linux kernel (approx. 640MB download):
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

大規模なプロジェクトでは、最初のクローン作成に時間がかかる場合がありますが、クローン作成が必要になるのは1回だけです。

cloneコマンドは、プロジェクトにちなんで名付けられた新しいディレクトリを作成します(上記の例では git または linux)。このディレクトリにcdすると、 working tree と呼ばれるプロジェクトファイルのコピーと、プロジェクトの歴史について全ての情報を含む .git という名前の特別なトップレベルディレクトリが含まれていることがわかります。

プロジェクトの別のバージョンをチェックアウトする方法

Gitは、ファイルのコレクションの履歴を保存するためのツールとして最もよく考えられています。プロジェクトのコンテンツの相互に関連するスナップショットの圧縮されたコレクションとして履歴を保存します。 Gitでは、このような各バージョンは commit と呼ばれます。

これらのスナップショットは、必ずしもすべてが古いものから新しいものへと一列に配置されているわけではなく、作業は、 branches と呼ばれる並行する開発ラインに沿って同時に進行する場合があります。これは、マージおよび分岐する場合があります。

単一のGitリポジトリで、複数のブランチの開発を追跡できます。これは、各ブランチの最新のコミットを参照する heads のリストを保持することによって行われます。 git-branch(1) コマンドは、ブランチヘッド(branch heads)を一覧表示します。

$ git branch
* master

新しくクローンされたリポジトリには、デフォルトで「master」という名前の単一のブランチヘッドが含まれ、作業ディレクトリはそのブランチヘッドによって参照されるプロジェクトの状態に初期化されます。

ほとんどのプロジェクトは tags も使用します。タグはヘッドのようであり、プロジェクトの履歴への参照であり、 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
...

タグは常にプロジェクトの同じバージョンを指すことが期待されますが、開発が進むにつれてヘッドは前進することが期待されます。

これらのバージョンのいずれかを指す新しいブランチヘッドを作成し、 git-switch(1) を使用してチェックアウトします:

$ git switch -c new v2.6.13

作業ディレクトリは、v2.6.13 のタグが付けられたときにプロジェクトが持っていた内容を反映し、 git-branch(1) は2つのブランチを表示し、アスタリスクは現在チェックアウトされているブランチを示します。

$ git branch
  master
* new

あなたがバージョン 2.6.17 を使用することにした場合は、以下のようにして、代わりに v2.6.17 を指すように現在のブランチを変更できます。

$ git reset --hard v2.6.17

注意: 現在のブランチヘッドが履歴内の特定のポイントへの唯一の参照である場合、そのブランチをリセットすると、それが指していた履歴を見つける方法がなくなる可能性があることに注意してください。したがって、このコマンドは慎重に使用してください。

Understanding History: Commits

プロジェクトの履歴のすべての変更は、コミットによって表されます。 git-show(1) コマンドは、現在のブランチでの最新のコミットを表示します:

$ git show
commit 17cf781661e6d38f737f15f53ab552f1e95960d7
Author: Linus Torvalds <torvalds@ppc970.osdl.org.(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) {

ご覧のとおり、コミットは、誰が最新の変更を行ったか、何をしたか、そしてその理由を示しています。

すべてのコミットには16進40桁のIDがあります。それは「オブジェクト名」または「SHA-1 ID」と呼ばれることもあり、「git show」出力の最初の行に表示されます。通常、タグやブランチ名などの短い名前でコミットを参照できますが、この長い名前も役立ちます。最も重要なことは、これはこのコミットにとってグローバルに一意の名前です。したがって、他の誰かにオブジェクト名を(たとえば電子メールで)伝えると、その名前が自分のリポジトリで行うのと同じコミットを参照することが保証されます(彼らのリポジトリにそのコミットがあると仮定します)。オブジェクト名はコミットの内容に対するハッシュとして計算されるため、名前が変更されてなければコミットも変更されてない事が保証されます。

実際、 Chapter 7, Gitの概念 では、ファイルデータやディレクトリの内容など、Gitの履歴に保存されているすべてのものが、その内容のハッシュである名前が付いたオブジェクトに保存されていることがわかります。

Understanding history: commits, parents, and reachability

(プロジェクトの最初のコミットを除く)すべてのコミットには、このコミットの前に何が起こったかを示す親コミットもあります。親の連鎖をたどると、最終的にはプロジェクトの最初に戻ります。

ただし、コミットは単純なリストを形成しません。 Gitを使用すると、開発ラインが分岐してから再収束します。2つの開発ラインが再収束するポイントは「マージ」と呼ばれます。したがって、マージを表すコミットは1つ以上の親を持つことができ、各親はそれぞれがそのポイントにつながる開発ラインの1つで最も最近のコミットを表します。

これがどのように働くを確認する最良の方法は、 gitk(1) コマンドを使用することです。現在Gitリポジトリでgitkを実行し、マージコミットを探すと、Gitが履歴を整理する方法を理解するのに役立ちます。

以下、コミットXがコミットYの祖先である場合、コミットXはコミットYから「到達可能」(reachable)であると言います。同様に、YはXの子孫である、コミットYからコミットXにつながる親のチェーンがあると言えます。

Understanding history: History diagrams

以下のような図を使用して、Gitの履歴を表す場合があります。コミットは「o」として表示され、コミット間の繋がりは、 - および / および \ で描かれた線で示されます。時間は左から右に進みます:

         o--o--o <-- Branch A
        /
 o--o--o <-- master
        \
         o--o--o <-- Branch B

特定のコミットについて話す必要がある場合は、文字「o」を別の文字または数字に置き換えることができます。

Understanding history: What is a branch?

正確にする必要がある場合は、「ブランチ」という言葉を使用して開発ラインを意味し、「ブランチヘッド」(branch head)(または単に「ヘッド」(head))という言葉を使用して、ブランチでの最新のコミットへの参照を意味します。上記の例では、「A」という名前のブランチヘッドは、1つの特定のコミットへのポインターですが、その時点までの3つのコミットの連なった線をすべて「ブランチA」の一部として参照します。

ただし、混乱が生じない場合は、ブランチとブランチヘッドの両方に「ブランチ」という用語を使用することがよくあります。

Manipulating branches

ブランチの作成、削除、および変更は、素早く・簡単です。コマンドの概要は以下のとおりです:

git branch
全てのブランチを一覧表示します。
git branch <branch>
現在のブランチと同じ履歴ポイントを参照して、 <branch> という名前の新しいブランチを作成します。
git branch <branch> <start-point>
<start-point> を参照して、 <branch> という名前の新しいブランチを作成します。これは、ブランチ名やタグ名を使用するなど、任意の方法で指定できます。
git branch -d <branch>
ブランチ `<branch>`を削除します。ブランチがそのアップストリームブランチに完全にマージされていないか、現在のブランチに含まれていない場合、このコマンドは警告とともに失敗します。
git branch -D <branch>
マージされたステータスに関係なく、ブランチ <branch> を削除します。
git switch <branch>
現在のブランチを <branch> にし、 <branch> によって参照されるバージョンを反映するように作業ディレクトリを更新します。
git switch -c <new> <start-point>
<start-point> を参照する新しいブランチ <new> を作成し、それをチェックアウトします。

特殊シンボル記号「HEAD」は、常に現在のブランチを参照するために使用できます。実際、Gitは .git ディレクトリにある HEAD という名前のファイルを使用して、現在のブランチを記憶しています。

$ cat .git/HEAD
ref: refs/heads/master

新しいブランチを作成せずに古いバージョンを調べる

git switch コマンドは通常、ブランチヘッドを想定していますが、 --detach を伴って呼び出されると任意のコミットも受け入れます。たとえば、タグによって参照されるコミットをチェックアウトできます:

$ git switch --detach v2.6.17
Note: checking out 'v2.6.17'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another switch.
(あなたは「切り離されたHEAD」状態にあります。あなたは周りを見回し、
実験的な変更を加えてコミットすることができます。また、別のswitchを実行することで、
他のブランチに影響を与えることなく、この状態で行ったコミットを破棄できます。)

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command again. Example:
(作成したコミットを保持するために新しいブランチを作成する場合は、
switchコマンドで `-c` を再度使用して、(現在または後で)作成できます。例:)

  git switch -c new_branch_name

HEAD is now at 427abfa Linux v2.6.17

次に、HEADはブランチではなくコミットのSHA-1を参照し、 git branch は、あなたがもはやブランチ上にいないことを示します:

$ cat .git/HEAD
427abfa28afedffadfca9dd8b067eb6d36bac53f
$ git branch
* (detached from v2.6.17)
  master

この場合、HEADは「切り離されている」(detached)と言います。

これは、新しいブランチの名前を作成しなくても、 特定のバージョンをチェックアウトする簡単な方法です。 必要に応じて、後でこのバージョンの新しいブランチ(またはタグ)を作成することもできます。

Examining branches from a remote repository

クローン作成時に作成された「master」ブランチは、クローン作成元のリポジトリにあるHEADのコピーです。ただし、そのリポジトリには他のブランチも含まれている可能性があり、ローカルリポジトリには、リモート追跡ブランチ(remote-tracking branch)と呼ばれる、これらのリモートブランチのそれぞれを追跡するブランチが保持されます。これは、 git-branch(1)-r オプションを使用して表示できます:

$ git branch -r
  origin/HEAD
  origin/html
  origin/maint
  origin/man
  origin/master
  origin/next
  origin/seen
  origin/todo

この例では、「origin」はリモートリポジトリ、または略して「リモート」(remote)と呼ばれます。このリポジトリのブランチは、私たちの観点からは「リモートブランチ」と呼ばれます。上記のリモート追跡ブランチは、クローン時にリモートブランチに基づいて作成され、 git fetch (つまり git pull でも)と git push によって更新されます。 詳細については、 the section called “Updating a repository with git fetch” を参照してください。

タグの場合と同じように、これらのリモート追跡ブランチの1つをあなた独自のブランチに構築することをお勧めします:

$ git switch -c my-todo-copy origin/todo

origin/todo を直接チェックアウトして調べたり、1回限りのパッチを作成したりすることもできます。 detached head を参照してください。

注意: 「origin」という名前は、clone元のリポジトリを参照するためにGitがデフォルトで使用する名前にすぎないことに注意してください。

Naming branches, tags, and other references

ブランチやリモート追跡ブランチやタグはすべてコミットへの参照です。すべての参照は、 refs で始まるスラッシュ(/)で区切られたパス名で名前が付けられます。これまで使用してきた名前は、実際には省略形です:

  • ブランチ testrefs/heads/test の略です。
  • タグ v2.6.18refs/tags/v2.6.18 の略です。
  • origin/masterrefs/remotes/origin/master の略です。

フルネームでの指定は、たとえば、同じ名前のタグとブランチが存在する場合に役立つことがあります。

(新しく作成されたrefは、実際にはその名前で指定されたパスで .git/refs ディレクトリに保存されます。ただし、効率上の理由から、単一のファイルにまとめてパックすることもできます。 git-pack-refs(1) を参照してください。)

もう1つの便利な短縮形として、リポジトリの「HEAD」は、そのリポジトリの名前を使用するだけで参照できます。したがって、たとえば、「origin」は通常、「リポジトリ origin のHEADブランチ」の短縮形です。

Gitが参照を調べるするパスの完全なリスト、および同じ省略名を持つ複数の参照がある場合に選択するために使用する順序については、 gitrevisions(7) の「SPECIFYING REVISIONS」セクションを参照してください。

Updating a repository with git fetch

リポジトリのクローンを作成し、独自の変更をいくつかコミットした後、あなたは元のリポジトリで更新を確認することをお勧めします。

引数を指定しない git-fetch コマンドは、あなたのリポジトリにある全てのリモート追跡ブランチを元のリポジトリにある最新バージョンに更新します。それはあなた自身のどのブランチにも影響しません。それはあなたクローンで作成した「master」ブランチでさえ影響しません。

Fetching branches from other repositories

あなたは git-remote(1) を使用して、クローン元以外のリポジトリからブランチを追跡することもできます。

$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
$ git fetch staging
...
From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
 * [new branch]      master     -> staging/master
 * [new branch]      staging-linus -> staging/staging-linus
 * [new branch]      staging-next -> staging/staging-next

新しいリモート追跡ブランチは、 git remote add にて、今回は staging と名付けた省略名で保存されます:

$ git branch -r
  origin/HEAD -> origin/master
  origin/master
  staging/master
  staging/staging-linus
  staging/staging-next

後で git fetch <remote> を実行すると、 <remote> という名前のリモート追跡ブランチが更新されます。

ファイル .git/config を調べると、Gitが新しい一節を追加したことがわかります:

$ cat .git/config
...
[remote "staging"]
        url = git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
        fetch = +refs/heads/*:refs/remotes/staging/*
...

これが、Gitがリモートのブランチを追跡できる理由です。 テキストエディタで .git/config を編集することにより、これらの設定オプションを変更または削除できます。 (詳細については、 git-config(1) の「CONFIGURATION FILE」セクションを参照してください。)

Chapter 2. Exploring Git history

Gitは、ファイルのコレクションの履歴を保存するためのツールとして最もよく考えられています。これは、ファイル階層のコンテンツの圧縮されたスナップショットを、これらのスナップショット間の関係を示す「コミット」とともに保存することによって行われます。

Gitは、プロジェクトの履歴を探索するための非常に柔軟で高速なツールを提供します。

私達は、プロジェクトにバグを導入したコミットを見つけるのに役立つ、ある専用ツールから始めることにします。

How to use bisect to find a regression

あなたのプロジェクトのバージョン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に伝え、テストするために残されたリビジョンの数が毎回約半分に削減されることに注目してください。

(今回は)約13回のテストの後、有罪コミット(guilty commit)のコミット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 visualizegit reset --hard fb47ddb2db を行う代わりに、現在のコミットをスキップすることをGitに伝えたい場合があります:

$ git bisect skip

ただし、この場合、Gitは、最初にスキップされたコミットとその後の不正なコミットの間で、最初の不正なコミットを最終的に判断できない可能性があります。

良いコミットと悪いコミットを区別できるテストスクリプトがある場合は、bisecするプロセスを自動化する方法もあります。この機能およびその他の git bisect 機能の詳細については、 git-bisect(1) を参照してください。

Naming commits

コミットに名前を付けるいくつかの方法をすでに見てきました:

  • 16進40桁のオブジェクト名
  • ブランチ名: 指定のブランチの先頭のコミットを参照します
  • タグ名: 指定のタグが指すコミットを参照します(ブランチとタグは 参照 の特殊なケースです)。
  • HEAD: 現在のブランチのheadを参照します

その他いろいろ: リビジョンに名前を付ける方法の完全なリストについては、 gitrevisions(7) のマニュアルページの「SPECIFYING REVISIONS」セクションを参照してください。 例をいくつか:

$ git show fb47ddb2 # オブジェクト名の最初の数文字は、
                    # 通常、一意に指定するのに十分です。
$ git show HEAD^    # HEADコミットの親
$ git show HEAD^^   # 祖父母(grandparent;親の親)
$ git show HEAD~4   # 曽祖父母(great-great-grandparent)

マージコミットには複数の親が存在する可能性があることを思い出してください。 デフォルトでは、 ^~ はそのコミットにリストされている最初の親を辿りますが、以下のように選択することもできます:

$ 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

Creating tags

特定のコミットを参照するタグを作成することもできます。以下を実行すると

$ git tag stable-1 1b2e1d63ff

あなたは stable-1 を使用して、コミット 1b2e1d63ff を参照できます。

これにより、「軽量」タグ(lightweight tag)が作成されます。タグにコメントを含め、場合によっては暗号で署名する場合は、代わりにタグオブジェクトを作成する必要があります。詳細については、 git-tag(1) のマニュアルページを参照してください。

Browsing revisions

git-log(1) コマンドは、コミットのリストを表示できます。それ自体で、親コミットから到達可能なすべてのコミットが表示されますが、より具体的なリクエストを行うこともできます。

$ git log v2.5..        # commits since (not reachable from) v2.5
$ git log test..master  # commits reachable from master but not test
$ git log master..test  # ...reachable from test but not master
$ git log master...test # ...reachable from either test or master,
                        #    but not both
$ git log --since="2 weeks ago" # commits from the last 2 weeks
$ git log Makefile      # commits which modify Makefile
$ git log fs/           # ... which modify any file under fs/
$ git log -S'foo()'     # commits which add or remove any file data
                        # matching the string 'foo()'

そしてもちろん、これらすべてを組み合わせることができます。 以下は、 Makefile または fs ディレクトリより下の任意のファイル、を触った v2.5 以降のコミットを検索します:

$ git log v2.5.. Makefile fs/

パッチを表示するようにgit logに依頼することもできます:

$ git log -p

その他の表示オプションについては、 git-log(1) のマニュアルページの --pretty オプションを参照してください。

git log は最新のコミットから始まり、親を介して逆方向に機能することに注意してください。 ただし、Gitの履歴には複数の独立した開発ラインが含まれる可能性があるため、コミットがリストされる順序は多少前後する可能性があります。

Generating diffs

git-diff(1) を使用して、任意の2つのバージョン間の差分(diff)を生成できます。

$ git diff master..test

これにより、2つのブランチの先端の間にdiffが生じます。共通の祖先からのdiffを見つけてテストしたい場合は、2つではなく3つのドットを使用できます:

$ git diff master...test

しばしば、あなたが必要なのは差分の代わりにパッチのセットである場合があります。これには、 git-format-patch(1) を使用できます。

$ git format-patch master..test

これは、masterからではなく、testから到達可能な各コミットのパッチを含むファイルを生成します。

Viewing old file versions

あなたは、最初に正しいリビジョンをチェックアウトするだけで、いつでも古いバージョンのファイルを表示できます。ただし、何もチェックアウトせずに古いバージョンの単一ファイルを表示できる方が便利な場合もあります。以下のコマンドはそれを行います:

$ git show v2.5:fs/locks.c

コロン(:)の前にはコミットを指定するものがあり、その後にはGitによって追跡されているファイルへのパスがあります。

Examples

Counting the number of commits on a branch

あなたが origin から分岐して以来、 mybranch に対して行ったコミットの数を知りたいとします:

$ git log --pretty=oneline origin..mybranch | wc -l

あるいは、この種のことは、指定されたすべてのコミットのSHA-1を一覧表示するだけの低レベルのコマンド git-rev-list(1) で行われることがよくあります:

$ git rev-list origin..mybranch | wc -l

2つのブランチが同じ履歴を指しているかどうかを確認します

あなたが、2つのブランチが履歴の同じポイントを指しているかどうかを確認したいとします。

$ git diff origin..master

プロジェクトの内容が2つのブランチで同じであるかどうかがわかります。ただし、理論的には、同じプロジェクトの内容が2つの異なる履歴ルートによって到達された可能性があります。あなたはオブジェクト名を比較できます:

$ git rev-list origin
e05db0fd4f31dde7005f075a84f96b360d05984b
$ git rev-list master
e05db0fd4f31dde7005f075a84f96b360d05984b

または、 ... 演算子は、一方の参照または他方の参照から到達可能なすべてのコミットを選択しますが、両方は選択しないことを思い出してください。ということで以下を実行すると

$ git log origin...master

この2つのブランチが等しい場合、コミットは返されません。

指定の修正を含む最初のタグ付きバージョンを検索する

コミット e05db0fd が特定の問題を修正したことを知っているとします。その修正を含む最も古いタグ付きリリースを見つけたいと思います。

ええ、もちろん、複数の答えがあるかもしれません。コミット e05db0fd の後に履歴が分岐した場合、複数の「最も早い」タグ付きリリースが存在する可能性があります。

あなたは e05db0fd 以降のコミットを視覚的に検査できます:

$ gitk e05db0fd..

または、あなたは git-name-rev(1) を使用できます。これにより、コミットの子孫の1つを指すタグが見つかった場合は、そのコミットに名前が与えられます(訳注:コミットの子孫の1つを指すタグが tags/v1.5.0-rc1 であったのでそこから辿って e05db0fd は tags/v1.5.0-rc1^0~23 という「名前」になった):

$ 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から到達できないコミットのみを出力するためです。

さらに別の方法として、 git-show-branch(1) コマンドは、指定の引数から到達可能なコミットを一覧表示し、その各行の左側にそのコミットが指定のどの引数から到達可能かを示す表示を行います。よって、あなたが以下のようなものを実行する場合

$ 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
...

そしてこれに続く行は以下のようになります

+ ++ [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if
available

これは、e05db0fdが、v1.5.0-rc0からではなく、v1.5.0-rc1とv1.5.0-rc2から到達可能であることを示しています。

指定のブランチに固有のコミットを表示する

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

標準のユーティリティcutとgrepを使用して、ブランチヘッド名だけを取得し、 master を削除できます:

$ 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 などのコミット選択構文の説明については、 gitrevisions(7) を参照してください。)

Creating a changelog and tarball for a software release

git-archive(1) コマンドは、プロジェクトの任意のバージョンからtarまたはzipアーカイブを作成できます。例えば:

$ git archive -o latest.tar.gz --prefix=project/ HEAD

HEADを使用して、各ファイル名の前に project/ が付いたgzip圧縮されたtarアーカイブを作成します。出力ファイル形式は、可能であれば出力ファイル拡張子から推測されます。詳細については git-archive(1) を参照してください。

1.7.7より古いバージョンのGitは、 `tar.gz`形式を認識していません。明示的にgzipを使用する必要があります:

$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz

ソフトウェアプロジェクトの新しいバージョンをリリースする場合は、リリースアナウンスに含める変更ログを同時に作成することをお勧めします。

Linus 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"

そしてそれから、シェルスクリプトが出力したコマンドが正常に見えることを確認した後、出力されたコマンドをカットアンドペーストします。

指定の内容を含むファイルを参照するコミットを検索する

誰かがあなたにファイルのコピーを渡し、与えられた内容を含むようにファイルを変更したコミットを、そのコミットの前と後のどちらで行ったかを尋ねます。 それを知るには、以下のようにします:

$  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) のマニュアルページが役に立つ事でしょう。

Chapter 3. Gitでの開発作業

あなたの名前をGitに教える

コミットを作成する前に、あなた自身をGitに紹介すべきです。これを行う最も簡単な方法は、 git-config(1) を使用することです:

$ git config --global user.name 'Your Name Comes Here'
$ git config --global user.email 'you@yourdomain.example.com'

これにより、あなたのホームディレクトリの .gitconfig という名前のファイルに以下のものが追加されます:

[user]
        name = Your Name Comes Here
        email = you@yourdomain.example.com

構成ファイルの詳細については、 git-config(1)の「CONFIGURATION FILE」セクションを参照してください。ファイルはプレーンテキストなので、お気に入りのエディタで編集することもできます。

Creating a new repository

新しいリポジトリをイチから作成するのは非常に簡単です:

$ mkdir project
$ cd project
$ git init

初期コンテンツ(たとえば tarball)がある場合:

$ tar xzvf project.tar.gz
$ cd project
$ git init
$ git add . # include everything below ./ in the first commit:
$ git commit

How to make a commit

新しいコミットの以下の3つのステップがあります:

  1. あなたのお気に入りのエディタを使用して、作業ディレクトリでいくつかの変更を加えます。
  2. あなたの変更をGitに知らせます。
  3. 手順2でGitに知らせた内容でコミットを作成します。

実際には、あなたが望むかぎりステップ1と2を何度でも行き来したり繰り返したりすることができます。ステップ3でコミットしたい内容を追跡するために、Gitは「インデックス」(index)と呼ばれる特別なステージング領域にツリーのコンテンツのスナップショットを保持します。

最初は、インデックスの内容はHEADの内容と同じになります。したがって、HEADとインデックスの違いを示すコマンド git diff --cached は、その時点では何も出力を生成しないはずです。

インデックスの変更は簡単です:

新規または変更されたファイルの内容でインデックスを更新するには、以下を使用します

$ 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とインデックス間の差異。
                    # 今、コミットを実行したら何がコミットされるかを知る。
$ git diff          # インデックスファイルと作業ディレクトリの違い。
                    # 今、コミットを実行したら、
                    # 何がそのコミットに「含まれない」かを知る。
$ git diff HEAD     # HEADと作業ツリーの違い。
                    # 今、 `commit -a` を実行したら何がコミットされるかを知る。
$ git status        # 上記のファイルごとの簡単な要約。

git-gui(1) を使用して、コミットを作成し、インデックスと作業ツリーファイルの変更を表示し、インデックスに含めるdiffハンクを個別に選択することもできます(diffハンクを右クリックして「Stage Hunk For Commit」を選択します)。

適切なコミットメッセージの作成

必須ではありませんが、変更を要約した1行の短い行(50文字未満)でコミットメッセージを開始し、その後に空行を続け、さらに詳細な説明を続けることをお勧めします。 コミットメッセージの最初の空行までのテキストはコミットタイトルとして扱われ、そのタイトルはGit全体で使用されます。たとえば、 git-format-patch(1) はコミットを電子メールに変換し、コミットタイトルを件名に使用し、コミットタイトル以降を本文として使用します。

Ignoring files

プロジェクトは、Gitで追跡したくないファイルを生成することがよくあります。これには通常、ビルドプロセスによって生成されたファイル、またはエディターによって作成された一時バックアップファイルが含まれます。もちろん、Gitでファイルを追跡しないのは、ファイルに対して「git add」を呼び出さないだけの問題です。しかし、これらの追跡されていないファイルがたくさん横たわっているのはすぐにうっとおしくなります。例えば それらは git add . を実質的に役に立たなくし、 git status の出力に表示され続けます。

あなたは、作業ディレクトリの最上位に .gitignore というファイルを作成し、以下のような内容で特定のファイルを無視するようにGitに指示できます:

# '#'で始まる行はコメントと見なされます。
# foo.txtという名前のファイルはすべて無視してください。
foo.txt
# (生成された) htmlファイルを無視します。
*.html
# 但し、手作業で管理されているfoo.htmlを除きます(無視しません)。
!foo.html
# オブジェクト(*.o)とアーカイブ(*.a)を無視します。
*.[oa]

構文の詳細な説明については、 gitignore(5) を参照してください。作業ツリーの他のディレクトリに .gitignore ファイルを配置することもでき、それらはそれらのディレクトリとその下のサブディレクトリに適用されます。 .gitignore ファイルは、他のファイルと同じようにリポジトリに追加できます .(通常どおり git add .gitignoregit commit を実行するだけです)。 これは、除外パターン(ビルド出力ファイルに一致するパターンなど)の場合に便利です。リポジトリのクローンを作成する他のユーザーにとっても意味があります。<

除外パターンが(特定のプロジェクトのすべてのリポジトリではなく、)特定のリポジトリにのみ影響するようにする場合は、代わりに、リポジトリ内の .git/info/exclude という名前のファイル、または core.excludesFile 構成変数で指定されたファイルに除外パターンを配置できます。一部のGitコマンドは、コマンドラインで直接除外パターンを指定することもできます。詳細については、 gitignore(5) を参照してください。

How to merge

git-merge(1)を使用して、2つに分岐したブランチを再結合できます:

$ git merge branchname

ブランチ branchname を現在のブランチにマージします。

マージは、 branchname で行われた変更と、履歴が分岐してから現在のブランチで最新のコミットまでに行われた変更とを組み合わせることによって行われます。作業ツリーは、この結合がクリーンに実行された場合はマージの結果によって上書きされ、この結合が競合を引き起こした場合は半マージ(half-merged)された結果によって上書きされます。したがって、マージの影響を受けたファイルと同じファイルに変更を加えたコミットされていない変更がある場合、Gitは続行を拒否します。ほとんどの場合、マージする前に変更をコミットする必要があります。コミットしない場合は、 git-stash(1) を使用すると、マージ中にこれらの変更を一旦取り除いて、後で再適用できます。

変更が十分に独立している場合、Gitは自動的にマージを完了し、結果をコミットします(または、fast-forwardの場合は、既存のコミットを再利用します。これについては以下を参照してください)。 一方、競合がある場合—たとえば、リモートブランチとローカルブランチで同じファイルが異なる方法で変更された場合—は警告が表示されます。出力は以下のようになります:

$ 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 merge が編集したものです)。あなたは競合を手動で解決した後(訳注:当該ファイルを手動で編集してセーブして)、新しいファイルを作成するときと同じように、インデックスを更新してGit commitを実行できます。

あなたがgitkを使用して結果のコミットを調べると、2つの親があり、1つは現在のブランチの先頭を指し、もう1つはもう一方のブランチの先頭を指していることがわかるはずです。

Resolving a merge

マージが自動的に解決されない場合、Gitはインデックスと作業ツリーを特別な状態のままにして、マージの解決に必要なすべての情報を提供します。

特に、競合のあるファイルはインデックスでマークされるため、問題を解決してインデックスを更新するまで、 git-commit(1) は失敗します:

$ git commit
file.txt: needs merge

また、 git-status(1) は、これらのファイルを "unmerged" としてリストし、競合のあるファイルの内容には、以下のように競合マーカー(conflict marker)が追加されます(訳注:ファイルの内容が以下のように変更されます):

<<<<<<< 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つの親があることを思い出してください。1つの親は現在のブランチの先端であるHEADになります。もう一方は、MERGE_HEADに一時的に保存されるもう一方のブランチの先端になります。

マージ中、インデックスは各ファイルについて3つのバージョンを保持します。これらの3つの「ファイルステージ」(file stages)はそれぞれ、ファイルの異なるバージョンを表します。

$ git show :1:file.txt  # 両方のブランチの共通の祖先にあるファイル
$ git show :2:file.txt  # the version from HEAD.
$ git show :3:file.txt  # the version from MERGE_HEAD.

git-diff(1) に競合を表示するように要求すると、競合するマージ結果間で3方向のdiffが実行され、ステージ2と3の作業ツリーで、内容がステージ2と3の両方があるハンクのみが混合されて表示されます(つまり、ハンクのマージ結果がステージ2からのみ取得される場合、その部分は競合せず、表示されません。ステージ3についても同じで、ハンクのマージ結果がステージ3からのみ取得される場合、その部分は競合せず、表示されません)。

上記の差分は、file.txt の作業ツリーバージョンと、ステージ2バージョンと、ステージ3バージョンとの間の違いを示しています。したがって、各行の前に単一の + または - を付ける代わりに、2つの桁を使用するようになりました。最初の桁は最初の親と作業ディレクトリのコピーの違いに使用され、2番目の列は2番目の親と 作業ディレクトリのコピーの違いに使用されます。(形式の詳細については、 linkgit: git-diff-files[1] の「COMBINED DIFF FORMAT」セクションを参照してください。)

あなたが編集するなどして、ちゃんと競合を解決した後(ただし、インデックスを更新する前)で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オプションを使用すると、これらのステージのいずれかに対して作業ディレクトリとdiffを取ることができます:

$ git diff -1 file.txt          # diff against stage 1
$ git diff --base file.txt      # same as the above
$ git diff -2 file.txt          # diff against stage 2
$ git diff --ours file.txt      # same as the above
$ git diff -3 file.txt          # diff against stage 3
$ git diff --theirs file.txt    # same as the above.

(デフォルトの)「ort」マージ戦略を使用する場合、 Git は、マージの結果で作業ツリー(working tree)を更新する前に、 書き込もうとしているツリーの状態を反映する AUTO_MERGE という名前の特別な ref を書き込みます。 自動的にマージができなかったテキストの競合を伴う競合パスは、 作業ツリーと同様に、 競合マーカーとともにこのツリーに書き込まれます。 したがって、AUTO_MERGE を git-diff(1) とともに使用すると、 競合を解決するためにこれまでに行った変更を表示できます。 上記と同じ例を使用して競合を解決すると、以下のようになります:

$ git diff AUTO_MERGE
diff --git a/file.txt b/file.txt
index cd10406..8bf5ae7 100644
--- a/file.txt
+++ b/file.txt
@@ -1,5 +1 @@
-<<<<<<< HEAD:file.txt
-Hello world
-=======
-Goodbye
->>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
+Goodbye world

diffを見ると、 競合マーカー(conflict markers)と両方のバージョンの内容行(content line)が削除され、 代わりに「Goodbye world」と書き込まれたことがわかります。

git-log(1)gitk(1) コマンドも、マージのためにに特別な援助を提供します:

$ git log --merge
$ gitk --merge

これらは、HEADまたはMERGE_HEADにのみ存在し、マージされていないファイルにアクセスするすべてのコミットを表示します。

あなたは git-mergetool(1) を使用することもできます。これにより、Emacsやkdiff3などの外部ツールを使用してマージされていないファイルをマージできます。

ファイル内の競合を解決するたびに、以下のようにしてインデックスを更新します:

$ git add file.txt

そうすると、そのファイルのさまざまなステージが「折りたたまれ」(collapsed)、その後、 git diff は(デフォルトでは)そのファイルのdiffを表示しなくなります。

マージを元に戻す

行き詰まり、あきらめて、混乱全体を捨てることにした場合は、いつでもマージ前の状態に戻ることができます

$ git merge --abort

または、あなたが破棄したいマージをすでにコミットしちゃってる場合は以下です

$ git reset --hard ORIG_HEAD

ただし、この最後のコマンドは危険な場合があります。コミット自体が別のブランチにマージされている可能性がある場合は、既にコミット済のコミットを破棄しないでください。これを行うと、さらにマージが混乱する可能性があります。

Fast-forward merges

上記に記載されていない特殊なケースが1つあり、それは異なる方法で処理されます。通常、マージは、マージされた2つの開発ラインのそれぞれを指す2つの親を持つマージコミットをもたらします。

けれども、現在のブランチが他のブランチの祖先である場合(したがって、現在のブランチに存在するすべてのコミットは、すでに他のブランチに含まれています)、Gitは「fast-forward」(早送り)を実行するだけです。現在のブランチのヘッドは、新しいコミットが作成されることなく、マージされたブランチのヘッドを指すように前方に移動されます。

作業ミスを修正する

作業ツリーを台無しにしたが、まだ間違いをコミットしていない場合は、作業ツリー全体を最後にコミットした状態に戻すことができます

$ git restore --staged --worktree :/

コミットしたのを後悔した時、問題を解決するためには、根本的に異なる2つの方法があります:

  1. あなたは、古いコミットによって行われたことをすべて元に戻す新しいコミットを作成できます。 あなたの間違いがすでに公表されているなら、これは正しいことです。
  2. あなたは、履歴を遡って古いコミットを変更できます。 すでに履歴を公開している場合は、これは絶対にやってはいけません。Gitは通常、プロジェクトの「履歴」が変更されることを期待しておらず、履歴が変更されたブランチから繰り返しマージを正しく実行することはできません。

新しいコミットによって作業ミスを修正する

以前の変更を元に戻す新しいコミットを作成するのは非常に簡単です。 git-revert(1) コマンドに不正なコミットへの参照を渡すだけです。たとえば、最新のコミットを元に戻すには、以下のようにします:

$ git revert HEAD

これにより、HEADの変更を元に戻す新しいコミットが作成されます。そしてあなたには新しいコミットのコミットメッセージを編集する機会が与えられます。

以前の変更を元に戻すこともできます。たとえば、最後から2番目を指定します:

$ git revert HEAD^

この場合、Gitは古い変更を元に戻そうとしますが、それ以降に行われた変更はそのまま残します。最近の変更が元に戻す変更と重複する場合は、resolving a mergeの場合と同様に、競合を手動で修正するように求められます。

履歴を巻き戻す事によって作業ミスを修正する

問題のあるコミットが最新のコミットであり、そのコミットをまだ公開していない場合は、git reset を使用して破棄することができます。

あるいは、create a new commitを実行する場合と同じように、作業ディレクトリを編集してインデックスを更新し、間違いを修正することもできます。

$ git commit --amend

これにより、古いコミットが変更を組み込んだ新しいコミットに置き換えられ、最初に古いコミットメッセージの方を編集する機会が与えられます。

繰り返しになりますが、すでに別のブランチにマージされている可能性のあるコミットに対してこれを実行しないでください。 その場合は、代わりに git-revert(1) を使用してください。

履歴のさらに古いコミットを置き換えることも可能ですが、これは別の章で扱う高度なトピックです。

Checking out an old version of a file

以前の悪い変更を元に戻すプロセスでは、 git-restore(1) を使用して特定のファイルの古いバージョンをチェックアウトすると便利な場合があります。

$ git restore --source=HEAD^ path/to/file

これは path/to/fileを、コミットHEAD^にある内容に置き換え、それに一致するようにインデックスを更新します。ブランチは変更されません。

作業ディレクトリを変更せずに古いバージョンのファイルを確認したい場合は、 git-show(1) を使用して確認できます:

$ git show HEAD^:path/to/file

これにより、指定のバージョンのファイルが表示されます。

作業中の作業を一時的に保存しておく

何か複雑なことに取り組んでいる最中に、無関係であるが明白で些細なバグを見つけたとします。取り組みを続行する前にちょいと修正したいと思います。 git-stash(1) を使用して作業の現在の状態を保存(stash;隠す)し、バグを修正した後(または、オプションで別のブランチで修正してから戻ってきた後)、保存(stash)しておいた進行中の変更を取り戻し(unstash)ます。

$ git stash push -m "work in progress for foo feature"

このコマンドは、変更を stash に保存し、現在のブランチの先端に一致するように作業ツリーとインデックスをリセットします。その後、通常どおりに(ちょいと)修正を行うことができます。

... edit and test ...
$ git commit -a -m "blorpl: typofix"

その後、 git stash pop で作業していたのに戻ることができます:

$ git stash pop

良好なパフォーマンスの確保

大規模なリポジトリでは、Gitは圧縮に頼って、履歴情報がディスクまたはメモリ内のスペースを占有しすぎないようにします。 一部のGitコマンドは git-gc(1) を自動的に実行する可能性があるため、手動で実行することを心配する必要はありません。 ただし、大規模なリポジトリの圧縮には時間がかかる場合があるため、都合の悪いときに自動圧縮が開始されないように、明示的に gc を呼び出すことをお勧めします。

信頼性の確保

リポジトリの破損をチェックする

git-fsck(1) コマンドは、リポジトリに対していくつかの自己整合性チェックを実行し、問題があれば報告します。これには時間がかかる場合があります。

$ 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 objects)に関するinfoメッセージが表示されます。これらはリポジトリにまだ存在しているが、どのブランチからも参照されていないオブジェクトであり、しばらくすると gc を使用して削除できます(削除される予定です)。 git fsck --no-dangling を実行してこれらのinfoメッセージを抑制し、errorメッセージだけを表示することができます。

失われた変更の回復

Reflogs

git reset --hardを使用してブランチを変更し、そのブランチが履歴内のその時点への唯一の参照であることに気付いたとします。

幸い、Gitには、各ブランチの以前の値のすべてを保持する「reflog」と呼ばれるログがあります。したがって、この場合でも、たとえば、以下を使用して古い履歴を見つけることができます。

$ git log master@{1}

これは、以前のバージョンの master ブランチヘッドから到達可能なコミットを一覧表示します。 この構文は、 git log だけでなく、コミットを受け入れる任意のGitコマンドで使用できます。 他のいくつかの例:

$ git show master@{2}           # See where the branch pointed 2,
$ git show master@{3}           # 3, ... changes ago.
$ gitk master@{yesterday}       # See where it pointed yesterday,
$ gitk master@{"1 week ago"}    # ... or last week
$ git log --walk-reflogs master # show reflog entries for master

HEAD用には個別のreflogが保持されるため、以下のようにできます

$ git show HEAD@{"1 week ago"}

現在のブランチが1週間前に指し示したものではなく、HEADが1週間前に指し示したものを表示します。これにより、チェックアウトした内容の履歴を確認できます。

reflogはデフォルトで30日間保持され、その後、たぶん刈り取られます(prune)。この刈り取りを制御する方法については、 git-reflog(1) や linkgit:git-gc [1] を参照してください。詳細については、 gitrevisions(7)の「SPECIFYING REVISIONS」セクションを参照してください。

注意: reflogの履歴は、通常のGitの履歴とは大きく異なることに注意してください。通常の履歴は同じプロジェクトで動作するすべてのリポジトリで共有されますが、reflogの履歴は共有されません。reflogは、ローカルリポジトリのブランチが時間の経過とともにどのように変化したかについてのみ通知します。

Examining dangling objects(宙ぶらりんオブジェクトを調べる)

状況によっては、reflogで保存できない場合があります。たとえば、ブランチを削除して、そこに含まれる履歴が必要であることに気付いたとします。reflogも削除されます。ただし、リポジトリをまだ刈り取りしていない場合でも、 git fsck が報告する宙ぶらりんオブジェクトで失われたコミットを見つけることができる場合があります。 詳細については、 the section called “Dangling objects(宙ぶらりんオブジェクト)”を参照してください。

$ git fsck
dangling commit 7281251ddd2a61e38657c827739c57015671a6b3
dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63
dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5
...

あなたは、たとえば、以下のようにして、これらの宙ぶらりんコミットの1つを調べることができます。

$ gitk 7281251ddd --not --all

これを言葉で説明すると、つまり、宙ぶらりんコミットによって記述されたコミット履歴を表示したいが、既存のすべてのブランチとタグによって記述された履歴は表示したくないということです。したがって、失われたコミットから到達可能な履歴を正確に取得できます。(そして、それが1つのコミットだけではない可能性があることに注意してください。これは「開発ラインの先端」(tip of the line)が宙ぶらりんだと報告するだけです。がしかし、全体に渡る深く複雑なコミット履歴が削除された可能性があります。)

履歴を元に戻したい場合は、いつでもそれを指す新しい参照を作成できます。たとえば、新しいブランチを作成できます:

$ git branch recovered-branch 7281251ddd

他のタイプの宙ぶらりんオブジェクト(ブロブとツリー)でもこれは可能であり、宙ぶらりんオブジェクトは他の状況でも発生する可能性があります。

Chapter 4. Sharing development with others

Getting updates with git pull

リポジトリのクローンを作成し、あなた独自の変更をいくつかコミットした後、元のリポジトリで更新を確認し、それらをあなた独自の作業にマージすることをお勧めします。

git-fetch(1) を使用したリモート追跡ブランチを最新の状態に保つ方法や、2つのブランチをマージする方法についてはすでに説明しました。したがって、元のリポジトリのmasterブランチからの変更を以下のようにマージできます:

$ git fetch
$ git merge origin/master

しかしながら、 git-pull(1) コマンドは、これを1ステップで実行する方法を提供します:

$ git pull origin master

実際、 あなたが master をチェックアウトしている場合、このブランチは、元のリポジトリのHEADブランチから変更を取得するように git clone 実行時に構成済です。多くの場合、以下のように単純なもので上記を達成できます

$ git pull

このコマンドは、リモートブランチからリモート追跡ブランチ origin/* に変更をフェッチし、そのデフォルトブランチを現在のブランチにマージします。

より一般的には、リモート追跡ブランチから作成されたブランチは、デフォルトでそのリモート追跡ブランチからプルされます。これらのデフォルトを制御する方法を学ぶためには、 git-config(1)branch.<name>.remotebranch.<name>.merge オプションの説明と、 git-checkout(1)--track オプションの説明を参照してください。

git pull はキーストロークを節約するだけでなく、プル元のブランチとリポジトリを文書化したデフォルトのコミットメッセージを生成するのにも役立ちます。

(注意: ただし、fast-forwardの場合、そのようなコミットは作成されないことに注意してください。代わりに、アップストリームブランチからの最新のコミットを指すようにブランチが更新されます。)

git pull コマンドに「リモートリポジトリ」として . を指定することもできます。この場合、現在のリポジトリからのブランチにマージされます。

$ git pull . branch
$ git merge branch

よってこれらは、ほぼ同等です。

Submitting patches to a project

変更がいくつかある場合、それらを送信する最も簡単な方法は、パッチとして電子メールで送信することです:

まずは git-format-patch(1) の使用例です:

$ git format-patch origin

そうすると、現在のディレクトリに番号付きの一連のファイルを生成します。現在のブランチのパッチごとに1つですが、 origin/HEAD についてのモノはありません。

git format-patch は、最初に送付状(cover letter)を付けることができます。 format-patch がコミットメッセージの後、パッチ自体の前に配置する3つの破線(---)の後に、個々のパッチに関するコメントを挿入できます。送付状の素材を追跡するために git notes を使用する場合、git format-patch --notes は同様の方法でコミットのnoteを含みます。

次に、これらをメールクライアントにインポートして、手動で送信できます。 ただし、一度に送信するものが多い場合は、 git-send-email(1) スクリプトを使用してプロセスを自動化することをお勧めします。まずはプロジェクトのメーリングリストを参照して、パッチを送信するための要件を決定してください。

Importing patches to a project

Gitは、このような電子メールで送信された一連のパッチをインポートするための git-am(1) (amは「apply mailbox」の略)と呼ばれるツールも提供します。パッチを含むすべてのメッセージを順番に1つのメールボックスファイル(patches.mbox など)に保存してから、以下を実行します

$ git am -3 patches.mbox

Gitは各パッチを順番に適用します。競合が見つかった場合は停止し、Resolving a mergeの説明に従って競合を修正できます。( -3 オプションは、Gitにマージを実行するように指示します。ツリーとインデックスを中止してそのままにしておく場合は、そのオプションを省略できます。)

競合解決の結果でインデックスが更新されたら、新しいコミットを作成する代わりに、以下を実行するだけです。

$ git am --continue

こうすると、Gitはコミットを作成し、メールボックスから残りのパッチを適用し続けます。

最終的な結果は、元のメールボックスのパッチごとに1つずつ、一連のコミットになります。各パッチを含むメッセージから作者とコミットログメッセージが取得されます。

Public Git repositories

プロジェクトに変更を送信する別の方法は、 git-pull(1) を使用して、リポジトリから変更をプルするようにそのプロジェクトのメンテナに指示することです。 「Getting updates with git pull」のセクションでは、これを「メイン」リポジトリから更新を取得する方法として説明しましたが、 他の向きでも同様に機能します。

あなたとメンテナの両方が同じマシン上にアカウントを持っている場合は、お互いのリポジトリから直接変更をプルすることができます。リポジトリURLを引数として受け入れるコマンドは、ローカルディレクトリ名も受け入れます:

$ git clone /path/to/repository
$ git pull /path/to/other/repository

または ssh URL を指定できます:

$ git clone ssh://yourhost/~you/repository

開発者が少ないプロジェクトの場合、または少数のプライベートリポジトリを同期する場合は、これで十分な場合があります。

けれども、これを行うためのより一般的な方法は、他のユーザーが変更をプルできるように、別のパブリックリポジトリ(通常は別のホスト上)を維持することです。これは通常、より便利であり、進行中の私的な作業を公に見える作業からきれいに分離することができます。

あなたは個人リポジトリで日常業務を継続しますが、定期的に個人リポジトリから公開リポジトリに変更を「プッシュ」して、他の開発者がそのリポジトリからプルできるようにします。したがって、パブリックリポジトリを持つ他の開発者が1人いる状況での変更のフローは、以下のようになります:

                      you push
your personal repo ------------------> your public repo
      ^                                     |
      |                                     |
      | you pull                            | they pull
      |                                     |
      |                                     |
      |               they push             V
their public repo <------------------- their repo

これを行う方法については、次のセクションで説明します。

Setting up a public repository

あなたの個人リポジトリがディレクトリ ~/proj にあると仮定します。 まず、リポジトリの新しいクローンを作成し、それがパブリックであることを意図していることを git daemon に伝えます:

$ git clone --bare ~/proj proj.git
$ touch proj.git/git-daemon-export-ok

結果のディレクトリ proj.git には、ベアgitリポジトリ(bare;生の、むき出しの)が含まれています。これは、 .git ディレクトリの内容であり、周囲にファイルがチェックアウトされていません。

次に、パブリックリポジトリをホストする予定のサーバーに proj.git をコピーします。 scpやrsyncなど、あなたが最も便利と思うツールを使う事ができます。

Gitプロトコルを介したGitリポジトリのエクスポート

これが推奨の方法です。

もし、他の誰かがサーバーを管理している場合は、リポジトリを配置するディレクトリと、リポジトリが表示される git:// URLを教えてもらってください。そして、以下の「Pushing changes to a public repository」のセクションまでスキップできます。

それ以外の場合は、あなたは git-daemon(1)を開始する必要があります。 ポート9418でリッスンします。デフォルトでは、Gitディレクトリのように見え、マジックファイルgit-daemon-export-okを含む任意のディレクトリへのアクセスが許可されます。一部のディレクトリパスを git daemon の引数として渡すと、さらにそれらのパスへのエクスポートが制限されます。

`gitdaemon`をinetdサービスとして実行することもできます。 詳細については、 git-daemon(1) のマニュアルページを参照してください。(特に「examples」セクションを参照してください。)

Exporting a git repository via HTTP

Gitプロトコルを使用した方がパフォーマンスと信頼性が向上しますが、Webサーバーが設定済のホストでは、HTTPエクスポートの設定が簡単な場合があります。

あなたは、新しく作成したベアGitリポジトリをWebサーバーによってエクスポートされるディレクトリに配置し、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からクローンを作成したりプルしたりできるようになります:

$ git clone http://yourserver.com/~you/proj.git

(HTTPでプッシュすることもできるWebDAVを使用した少し洗練されたセットアップについては、 setup-git-server-over-http も参照してください。)

Pushing changes to a public repository

上記の2つの手法(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 と同様に、これがfast-forwardにならない場合、 git push は文句を言います。 この場合の処理の詳細については、次のセクションを参照してください。

注意: push のターゲットは通常、ベアリポジトリであることに注意してください。チェックアウトされた作業ツリーがあるリポジトリにプッシュすることもできますが、混乱を防ぐために、現在チェックアウトされているブランチを更新するためのプッシュはデフォルトで拒否されます。詳細については、 git-config(1)の receive.denyCurrentBranch オプションの説明を参照してください。

git fetch と同様に、入力を節約するための構成オプションを設定することもできます。たとえば:

$ git remote add public-repo ssh://yourserver.com/~you/proj.git

とすると、 .git/config は以下のようになります:

[remote "public-repo"]
        url = yourserver.com:proj.git
        fetch = +refs/heads/*:refs/remotes/example/*

そうすると、以下のようにするだけで上記と同じプッシュをする事ができます

$ git push public-repo master

詳細については、 git-config(1)remote.<name>.urlbranch.<name>.remoteremote.<name>.push オプションの説明を参照してください。

プッシュが失敗した場合の対処方法

プッシュによってリモートブランチでfast-forwardが発生しない場合は、以下のようなエラーで失敗します:

 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to '...'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

これは、たとえば、以下の場合に発生する可能性があります:

あなたはブランチ名の前にプラス記号(+)を付けることで、 git push に強制的に更新を実行させることができます:

$ git push ssh://yourserver.com/~you/proj.git +master

注意: 上記では + 記号が追加されていることに注意してください。または、以下のように、 -f フラグを使用してリモート更新を強制することもできます:

$ git push -f ssh://yourserver.com/~you/proj.git master

通常、パブリックリポジトリのブランチヘッドが変更されると、以前にポイントしたコミットの子孫を指すように変更されます。この状況でプッシュを強制することにより、あなたはその慣習を破ることになります。(the section called “履歴の書き換えに関する問題”を参照してください。)

それでも、これは進行中のパッチシリーズを公開する簡単な方法を必要とする人々にとって一般的な方法であり、これがブランチの管理方法であることを他の開発者に警告する限り、許容できる妥協案です。

他の人が同じリポジトリにプッシュする権利を持っている場合、この方法でプッシュが失敗する可能性もあります。その場合、正しい解決策は、最初にプル、またはフェッチとそれに続くリベースのいずれかの作業を更新した後にプッシュを再試行することです。詳細については、次のセクション および gitcvs-migration(7) を参照してください。

Setting up a shared repository

共同作業するもう1つの方法は、CVSで一般的に使用されているモデルと同様のモデルを使用することです。このモデルでは、特別な権限を持つ複数の開発者がすべて、単一の共有リポジトリにプッシュおよびプルします。これを設定する方法については、 gitcvs-migration(7) を参照してください。

けれども、共有リポジトリに対するGitのサポートに問題はありませんが、Gitがサポートする、パッチをやり取りし公開リポジトリからプルすることによる共同作業モードは中央共有リポジトリに比べて非常に多くの利点があるため、この操作モードは通常は推奨されません:

  • パッチをすばやくインポートしてマージするGitの機能により、1人のメンテナが非常に高いレートでも受信した変更を処理できます。 そして、それが多すぎる場合、 git pull は、そのメンテナがこのジョブを他のメンテナに委任するための簡単な方法を提供しますが、それでも着信変更のオプションのレビューを許可します。
  • すべての開発者のリポジトリにはプロジェクト履歴の同じ完全なコピーがあるため、特別なリポジトリはありません。また、相互の合意によって、またはメンテナが応答しなくなったり、作業が困難になったりしたために、別の開発者がプロジェクトのメンテナンスを引き継ぐのは簡単です。
  • 「コミッター」の中央グループがないということは、誰が「グループ内側」なのか、誰が「グループ外側」なのかについての正式な決定の必要性が少ないことを意味します。

Allowing web browsing of a repository

gitweb cgiスクリプトを使用すると、Gitをインストールしなくても、プロジェクトのリビジョン、ファイルの内容、ログを簡単に参照できます。 RSS/Atomフィードやblame/annotationの詳細などの機能をオプションで有効にすることができます。

git-instaweb(1) コマンドは、gitwebを使用してリポジトリの閲覧を開始する簡単な方法を提供します。 instawebを使用する場合のデフォルトのサーバーはlighttpdです。

CGIまたはPerl対応サーバーを使用した永続的なインストールのセットアップの詳細については、Gitソースツリーのファイル gitweb/INSTALL および gitweb(1) を参照してください。

最小限の履歴でGitリポジトリを取得する方法

履歴が切り捨てられたshallow cloneは、プロジェクトの最近の履歴のみに関心があり、アップストリームから完全な履歴を取得するのにコストがかかる場合に役立ちます。

shallow cloneは、 git-clone(1)--depth スイッチを指定することで作成されます。深さは後で git-fetch(1)--depth スイッチで変更するか、または、完全な履歴を --unshallow で復元することができます。

shallow clone内でのマージは、マージベースが取得してきた最近の履歴にある限り機能します。そうでなければ、無関係な履歴をマージするようなものになり、大きな競合が発生する可能性があります。この制限により、このようなリポジトリはマージベースのワークフローでの使用に適さなくなる可能性があります。

Examples

Maintaining topic branches for a Linux subsystem maintainer

ここでは、Tony LuckがLinuxカーネルのIA64アーキテクチャーのメンテナーとしてGitをどのように使用しているかを説明します。

彼は2つのパブリックブランチを使用しています:

  • 他の進行中の開発と統合されたときにパッチがある程度の露出を得ることができるように、パッチが最初に配置される「test」ツリー。このツリーは、Andrewが必要なときにいつでも -mm にプルするために使用できます。
  • テスト済のパッチが最終的な健全性チェックのために移動され、それらを上流のLinusに送信(彼に「プルしてください」というリクエストを送信)するための手段としての「release」ツリー

彼はまた、パッチの論理グループをそれぞれ含む一時的なブランチ(トピックブランチ)の組を使用します。

これを設定するには、最初にLinusのパブリックツリーのクローンを作成して作業ツリーを作成します。

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git work
$ cd work

Linusのツリーは、 origin/master という名前のリモート追跡ブランチに保存され、 git-fetch(1) を使用して更新できます。 git-remote(1) を使用して "remote" を設定し、 git-fetch(1) を使用して他のパブリックツリーを追跡し、それらを最新の状態に保つことができます。Chapter 1, Repositories and Branches を参照してください。

そして今、作業するブランチを作成します。 これらは現在の origin/master ブランチの先端から始まり、デフォルトでLinusからの変更をマージするように設定する必要があります(git-branch(1)--track オプションを使用)。

$ git branch --track test origin/master
$ git branch --track release origin/master

これらは、 git-pull(1) を使用して簡単に最新の状態に保つことができます。

$ git switch test && git pull
$ git switch release && git pull

重要な注意点! これらのブランチにローカルの変更がある場合、このマージは履歴にコミットオブジェクトを作成します(ローカルの変更なしで、Gitは単に「fast-forward」マージを実行します)。 多くの人がこれがLinuxの歴史に生み出す「ノイズ」を嫌います。つまり、Linusにリリースブランチからプルするように依頼すると、これらのノイズの多いコミットが永続的な履歴の一部になるため、 release ブランチでこれを気まぐれに行うことは避けてください。

いくつかの構成変数(git-config(1) を参照)を使用すると、両方のブランチをパブリックツリーに簡単にプッシュできます。(the section called “Setting up a public repository” を参照してください。)

$ cat >> .git/config <<EOF
[remote "mytree"]
        url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux.git
        push = release
        push = test
EOF

次に、 git-push(1) を使用して、testツリーとreleaseツリーの両方をプッシュできます:

$ git push mytree

または、以下を使用してtestブランチとreleaseブランチの1つだけをプッシュします:

$ git push mytree test

または

$ git push mytree release

次に、コミュニティからいくつかのパッチを適用します。 このパッチ(または関連するパッチのグループ)を保持するブランチの短い名前を考えて、Linusのブランチの最近の安定したタグから新しいブランチを作成します。あなたのブランチの為に安定したベースを選択すると、以下のようになります: 1) 無関係でおそらく軽くテストされた変更を含めることを避けるのは、あなた自身への助けになります。 2) git bisect を使用して問題を見つける将来のバグハンターを支援する

$ git switch -c speed-up-spinlocks v2.6.35

次に、パッチを適用し、いくつかのテストを実行して、変更をコミットします。 パッチがマルチパートシリーズの場合は、それぞれを個別のコミットとしてこのブランチに適用する必要があります。

$ ... patch ... test  ... commit [ ... patch ... test ... commit ]*

この変更の状態に満足したら、公開する準備として「test」ブランチにマージできます:

$ git switch test && git merge speed-up-spinlocks

ここで競合が発生する可能性はほとんどありません…ただし、この手順にしばらく時間を費やし、アップストリームから新しいバージョンをプルした場合は、競合が発生する可能性があります。

しばらくして、十分な時間が経過してテストが完了したら、同じブランチを「release」ツリーにプルして、アップストリームに進む準備をすることができます。ここで、各パッチ(またはパッチシリーズ)を独自のブランチに保持することの価値を確認できます。 これは、パッチを任意の順序で「release」ツリーに移動できることを意味します。

$ git switch release && git merge speed-up-spinlocks

しばらくすると、いくつかのブランチが作成され、それぞれに適切に選択された名前があるにもかかわらず、ブランチの目的や状態を忘れてしまう可能性があります。特定のブランチにどのような変更があるか思い出すには以下を使用します:

$ git log linux..branchname | git shortlog

testブランチまたはreleaseブランチにすでにマージされているかどうかを確認するには、以下を使用します:

$ git log test..branchname

または

$ git log release..branchname

(このブランチがまだマージされていない場合は、いくつかのログエントリが表示されます。マージされている場合、出力はありません。)

パッチがその大きなサイクルを完了すると(testからreleaseに移動し、Linusによってプルされ、最後にローカルの origin/master ブランチに戻るというサイクル)、この、変更のためのブランチは不要になります。あなたは以下の出力時にそれを悟ります:

$ git log origin..branchname

これの出力が空です。この時点で、このブランチを削除できます:

$ git branch -d branchname

一部の変更は非常に簡単なので、個別のブランチを作成してから、testブランチとreleaseブランチのそれぞれにマージする必要はありません。これらの変更については、 release ブランチに直接適用してから、それを test ブランチにマージしてください。

作業を mytree にプッシュした後、 git-request-pull(1) を使用して、Linusに送信するための、「please pull」リクエストメッセージを準備できます:

$ git push mytree
$ git request-pull origin mytree release

これをさらに単純化するスクリプトのいくつかを以下に示します。

==== update script ====
# Gitツリーのブランチを更新します。更新するブランチがoriginの場合は、kernel.orgからプルします。それ以外の場合は、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 ====
# report on status of my ia64 Git tree

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

Chapter 5. Rewriting history and maintaining patch series

通常、コミットはプロジェクトにのみ追加され、削除または置換されることはありません。 Gitはこの仮定に基づいて設計されており、これに違反すると、(たとえば、)Gitのマージ機構が間違ったことを実行することになります。

しかしながら、この仮定に違反することが役立つ場合があります。

Creating the perfect patch series

例えば、あなたが大規模なプロジェクトの貢献者で、複雑な機能を追加したとき、他の開発者があなたの変更を読んでそれらが正しいかどうかを確認するために、あなたがそれぞれの変更を行った理由を理解しやすい方法で、提示したいとします。

すべての変更をそれぞれ単一のパッチ(またはコミット)として提示すると、一度にすべてを消化するには多すぎると思える事があります。

あなたの仕事の全履歴を提示し、その間違い・訂正・行き詰まりでいっぱいいっぱいにしたら、それを見る人達は閉口するかもしれません。

したがって、理想なのは、通常、以下のような一連のパッチを作成することです:

  1. 各パッチは順番に適用できます。
  2. 各パッチには、変更を説明するメッセージとともに、単一の論理的な変更が含まれています。
  3. デグレを引き起こすパッチはありません。シリーズの最初の部分を適用した後でも、結果のプロジェクトはコンパイルおよび動作し、以前にはなかったバグはありません。
  4. このシリーズ全体は、あなた自身の(これらのパッチシリーズよりおそらくもっともっと厄介であった)開発作業で行ったのと同じ最終結果を生み出します。

これを行うのに役立ついくつかのツールを紹介し、それらの使用方法を説明してから、履歴を書き換えているために発生する可能性のあるいくつかの問題について説明します。

Keeping a patch series up to date using git rebase

リモート追跡ブランチ origin にブランチ mywork を作成し、その先頭にいくつかのコミットを作成するとします:

$ git switch -c mywork origin
$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit
...

myworkへのマージは実行されていないため、 origin の先頭にあるパッチの単純な線形シーケンスです:

 o--o--O <-- origin
        \
         a--b--c <-- 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

しかしながら、myworkの履歴をマージせずに単純な一連のコミットを保持したい場合は、代わりに git-rebase(1) を使用することを選択できます:

$ git switch mywork
$ git rebase origin

これにより、各コミットがmyworkから削除され、一時的にパッチとして( .git/rebase-apply という名前のディレクトリに)保存され、最新バージョンのoriginを指すようにmyworkが更新され、保存された各パッチが新しいmyworkに適用されます。結果は以下のようになります:

 o--o--O--o--o--o <-- origin
                 \
                  a'--b'--c' <-- mywork

その過程で、競合が見つかる可能性があります。その場合、rebaseは停止し、競合を修正できるようになります。 競合を修正した後は、それらのコンテンツに対して git add を使用してインデックスを更新します。そして ` git commit` を実行する代わりに以下を実行します:

$ git rebase --continue

そうすると、Gitは残りのパッチを適用し続けます。

あなたは、いつでも --abort オプションを使用してこの作業を中止し、リベースを開始する前の状態にmyworkを戻すことができます:

$ git rebase --abort

ブランチ内の多数のコミットを並べ替えたり編集したりする必要がある場合は、 git rebase -i を使用する方が簡単な場合があります。これにより、コミットを並べ替えたり押しつぶしたり(squash)、リベース中に個別に編集できるようにマークを付けたりできます。詳細については the section called “Using interactive rebases” を、代替案については the section called “Reordering or selecting from a patch series” を参照してください。

Rewriting a single commit

the section called “履歴を巻き戻す事によって作業ミスを修正する”で、以下のようにして最新のコミットを置き換えることができることを確認しました

$ git commit --amend

これにより、古いコミットが変更を組み込んだ新しいコミットに置き換えられ、最初に古いコミットメッセージを編集する機会が与えられます。これは、最後のコミットでのタイプミスを修正したり、ステージングが不十分なコミットのパッチの内容を調整したりする場合に役立ちます。

履歴のより深いところのコミットを修正する必要がある場合は、対話的rebaseの「edit」命令を使用できます。

Reordering or selecting from a patch series

時々、あなたはあなたの歴史のより深いところにコミットを編集したいです。 1つのアプローチとしては、 git format-patch を使用して一連のパッチを作成し、パッチ適用前の状態にリセットすることです:

$ git format-patch origin
$ git reset --hard origin

そして git-am(1) を使用してパッチを再度適用する前に、必要に応じてパッチを変更、並べ替え、または削除します:

$ git am *.patch

Using interactive rebases

対話的なリベースで一連のパッチを編集することもできます。 これは format-patch を使用してパッチシリーズを並べ替えるのと同じなので、あなたの最も好きなインターフェースを使用してください。

現在のHEADを、そのまま保持する最後のコミットに基づいてリベースします。たとえば、最後の5つのコミットを並べ替える場合は、以下を使用します:

$ git rebase -i HEAD~5

これにより、リベースを実行するために実行する手順のリストが表示されたエディタが開きます。

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

# Rebase c0ffeee..deadbee onto c0ffeee
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

コメントで説明されているように、リストを編集することで、コミットの並べ替え、まとめ、コミットメッセージの編集などを行うことができます。満足したら、リストを保存してエディタを閉じると、リベースが開始されます。

リベースは、 pickedit に置き換えられた場合、またはリスト内のステップが競合を機械的に解決できず、あなたの助けが必要な場合に停止します。競合の編集や解決が完了したら、 git rebase --continue で続行できます。途中で物事があまりにもスパゲッティ(hairy)になっていると判断した場合は、いつでも git rebase --abort で救済することができます。リベースが完了した後でも、reflogを使用して、元のブランチを回復できます。

手順と追加のヒントの詳細については、 git-rebase(1) の「INTERACTIVE MODE」セクションを参照してください。

Other tools

パッチシリーズを維持する目的で存在するStGitなど、他のツールは多数ありますが、それらはこのマニュアルの範囲外です。

履歴の書き換えに関する問題

ブランチの履歴を書き換える際の主な問題は、マージに関係しています。誰かがあなたのブランチをフェッチしてブランチにマージすると、以下のような結果になります:

 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には、新しいヘッドが古いヘッドの更新バージョンであることを知る方法がありません。この状況は、2人の開発者が独立して新旧のヘッドで並行して作業を行った場合とまったく同じように扱われます。この時点で、誰かが新しいヘッドをブランチにマージしようとすると、Gitは古いものを新しいものに置き換えようとするのではなく、2つの(古いものと新しい)開発ラインをマージしようとします。結果は予想外の可能性があります。

あなたは、履歴が書き換えられたブランチを公開することを選択することもできます。他の人がそれらのブランチを調べたりテストしたりするためにそれらのブランチをフェッチできると便利な場合がありますが、そのようなブランチを自分の作業に引き込もうとしないでください。

適切なマージをサポートする、本当に分散開発を行っている場合、公開されたブランチを書き直してはいけません。

なぜマージコミットをbisectすることが線形履歴をbisectするよりも難しい場合があるのか

git-bisect(1) コマンドは、マージコミットを含む履歴を正しく処理します。ただし、検出されたコミットがマージコミットである場合、ユーザーは、そのコミットが問題を引き起こした理由を理解するために、通常よりも慎重に作業する事を心がけて下さい。

以下の履歴があるとします:

      ---Z---o---X---...---o---A---C---D
          \                       /
           o---o---Y---...---o---B

開発の(図の)上側のラインで、コミットXでZに存在する関数の1つの意味が変更されたとします。AにつながるZからのコミットは、関数の実装とZに存在するすべての呼び出し側の両方を変更し、一貫性を保つために、追加するする新しい呼び出し側も同様にします。Aにはバグはありません。

その間に、開発の(図の)下側のラインで、誰かがコミットYでその関数の新しい呼び出し側を追加するとします。BにつながるZからのコミットはすべて、その関数の古いセマンティクスを想定しており、呼び出し元と呼び出し先は互いに一貫しています。Bにもバグはありません。

さらに、この上下2つの開発ラインがCできれいにマージされるため、競合の解決は必要ないとします。

それにもかかわらず、開発の下側のラインで追加された呼び出し側は、開発の上側ラインに導入された新しいセマンティクスに変換されていないため、Cのコードは壊れています。したがって、Dがbad、Zがgoodで、git-bisect(1) がCを原因として特定していることだけを知っている場合、あなたは問題がこのセマンティクスの変更によるものであることをどのようにして解き明かしますか?

git bisect の結果が非マージコミットである場合、通常はそのコミットだけを調べることで問題を発見できるはずです。開発者は、変更を小さな自己完結型のコミットに分割することで、これを簡単に行うことができます。 ただし、上記の場合は、単一のコミットを調べても問題が明らかではないため、これは役に立ちません。代わりに、開発の全体的な俯瞰が必要です。さらに悪いことに、問題のある関数のセマンティクスの変更は、開発の上側ラインの変更のほんの一部にすぎない可能性があります。

一方、Cでマージする代わりに、Aの上にZからBまでの履歴をリベースした場合、以下の線形履歴が得られます:

    ---Z---o---X--...---o---A---o---o---Y*--...---o---B*--D*

ZとD*の間のbisectは、単一の犯人コミットY*にヒットし、Y*が壊れた理由を理解するのはおそらく簡単でしょう。

多くの経験豊富なGitユーザーが、他の方法でマージが多いプロジェクトで作業している場合でも、公開する前に最新のアップストリームバージョンにリベースすることで履歴を線形に保つのは、これが理由の一つです。

Chapter 6. Advanced branch management

Fetching individual branches

git-remote(1) を使用する代わりに、一度に1つのブランチを更新し、それを任意の名前でローカルに保存することもできます:

$ git fetch origin todo:my-todo-work

最初の引数 origin は、最初にクローンを作成したリポジトリからフェッチするようにGitに指示するだけのものです。2番目の引数は、Gitに todo という名前のブランチをリモートリポジトリからフェッチし、それを refs/heads/my-todo-work という名前でローカルに保存するように指示します。

以下のように、他のリポジトリからブランチをフェッチすることもできます。

$ git fetch git://example.com/proj.git master:example-master

これは example-master という名前の新しいブランチを作成し、指定されたURLのリポジトリの master という名前のブランチをその中に保存します。example-master という名前のブランチがすでにある場合は、 example.com の masterブランチによって指定されたコミットにfast-forwardしようとします。これについて詳細は次のセクションを参照下さい:

git fetch and fast-forwards

前の例では、既存のブランチを更新するときに、 git fetch は、新しいコミットを指すようにブランチのコピーを更新する前に、リモートブランチの最新のコミットがブランチのコピーの最新のコミットの子孫であることを確認します。Gitはこのプロセスをfast-forwardと呼んでいます。

fast-forwardになるのは以下のような感じの場合です:

 o--o--o--o <-- old head of the branch
           \
            o--o--o <-- new head of the branch

場合によっては、新しいヘッドが実際には古いヘッドの子孫ではない可能性があります。たとえば、開発者は重大なミスがあったことに気づき、バックトラックすることを決定した可能性があり、その結果、以下のような状況になります:

 o--o--o--o--a--b <-- old head of the branch
           \
            o--o--o <-- new head of the branch

この場合、 git fetch は失敗し、警告を出力します。

その場合でも、次のセクションで説明するように、Gitを強制的に新しいヘッドに更新することができます。 ただし、上記の状況では、それらを指す独自の参照をすでに作成している場合を除き、「a」や「b」というラベルの付いたコミットが失われる可能性があることに注意してください。

Forcing git fetch to do non-fast-forward updates

ブランチの新しいヘッドが古いヘッドの子孫ではないためにgit fetchが失敗した場合は、以下のように更新を強制できます:

$ git fetch git://example.com/proj.git +master:refs/remotes/example/master

注意: + 記号が追加されていることに注意してください。または、以下のように、 -f フラグを使用して、フェッチされたすべてのブランチを強制的に更新することもできます:

$ git fetch -f origin

これは、前のセクションで述べたとおり、ポイントされた古いバージョンのexample/masterが失われる可能性があることに注意してください。

Configuring remote-tracking branches

前述したように、 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 remote add example git://example.com/proj.git

とすると、 .git/config は以下のようになります:

[remote "example"]
        url = git://example.com/proj.git
        fetch = +refs/heads/*:refs/remotes/example/*

注意: 上記の設定は、 git-remote(1) を使用する代わりに、ファイル .git/config を直接編集することでも機能することに注意してください。

リモートを構成した後、以下の3つのコマンドは同じことを行います:

$ git fetch git://example.com/proj.git +refs/heads/*:refs/remotes/example/*
$ git fetch example +refs/heads/*:refs/remotes/example/*
$ git fetch example

上記の構成オプションの詳細については git-config(1) を、refspec構文の詳細については git-fetch(1) を参照してください。

Chapter 7. Gitの概念

Gitは、少数のシンプルで強力なアイデアに基づいて構築されています。これらのアイデアを理解しなくても物事を成し遂げることは可能ですが、理解した上での方がGitをはるかに直感的に理解できるようになります。

私達は、最も重要なオブジェクト データベースインデックスから始めることにします。

The Object Database

私達はすでにthe section called “Understanding History: Commits”で、すべてのコミットが40桁の「オブジェクト名」で保存されているのを見ました。実際、プロジェクトの履歴を表すために必要なすべての情報は、そのような名前のオブジェクトに格納されています。いずれの場合も、名前はオブジェクトのコンテンツのSHA-1ハッシュを使用して計算されます。SHA-1ハッシュは暗号化ハッシュ関数です。それが私たちにとって意味することは、同じ名前の2つの異なるオブジェクトを見つけることは不可能であるということです。これには多くの利点があります:

  • Gitは、名前を比較するだけで、2つのオブジェクトが同一であるかどうかをすばやく判断できます。
  • オブジェクト名はすべてのリポジトリで同じ方法で計算されるため、2つのリポジトリに保存されている同じコンテンツ(same content)は常に同じ名前(same name)で保存されます。
  • Gitは、オブジェクトの名前がまだその内容のSHA-1ハッシュであるかどうかを確認することによって、オブジェクトを読み取るときにエラーを検出できます。

(オブジェクトのフォーマットとSHA-1計算の詳細については、the section called “Object storage format”を参照してください。)

オブジェクトには "blob"、"tree"、"commit"、"tag" の4種類があります。

  • ブロブ オブジェクト("blob" object)は、ファイルデータを格納するために使用されます。
  • ツリーオブジェクト("tree" object)は、1つ以上のブロブオブジェクトをディレクトリ構造に結び付けます。さらに、ツリーオブジェクトは他のツリーオブジェクトを参照できるため、ディレクトリ階層が作成されます。
  • コミットオブジェクト("commit" object)は、そのようなディレクトリ階層をリビジョンの有向非巡回グラフ(directed acyclic graph)に結び付けます。各コミットには、その時点でディレクトリ階層を指定する正確に1つのツリーのオブジェクト名が含まれます。さらにコミットは、そのディレクトリ階層に到達した方法の履歴を説明する「親」コミットオブジェクトを参照します。
  • タグオブジェクト("tag" object)はシンボリックに識別を行い、他のオブジェクトに署名するために使用できます。 これには、オブジェクト名と別のオブジェクトのタイプ、(もちろん)シンボリック名、およびオプションで署名(signature)が含まれます。

オブジェクトタイプの詳細:

Commit Object

「commit」オブジェクトは、ツリーの物理的な状態を、そこに到達した方法とその理由の説明にリンクします。 --pretty=raw オプションを伴った git-show(1) または git-log(1) を使用し、あなたのお気に入りのコミットを調べてみます:

$ git show -s --pretty=raw 2be7fcb476
commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4
tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf
parent 257a84d9d02e90447b149af58b271c19405edb6a
author Dave Watson <dwatson@mimvista.com> 1187576872 -0400
committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700

    Fix misspelling of 'suppress' in docs

    Signed-off-by: Junio C Hamano <gitster@pobox.com>

コミットは以下のように定義されています:

  • ツリー(a tree): ツリーオブジェクトのSHA-1名(以下で定義)。特定の時点でのディレクトリの内容を表します。
  • 親(親達)(parent(s)): プロジェクトの履歴の直前のステップを表す、いくつかのコミットのSHA-1名。上記の例には1つの親があります。マージコミットには複数のコミットが含まれる場合があります。親のないコミットは「ルート」コミット(root commit)と呼ばれ、プロジェクトの初期リビジョンを表します。各プロジェクトには、少なくとも1つのルート(root)が必要です。プロジェクトは複数のルーツ(roots)を持つこともできますが、それは一般的ではありません(または必ずしも良い考えではありません)。
  • 作者(an author): この変更の責任者の名前とその日付。
  • コミッター(a committer):実際にコミットを作成した人の名前と、コミットが行われた日付。 これは、作者(auther)とは異なる場合があります。たとえば、作者がパッチを作成し、それを使用してコミットを作成した人に電子メールで送信した場合です。
  • このコミットを説明するコメント。

注意: コミット自体には、実際に何が変更されたかに関する情報は含まれていないことに注意してください。すべての変更は、このコミットによって参照されるツリーの内容を、その親に関連付けられているツリーと比較することによって計算されます。特に、Gitはファイルの名前変更を明示的に記録しようとはしませんが、パスの変更時に同じファイルデータが存在することで名前変更が示唆される場合を特定できます。 (たとえば、 git-diff(1)-M オプションを参照してください。)

コミットは通常、 git-commit(1) によって作成されます。これにより、親が通常は現在のHEADであり、ツリーが現在インデックスに格納されているコンテンツから取得されるコミットが作成されます。

Tree Object

汎用性の高い git-show(1) コマンドを使用してツリーオブジェクトを調べることもできますが、あなたは 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
...

ご覧のとおり、ツリーオブジェクトにはエントリのリストが含まれており、各エントリにはモード(mode)とオブジェクトタイプ(object type)とSHA-1名(SHA-1 name)と名前(name)があり、名前でソートされています。これは、単一のディレクトリツリーの内容を表します。

オブジェクトタイプは、ファイルの内容を表すブロブ(blob)でも、サブディレクトリの内容を表す別のツリーでもかまいません。 ツリー(tree)とブロブ(blob)は、他のすべてのオブジェクトと同様に、その内容のSHA-1ハッシュによって名前が付けられるため、内容(再帰的にすべてのサブディレクトリの内容を含む)が同一である場合に限り、2つのツリーは同じSHA-1名を持ちます。これにより、Gitは、同じオブジェクト名を持つエントリを無視できるため、2つの関連するツリーオブジェクト間の違いをすばやく判断できます。

(注意: サブモジュールが存在する場合、ツリーにはエントリとしてコミットが含まれる場合もあります。その文書については、Chapter 8, Submodules を参照してください。)

注意: すべてのファイルのモードが644または755であることに注意してください。Gitは実際には実行可能ビット(executable bit)にのみ注意を払います。

Blob Object

あなたは git-show(1) を使用して、ブロブ(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)オブジェクトは、データのバイナリブロブ(バイナリの小さい塊)に他なりません。それは他のものを参照したり、いかなる種類の属性も持っていません。

ブロブはそのデータによって完全に定義されるため、ディレクトリツリー(またはリポジトリの複数の異なるバージョン)内の2つのファイルの内容が同じである場合、それらは同じブロブオブジェクトを共有します。オブジェクトはディレクトリツリー内の場所から完全に独立しており、ファイルの名前を変更しても、ファイルが関連付けられているオブジェクトは変更されません。

注意: <revision>:<path> 構文で git-show(1) を使用すると、任意のツリーまたはブロブオブジェクトを調べることができることに注意してください。これは、現在チェックアウトされていないツリーの内容を参照する場合に役立つことがあります。

Trust(信頼性)

あなたが、とあるソースからブロブのSHA-1名を受け取り、そのコンテンツを別の(おそらく信頼できない)ソースから受け取った場合でも、SHA-1名が一致する限り、それらの内容が正しいと信頼できます。これは、SHA-1が、同じハッシュを生成する異なるコンテンツを見つけることが不可能になるように設計されているためです。

同様に、あなたがディレクトリ全体のコンテンツを信頼するためには、最上位ツリーオブジェクトのSHA-1名を信頼する事だけでよいのです。信頼できるソースからコミットのSHA-1名を受け取った場合、あなたは、そのコミットの親を介して到達可能なコミットの履歴全体、およびそれらのコミットによって参照されるツリーのすべてのコンテンツを簡単に確認できます。

したがって、システムに実際の信頼を導入するには、トップレベルのコミットの名前を含む「1つの」特別なメモにデジタル署名するだけです。あなたのデジタル署名は、あなたがそのコミットを信頼していることを他の人に示し、コミットの履歴の不変性は、彼らが履歴全体を信頼できることを他の人に伝えます。

いいかえると、あなたは、トップコミットの名前(SHA-1ハッシュ)を通知する一通の電子メールを送信するだけでアーカイブ全体を簡単に検証し、GPG/PGPなどを使用してその電子メールにデジタル署名することができます。

これを支援するために、Gitはタグオブジェクトも提供しています…

Tag Object

タグオブジェクトには、オブジェクトとオブジェクトタイプとタグ名とタグを作成した人の名前("tagger")と、 git-cat-file(1) を使用して確認できるように、署名を含む可能性のあるメッセージが含まれます:

$ git cat-file tag v1.5.0
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v1.5.0
tagger Junio C Hamano <junkio@cox.net> 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)を使用して、「軽量タグ」(lightweight tags)を作成することもできます。これは、タグオブジェクトではなく、名前が refs/tags/ で始まる単純な参照です)。

Gitがオブジェクトを効率的に保存する方法: pack files

新しく作成されたオブジェクトは、最初にオブジェクトのSHA-1ハッシュにちなんで名付けられたファイル名のファイルとして作成されます( .git/objects に保存されます)。

残念ながら、プロジェクトに多くのオブジェクトがあると、このシステムは非効率になります。 古いプロジェクトで以下を試してください:

$ git count-objects
6930 objects, 47620 kilobytes

最初の数は、個々のファイルに保持されているオブジェクトの数です。 2つ目は、これらの「緩い」オブジェクト("loose" objects)が占めるスペースの量です。

あなたは、これらの緩いオブジェクトを「パックファイル」(pack file)に移動することで、スペースを節約し、Gitを高速化できます。このファイルには、オブジェクトのグループが効率的な圧縮形式で保存されます。 パックファイルのフォーマットの詳細については、 gitformat-pack(5) をご覧ください。

緩いオブジェクトをパックに入れるには、 git repack を実行するだけです:

$ git repack
Counting objects: 6020, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6020/6020), done.
Writing objects: 100% (6020/6020), done.
Total 6020 (delta 4070), reused 0 (delta 0)

これにより、現在アンパックされているすべてのオブジェクトを含む単一の「パックファイル」が .git/objects/pack/ に作成されます。 あなたは、その後、以下を実行できます:

$ git prune

これは、今やパックに格納済の緩いオブジェクト("loose" objects)をすべて削除します。これにより、参照されていないオブジェクトも削除されます(たとえば、 git reset を使用してコミットを削除すると作成される可能性があります)。 .git/objects ディレクトリを確認するか、以下を実行することで、緩んだオブジェクトがなくなったことを確認できます。

$ git count-objects
0 objects, 0 kilobytes

オブジェクトファイルはなくなりましたが、それらのオブジェクトを参照するコマンドは、以前とまったく同じように機能します。

git-gc(1) コマンドは、パッキング(packing)やプルーニング(pruning)を実行します。このため、通常、あなたに必要な高レベルのコマンドはこのコマンドだけです。

Dangling objects(宙ぶらりんオブジェクト)

git-fsck(1) コマンドは、宙ぶらりんオブジェクト(dangling objects)について文句を言うことがあります。それらは問題ではありません。

宙ぶらりんオブジェクトの最も一般的な原因は、ブランチをリベースしたか、ブランチをリベースした他の誰かからプルしたことです。 Chapter 5, Rewriting history and maintaining patch series を参照してください。 その場合、元のブランチの古いヘッドは、それが指しているすべてのものと同様に、まだ存在しています。ブランチポインタ自体は、別のポインタに置き換えたため、存在していません。

宙ぶらりんオブジェクトを引き起こす他の状況もあります。たとえば、ファイルに「git add」を実行したために「宙ぶらりんブロブ」が発生する可能性があります。実際にコミットして全体像の一部にする前に、そのファイル内の別の何かを変更し、その更新されたものをコミットした場合—この時最初に追加した古い状態は、コミットまたはツリーによってポイントされないため、宙ぶらりんブロブオブジェクトになります。

同様に、 "ort" マージ戦略が実行され、交差マージ(criss-cross merge)があり、したがって複数のマージベースがあることがわかった場合(これはかなり珍しいですが、実際に発生します)、一時的な内部マージベースとして1つの一時的な中間ツリーを生成します。そして、それらは実際のオブジェクトですが、最終結果はそれらを指すことにはならないため、リポジトリ内で「宙ぶらりん」になります(または、交差マージが多数あり、マージベースが3つ以上ある場合は、さらに多くの可能性があります)。

一般的に、宙ぶらりんオブジェクトは心配する必要はありません。それらは非常に役立つことさえあります。あなたが何かを台無しにした場合、宙ぶらりんオブジェクトはあなたがあなたの古いツリーを回復する方法になることができます(たとえば、リベースを行った後で、本当はリベースをやりたくなかった気づきました。持っている宙ぶらりんオブジェクトを確認して、ヘッドを古い宙ぶらりん状態にリセットすることができます)。

コミットの場合は、あなたは以下を使用できます:

$ gitk <dangling-commit-sha-goes-here> --not --all

これは、指定されたコミットから到達可能なすべての履歴を要求しますが、ブランチ、タグ、またはその他の参照からは要求しません。 必要なものであると判断した場合は、いつでも新しい参照を作成できます。たとえば、

$ git branch recovered-branch <dangling-commit-sha-goes-here>

ブロブとツリーの場合、同じことはできませんが、それでも調べることができます。 あなたはただ以下のようにするだけです。

$ git show <dangling-blob/tree-sha-goes-here>

ブロブの内容が何であるか(または、ツリーの場合、基本的にそのディレクトリの ls したのが何であるか)を示すために、それはあなたに宙ぶらりんオブジェクトを残した操作が何であったかについてのいくらかの考えを与えるかもしれません。

通常、宙ぶらりんのブロブやツリーはあまり興味深くはありません。ほとんどの場合、それらは中途のマージベースであるか(手動で修正した競合するマージがあった場合、ブローブにはマージからの競合マーカーが含まれることがよくあります)、または、git fetch で、あなたが ^C か何かで中断すると、オブジェクトデータベースに新しいオブジェクトの一部を残す事が発生しますが、それらは役に立たない単なる宙ぶらりんです。

とにかく、あなたは宙ぶらりん状態に興味がないと確信したら、あなたは到達できないすべてのオブジェクトを削除できます:

$ git prune

そしてそれらは消え去ります。(あなたは、静止した、活動していないリポジトリでのみ git prune を実行すべきです。これは、ファイルシステムのfsckリカバリを実行するようなものです。あなたはfsckを、ファイルシステムがマウントされている間は実行したくないでしょう。同様に git prune は、リポジトリへの同時アクセスの場合でも害を及ぼさないように設計されていますが、紛らわしいメッセージや恐ろしいメッセージを受け取る可能性があります。)

破損したリポジトリの回復

設計上、Gitは信頼できるデータを慎重に扱います。ただし、Git自体にバグがない場合でも、ハードウェアまたはオペレーティングシステムのエラーによってデータが破損する可能性があります。

このような問題に対する最初の防御策はバックアップです。cloneを使用するか、cp、tar、またはその他のバックアップメカニズムを使用して、Gitディレクトリをバックアップできます。

最後の手段として、破損したオブジェクトを検索し、手動で置き換えることができます。その処理でさらに破損してしまう場合に備えて、これを試行する前にリポジトリをバックアップしてください。

私達は、問題は単一の欠落または破損したブロブであると想定しますが、これは解決可能な問題である場合があります。 (欠落しているツリー、特にコミットを回復することは*非常に*困難です)。

開始する前に、破損があることを確認し、 git-fsck(1) を使用して破損がどこにあるかを把握します。これにはけっこう時間がかかる場合があります。

出力が次のようになっているとします:

$ git fsck --full --no-dangling
broken link from    tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
              to    blob 4b9458b3786228369c63936db65827de3cc06200
missing blob 4b9458b3786228369c63936db65827de3cc06200

これで、ブロブ 4b9458b3 が欠落しており、ツリー 2d9263c6 がそれを指していることがわかります。欠落しているブロブブジェクトのコピーが1つだけ見つかった場合は、おそらく他のリポジトリで、それを .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
...

これで、欠落しているブロブが myfile という名前のファイルのデータであることがわかりました。 また、ディレクトリを特定できる可能性もあります。たとえば、「somedirectory」にあるとします。 運が良ければ、不足しているコピーは、作業ツリーの somedirectory/myfile でチェックアウトしたコピーと同じである可能性があります。 git-hash-object(1) を使用して、それが正しいかどうかをテストできます:

$ git hash-object -w somedirectory/myfile

これにより、 somedirectory/myfile の内容を含むブロブオブジェクトが作成および保存され、そのオブジェクトの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」であり、直前のバージョンが「oldsha」であったことを示しています。 また、oldshaから4b9458bへの変更、および4b9458bからnewshaへの変更に伴うコミットメッセージも分かります。

十分に小さな変更をコミットしている場合は、中間状態4b9458bの内容が再構築するのに適している可能性があります。

もしそれが可能でであれば、あなたは不足しているオブジェクトを以下のように再作成できます。

$ git hash-object -w <recreated-file>

そしてあなたのリポジトリは再び正常になります。

(ところで、あなたは fsck を無視する事がでます。以下の通り始めます。

$ git log --raw --all

そして、その全体の中で行方不明のオブジェクト(4b9458b)のshaを探しました。そうするかどうかはあなた次第です—Gitは多くの情報を*持っていて*、それはただ1つの特定のブロブバージョンを失っているだけです。

The index

インデックスは、ソートされたパス名のリストを含むバイナリファイル(通常は .git/index に保持されます)であり、それぞれパーミッションとブロブオブジェクトの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) を実行すると、インデックスからこのツリーオブジェクトが生成され、オブジェクトデータベースに保存され、新しいコミットに関連付けられたツリーオブジェクトとして使用されます。

  2. インデックスを使用すると、インデックスが定義するツリーオブジェクトと作業ツリーをすばやく比較できます。

    これは、エントリごとにいくつかの追加データ(最終変更時刻など)を保存することによって行われます。このデータは上に表示されず、作成されたツリーオブジェクトに保存されませんが、作業ディレクトリ内のどのファイルがインデックスに保存されているものと異なるかをすばやく判断するために使用できるため、Gitはそのようなファイルからすべてのデータを読み取って変更を探す必要がありません。

  3. 異なるツリーオブジェクト間のマージの競合に関する情報を効率的に表すことができるため、各パス名を、関連するツリーに関する十分な情報に関連付けて、それらの間で3方向のマージを作成できます。

    私達はマージ中に、インデックスが1つのファイルの複数のバージョン(「ステージ」と呼ばれる)を格納できることを the section called “マージ作業中に競合解決の助けを得る” で確認しました。上記の git-ls-files(1) 出力の3番目の列はステージ番号であり、マージの競合があるファイルの場合は0以外の値を取ります。

したがって、インデックスは一種の一時的なステージング領域であり、作業中のツリーで埋められます。

あなたがインデックスを完全に吹き飛ばしても、それが記述されたツリーの名前を持っている限り、通常、情報は失われていません。

Chapter 8. Submodules

大規模なプロジェクトは、多くの場合、より小さな自己完結型のモジュールで構成されます。たとえば、組み込みLinuxディストリビューションのソースツリーには、いくつかのローカルな変更を加えたディストリビューション内のすべてのソフトウェアが含まれます。例えば動画プレーヤーは、特定の既知の動作バージョンの解凍ライブラリに対して構築する必要がある場合があります。複数の独立したプログラムがすべて同じビルドスクリプトを共有する場合があります。

一元化されたリビジョン管理システムでは、これは多くの場合、すべてのモジュールを1つのリポジトリに含めることで実現されます。 開発者は、すべてのモジュールをチェックアウトすることも、操作する必要のあるモジュールのみをチェックアウトすることもできます。 物事を移動したり、APIや翻訳を更新したりしながら、1回のコミットで複数のモジュール間でファイルを変更することもできます。

Gitでは部分的なチェックアウトが許可されていないため、Gitでこのアプローチを複製すると、開発者は触れたくないモジュールのローカルコピーを保持する必要があります。巨大なチェックアウトでのコミットは、Gitがすべてのディレクトリをスキャンして変更を確認する必要があるため、あなたが期待するよりもずっと遅くなります。モジュールに多くのローカル履歴がある場合、クローン操作は永遠に終わりません。

プラス面として、分散型リビジョン管理システムは、外部ソースとはるかにうまく統合できます。 一元化されたモデルでは、外部プロジェクトの単一の任意のスナップショットが独自のリビジョン管理からエクスポートされ、ベンダーブランチのローカルリビジョン管理にインポートされます。すべての歴史は隠されています。分散リビジョン管理を使用すると、外部履歴全体のクローンを作成し、開発をより簡単に追跡して、ローカルの変更を再マージできます。

Gitはサブモジュールのサポートにより、リポジトリに外部プロジェクトのチェックアウトをサブディレクトリとして含めることができます。サブモジュールは独自のIDを維持します。サブモジュールサポートは、サブモジュールリポジトリの場所とコミットIDを格納するだけなので、包含するプロジェクト("superproject")を複製する他の開発者は、同一リビジョンのすべてのサブモジュールを簡単に複製できます。スーパープロジェクトの部分的なチェックアウトができます。あなたはサブモジュールのクローンを作成しない、一部、またはすべてを複製するようにGitに指示できます。

git-submodule(1) コマンドは、Git1.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

Note

!スーパープロジェクトを公開する予定がある場合は、ここでローカルURLを使用してはいけません!

git submodule が作成したファイルをご覧下さい:

$ ls -a
.  ..  .git  .gitmodules  a  b  c  d

git submodule add <repo> <path> コマンドは以下のことを行います:

  • サブモジュールを <repo> から現在のディレクトリの下の指定された <path> に複製し、デフォルトでmasterブランチをチェックアウトします。
  • サブモジュールのクローンパスを gitmodules(5) ファイルに追加し、このファイルをインデックスに追加して、コミットの準備をします。
  • サブモジュールの現在のコミットIDをインデックスに追加し、コミットの準備をします。

スーパープロジェクトをコミットします:

$ 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

Note

上記のコミットオブジェクト名はユーザーによって異なりますが、リポジトリのHEADコミットオブジェクト名と一致している必要があります。 git ls-remote ../a を実行することで確認できます。

サブモジュールを引き出すのは2段階のプロセスです。 最初に git submodule init を実行して、サブモジュールリポジトリのURLを .git/config に追加します:

$ git submodule init

次に、 git submodule update を使用してリポジトリのクローンを作成し、スーパープロジェクトでチェックアウトされているコミットを確認します:

$ git submodule update
$ cd a
$ ls -a
.  ..  .git  a.txt

git submodule updategit submodule add の大きな違いの1つは、 git submodule update がブランチの先端ではなく、特定のコミットをチェックアウトすることです。これは、タグをチェックアウトするようなものです。ヘッドが切り離されているため、あなたはブランチで作業していません。

$ git branch
* (detached from d266b98)
  master

サブモジュール内で変更を加えたい場合で、ヘッドが切り離されている場合は、ブランチを作成またはチェックアウトし、変更を加え、サブモジュール内で変更を公開してから、スーパープロジェクトを更新して新しいコミットを参照する必要があります:

$ git switch master

または

$ git switch -c 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では、サブモジュールで新しいファイルや変更されたファイルをコミットすることを簡単に忘れてしまう可能性があります。これにより、サブモジュールの変更をプッシュしないのと同様の問題が発生します。 Git 1.7.0以降、スーパープロジェクトの git statusgit diff はどちらも、サブモジュールが誤ってそのような状態になるのを防ぐために、新しいファイルまたは変更されたファイルが含まれている場合に変更されたものとして表示します。 git diff は、パッチ出力を生成するとき、または --submodule オプションとともに使用するときに、作業ツリー側に -dirty と付加します:

$ git diff
diff --git a/sub b/sub
--- a/sub
+++ b/sub
@@ -1 +1 @@
-Subproject commit 3f356705649b5d566d97ff843cf193359229a453
+Subproject commit 3f356705649b5d566d97ff843cf193359229a453-dirty
$ git diff --submodule
Submodule sub 3f35670..3f35670-dirty:

また、スーパープロジェクトでこれまでに記録されたコミットを超えて、サブモジュールのブランチを巻き戻さないでください。

最初にブランチをチェックアウトせずにサブモジュール内で変更を加えてコミットした場合、 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

Note

その変更は、サブモジュールのreflogに引き続き表示されます。

サブモジュールの作業ツリーにコミットされていない変更がある場合、 git submodule update はそれらを上書きしません。 代わりに、ダーティブランチから切り替えることができないという通常の警告が表示されます。

Chapter 9. Low-level Git operations

高レベルのコマンドの多くは、かつては低レベルのGitコマンドの小さなコアを使用するシェルスクリプトとして実装されていました。これらは、Gitで通常とは異なることを行う場合、またはGitの内部動作を理解する方法としても役立ちます。

Object access and manipulation

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) で確認できますが、通常は両方に git-tag(1) を使用する方が簡単です。

The Workflow

git-commit(1)git-restore(1) などの高レベルの操作は、作業ツリー、インデックス、およびオブジェクトデータベース間でデータを移動することによって機能します。 Gitは、これらの各ステップを個別に実行する低レベルの操作を提供します。

通常、すべてのGit操作はインデックスファイルで機能します。一部の操作はインデックスファイルに対して*純粋に*機能します(インデックスの現在の状態を示す)が、ほとんどの操作は、インデックスファイルとデータベースまたは作業ディレクトリの間でデータを移動します。 したがって、Git操作には4つの主要な組み合わせがあります:

working directory → index

git-update-index(1) コマンドは、作業ディレクトリからの情報でインデックスを更新します。 通常、以下のように、更新するファイル名を指定するだけでインデックス情報を更新します:

$ git update-index filename

ただし、ファイル名のグロブなどでよくある間違いを避けるために、コマンドは通常、まったく新しいエントリを追加したり、古いエントリを削除したりしません。つまり、通常は既存のキャッシュエントリを更新するだけです。

つまり、特定のファイルがもう存在しないこと、または新しいファイルを追加する必要があることをGitに伝えるには、それぞれ --remove フラグと --add フラグを使用する必要があります。

注意! --remove フラグは、そのフラグの後ろに記述されたファイル名が必ずしも削除されることを意味しません。ファイルがディレクトリ構造にまだ存在する場合、インデックスは削除されずに新しいステータスで更新されます。 --remove が意味する唯一のことは、update-indexが削除されたファイルを有効なものと見なし、ファイルが実際に存在しなくなった場合、それに応じてインデックスを更新することです。

特別な場合として、 git update-index --refresh を実行することもできます。これにより、各インデックスの「統計」情報が現在の統計情報と一致するように更新されます。オブジェクトのステータス自体は「更新されません」。オブジェクトが古いバッキングストアオブジェクトとまだ一致するかどうかをすばやくテストするために使用されるフィールドのみが更新されます。

以前紹介した git-add(1) は、 git-update-index(1) の単なるラッパーです。

index → object database

あなたは、プログラムを使用して、現在のインデックスファイルを「ツリー」オブジェクトに書き込みます

$ git write-tree

これにはオプションはありません。現在のインデックスをその状態を説明するツリーオブジェクトのセットに書き出すだけです。そして結果の最上位ツリーの名前が返されます。 そのツリーを使用して、いつでも別の方向からインデックスを再生成できます:

object database → index

オブジェクトデータベースから「ツリー」ファイルを読み取り、それを使用して現在のインデックスにデータを入力します(そして上書きします。後で復元する可能性のある未保存の状態がインデックスに含まれている場合は、やってはいけません!)。通常の操作は以下の通り

$ git read-tree <SHA-1 of tree>

これで、インデックスファイルは前に保存したツリーと同等になります。 ただし、これは「インデックス」ファイルのみです。作業ディレクトリの内容は変更されていません。

index → working directory

ファイルを「チェックアウト」することにより、インデックスから作業ディレクトリを更新します。これはあまり一般的な操作ではありません。通常はファイルを更新したままにし、作業ディレクトリに書き込むのではなく、作業ディレクトリの変更についてインデックスファイルに通知するためです(つまり、 git update-index )。

ただし、新しいバージョンにジャンプしたり、他の人のバージョンをチェックアウトしたり、以前のツリーを復元したりする場合は、インデックスファイルにread-treeを入力してから、結果をチェックアウトする必要があります。

$ git checkout-index filename

または、すべてのインデックスをチェックアウトする場合は、 -a を使用します。

注意! git checkout-index は通常、古いファイルの上書きを拒否するため、古いバージョンのツリーが既にチェックアウトされている場合は、 -f フラグを指定する( -a フラグ または filename より前に指定する)とチェックアウトを強制します。

最後に、これまでの範疇にない半端な話題をいくつか:

全てを結び付ける

git write-tree でインスタンス化したツリーをコミットするには、そのツリーとその背後にある履歴(最も注目すべきは、履歴上、その前にあった「親」のコミットです)を参照する「commit」オブジェクトを作成します。

通常、「コミット」には1つの親があります。つまり、特定の変更が行われる前のツリーの以前の状態です。ただし、2つ以上の親コミットを持つ場合もあります。その場合、そのようなコミットは、他のコミットによって表される2つ以上の以前の状態をまとめる(「マージする」)ため、「マージ」と呼びます。

つまり、「ツリー」は作業ディレクトリの特定のディレクトリ状態を表し、「コミット」は時間内のその状態を表し、どのようにしてそこに到達したかを説明します。

コミット時の状態を説明するツリーと親のリストをオブジェクトに与えることで、コミットオブジェクトを作成します:

$ git commit-tree <tree> -p <parent> [(-p <parent2>)...]

次に、stdinでコミットの理由を示します(パイプまたはファイルからのリダイレクト、またはttyで入力するだけ)。

git commit-tree は、そのコミットを表すオブジェクトの名前を返します。あなたはそれを後で使用できるように保存しておくべきです。 通常、新しい HEAD 状態をコミットし、Gitはその状態に関するメモをどこに保存するかを気にしませんが、実際には、最後にコミットされた状態が何であったかを常に確認できるように、 .git/HEAD が指すファイルに結果を書き込む傾向があります。

以下は、さまざまな部分がどのように組み合わされるかを示す図です:

                     commit-tree
                      commit obj
                       +----+
                       |    |
                       |    |
                       V    V
                    +-----------+
                    | Object DB |
                    |  Backing  |
                    |   Store   |
                    +-----------+
                       ^
           write-tree  |     |
             tree obj  |     |
                       |     |  read-tree
                       |     |  tree obj
                             V
                    +-----------+
                    |   Index   |
                    |  "cache"  |
                    +-----------+
         update-index  ^
             blob obj  |     |
                       |     |
    checkout-index -u  |     |  checkout-index
             stat      |     |  blob obj
                             V
                    +-----------+
                    |  Working  |
                    | Directory |
                    +-----------+

Examining the data

あなたはさまざまなヘルパーツールを使用して、オブジェクトデータベースとインデックスに表示されているデータを調べることができます。すべてのオブジェクトについて、 git-cat-file(1) を使用して、オブジェクトに関する詳細を調べることができます:

$ git cat-file -t <objectname>

上記はオブジェクトのタイプを表示します。タイプを取得すると(もっとも、通常はオブジェクトの配置場所で自明ではありますけれども)、以下を使用できます

$ git cat-file blob|tree|commit|tag <objectname>

とすると、その内容を表示します。 注意! ツリーの内容はバイナリです。よって、その内容を表示するための「git ls-tree」と呼ばれる特別なヘルパーが、内容のバイナリをより読みやすい形式に変換します。

「コミット」オブジェクトは小さく、かなり自明である傾向があるため、それらを確認することは特に有益です。 特に、あなたが .git/HEAD に先頭コミット名を付けるという規則に従うと、以下のことができます。

$ git cat-file commit HEAD

は、先頭コミットが何であったかを表示します。

Merging multiple trees

Gitは、3方向マージを実行するのに役立ちます。これは、マージ手順を数回繰り返すことで、多方向マージに使用できます。 通常の状況では、3方向マージ(2ラインの履歴の調整)を1回だけ実行して結果をコミットしますが、必要に応じて、一度に複数のブランチをマージできます。

3方向マージを実行するには、マージする2つのコミットから開始し、最も近い共通の親(3番目のコミット)を見つけて、これら3つのコミットに対応するツリーを比較します。

マージの「ベース」を取得するには、2つのコミットの共通の親を検索します:

$ git merge-base <commit1> <commit2>

これにより、両方が基づいているコミットの名前が出力されます。これらのコミットのツリーオブジェクトを検索する必要があります。これは、以下の方法で簡単に実行できます

$ git cat-file commit <commitname> | head -1

これは、ツリーオブジェクト情報は常にコミットオブジェクトの最初の行にからです。

マージする3つのツリー(1つの「元の」ツリー(別名共通ツリー)と2つの「結果」ツリー(別名マージするブランチ))が分かったら、インデックスに「マージ」読み取りを行います。 古いインデックスの内容を破棄する必要がある場合には文句を言うので、それらをコミット済であることをことを確認する必要があります。実際、通常は常に最後のコミットに対してマージを実行します(したがって、とにかく現在のインデックスにあるものと一致する必要があります)。

マージを行うには以下を行います

$ git read-tree -m -u <origtree> <yourtree> <targettree>

これにより、とるにたりない些細な3方向マージ操作(trivial merge operations)の全てがインデックスファイルに対して直接実行され、 git write-tree を使用して結果を書き出すことができます。(訳注:trivial;ラテン語「3 本の道が合する所,ありふれたこと」の意がある)

Merging multiple trees, continued

悲しいことに、多くのマージはとるにたりないものでもなく些細なものでもありません。 追加、移動、または削除されたファイルがある場合、または両方のブランチが同じファイルを変更した場合は、「マージエントリ」を含むインデックスツリーが残ります。 このようなインデックスツリーはツリーオブジェクトに書き出すことはできません。結果を書き出す前に、他のツールを使用してこのようなマージの衝突を解決する必要があります。

このようなインデックスの状態は、 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 出力の各行は、ブロブモードビット、ブロブ SHA-1、「ステージ番号」、ファイル名 です。 「ステージ番号」は、Gitがどのツリーから来たかを示す方法です。ステージ1は「$orig」ツリーに対応し、ステージ2は「HEAD」ツリーに対応し、ステージ3は「$target」ツリーに対応します。

ささいなマージは git read-tree -m 内で行われると先に述べました。 たとえば、ファイルが $orig から、 HEAD または $target に変更されなかった場合、または同様に、ファイルが $orig から HEAD に変更され、かつ、 $orig$target に変更された場合、最終的な結果は明らかに HEAD にあるものです。上記の例が示すのは、ファイル hello.c$orig から HEAD に、かつ、 $ orig から $target に別の方法で変更されたことです。これは、以下のように、あなたのお気に入りの3方向マージプログラム、例えば、これら3つのステージのブロブオブジェクトに対する diff3 または merge またはGit独自の merge-file を実行することで解決できます:

$ 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自体でさえも、このために git cat-file を3回実行する人は誰もいません。ステージを一時ファイルに抽出し、その上で「マージ」スクリプトを呼び出す git merge-index プログラムがあります:

$ git merge-index git-merge-one-file hello.c

そして、これが、より高いレベルの git merge -s resolve の実装になっています。

Chapter 10. Hacking Git

この章では、おそらくGit開発者だけが理解する必要があるGit実装の内部の詳細について説明します。

Object storage format

すべてのオブジェクトには、オブジェクトの形式(つまり、オブジェクトの使用方法や他のオブジェクトの参照方法)を識別する、静的に決定された「タイプ」( "type" )があります。 現在、「blob」、「tree」、「commit」、「tag」の4つの異なるオブジェクトタイプがあります。

オブジェクトタイプに関係なく、すべてのオブジェクトは次の特性を共有します: すべてzlibで圧縮され、タイプを指定するだけでなく、オブジェクト内のデータに関するサイズ情報も提供するヘッダーがあります。オブジェクトの名前付けに使用されるSHA-1ハッシュは、 元のデータ+ヘッダー のハッシュであるため、 sha1sum filefile のオブジェクト名と一致しないことに注意してください。

その結果、オブジェクトの一般的な整合性は、オブジェクトの内容やタイプに関係なく常にテストできます。すべてのオブジェクトは、 (a)ハッシュがファイルの内容と一致、かつ、 (b)オブジェクトが、 <ascii type without space> + <space> + <ascii decimal size> + <byte\0> + <binary object data> というシーケンスのバイトストリームへちゃんと解凍できることで検証できます。

構造化オブジェクトは、その構造と他のオブジェクトへの接続をさらに検証できます。 これは通常、すべてのオブジェクトの完全な依存関係グラフを生成し、(ハッシュを介して表面的な一貫性を検証するだけでなく)内部の一貫性を検証する gitfsck プログラムを使用して行われます。

Gitのソースコードを俯瞰する

新しい開発者がGitのソースコードを見つけるのは必ずしも簡単ではありません。この節では、あなたがどこから始めればよいかを示すちょっとしたガイダンスを提供します。

開始するのに適した場所は、最初のコミットの内容から始めることです:

$ git switch --detach e83c5163

最初のリビジョンは、Gitが今日持っているほとんどすべての基礎を築きますが、一度に読むには十分小さくまとまっています。

そのリビジョン以降、用語が変更されていることに注意してください。 たとえば、そのリビジョンのREADMEは、 "changeset" という単語を使用して、今、 commit と呼んでいるものを説明しています。

また、 私たちはそれを「キャッシュ」(cache)と呼ぶのではなく、「インデックス」(index)と呼びます。 ただし、ファイルの名前は依然として read-cache.h です。

あなたが最初のコミットでGitの考え方を把握したなら、あなたはより新しいバージョンをチェックして、 read-cache-ll.hobject.hcommit.h を読み下すべきです。

初期のGitは、(UNIXの伝統でである)非常に単純なプログラムの集まりであり、スクリプトで使用して、出力を別のプログラムにパイプしていました。 新しいものをテストするのが簡単だったので、これは初期開発に適していました。ただし、最近、これらのパーツの多くが組み込みになり、コアの一部が「解放」( "libified" )されました。つまり、パフォーマンス、移植性、およびコードの重複を避けるために libgit.a に配置されました。

今や、 あなたはインデックスが何であるかを知り(そして、それに対応するデータ構造は read-cache-ll.h にある)、 その最初のメンバーである struct object から共通の構造を継承するオブジェクトタイプ(blobs と trees と commits と tags)が幾つかあることを知りました(したがって、 たとえば (struct object *)commit とキャストして、 &commit->object と全く同様の事を行う事ができます。 つまり、 オブジェクト名とフラグを取得を取得できるのです)。

ここで、この情報を吸収するために一服するとしましょう。

次のステップ: オブジェクトの命名に慣れてください。 the section called “Naming commits” を読んでください。 オブジェクトに名前を付ける方法はたくさんあります(リビジョンだけではありません!)。 これらはすべて sha1_name.c で処理されます。 関数 get_sha1() をざっと見てください。特別な処理の多くは、 get_sha1_basic() などの関数によって実行されます。

リビジョンウォーカー。これはGitの最も解放(libified)された部分にあなたがうまく適応できるようにします。

基本から行きましょう。 git log の初期バージョンは、シェルスクリプトでした:

$ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
        LESS=-S ${PAGER:-less}

これはどういう意味か?

git rev-list は、リビジョンウォーカーのオリジナルバージョンであり、「常に」stdoutにリビジョンのリストを出力します。ほとんどの新しいGitコマンドは git rev-list を使用するスクリプトとして開始されるため、これは引き続き機能し続ける必要があります。

gitrev-parse はもはやそれほど重要ではありません。これは、スクリプトによって呼び出されたさまざまな配管コマンドに関連するオプションを除外するためにのみ使用されました。

git rev-list が行ったことのほとんどは、 revision.crevision.h に含まれています。 オプションを rev_info という名前の構造体でラップします。この構造体は、どのリビジョンをどのように歩くか(walk)などを制御します。

git rev-parse`の元々の仕事は、今や関数 `setup_revisions() によって実行されるようになりました。この関数は、リビジョンとリビジョンウォーカーの一般的なコマンドラインオプションを解析します。この情報は、後で使用できるように構造体 rev_info に保存されます。 あなたは setup_revisions() を呼び出した後、独自のコマンドラインオプション解析を行うことができます。 その後、初期化のために prepare_revision_walk() を呼び出す必要があります。その後は、関数 get_revision() を使用してコミットを1つずつ取得できます。

リビジョンウォーキング処理の詳細に興味があるなら cmd_log() の最初の実装をご覧ください。 git show v1.3.0~155^2~4 を呼び出し、その関数までスクロールダウンします( setup_pager() を直接呼び出す必要がなくなったことに注意してください)。

今日では git log は組み込みです。つまり、コマンド git に含まれていることを意味します。ビルトインのソースは、

  • cmd_<bla> と呼ばれる関数で、通常は builtin.h で宣言され、builtin/<bla.c> で定義されます(Gitの古いバージョンでは、代わりに builtin-<bla>.c に含まれていたことに注意してください)。
  • git.ccommands[] 配列のエントリ、および
  • MakefileBUILTIN_OBJECTS エントリ。

しばしば1つのソースファイルに複数のビルトインが含まれている場合があります。たとえば、 cmd_whatchanged()cmd_log() はどちらも、かなりのコードを共有しているため、 builtin/log.c にあります。その場合、それらが存在する .c ファイルのように名前が付けられていないコマンドは、 MakefileBUILT_INS にリストされている必要があります。

`git log`は、元のスクリプトよりもC言語版の方が複雑に見えますが、これにより、柔軟性とパフォーマンスが大幅に向上します。

ここでも、一息入れることをお勧めします。

レッスン3: ソースコードを読んで学習します。マヂで、それはGitの機構について学ぶための最良の方法です(あなたが基本的な概念を知った後であれば、ですが)。

そして、あなたが興味を持っている事について考えてください、例えば、「どうすればそれのオブジェクト名を知っているだけでブロブ(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_sha1()`は成功で0を返します。これは一部の新しいGitハッカーを驚かせるかもしれませんが、UNIXには、さまざまなエラーの場合にさまざまな負の数を返し、成功した場合は0を返すという長い伝統があります。
  • get_sha1() の関数シグネチャの変数 sha1unsigned char * ですが、実際には unsigned char[20] へのポインタであることが期待されています。この変数には、指定されたコミットの160ビットのSHA-1が含まれます。SHA-1が unsigned char * として渡される場合は常に、 char * として渡される16進文字のASCII表現とは対照的に、バイナリ表現であることに注意してください。

コード全体でこれらの両方が表示されます。

さて、肝心の部分は:

        case 0:
                buf = read_object_with_reference(sha1, argv[1], &size, NULL);

これはブロブ(実際にはブロブだけでなくあらゆるタイプのオブジェクト)を読み取る方法です。 関数 read_object_with_reference() が実際にどのように機能するかを知るには、(Gitのリポジトリで 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" を検索し、数行前に戻って、それがコミット 18449ab0 にあることを確認します。このオブジェクト名をコピーして、コマンドラインに貼り付けます。

$ git show 18449ab0

できた!

別の例: スクリプトを組み込みにするために何をすべきかを調べます:

$ git log --no-merges --diff-filter=A builtin/*.c

ほらこのとおり。Gitは実際、Git自体のソースを見つけるのに最適なツールです!

Chapter 11. Git用語集

Table of Contents

Git解説

Git解説

alternate object database
代役(alternate)メカニズムを介して、 リポジトリオブジェクトデータベース の一部を "alternate" (代役) 呼ばれる別のオブジェクトデータベースから継承できます。
bare repository
ベアリポジトリは通常、適切な名前のディレクトリで、接尾辞は .git で、リビジョン管理下にあるファイルのローカルでチェックアウトされたコピーはありません。つまり、通常は非表示の .git サブディレクトリに存在するすべてのGit管理ファイルと制御ファイルは、代わりに repository.git ディレクトリに直接存在し、他のファイルは存在せず、チェックアウトされません。通常、公開リポジトリの発行者は、ベアリポジトリを利用可能にします。
blob object(ブロブオブジェクト)
型の無いオブジェクト。例:ファイルの中身。
branch
「ブランチ」は開発ラインです。ブランチの最新のコミットは、そのブランチの先端(the tip of that branch)と呼ばれます。ブランチの先端はブランチheadによって 参照 され、ブランチで追加の開発が行われると前進します。単一のGit リポジトリは任意の数のブランチを追跡できますが、あなたの作業ツリーはそのうちの1つ(「current branch」(現在のブランチ)または「checked out branch」(チェックアウトされたブランチ))に関連付けられ、 HEAD はそのブランチを指します。
cache
廃止。 index を使ってください。
chain(チェーン,チェイン)
オブジェクトのリスト。リスト内の各 オブジェクト には、その後ろへの参照が含まれます(たとえば、 コミット の後ろはその の1つである可能性があります)。
changeset
"コミット" の BitKeeper/cvsps での言い方です。 Git は変更(change)ではなく状態(state)を保存するため、Gitでコミットを「changeset」と呼ぶのはナンセンスです。
checkout
作業ツリーの全部または一部をオブジェクトデータベースツリーオブジェクトまたはブロブで更新し、作業ツリー全体が新しいブランチを指している場合は、インデックスHEADを更新する操作。
cherry-picking(チェリーピック,チェリーピッキング)
SCM の専門用語では、 "cherry pick" (つまみ食い)とは、一連の変更(通常はコミット)から変更のサブセットを選択し、それらを別のコードベースの上に新しい一連の変更として記録することを意味します。Gitでは、これは "git cherry-pick" コマンドによって実行され、既存のコミットによって導入された変更を抽出し、現在のブランチの先端に基づいてそれを新しいコミットとして記録します。
clean
現在のヘッドが参照するリビジョン作業ツリーが完全に一致(correspond)しているのであれば、その作業ツリーはクリーンです。「dirty」も参照下さい。
commit

名詞として: Gitの履歴における一つのポイント。プロジェクトの履歴全体は、相互に関連する一連のコミットとして表されます。「コミット」という言葉は、他のリビジョン管理システムが「リビジョン」または「バージョン」という言葉を使用するのと同じ場所で、Gitによってよく使用されます。 コミットオブジェクト の省略形としても使用されます。

動詞として(コミットする): インデックスの現在の状態を表す新しいコミットを作成し、その新しいコミットをポイントするようにHEADを進めることにより、プロジェクトの状態の新しいスナップショットをGit履歴に保存する操作。

commit graph concept, representations and usage(コミットグラフの概念、表現、使用法)
ブランチ先端によって参照されるコミットのチェインを使用して、 オブジェクト・データベース内のコミットによって形成されるDAG構造の同義語。 この構造はコミット・グラフの決定版です。 グラフはたとえば "commit-graph" file のように他の方法でも表現できます。
commit-graph file
"commit-graph" という(通常はハイフンでつながれたファイル名の)ファイルは、 コミット・グラフ・ウォークを高速化する commit chart の補足表現です。 "commit-graph" ファイルは、 .git/objects/info ディレクトリまたは代替オブジェクト・データベースの info ディレクトリに保存されます。
commit object
、コミッター、作者、日付、保存されたリビジョンの最上位ディレクトリに対応するツリーオブジェクトなど、特定のリビジョンに関する情報を含むオブジェクト
commit-ish (also committish)(コミットっぽい;コミット風)
コミットオブジェクトまたは、コミットオブジェクトに再帰的に逆参照可能なオブジェクトコミットオブジェクトや、コミットオブジェクトを指すタグオブジェクトや、コミットオブジェクトを指すタグオブジェクトを指すタグオブジェクトなどは全てcommit-ish(commit-ishes)です。
core Git
Gitの基本的なデータ構造とユーティリティ。これは、限定的なソースコード管理ツールのみです。
DAG
有向非巡回グラフ(Directed acyclic graph)。 コミットオブジェクト は、(有向の)親を持ち、コミットオブジェクトのグラフが非巡回(同じ オブジェクト で開始・終了する チェイン はありません)であるため有向非巡回グラフを形成します。
dangling object
(ぶらぶら揺れるオブジェクト)他の到達不能オブジェクトからでも到達できない到達不能オブジェクト。 danglingオブジェクトには、リポジトリ内の任意のrefまたはオブジェクトからの参照はありません。
detached HEAD

通常、HEADブランチの名前を格納し、HEADが示す履歴を操作するコマンドは、HEADが指すブランチの先端につながる履歴を操作します。 ただし、Gitでは、必ずしも特定のブランチの先端ではない任意のコミットチェックアウトすることもできます。このような状態のHEADを「detached」(切り離されている、デタッチされている)と呼びます。

注意: 現在のブランチの履歴を操作するコマンド(たとえば、その上に新しい履歴を構築するための git commit)は、HEADがデタッチされている間も機能することに注意してください。それらは、ブランチに影響を与えることなく、更新された履歴の先端を指すようにHEADを更新します。現在のブランチに関する情報を更新または照会するコマンド(たとえば、現在のブランチが統合するリモートトラッキングブランチを設定する git branch --set-upstream-to)は、この状態で問い合わせる(実際の)現在のブランチがないため、明らかに機能しません。

directory
あなたが "ls" で得られる一覧の事 :-)
dirty
作業ツリーで、現在のブランチに対してコミットされてない変更が含まれている場合、「作業ツリーはダーティーである」と言われます。
evil merge
邪悪なマージとは、どの にも表示されない変更を導入する マージ です。
fast-forward
fast-forward(早送り)は、とあるリビジョンに、その子孫である別のブランチの変更をマージする特殊なタイプのマージです。このような場合、新しいマージコミットを行うのではなく、マージするブランチと同じリビジョンを指すようにブランチを更新するだけです。これは、リモートリポジトリリモート追跡ブランチで頻繁に発生します。
fetch
ブランチをフェッチするということは、リモートリポジトリからブランチのhead refを取得して、ローカルのオブジェクトデータベースに欠落しているオブジェクトを見つけ、そして欠落したオブジェクトを取得することを意味します。 git-fetch(1) も参照してください。
file system
リーナス・トーバルズは当初、Gitをユーザー空間ファイルシステム、つまりファイルとディレクトリを保持するインフラストラクチャとして設計しました。これにより、Gitの効率と速度が保証されました。
Git archive
リポジトリ の同義語(arch people 向け)。
gitfile
実際のリポジトリであるディレクトリを指す、作業ツリーのルートにあるプレーンファイル .git
grafts

graftsは、コミットの偽の祖先情報を記録することで、他の点では異なる2つの開発ラインを結合できます。こうすることで、あるコミットが持つの組を、コミット作成時に記録されたものとは異なるものとして Git に見せかけることができるのです。これは .git/info/grafts ファイルを介して構成されます。

注意: graftsメカニズムは時代遅れであり、リポジトリ間でオブジェクトを転送する際に問題が発生する可能性があることに注意してください。 同じことを行うためのより柔軟で堅牢なシステムについては、 git-replace(1) を参照してください。

hash
Gitの文脈では オブジェクト名 と同義語。
head
ブランチ の先端にある コミット への、 名付けられた参照 です。パックされた参照を使用する場合を除いて、headは $GIT_DIR/refs/heads/ ディレクトリのファイルに保存されます。 (linkgit: git-pack-refs[1] を参照してください。)
HEAD
現在のブランチ。 より詳細に言うと、あなたの作業ツリーは通常、HEADによって参照されるツリーの状態から派生します。HEADは、リポジトリ内のhead達のうちの一つへの参照です。ただし、detached HEADをの場合は、任意のコミットを直接参照しています。
head ref
head の同義語。
hook
いくつかのGitコマンドの通常の実行中に、開発者が機能を追加したりチェックしたりできるようにするオプションのスクリプトを呼び出します。通常、フックを使用すると、コマンドを事前に確認して中止することができ、そしてまた、操作の完了後に事後通知を行うことができます。フックスクリプトは $GIT_DIR/hooks/ ディレクトリにあり、ファイル名から .sample サフィックスを削除するだけで有効になります。以前のバージョンのGitでは、それらを実行可能にする必要がありました。
index
状態情報を含むファイルのコレクションで、その内容はオブジェクトとして保存されます。インデックスは、あなたの作業ツリーの保存バージョンです。正直なところ、これには、マージのときに使用される、作業ツリーの2番目および3番目のバージョンを含めることもできます。
index entry
インデックスに保存されている特定のファイルに関する情報。マージが開始されたが、まだ終了していない場合(つまり、インデックスにそのファイルの複数のバージョンが含まれている場合)、インデックスエントリをマージ解除(unmerge)できます。
master
デフォルトの開発 ブランチ 。 Git リポジトリ を作成するたびに、「master」という名前のブランチが作成され、アクティブなブランチになります。 ほとんどのローカル開発に含まれていますが、これは純粋に慣例によるものであり、必須ではありません。
merge

動詞として: 別のブランチ(あるいは外部のリポジトリから)の内容を現在のブランチに取り込むこと。マージされたブランチが別のリポジトリからのものである場合、これは最初にリモートブランチをフェッチし、次に結果を現在のブランチにマージすることによって行われます。このフェッチ操作とマージ操作の組み合わせは、プル(pull)と呼ばれます。マージは、ブランチが分岐してから行われた変更を識別し、それらすべての変更を一緒に適用する自動プロセスによって実行されます。変更が競合する場合は、マージを完了するために手動による介入が必要になる場合があります。

名詞として: fast-forwardでない限り、マージ成功の結果として、マージされたブランチの先端をに持つ新しいコミットが作成されます。このコミットは「マージコミット」と呼ばれます。または単に「マージ」と呼ばれることもあります。

object(オブジェクト)
Gitの保管ユニット(unit of storage)。その内容による SHA-1 によって一意に識別されます。したがって、オブジェクトを変更することはできません。
object database
「オブジェクト」の組を格納し、個々のオブジェクトはそのオブジェクト名によって識別されます。オブジェクトは通常、 $GIT_DIR/objects/ にあります。
object identifier (oid)
object name (オブジェクト名)の同義語
object name(オブジェクト名)
オブジェクト> の一意の識別子。オブジェクト名は通常、40文字の16進文字列で表されます。一般に <<def_SHA1 とも呼ばれます。
object type(オブジェクトタイプ)
コミット 識別子」または「ツリー 識別子」または「タグ 識別子」または「ブロブ 識別子」のいずれかで、 オブジェクト のタイプを表します。
octopus
3つ以上(more than two)のブランチをマージします
origin
デフォルトの上流(upstream)リポジトリ。ほとんどのプロジェクトには、追跡する上流プロジェクトが少なくとも1つあります。デフォルトでは、「origin」がその目的で使用されます。新しい上流更新分は、 origin/name-of-upstream-branch という名前の リモート追跡ブランチにフェッチされます。これは、 git branch-r を使用して確認できます。
overlay
cp -R が宛先ディレクトリの内容を更新するのと同様に、ファイルを更新して作業ディレクトリに追加するのみで削除を行いません。これは、インデックスまたはツリー風の何か(tree-ish)からファイルをチェックアウトするときのcheckoutのデフォルトモードです。対照的に、オーバーレイなしモード(no-overlay mode)では、 rsync --delete と同様に、ソース側に存在しない追跡ファイルは削除されます。
pack
1つのファイルに圧縮されたオブジェクトの組(スペースを節約するため、またはそれらを効率的に送信するため)。
pack index
パックの内容に効率的にアクセスするのに役立つ、パック内のオブジェクトの識別子とその他の情報のリスト。
pathspec

(パススペック):Gitコマンドでパスを制限するために使用されるパターン。

pathspec は、「git ls-files」や「git ls-tree」や「git add」や「git grep」や「git diff」や「git checkout」や、ツリーまたは作業ツリー(working tree)のサブセットへの操作の為にスコープを制限する他の多くのコマンドの、コマンドラインで使用されます。 パスが現在のディレクトリまたはトップレベルのどちらを基準にしているかについては、 各コマンドのドキュメントを参照してください。 pathspec の構文は以下のとおりです:

  • どのパスもそれ自体と一致します
  • 最後がスラッシュであるpathspecは、ディレクトリプレフィックスを表します。そのpathspecのスコープは、そのサブツリーに制限されています。
  • pathspecの残りの部分は、pathnameの残りの部分のパターンです。 ディレクトリプレフィックスに関連するパスは、 fnmatch(3) を使用してそのパターンと照合されます。特に、 *? はディレクトリ区切り文字と一致させる事ができます。

たとえば、 Documentation/*.jpg は、 Documentation/chapter_1/figure_1.jpg を含む、Documentationサブツリー内のすべての .jpg ファイルと一致します。

コロン(:)で始まるpathspecには特別な意味があります。短い形式では、先頭のコロン(:)の後に0個以上の「魔法記号」(magic signature)(オプションで別のコロン(:)で終了)が続き、残りはパスと照合するパターンです。「魔法記号」は、英数字、グロブ、正規表現の特殊文字でもコロンでもないASCII記号で構成されます。パターンが「魔法記号」シンボルセットに属さず、コロンではない文字で始まる場合、「魔法記号」を終了するオプションのコロンは省略できます。

長い形式では、先頭のコロン(:)の後に開き括弧(() 、0個以上の「魔法単語」(magic words)のコンマ区切りリスト、および閉じ括弧()) が続き、残りは次のパターンです。パスと一致します。

コロンのみのpathspecは、「pathspecが無い」ことを意味します。 この形式は、他のpathspecと組み合わせないでください。

top
魔法単語 top (魔法記号: /)は、サブディレクトリ内からコマンドを実行している場合でも、作業ツリーのルートからパターンを一致させます。
literal
* または ? などのパターンのワイルドカードはリテラル文字として扱われます。
icase
(英文字の)大文字小文字区別せずにマッチ
glob

Gitはパターンを、 FNM_PATHNAMEフラグを指定した fnmatch(3) に消費されるのに適したシェルグロブとして扱います。パターン内のワイルドカードは、パス名内の / と一致しません。 たとえば、「Documentation/*.html」は「Documentation/git.html」と一致しますが、「Documentation/ppc/ppc.html」または「tools/perf/Documentation/perf.html」とは一致しません。

フルパス名と一致するパターンの2つの連続するアスタリスク ** は、特別な意味を持つ場合があります:

  • 先頭の ** の後にスラッシュが続く場合は、すべてのディレクトリで一致することを意味します。たとえば、 **/foo は、パターン foo と同じように、ファイルまたはディレクトリ foo のどこにでも一致します。 **/foo/bar は、ディレクトリ foo の直下にあるファイルまたはディレクトリ bar と一致します。
  • 末尾の /** は、内部のすべてに一致します(matches everything inside)。たとえば、 abc/** は、 .gitignore ファイルの場所を基準にして、ディレクトリ "abc" 内のすべてのファイルと無限の深さで一致します。
  • スラッシュの後に2つの連続するアスタリスクが続く場合、スラッシュは0個以上のディレクトリに一致します。 たとえば、 a/**/ba/ba/x/ba/x/y/b などと一致します。
  • 他の連続するアスタリスクは無効と見なされます。

    グロブ魔法はリテラル魔法と互換性がありません。

attr

attr: の後には、スペースで区切られた「属性要件」(attribute requirements)のリストがあります。パスが一致すると見なされるには、これらすべてを満たす必要があります。これは、通常の非魔法pathspecパターンマッチングに追加されます。 gitattributes(5) 参照。

パスの各属性要件は、以下のいずれかの形式を取ります:

  • ATTR では、属性 ATTR を設定する必要があります。
  • -ATTR では、属性 ATTR が設定されていない必要があります。
  • ATTR = VALUE では、属性 ATTR を文字列 VALUE に設定する必要があります。
  • !ATTR では、属性 ATTR が指定されていない必要があります。

    注意: ツリーオブジェクトと照合する場合、属性は、指定されたツリーオブジェクトからではなく、作業ツリーから取得されることに注意してください。

exclude
パスが非除外pathspecと一致すると、すべての除外pathspec(魔法記号: ! またはその同義語 ^)が実行されます。一致する場合、パスは無視されます。非除外pathspecがない場合、pathspecなしで呼び出されたかのように、除外が結果セットに適用されます。
parent(親)
コミットオブジェクトには、開発ラインで論理的に前にあったもののリスト、つまり親が含まれています(あるいは、前・親が無い場合は空です)。
pickaxe
pickaxe(ピカクス;十字鋤;鶴嘴;つるはし)という用語は、特定のテキスト文字列を追加または削除する変更を選択するのに役立つdiffcoreルーチンのオプションを指します。 --pickaxe-all オプションを使用すると、特定のテキスト行などを導入または削除した完全なチェンジセットを表示するために使用できます。 git-diff(1) を参照してください。
plumbing(配管)
core Git のキュートな呼び方。
porcelain(磁器)
core Gitに依存するプログラムとプログラムスイートのキュートな名前で、コアGitへの高レベルのアクセスを示します。磁器(porcelain)は、配管(plumbing)よりも多くのSCMインターフェースを公開します。
per-worktree ref
グローバルではなく、 worktreeごとのref。これは現在、HEADrefs/bisect/ で始まるすべてのrefのみですが、今後、他の普通でないrefが含まれる可能性があります。
pseudoref
疑似参照(pseudoref)は $GIT_DIR の下にあるファイルのクラスであり、rev-parseしたときrefのように動作しますが、それはgitによって特別扱されます。疑似参照はすべて大文字の名前を持ち、かつ、常にSHA-1とそれに続く空白(whitespace)で構成される行で始まります。したがって、HEADは疑似参照ではありません。なぜならHEADはシンボリック参照である場合があるためです。オプションで、いくつかの追加データが含まれる場合があります。例としては MERGE_HEADCHERRY_PICK_HEAD があります。 per-worktree refs とは異なり、これらのファイルはシンボリックrefにすることはできず、reflogを含めることはできません。 また、通常のref更新機構を使用して更新することもできません。代わりに、ファイルに直接書き込むことによって更新されます。ただし、それらはrefであるかのように読み取ることができるため、 git rev-parse MERGE_HEAD は機能します。
pull
ブランチをプルするとは、それをフェッチマージすることを意味します。 git-pull(1) も参照してください。
push
ブランチをプッシュするとは、リモートリポジトリからブランチのヘッド参照を取得し、それがブランチのローカルヘッド参照の祖先であるかどうかを確認し、そしてその場合ローカルヘッド参照から到達可能であり、かつ、リモートリポジトリに欠落しているすべてのオブジェクトを、リモートオブジェクトデータベースに持っていき、リモートヘッド参照を更新します。リモートヘッドがローカルヘッドの祖先でない場合、プッシュは失敗します。
reachable(到達可能)
特定のコミットのすべての祖先は、その特定のコミットから到達可能(reachable)であると言われます。より一般的には、タグ付けしたものだったり、親またはツリーへのコミットオブジェクトだったり、ツリーに含まれるツリーやブロブだったり、をたどるチェーンによって、あるオブジェクトから別のオブジェクトに到達できる場合に到達が可能です。
reachability bitmaps
到達可能性ビットマップは、 パックファイル内またはマルチパック・インデックス(MIDX)内の選択されたコミットの組の 到達可能性 に関する情報を格納し、 オブジェクトの検索を高速化します。 そのビットマップは ".bitmap" ファイルに保存されます。 リポジトリでは、最大 1 つのビットマップ・ファイルを使用できます。 ビットマップ・ファイルは、 1 つのパック、 またはリポジトリのマルチパック・インデックス(存在する場合)のいずれかに属することができます。
rebase
一連の変更をブランチから別のベースに再適用し、そのブランチのヘッドを再適用した結果にリセットします。
ref

オブジェクト名または別のrefを指す refs/ で始まる名前(例: refs/heads/master;別のrefを指すrefは シンボリックref と呼ぶ)。便宜上、Gitコマンドの引数として使用する場合は refs/ を省略できる場合があります。詳細については gitrevisions(7) を参照してください。 refs は repository に保存されます。

ref名前空間は階層的です。さまざまなサブ階層がさまざまな目的で使用されます(たとえば、 refs/heads/ 階層はローカルブランチを表すために使用されます)。

refs/ で始まらない特別な目的のrefがいくつかあります。最も注目すべき例は HEAD です。

reflog
reflogは、refのローカルの「履歴」を示します。 つまり、このリポジトリの最後の3番目のリビジョンが何であったか、およびこのリポジトリの昨日の午後9時14分時点での「現在の状態」が何であったかを知ることができます。詳細については git-reflog(1) を参照してください。
refspec
「refspec」は、フェッチプッシュによって使用され、リモートrefとローカルrefの間のマッピングを記述します。
remote repository
同じプロジェクトを追跡するために使用されるが、別の場所にあるリポジトリ。リモートと通信するには、フェッチまたはプッシュを参照してください。
remote-tracking branch
別のリポジトリからの変更を追跡するために使用されるref。 これは通常、 refs/remotes/foo/bar のように見え(「foo」という名前のリモートで「bar」という名前のブランチを追跡することを示します)、構成されたフェッチrefspecの右側(right-hand-side)に一致します。リモート追跡ブランチには、直接の変更を含めたり、ローカルコミットを行ったりしないでください。
repository
<< def_ref,refs>>のコレクションと、refから到達可能なすべてのオブジェクトを含むオブジェクトデータベース。1つまたは複数の磁器コマンドからのメタデータが付随している可能性があります。リポジトリは、代替メカニズムを介してオブジェクトデータベースを他のリポジトリと共有できます。
resolve
失敗した自動 マージ が残したものを手動で修正する操作。
revision
コミット (名詞) の同義語
rewind(巻き戻し)
開発の一部を破棄する、つまり、 head を以前の リビジョン に割り当てる。
SCM
Source code management (tool).
SHA-1
セキュアハッシュアルゴリズム1(Secure Hash Algorithm 1);暗号化ハッシュ関数。 Git界隈ではオブジェクト名の同義語として使用されます。
shallow clone
ほとんどの場合 shallowリポジトリ の同義語ですが、この言い方は、 git clone --depth=... コマンドを実行して作成されたこと明言したものです。
shallow repository
浅いリポジトリ(shallow repository)には不完全な履歴があり、そのコミットの一部では親が削除されて(cauterized away)います(言い換えると、Gitは、コミットオブジェクトに記録があっても、これらのコミットには親がないふりをするように指示されます)。これは、アップストリームで記録された実際の履歴がはるかに大きい場合でも、プロジェクトの最近の履歴のみに関心がある場合に役立つことがあります。浅いリポジトリは、 git-clone(1)--depth オプションを指定することで作成され、その履歴は後で git-fetch(1) で深めることができます。
stash entry
ダーティ な作業ディレクトリの内容とインデックスを、将来の再利用のために一時的に保存するのに使用される オブジェクト
submodule
とあるリポジトリの内部で、それとは別個のプロジェクトの履歴を保持する リポジトリ (ここで、その、とあるリポジトリを スーパープロジェクト と呼びます)。
superproject
作業ツリー内の他のプロジェクトのリポジトリを サブモジュール として参照する リポジトリ 。 スーパープロジェクトは、含まれているサブモジュールのコミットオブジェクトの名前を知っています(ただし、そのコピーは保持していません)。
symref
シンボリックref: SHA-1 ID自体を含む代わりに「ref: refs/some/thing」の形式であり、参照されると、この参照を再帰的に逆参照します。 <<def_HEAD,HEAD>> はsymrefの代表的な例です。シンボリックrefは git-symbolic-ref(1) コマンドで操作されます。
tag
任意のタイプのオブジェクトを指す refs/tags/ 名前空間の下のref(通常、タグは タグ または コミットオブジェクト のいずれかを指します)。headとは対照的に、タグは commit コマンドによって更新されません。Gitタグは、Lispタグとは何の関係もありません(Git界隈では、それはオブジェクトタイプと呼ばれます)。タグは通常、コミットの祖先チェーンの特定のポイントをマークするために使用されます。
tag object
別のオブジェクトを指すrefを含むオブジェクト。これには、コミットオブジェクトのようにメッセージを含めることができます。またPGP署名を含めることもでき、その場合、「署名付きタグオブジェクト」(signed tag object)と呼ばれます。
topic branch
開発者が概念的な開発ラインを識別するために使用する通常のGitブランチ。(従来のSCMに比べて)ブランチは非常に簡単で処理コストが掛からないため、それぞれが非常に明確に定義された概念または小さな増分であるが関連する変更を含む、いくつかの小さなブランチを持つことが望ましい場合がよくあります。
tree
作業ツリー、または、ツリーオブジェクトとそれに依存するブロブやツリーオブジェクト(つまり、作業ツリーの保存された表現)、のいずれか。
tree object
ファイル名とモードのリスト、および関連するブロブやツリーオブジェクトへのrefを含むオブジェクト。<< def_tree,ツリー>>とディレクトリは同じ意味です。
tree-ish (also treeish)
ツリーっぽい何か。ツリーオブジェクトに再帰的に逆参照できる ツリーオブジェクト または オブジェクト です。 コミットオブジェクト を逆参照すると、その リビジョン の最上位 ディレクトリ> に対応するツリーオブジェクトが生成されます。※右記は全てツリーっぽい(tree-ish)モノです: <<def_commit-ish 、ツリーオブジェクト、ツリーオブジェクトを指す タグオブジェクト 、タグオブジェクトを指すタグオブジェクト
unmerged index
マージされていないインデックスエントリを含むインデックス
unreachable object
ブランチ または タグ またはその他の参照から 到達可能 ではない(辿れない、ポイントされてない) オブジェクト
upstream branch
当該のブランチからマージされる(または当該のブランチがリベースされる)デフォルトのブランチ。これは、 branch.<name>.remotebranch.<name>.merge を介して構成されます。Aのアップストリームブランチが origin/B の場合、「Aは origin/B を追跡しています」と言うことがあります。
working tree
実際にチェックアウトされたファイル群のツリー。 作業ツリーには通常、 HEAD コミットのツリーの内容に加えて、任意の、まだコミットされていないローカルの変更が含まれています。
worktree
(ワークツリー):リポジトリには、ゼロ(ベア・リポジトリ)または1つ以上の worktree を当てはめる(attach)ことができます。 1 つの「worktree」は「作業ツリー」(working tree)とリポジトリ・メタデータで構成され、 そのほとんどは単一のリポジトリの他の worktree 間で共有され、一部は worktree ごとに個別に維持されます(例: インデックスやHEADやMERGE_HEADなどの疑似ref(pseudorefs)、worktreeごとの ref や worktree ごとの構成ファイル)。

Appendix A. Git Quick Reference

これは主要なコマンドの簡単な要約です。ここまでの章を読めば、これらがどのように機能するか、より詳細に説明してあります。

Creating a new repository

tarballから持ってくる:

$ 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

Managing branches

$ git branch                    # list all local branches in this repo
$ git switch test               # switch working directory to branch "test"
$ git branch new                # create branch "new" starting at current HEAD
$ git branch -d new             # delete branch "new"

現在のHEAD(デフォルト)に基づいて新しいブランチを作成する代わりに、以下を使用します:

$ git branch new test    # branch named "test"
$ git branch new v2.6.15 # tag named v2.6.15
$ git branch new HEAD^   # commit before the most recent
$ git branch new HEAD^^  # commit before that
$ git branch new test~10 # ten commits before tip of branch "test"

新しいブランチを作成と同時にその新しいブランチに切り替えます:

$ git switch -c new v2.6.15

あなたがクローンを作成した元のリポジトリから持ってきてブランチを更新・調査します:

$ git fetch             # update
$ git branch -r         # list
  origin/master
  origin/next
  ...
$ git switch -c masterwork origin/master

別のリポジトリからブランチを取得し、あなたのリポジトリで新しい名前を付けます:

$ 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                    # list remote repositories
example
origin
$ git remote show example       # get details
* remote example
  URL: git://example.com/project.git
  Tracked remote branches
    master
    next
    ...
$ git fetch example             # update branches from example
$ git branch -r                 # list all remote branches

Exploring history

$ gitk                      # visualize and browse history
$ git log                   # list all commits
$ git log src/              # ...modifying src/
$ git log v2.6.15..v2.6.16  # ...in v2.6.16, not in v2.6.15
$ git log master..test      # ...in branch test, not in branch master
$ git log test..master      # ...in branch master, but not in test
$ git log test...master     # ...in one branch, not in both
$ git log -S'foo()'         # ...where difference contain "foo()"
$ git log --since="2 weeks ago"
$ git log -p                # show patches as well
$ git show                  # most recent commit
$ git diff v2.6.15..v2.6.16 # diff between two tagged versions
$ git diff v2.6.15..HEAD    # diff with current head
$ git grep "foo()"          # search working directory for "foo()"
$ git grep v2.6.15 "foo()"  # search old tree for "foo()"
$ git show v2.6.15:a.txt    # look at old version of a.txt

デグレ(regression)を探します:

$ git bisect start
$ git bisect bad                # current version is bad
$ git bisect good v2.6.13-rc2   # last known good revision
Bisecting: 675 revisions left to test after this
                                # test here, then:
$ git bisect good               # if this revision is good, or
$ git bisect bad                # if this revision is bad.
                                # repeat until done.

Making changes

Gitにあなたが誰なのか教えてやってください:

$ cat >>~/.gitconfig <<\EOF
[user]
        name = Your Name Comes Here
        email = you@yourdomain.example.com
EOF

次のコミットに含めるファイルの内容を選択してから、コミットします:

$ git add a.txt    # updated file
$ git add b.txt    # new file
$ git rm c.txt     # old file
$ git commit

または、ワンステップでコミットを準備・作成します:

$ git commit d.txt # use latest content only of d.txt
$ git commit -a    # use latest content of all tracked files

Merging

$ git merge test   # merge branch "test" into the current branch
$ git pull git://example.com/project.git master
                   # fetch and merge in remote branch
$ git pull . test  # equivalent to git merge test

Sharing your changes

パッチの輸出入(inport/export):

$ git format-patch origin..HEAD # format a patch for each commit
                                # in HEAD but not in origin
$ git am mbox # import patches from the mailbox "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

Repository maintenance

破損(corruption)を確認します:

$ git fsck

再圧縮し、未使用の残骸を取り除きます:

$ git gc

Appendix B. Notes and todo list for this manual

Table of Contents

Todo list

Todo list

これらは未だ作業中です。

基本的な要件:

  • Gitの特別な知識がなくても、UNIXコマンドラインの基本を理解していれば、インテリジェントな人であれば最初から最後まで読みやすくする必要があります。必要に応じて、その他の前提条件が発生したときに具体的に言及する必要があります。
  • 可能な場合は常に、セクションの見出しで、実行方法を説明するタスクを、必要以上の知識を必要としない言語で明確に説明する必要があります。たとえば、 "the git am command" ではなく "importing patches into a project" です。

明確な章の依存関係グラフを作成する方法を考えてください。これにより、人々は必ずしもその間のすべてを読むことなく、重要なトピックにたどり着くことができます。

省略されている他のものについては、 Documentation/ を浚ってください。特に:

  • howtoと
  • technical/ のいくつかと
  • フック
  • git(1) のコマンドの一覧

メールアーカイブをスキャンして、省略されている他のものを探します

マニュアルページをスキャンして、このマニュアルが提供するよりも多くの背景を想定しているものがあるかどうかを確認します。

より良い例を追加します。 クックブックの例だけのセクションを記述するのは良い考えです。 たいてい「高度な例」セクションを標準の章の最後のセクションにしますよね?

必要に応じて、用語集への相互参照を含めます。

CVSやSubversionや一連のリリースtarballのインポートなど、他のバージョン管理システムの操作に関するセクションを追加します。

配管コマンドの使用とスクリプトの作成に関する章を作成します。

代替、clone -reference、等。

リポジトリの破損からの回復の詳細は、以下: https://lore.kernel.org/git/Pine.LNX.4.64.0702272039540.12485@woody.linux-foundation.org/ https://lore.kernel.org/git/Pine.LNX.4.64.0702141033400.3604@woody.linux-foundation.org/