セットアップと設定
プロジェクトの取得と作成
基本的なスナップショット作成
ブランチとマージ
プロジェクトの共有と更新
検査と比較
パッチ適用
デバッグ
メール
外部システム
サーバー管理
ガイド
- gitattributes
- コマンドラインインターフェースの慣習
- 日常的なGit
- よくある質問 (FAQ)
- 用語集
- フック
- gitignore
- gitmodules
- リビジョン
- サブモジュール
- チュートリアル
- ワークフロー
- すべてのガイド...
管理
プラミングコマンド
- 2.48.1 → 2.49.0 変更なし
-
2.48.0
2025-01-10
- 2.46.1 → 2.47.2 変更なし
-
2.46.0
2024-07-29
- 2.45.1 → 2.45.3 変更なし
-
2.45.0
2024-04-29
- 2.44.1 → 2.44.3 変更なし
-
2.44.0
2024-02-23
- 2.43.2 → 2.43.6 変更なし
-
2.43.1
2024-02-09
-
2.43.0
2023-11-20
- 2.42.1 → 2.42.4 変更なし
-
2.42.0
2023-08-21
- 2.39.1 → 2.41.3 変更なし
-
2.39.0
2022-12-12
- 2.38.1 → 2.38.5 変更なし
-
2.38.0
2022-10-02
- 2.36.1 → 2.37.7 変更なし
-
2.36.0
2022-04-18
- 2.34.1 → 2.35.8 変更なし
-
2.34.0
2021-11-15
- 2.33.1 → 2.33.8 変更なし
-
2.33.0
2021-08-16
- 2.32.1 → 2.32.7 変更なし
-
2.32.0
2021-06-06
- 2.30.1 → 2.31.8 変更なし
-
2.30.0
2020-12-27
- 2.28.1 → 2.29.3 変更なし
-
2.28.0
2020-07-27
- 2.25.1 → 2.27.1 変更なし
-
2.25.0
2020-01-13
- 2.24.1 → 2.24.4 変更なし
-
2.24.0
2019-11-04
- 2.23.1 → 2.23.4 変更なし
-
2.23.0
2019-08-16
- 2.22.1 → 2.22.5 変更なし
-
2.22.0
2019-06-07
- 2.21.1 → 2.21.4 変更なし
-
2.21.0
2019-02-24
- 2.19.1 → 2.20.5 変更なし
-
2.19.0
2018-09-10
- 2.18.1 → 2.18.5 変更なし
-
2.18.0
2018-06-21
- 2.17.0 → 2.17.6 変更なし
-
2.16.6
2019-12-06
-
2.15.4
2019-12-06
-
2.14.6
2019-12-06
-
2.13.7
2018-05-22
-
2.12.5
2017-09-22
- 2.9.5 → 2.11.4 変更なし
-
2.8.6
2017-07-30
-
2.7.6
2017-07-30
-
2.6.7
2017-05-05
- 2.5.6 変更なし
-
2.4.12
2017-05-05
- 2.3.10 変更なし
-
2.2.3
2015-09-04
-
2.1.4
2014-12-17
-
2.0.5
2014-12-17
はじめに
Gitは高速な分散型リビジョン管理システムです。
このマニュアルは、基本的なUNIXコマンドラインスキルを持つが、Gitの予備知識がない人でも読めるように設計されています。
リポジトリとブランチ および Gitの履歴を探る では、Gitを使用してプロジェクトをフェッチし、調査する方法を説明しています。これらの章を読んで、ソフトウェアプロジェクトの特定のバージョンを構築およびテストしたり、リグレッションを検索したりする方法などを学んでください。
さらに、より専門的なトピックについては、別の章で説明します。
包括的なリファレンスドキュメントは、manページまたは git-help[1] コマンドで利用できます。例えば、`git clone
$ man git-clone
または
$ git help clone
後者の場合、お好みのマニュアルビューアを使用できます。git-help[1] で詳細を確認してください。
また、説明なしでGitコマンドの簡単な概要については、Gitクイックリファレンス を参照してください。
最後に、このマニュアルをより完全にするための協力方法については、このマニュアルの注記とToDoリスト を参照してください。
リポジトリとブランチ
Gitリポジトリの取得方法
このマニュアルを読みながら実験できるように、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
大規模なプロジェクトの場合、最初のクローンは時間がかかるかもしれませんが、クローンは一度だけ行えば十分です。
クローンコマンドは、プロジェクト名(上記の例では`git`または`linux`)の新しいディレクトリを作成します。このディレクトリに`cd`すると、プロジェクトファイルのコピー(ワーキングツリーと呼ばれる)と、プロジェクトの履歴に関するすべての情報を含む`.git`という特殊なトップレベルディレクトリがあることがわかります。
プロジェクトの異なるバージョンをチェックアウトする方法
Gitは、ファイルのコレクションの履歴を保存するためのツールと考えるのが最も適切です。プロジェクトの内容の相互に関連するスナップショットを圧縮したコレクションとして履歴を保存します。Gitでは、このような各バージョンをコミットと呼びます。
これらのスナップショットは、必ずしもすべてが古いものから新しいものへと一直線に並べられているわけではありません。代わりに、作業は同時に並行する開発ライン(ブランチと呼ばれる)に沿って進むことができ、それらはマージしたり分岐したりします。
単一のGitリポジトリで複数のブランチでの開発を追跡できます。これは、各ブランチの最新のコミットを参照するヘッドのリストを保持することで行われます。git-branch[1] コマンドは、ブランチヘッドのリストを表示します。
$ git branch * master
新しくクローンされたリポジトリには、デフォルトで「master」という名前の単一のブランチヘッドが含まれ、ワーキングディレクトリはそのブランチヘッドが参照するプロジェクトの状態に初期化されます。
ほとんどのプロジェクトでは、タグも使用します。タグはヘッドと同様に、プロジェクトの履歴への参照であり、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
現在のブランチヘッドが履歴の特定の時点への唯一の参照であった場合、そのブランチをリセットすると、以前参照していた履歴を見つける方法がなくなる可能性があることに注意してください。したがって、このコマンドは慎重に使用してください。
履歴の理解:コミット
プロジェクトの履歴におけるすべての変更は、コミットによって表されます。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) {
ご覧のとおり、コミットは誰が、何を、なぜ最新の変更を行ったかを示します。
すべてのコミットには40桁の16進数IDがあり、「オブジェクト名」または「SHA-1 ID」とも呼ばれ、`git show`出力の最初の行に表示されます。通常、タグやブランチ名のような短い名前でコミットを参照できますが、この長い名前も便利です。最も重要なのは、このコミットのグローバルに一意な名前であることです。したがって、他の誰かにオブジェクト名(例えばメールで)を伝えた場合、その名前は彼らのリポジトリ(彼らのリポジトリにそのコミットが存在することを前提とします)であなたのリポジトリと同じコミットを参照することが保証されます。オブジェクト名はコミットの内容のハッシュとして計算されるため、その名前も変更されることなくコミットが変更されることはありません。
実際、Gitの概念 では、ファイルデータやディレクトリの内容を含め、Gitの履歴に保存されているすべてのものは、その内容のハッシュである名前を持つオブジェクトに保存されていることがわかります。
履歴の理解:コミット、親、到達可能性
すべてのコミット(プロジェクトの最初のコミットを除く)には、そのコミットより前に何が起こったかを示す親コミットがあります。親のチェーンをたどると、最終的にプロジェクトの始まりに戻ります。
しかし、コミットは単純なリストを形成しません。Gitは、開発ラインが分岐し、その後再収束することを可能にし、2つの開発ラインが再収束する点を「マージ」と呼びます。したがって、マージを表すコミットは複数の親を持つことができ、各親はその時点に至る開発ラインのいずれかの最新のコミットを表します。
これがどのように機能するかを確認する最良の方法は、gitk[1] コマンドを使用することです。Gitリポジトリでgitkを実行し、マージコミットを探すことで、Gitが履歴をどのように整理しているかを理解するのに役立ちます。
以下では、コミットXがコミットYの祖先である場合、コミットXはコミットYから「到達可能(reachable)」であると言います。 equivalently、YはXの子孫である、またはコミットYからコミットXへ続く親のチェーンがある、と言うこともできます。
ブランチの操作
ブランチの作成、削除、変更は素早く簡単です。以下にコマンドの概要を示します。
-
git branch
-
すべてのブランチをリスト表示します。
-
git branch <branch>
-
現在のブランチと同じ履歴の時点を参照して、`<branch>`という名前の新しいブランチを作成します。
-
git branch <branch> <start-point>
-
`<start-point>`を参照して、`<branch>`という名前の新しいブランチを作成します。`<start-point>`は、ブランチ名やタグ名を含む、好きな方法で指定できます。
-
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. 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: 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)」と言います。
これは、新しいブランチの名前を考えることなく特定のバージョンをチェックアウトする簡単な方法です。後で必要であれば、このバージョンの新しいブランチ(またはタグ)を作成することもできます。
リモートリポジトリのブランチを調査する
クローン時に作成された「master」ブランチは、クローン元のリポジトリのHEADのコピーです。そのリポジトリには他のブランチがあったかもしれませんが、ローカルリポジトリはそれらのリモートブランチのそれぞれを追跡するブランチを保持しています。これらはリモート追跡ブランチと呼ばれ、git-branch[1] の`-r`オプションを使用して表示できます。
$ git branch -r origin/HEAD origin/html origin/maint origin/man origin/master origin/next origin/seen origin/todo
この例では、「origin」はリモートリポジトリ、または略して「リモート」と呼ばれます。このリポジトリのブランチは、我々の視点からは「リモートブランチ」と呼ばれます。上記のリストにあるリモート追跡ブランチは、クローン時のリモートブランチに基づいて作成され、`git fetch`(したがって`git pull`)および`git push`によって更新されます。詳細については、git fetchによるリポジトリの更新 を参照してください。
タグの場合と同様に、これらのリモート追跡ブランチのいずれかを自分のブランチでベースとして使用したい場合があります。
$ git switch -c my-todo-copy origin/todo
`origin/todo`を直接チェックアウトして調査したり、一度限りのパッチを作成したりすることもできます。デタッチされたHEAD を参照してください。
「origin」という名前は、Gitがデフォルトでクローン元のリポジトリを参照するために使用する名前であることに注意してください。
ブランチ、タグ、およびその他の参照の名前付け
ブランチ、リモート追跡ブランチ、およびタグはすべてコミットへの参照です。すべての参照は`refs`から始まるスラッシュ区切りのパス名で命名されます。これまでに使用してきた名前は実際には省略形です。
-
ブランチ`test`は`refs/heads/test`の省略形です。
-
タグ`v2.6.18`は`refs/tags/v2.6.18`の省略形です。
-
`origin/master`は`refs/remotes/origin/master`の省略形です。
例えば、タグとブランチに同じ名前が存在する場合、完全な名前が役立つことがあります。
(新しく作成された参照は実際には`.git/refs`ディレクトリに、その名前に指定されたパスの下に保存されます。ただし、効率上の理由から、それらは単一のファイルにまとめてパックされることもあります。git-pack-refs[1] を参照してください。)
もう一つの便利なショートカットとして、リポジトリの「HEAD」は、そのリポジトリの名前だけで参照できます。したがって、例えば「origin」は通常、リポジトリ「origin」内のHEADブランチのショートカットです。
Gitが参照をチェックするパスの完全なリスト、および同じショートハンド名を持つ複数の参照がある場合にどちらを選択するかを決定するために使用する順序については、gitrevisions[7] の「SPECIFYING REVISIONS」セクションを参照してください。
git fetchによるリポジトリの更新
リポジトリをクローンし、いくつかの変更をコミットした後、元のリポジトリの更新を確認したい場合があります。
`git-fetch`コマンドは、引数なしで実行すると、すべてのリモート追跡ブランチを元のリポジトリで見つかった最新バージョンに更新します。これは、クローン時に作成された「master」ブランチを含め、自分のブランチには一切触れません。
他のリポジトリからブランチをフェッチする
クローン元のリポジトリ以外のリポジトリのブランチも、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
`.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」セクションを参照してください。)
Git履歴の探索
Gitは、ファイルのコレクションの履歴を保存するためのツールと考えるのが最も適切です。これは、ファイル階層の内容の圧縮されたスナップショットと、これらのスナップショット間の関係を示す「コミット」を保存することによって行われます。
Gitは、プロジェクトの履歴を探索するための非常に柔軟で高速なツールを提供します。
まず、プロジェクトにバグを導入したコミットを見つけるのに役立つ、特殊なツールから始めます。
bisectを使用してリグレッションを見つける方法
プロジェクトのバージョン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はどのブランチからもデタッチされ、直接コミット(コミットID 65934)を指しています。このコミットは「master」から到達可能ですが、v2.6.18からは到達できません。コンパイルしてテストし、クラッシュするかどうかを確認します。クラッシュすると仮定します。次に、
$ git bisect bad Bisecting: 1769 revisions left to test after this [7eff82c8b1511017ae605f0c99ac275a7e21b867] i2c-core: Drop useless bitmaskings
古いバージョンをチェックアウトします。このように続けて、各段階でGitが提供するバージョンが良いか悪いかを伝え、テストに残されたリビジョン数が毎回約半分に削減されることに注目してください。
約13回のテスト(この場合)の後、問題のあるコミットのコミットIDが出力されます。その後、git-show[1] でコミットを調査し、誰が作成したかを確認し、コミットIDを記載したバグレポートをメールで送信できます。最後に、以下を実行します。
$ git bisect reset
以前いたブランチに戻るために。
`git bisect`が各時点でチェックアウトするバージョンは単なる提案であり、良いアイデアだと思うなら別のバージョンを試しても自由であることに注意してください。例えば、たまに無関係なものを壊したコミットにたどり着くことがあります。その場合は以下を実行します。
$ git bisect visualize
これによりgitkが実行され、選択したコミットに「bisect」というマーカーが付けられます。近くにある安全そうなコミットを選択し、そのコミットIDをメモし、以下でチェックアウトします。
$ git reset --hard fb47ddb2db
その後テストし、必要に応じて`bisect good`または`bisect bad`を実行して続行します。
`git bisect visualize`と`git reset --hard fb47ddb2db`の代わりに、現在のコミットをスキップしたいとGitに伝えるだけで済むかもしれません。
$ git bisect skip
ただし、この場合、Gitは最終的に、スキップされた最初のコミットと後の悪いコミットの間に、最初に悪いコミットを特定できない可能性があります。
良いコミットと悪いコミットを区別できるテストスクリプトがある場合、バイセクトプロセスを自動化する方法もあります。これと他の`git bisect`機能の詳細については、git-bisect[1] を参照してください。
コミットの命名
コミットを命名するいくつかの方法をすでに見てきました。
-
40桁の16進数オブジェクト名
-
ブランチ名:指定されたブランチのヘッドにあるコミットを参照します
-
タグ名:指定されたタグが指すコミットを参照します(ブランチとタグは参照の特殊なケースであると見てきました)。
-
HEAD:現在のブランチのヘッドを参照します
他にも多くの方法があります。リビジョンを命名するすべての方法の完全なリストについては、gitrevisions[7] manページの「SPECIFYING REVISIONS」セクションを参照してください。いくつかの例を挙げます。
$ git show fb47ddb2 # the first few characters of the object name # are usually enough to specify it uniquely $ git show HEAD^ # the parent of the HEAD commit $ git show HEAD^^ # the grandparent $ git show HEAD~4 # the great-great-grandparent
マージコミットは複数の親を持つ場合があることを思い出してください。デフォルトでは、`^`と`~`はコミットにリストされている最初の親に従いますが、選択することもできます。
$ git show HEAD^1 # show the first parent of HEAD $ git show HEAD^2 # show the second parent of HEAD
HEADに加えて、コミットにはいくつかの特別な名前があります。
マージ(後で説明)や、現在チェックアウトされているコミットを変更する`git reset`などの操作は、通常、現在の操作の前にHEADが持っていた値をORIG_HEADに設定します。
`git fetch`操作は、常に最後にフェッチされたブランチのヘッドをFETCH_HEADに保存します。例えば、操作のターゲットとしてローカルブランチを指定せずに`git fetch`を実行した場合、
$ git fetch git://example.com/proj.git theirbranch
フェッチされたコミットは、FETCH_HEADから引き続き利用可能です。
マージについて説明する際に、現在のブランチにマージしている他のブランチを参照する特別な名前MERGE_HEADについても説明します。
git-rev-parse[1] コマンドは、コミットの特定の名前をそのコミットのオブジェクト名に変換するのに時々役立つ低レベルコマンドです。
$ git rev-parse origin e05db0fd4f31dde7005f075a84f96b360d05984b
タグの作成
特定のコミットを参照するタグを作成することもできます。実行後、
$ git tag stable-1 1b2e1d63ff
`stable-1`を使用して、コミット1b2e1d63ffを参照できます。
これは「軽量(lightweight)」タグを作成します。タグにコメントを含めたい場合や、暗号的に署名したい場合は、代わりにタグオブジェクトを作成する必要があります。詳細については、git-tag[1] manページを参照してください。
リビジョンの参照
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()'
もちろん、これらすべてを組み合わせることもできます。以下は、v2.5以降で`Makefile`または`fs`以下の任意のファイルに影響を与えたコミットを見つけます。
$ git log v2.5.. Makefile fs/
`git log`にパッチを表示させることもできます。
$ git log -p
その他の表示オプションについては、git-log[1] manページの`--pretty`オプションを参照してください。
`git log`は最新のコミットから始まり、親をさかのぼって動作することに注意してください。ただし、Gitの履歴は複数の独立した開発ラインを含むことができるため、コミットがリストされる特定の順序は多少任意である場合があります。
差分の生成
git-diff[1] を使用して、任意の2つのバージョン間の差分を生成できます。
$ git diff master..test
これにより、2つのブランチの先端間の差分が生成されます。共通の祖先からテストまでの差分を見つけたい場合は、2つのドットの代わりに3つのドットを使用できます。
$ git diff master...test
代わりに一連のパッチが必要な場合もあります。この目的にはgit-format-patch[1] を使用できます。
$ git format-patch master..test
`test`から到達可能だが`master`からは到達できない各コミットに対するパッチを含むファイルを生成します。
古いファイルバージョンの表示
ファイルの古いバージョンは、まず正しいリビジョンをチェックアウトすることでいつでも表示できます。しかし、何もチェックアウトせずに単一ファイルの古いバージョンを表示できると便利な場合があります。このコマンドはそれを実現します。
$ git show v2.5:fs/locks.c
コロンの前にはコミットを命名する任意のものが指定でき、コロンの後にはGitによって追跡されているファイルへの任意のパスを指定できます。
例
ブランチ上のコミット数を数える
`mybranch`が`origin`から分岐して以来、いくつのコミットを行ったかを知りたいとします。
$ 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] を使用することもできます。これは、コミットの子孫のいずれかを指すタグに基づいてコミットに名前を付けます。
$ 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-rc1から、v1.5.0-rc2から到達可能であり、v1.5.0-rc0からは到達不可能であることを示しています。
特定のブランチに固有のコミットを表示する
`master`という名前のブランチヘッドから到達可能だが、リポジトリ内の他のどのヘッドからも到達できないすべてのコミットを見たいとします。
このリポジトリのすべてのヘッドは、git-show-ref[1] を使用してリストできます。
$ git show-ref --heads bf62196b5e363d73353a9dcf094c59595f3153b7 refs/heads/core-tutorial db768d5504c1bb46f63ee9d6e1772bd047e05bf9 refs/heads/maint a07157ac624b2524a059a3414e99f6f44bebc1e7 refs/heads/master 24dbc180ea14dc1aebe09f14c8ecf32010690627 refs/heads/tutorial-2 1e87486ae06626c2f31eaa63d26fc0fd646c8af2 refs/heads/tutorial-fixes
標準ユーティリティの`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] を参照してください。)
ソフトウェアリリース用の変更履歴とtarballの作成
git-archive[1] コマンドは、プロジェクトの任意のバージョンからtarまたはzipアーカイブを作成できます。例えば、
$ git archive -o latest.tar.gz --prefix=project/ HEAD
はHEADを使用して、各ファイル名の前に`project/`が付いたgzip圧縮されたtarアーカイブを生成します。出力ファイル形式は、可能であれば出力ファイルの拡張子から推測されます。詳細については、git-archive[1] を参照してください。
Gitのバージョンが1.7.7より古い場合、`tar.gz`形式を認識しないため、`gzip`を明示的に使用する必要があります。
$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz
ソフトウェアプロジェクトの新しいバージョンをリリースする場合、リリース発表に含める変更履歴も同時に作成したい場合があります。
例えば、リーナス・トーバルズは、新しいカーネルリリースをタグ付けし、その後以下を実行して行います。
$ 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] のmanページが役立つかもしれません。
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」セクションを参照してください。ファイルはプレーンテキストなので、お気に入りのエディタで編集することもできます。
新しいリポジトリの作成
ゼロから新しいリポジトリを作成するのは非常に簡単です。
$ 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
コミットを作成する方法
新しいコミットを作成するには3つのステップが必要です。
-
お気に入りのエディタを使用して、ワーキングディレクトリにいくつかの変更を加えます。
-
Gitにあなたの変更を伝えます。
-
ステップ2でGitに伝えた内容を使用してコミットを作成します。
実際には、ステップ1と2を好きなだけ交互に行ったり、繰り返したりできます。ステップ3でコミットしたい内容を追跡するために、Gitはツリーの内容のスナップショットを「インデックス」と呼ばれる特別なステージングエリアに保持します。
最初、インデックスの内容は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 # difference between HEAD and the index; what # would be committed if you ran "commit" now. $ git diff # difference between the index file and your # working directory; changes that would not # be included if you ran "commit" now. $ git diff HEAD # difference between HEAD and working tree; what # would be committed if you ran "commit -a" now. $ git status # a brief per-file summary of the above.
git-gui[1] を使用して、コミットの作成、インデックスとワーキングツリーファイルの変更の表示、および差分ハンクを個別に選択してインデックスに含める(差分ハンクを右クリックして「Stage Hunk For Commit」を選択)こともできます。
良いコミットメッセージの作成
必須ではありませんが、コミットメッセージは、変更を要約する短い(50文字以内)1行で始め、その後に空行、そしてより詳細な説明を続けるのが良い習慣です。コミットメッセージの最初の空行までのテキストはコミットタイトルとして扱われ、そのタイトルはGit全体で使用されます。例えば、git-format-patch[1] はコミットをメールに変換し、件名にタイトルを、本文にコミットの残りの部分を使用します。
ファイルの無視
プロジェクトでは、Gitで追跡したくないファイルが生成されることがよくあります。これには通常、ビルドプロセスによって生成されたファイルや、エディタによって作成された一時的なバックアップファイルが含まれます。もちろん、Gitでファイルを追跡*しない*というのは、それらに対して`git add`を呼び出*さない*だけの問題です。しかし、これらの追跡されていないファイルが散乱しているとすぐに煩わしくなります。例えば、それらは`git add .`を実質的に役に立たなくし、`git status`の出力に常に表示され続けます。
ワーキングディレクトリのトップレベルに`.gitignore`というファイルを作成し、次のような内容を記述することで、Gitに特定のファイルを無視するよう指示できます。
# Lines starting with '#' are considered comments. # Ignore any file named foo.txt. foo.txt # Ignore (generated) html files, *.html # except foo.html which is maintained by hand. !foo.html # Ignore objects and archives. *.[oa]
構文の詳細については、gitignore[5] を参照してください。`.gitignore`ファイルをワーキングツリーの他のディレクトリに配置することもでき、それらはそれらのディレクトリとそのサブディレクトリに適用されます。`.gitignore`ファイルは、他のファイルと同様にリポジトリに追加できます(通常通り`git add .gitignore`と`git commit`を実行するだけです)。これは、除外パターン(ビルド出力ファイルに一致するパターンなど)が、リポジトリをクローンする他のユーザーにとっても意味がある場合に便利です。
除外パターンが特定の(与えられたプロジェクトのすべての)リポジトリにのみ影響するようにしたい場合は、代わりにリポジトリ内の`.git/info/exclude`という名前のファイル、または`core.excludesFile`設定変数で指定された任意のファイルにそれらを記述することができます。一部のGitコマンドは、コマンドラインで直接除外パターンを受け取ることもできます。詳細については、gitignore[5] を参照してください。
マージの方法
git-merge[1] を使用して、分岐した2つの開発ブランチを再結合できます。
$ git merge branchname
`branchname`ブランチの開発を現在のブランチにマージします。
マージは、`branchname`で行われた変更と、履歴が分岐してからの現在のブランチの最新コミットまでに行われた変更を組み合わせることによって行われます。この結合がクリーンに行われた場合、ワーキングツリーはマージの結果で上書きされ、競合が発生した場合は半マージされた結果で上書きされます。したがって、マージによって影響を受けるファイルと同じファイルに未コミットの変更がある場合、Gitは続行を拒否します。ほとんどの場合、マージを行う前に変更をコミットする必要があります。そうしない場合、git-stash[1] を使用して、マージ中にこれらの変更を一時的に退避させ、後で再適用することができます。
変更が十分に独立している場合、Gitは自動的にマージを完了し、結果をコミットします(または、fast-forward の場合は既存のコミットを再利用します。下記参照)。一方、競合がある場合(例えば、同じファイルがリモートブランチとローカルブランチで2つの異なる方法で変更された場合)、警告が表示されます。出力は次のようになるかもしれません。
$ git merge next 100% (4/4) done Auto-merged file.txt CONFLICT (content): Merge conflict in file.txt Automatic merge failed; fix conflicts and then commit the result.
問題のあるファイルには競合マーカーが残され、手動で競合を解決した後、内容でインデックスを更新し、新しいファイルを作成する場合と同様にGitコミットを実行できます。
結果のコミットを`gitk`で調べると、2つの親があることがわかります。1つは現在のブランチの先端を指し、もう1つはもう一方のブランチの先端を指しています。
マージの解決
マージが自動的に解決されない場合、Gitはインデックスとワーキングツリーを特別な状態にし、マージの解決に必要なすべての情報を提供します。
競合のあるファイルはインデックスで特別にマークされるため、問題を解決してインデックスを更新するまで、git-commit[1] は失敗します。
$ git commit file.txt: needs merge
また、git-status[1] はこれらのファイルを「unmerged」としてリストし、競合のあるファイルには次のような競合マーカーが追加されます。
<<<<<<< HEAD:file.txt Hello world ======= Goodbye >>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
競合を解決するためにファイルを編集し、その後、
$ git add file.txt $ git commit
コミットメッセージには、マージに関する情報がすでに記入されていることに注意してください。通常、このデフォルトメッセージは変更せずに使用できますが、必要に応じて独自のコメントを追加することもできます。
上記は、単純なマージを解決するために知っておくべきすべてです。しかし、Gitは競合の解決を支援する追加情報も提供します。
マージ中の競合解決ヘルプの取得
Gitが自動的にマージできたすべての変更はすでにインデックスファイルに追加されているため、git-diff[1] は競合のみを表示します。これは通常とは異なる構文を使用します。
$ git diff diff --cc file.txt index 802992c,2b60207..0000000 --- a/file.txt +++ b/file.txt @@@ -1,1 -1,1 +1,5 @@@ ++<<<<<<< HEAD:file.txt +Hello world ++======= + Goodbye ++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
この競合を解決した後にコミットされるコミットは、通常の1つではなく2つの親を持つことを思い出してください。1つの親はHEAD、すなわち現在のブランチの先端であり、もう1つは他のブランチの先端であり、MERGE_HEADに一時的に保存されます。
マージ中、インデックスは各ファイルの3つのバージョンを保持します。これら3つの「ファイルステージ」は、ファイルの異なるバージョンを表します。
$ git show :1:file.txt # the file in a common ancestor of both branches $ git show :2:file.txt # the version from HEAD. $ git show :3:file.txt # the version from MERGE_HEAD.
git-diff[1] に競合を表示するように要求すると、ワーキングツリー内の競合したマージ結果とステージ2およびステージ3の間で3方向の差分を実行し、両側から内容が混ざり合ったハンクのみを表示します(つまり、ハンクのマージ結果がステージ2のみから来る場合、その部分は競合しておらず、表示されません。ステージ3も同様です)。
上記の差分は、file.txtのワーキングツリーバージョンとステージ2およびステージ3のバージョンとの違いを示しています。したがって、各行の前に単一の`+`または`-`を付ける代わりに、2つの列を使用します。最初の列は最初の親とワーキングディレクトリコピーの間の違いに使用され、2番目の列は2番目の親とワーキングディレクトリコピーの間の違いに使用されます。(フォーマットの詳細については、git-diff-files[1] の「COMBINED DIFF FORMAT」セクションを参照してください。)
明白な方法で競合を解決した後(ただしインデックスを更新する前)、差分は次のようになります。
$ git diff diff --cc file.txt index 802992c,2b60207..0000000 --- a/file.txt +++ b/file.txt @@@ -1,1 -1,1 +1,1 @@@ - Hello world -Goodbye ++Goodbye world
これは、解決済みのバージョンが最初の親から「Hello world」を削除し、2番目の親から「Goodbye」を削除し、以前はどちらにもなかった「Goodbye world」を追加したことを示しています。
いくつかの特別なdiffオプションを使用すると、これらのいずれかのステージとワーキングディレクトリを比較できます。
$ git diff -1 file.txt # 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は書き込もうとしているツリーの状態を反映するAUTO_MERGEという名前の参照を書き込みます。自動的にマージできなかったテキスト競合のある競合パスは、ワーキングツリーと同様に競合マーカーとともにこのツリーに書き込まれます。したがって、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
差分を見ると、競合マーカーとコンテンツ行の両方のバージョンを削除し、代わりに「Goodbye world」を書き込んだことがわかります。
git-log[1] と gitk[1] コマンドも、マージに関する特別なヘルプを提供します。
$ git log --merge $ gitk --merge
これらは、HEADまたはMERGE_HEADにのみ存在し、未マージのファイルに影響するすべてのコミットを表示します。
Emacsやkdiff3などの外部ツールを使用して、未マージファイルをマージできるgit-mergetool[1] も使用できます。
ファイル内の競合を解決し、インデックスを更新するたびに、
$ git add file.txt
そのファイルの異なるステージは「折りたたまれ」、その後`git diff`は(デフォルトで)そのファイルの差分を表示しなくなります。
マージの取り消し
もし行き詰まって、すべてを諦めて破棄することにした場合でも、いつでも以下のコマンドでマージ前の状態に戻ることができます。
$ git merge --abort
または、破棄したいマージをすでにコミットしてしまった場合、
$ git reset --hard ORIG_HEAD
ただし、この最後のコマンドは場合によっては危険です。すでにコミットしたコミットが別のブランチにマージされている可能性がある場合は、決してそのコミットを破棄しないでください。そうすると、それ以降のマージが混乱する可能性があります。
Fast-forwardマージ
上記で触れていない、異なった扱いの特殊なケースが1つあります。通常、マージは、マージされた2つの開発ラインのそれぞれを指す2つの親を持つマージコミットを生成します。
ただし、現在のブランチがもう一方の祖先である場合(つまり、現在のブランチに存在するすべてのコミットがすでに他のブランチに含まれている場合)、Gitは単に「fast-forward」を実行します。新しいコミットが作成されることなく、現在のブランチのヘッドはマージされたブランチのヘッドを指すように前方に移動されます。
間違いの修正
ワーキングツリーを台無しにしてしまったが、まだ間違いをコミットしていない場合、以下のコマンドでワーキングツリー全体を最後にコミットされた状態に戻すことができます。
$ git restore --staged --worktree :/
後でコミットしなければよかったと思うコミットをしてしまった場合、問題を修正する方法は根本的に2つあります。
-
古いコミットによって行われたことを元に戻す新しいコミットを作成できます。この方法は、間違いがすでに公開されている場合に正しい方法です。
-
古いコミットに戻って変更することができます。ただし、履歴をすでに公開している場合は、決してこれを行わないでください。Gitは通常、プロジェクトの「履歴」が変更されることを想定しておらず、履歴が変更されたブランチからの繰り返しマージを正しく実行できません。
新しいコミットで間違いを修正する
以前の変更を元に戻す新しいコミットを作成するのは非常に簡単です。git-revert[1] コマンドに問題のあるコミットへの参照を渡すだけです。例えば、最新のコミットを元に戻すには、
$ git revert HEAD
これにより、HEADの変更を元に戻す新しいコミットが作成されます。新しいコミットのコミットメッセージを編集する機会が与えられます。
以前の変更、例えば最後から2番目の変更を元に戻すこともできます。
$ git revert HEAD^
この場合、Gitはそれ以降に行われた変更をそのまま残しつつ、古い変更を元に戻そうとします。より新しい変更が元に戻す変更と重なる場合、マージの解決 の場合と同様に、手動で競合を修正するよう求められます。
履歴を書き換えることによる間違いの修正
問題のあるコミットが最新のコミットであり、まだそのコミットを公開していない場合、git reset
を使用して破棄する ことができます。
または、ワーキングディレクトリを編集し、インデックスを更新して間違いを修正することができます。新しいコミットを作成する ときと同じように、その後以下を実行します。
$ git commit --amend
これにより、古いコミットをあなたの変更を組み込んだ新しいコミットで置き換え、まず古いコミットメッセージを編集する機会が与えられます。
繰り返しになりますが、すでに別のブランチにマージされている可能性のあるコミットに対してこれを行ってはなりません。その場合は、代わりにgit-revert[1] を使用してください。
履歴のさらに古いコミットを置き換えることも可能ですが、これは別の章で扱う上級トピックです。
ファイルの古いバージョンをチェックアウトする
以前の悪い変更を取り消す過程で、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] を使用して、現在の作業状態を保存し、バグを修正した後(または、別のブランチで修正してから戻ってきた後)、作業中の変更をアンスタッシュすることができます。
$ 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)に関する情報メッセージが表示されます。これらはリポジトリ内にまだ存在するものの、どのブランチからも参照されなくなったオブジェクトであり、しばらくすると`gc`によって削除されます(また、削除されます)。これらのメッセージを抑制し、実際のエラーのみを表示するには、`git fsck --no-dangling`を実行できます。
失われた変更の回復
参照ログ(Reflog)
例えば、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には個別の参照ログが保持されているため、
$ git show HEAD@{"1 week ago"}
は、現在のブランチが1週間前に何を指していたかではなく、HEADが1週間前に何を指していたかを示します。これにより、チェックアウトした履歴を確認できます。
参照ログはデフォルトで30日間保持され、その後は剪定される可能性があります。この剪定を制御する方法については、git-reflog[1] および git-gc[1] を参照し、詳細については、gitrevisions[7] の「SPECIFYING REVISIONS」セクションを参照してください。
参照ログの履歴は、通常のGit履歴とは大きく異なることに注意してください。通常の履歴は同じプロジェクトで作業するすべてのリポジトリで共有されますが、参照ログの履歴は共有されません。それは、あなたのローカルリポジトリ内のブランチが時間の経過とともにどのように変化したかのみを教えてくれます。
ぶら下がりオブジェクトの調査
場合によっては、参照ログが役に立たないことがあります。例えば、ブランチを削除した後、そのブランチに含まれていた履歴が必要だと気づいたとします。参照ログも削除されますが、まだリポジトリを剪定していない場合、`git fsck`が報告するぶら下がりオブジェクトの中に失われたコミットを見つけることができるかもしれません。詳細については、ぶら下がりオブジェクト を参照してください。
$ git fsck dangling commit 7281251ddd2a61e38657c827739c57015671a6b3 dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63 dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5 ...
例えば、以下を使用して、それらのぶら下がりコミットの1つを調査できます。
$ gitk 7281251ddd --not --all
これはその名の通りで、ぶら下がりコミットによって記述されるコミット履歴を見たいが、既存のすべてのブランチやタグによって記述される履歴は見たくない、ということを意味します。したがって、そのコミットから到達可能であり、失われた履歴だけを正確に取得できます。(そして、それが単一のコミットであるとは限らないことに注意してください。私たちは「行の先端」だけをぶら下がっていると報告しますが、削除された深くて複雑なコミット履歴全体があるかもしれません。)
履歴を元に戻したいと決めた場合、常にそれを指す新しい参照、例えば新しいブランチを作成することができます。
$ git branch recovered-branch 7281251ddd
他の種類のぶら下がりオブジェクト(ブロブやツリー)も存在し、ぶら下がりオブジェクトは他の状況でも発生する可能性があります。
他者との開発共有
git pullによる更新の取得
リポジトリをクローンし、いくつかの変更をコミットした後、元のリポジトリの更新を確認し、自分の作業にマージしたい場合があります。
すでに、git-fetch[1] を使用してリモート追跡ブランチを最新の状態に保つ方法 と、2つのブランチをマージする方法を見てきました。したがって、元のリポジトリのマスターブランチからの変更を以下でマージできます。
$ git fetch $ git merge origin/master
しかし、git-pull[1] コマンドは、これを1つのステップで行う方法を提供します。
$ git pull origin master
実際、`master`をチェックアウトしている場合、このブランチは`git clone`によって、originリポジトリのHEADブランチから変更を取得するように設定されています。そのため、多くの場合、単純なコマンドで上記を達成できます。
$ git pull
このコマンドは、リモートブランチからあなたのリモート追跡ブランチ`origin/*`に変更をフェッチし、デフォルトブランチを現在のブランチにマージします。
より一般的には、リモート追跡ブランチから作成されたブランチは、デフォルトでそのブランチからプルします。これらのデフォルトを制御する方法については、git-config[1] の`branch.
キーボードの入力数を減らすだけでなく、`git pull`は、プル元のブランチとリポジトリを文書化するデフォルトのコミットメッセージを生成することで、あなたを助けます。
(ただし、fast-forward の場合はそのようなコミットは作成されないことに注意してください。代わりに、あなたのブランチは単に上流ブランチの最新コミットを指すように更新されます。)
`git pull`コマンドは、「リモート」リポジトリとして`.`を指定することもでき、その場合、現在のリポジトリからブランチをマージするだけです。したがって、以下のコマンドは、
$ git pull . branch $ git merge branch
ほぼ同等です。
プロジェクトへのパッチの提出
変更が少ない場合、最も簡単な提出方法は、パッチとしてメールで送信することかもしれません。
まず、git-format-patch[1] を使用します。例えば、
$ git format-patch origin
は、現在のブランチには存在するが`origin/HEAD`には存在しない各パッチに対して、現在のディレクトリに番号付きのファイルシリーズを生成します。
`git format-patch`は、最初の「カバーレター」を含めることができます。個々のパッチに関するコメントは、`format-patch`がコミットメッセージの後に配置する3つのダッシュ行の後、パッチ自体の前に挿入できます。カバーレターの素材を`git notes`で追跡している場合、`git format-patch --notes`は同様の方法でコミットのノートを含めます。
その後、これらをメールクライアントにインポートし、手動で送信できます。ただし、一度に多くのものを送信する必要がある場合は、プロセスを自動化するためにgit-send-email[1] スクリプトを使用することをお勧めします。パッチ提出の要件を確認するために、まずプロジェクトのメーリングリストを参照してください。
プロジェクトへのパッチのインポート
Gitには、そのようなメールで送信された一連のパッチをインポートするためのgit-am[1](amは「apply mailbox」の略)というツールもあります。パッチを含むすべてのメッセージを順番に1つのメールボックスファイル(例: `patches.mbox`)に保存し、その後以下を実行します。
$ git am -3 patches.mbox
Gitは各パッチを順番に適用します。競合が見つかった場合、停止し、「マージの解決」で説明されているように競合を修正できます。(`-3`オプションはGitにマージを実行するよう指示します。もし単に中止してツリーとインデックスをそのままにしたい場合は、このオプションを省略できます。)
競合解決の結果でインデックスが更新されたら、新しいコミットを作成する代わりに、以下を実行するだけです。
$ git am --continue
するとGitはあなたのためにコミットを作成し、メールボックスからの残りのパッチの適用を続行します。
最終的な結果は、元のメールボックス内の各パッチに対応する一連のコミットとなり、各パッチを含むメッセージから作者情報とコミットログメッセージがそれぞれ取得されます。
公開Gitリポジトリ
プロジェクトに変更を提出するもう一つの方法は、そのプロジェクトのメンテナにgit-pull[1] を使用してあなたのリポジトリから変更をプルするように伝えることです。「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
これを行う方法については、以下のセクションで説明します。
公開リポジトリのセットアップ
個人リポジトリが`~/proj`ディレクトリにあると仮定します。まず、リポジトリの新しいクローンを作成し、`git daemon`にそれが公開用であることを伝えます。
$ git clone --bare ~/proj proj.git $ touch proj.git/git-daemon-export-ok
結果として生成される`proj.git`ディレクトリには、「ベア(bare)」Gitリポジトリが含まれています。これは、`.git`ディレクトリの内容だけであり、その周囲にチェックアウトされたファイルは含まれていません。
次に、`proj.git`を公開リポジトリをホストする予定のサーバーにコピーします。scp、rsync、または最も便利な方法を使用できます。
GitプロトコルによるGitリポジトリのエクスポート
これが推奨される方法です。
もし他の誰かがサーバーを管理している場合、リポジトリを配置するディレクトリと、それが表示される`git://` URLを教えてもらうべきです。その後、以下の「公開リポジトリへの変更のプッシュ」セクションに進むことができます。
そうでなければ、git-daemon[1] を起動するだけで十分です。これはポート9418でリッスンします。デフォルトでは、Gitディレクトリのように見え、`git-daemon-export-ok`というマジックファイルを含む任意のディレクトリへのアクセスを許可します。`git daemon`の引数としていくつかのディレクトリパスを渡すと、エクスポートはこれらのパスにさらに制限されます。
`git daemon`をinetdサービスとして実行することもできます。詳細については、git-daemon[1] のmanページを参照してください。(特に「EXAMPLES」セクションを参照してください。)
HTTPによるGitリポジトリのエクスポート
Gitプロトコルはより優れたパフォーマンスと信頼性を提供しますが、ウェブサーバーが設定されているホストでは、HTTPエクスポートの方が設定が簡単かもしれません。
新しく作成したベアGitリポジトリをウェブサーバーによってエクスポートされるディレクトリに配置し、ウェブクライアントが必要とする追加情報を提供するためにいくつかの調整を行うだけです。
$ 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 も参照してください。)
公開リポジトリへの変更のプッシュ
上記で概説した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`のターゲットは通常、ベア(bare)リポジトリであることに注意してください。チェックアウトされたワーキングツリーを持つリポジトリにプッシュすることもできますが、現在チェックアウトされているブランチを更新するプッシュは、混乱を防ぐためにデフォルトで拒否されます。詳細については、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>.url
、branch.<name>.remote
、および remote.<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 reset --hard
を使用した場合、または -
すでに公開されたコミットを置き換えるために
git commit --amend
を使用した場合(履歴を書き換えることによる間違いの修正 のように)、または -
すでに公開されたコミットをリベースするために
git rebase
を使用した場合(git rebase を使ってパッチシリーズを最新に保つ のように)。
ブランチ名の前にプラス記号を付けることで、git push
に強制的に更新を実行させることができます。
$ git push ssh://yourserver.com/~you/proj.git +master
+
記号が追加されている点に注意してください。あるいは、リモートの更新を強制するために -f
フラグを使用することもできます。
$ git push -f ssh://yourserver.com/~you/proj.git master
通常、公開リポジトリのブランチヘッドが変更される場合、それは以前に指していたコミットの子孫を指すように変更されます。この状況でプッシュを強制すると、その慣例が破られます。(履歴の書き換えにおける問題 を参照してください。)
しかし、これは進行中のパッチシリーズを公開するための簡単な方法を必要とする人々にとって一般的な慣行であり、そのブランチをどのように管理するつもりであるかを他の開発者に警告する限り、許容できる妥協点です。
他の人が同じリポジトリにプッシュする権利を持っている場合も、このようにプッシュが失敗することがあります。その場合、正しい解決策は、まず自分の作業を更新してからプッシュを再試行することです。これには、プルを行うか、フェッチの後にリベースを行う方法があります。詳細については、次のセクション と gitcvs-migration[7] を参照してください。
共有リポジトリのセットアップ
共同作業を行う別の方法は、CVS で一般的に使用されているモデルに似たものを使用することです。このモデルでは、特別な権限を持つ複数の開発者が、1つの共有リポジトリに対してプッシュおよびプルを行います。設定方法については、gitcvs-migration[7] を参照してください。
しかし、Git の共有リポジトリのサポートには何の問題もありませんが、この操作モードは一般的には推奨されません。単に、Git がサポートする共同作業モード(パッチの交換や公開リポジトリからのプルによるもの)が、中央の共有リポジトリに比べて非常に多くの利点があるためです。
-
Git がパッチを迅速にインポートおよびマージできる機能により、単一のメンテナーが非常に高い頻度で入ってくる変更を処理することができます。そして、それが多すぎると、
git pull
は、メンテナーがこの作業を他のメンテナーに委譲しながらも、入ってくる変更のオプションのレビューを可能にする簡単な方法を提供します。 -
すべての開発者のリポジトリはプロジェクト履歴の完全なコピーを同じように持っているため、特別なリポジトリは存在せず、別の開発者がプロジェクトのメンテナンスを簡単に引き継ぐことができます。これは、相互の合意によるものか、メンテナーが反応しなくなったか、共同作業が困難になったためです。
-
中央の「コミッター」グループが存在しないため、「参加者」と「不参加者」について正式な決定を下す必要性が少なくなります。
リポジトリのウェブブラウジングを許可する
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 内でのマージは、マージベースが最近の履歴にある限り機能します。そうでない場合は、関連のない履歴のマージのようになり、大きな競合が発生する可能性があります。この制限により、そのようなリポジトリはマージベースのワークフローでの使用に適さない場合があります。
例
Linux サブシステムメンテナーのためのトピックブランチの維持
これは、Tony Luck が Linux カーネルの IA64 アーキテクチャのメンテナーとしての役割で Git をどのように使用しているかを説明します。
彼は2つの公開ブランチを使用しています。
-
「テスト」ツリー。パッチは最初にここに入れられ、他の進行中の開発と統合されたときに露出されるようにします。このツリーは、Andrew がいつでも -mm にプルできるようになっています。
-
「リリース」ツリー。テスト済みのパッチは、最終的な健全性チェックのためにここに移され、Linus にそれらを上流に送るための手段として(「please pull」リクエストを送ることで)使われます。
彼はまた、それぞれが論理的なパッチのグループを含む一時的なブランチ(「トピックブランチ」)のセットを使用しています。
これを設定するには、まず 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] を使用して「リモート」を設定し、git-fetch[1] を使用して最新の状態を保つことで、他の公開ツリーを追跡できます。リポジトリとブランチ を参照してください。
次に、作業するブランチを作成します。これらは 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 履歴に作成する「ノイズ」を嫌うため、release
ブランチでこれを軽率に行うべきではありません。なぜなら、これらのノイズの多いコミットは、Linus に release ブランチからのプルを依頼するときに永続的な履歴の一部となるからです。
いくつかの設定変数(git-config[1] 参照)を使用すると、両方のブランチを公開ツリーに簡単にプッシュできます。(公開リポジトリのセットアップ を参照してください。)
$ 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] を使用してテストツリーとリリースツリーの両方をプッシュできます。
$ git push mytree
または、テストブランチとリリースブランチのいずれか一方だけをプッシュすることもできます。
$ 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
それがすでにテストブランチまたはリリースブランチにマージされているかどうかを確認するには、以下を使用します。
$ git log test..branchname
または
$ git log release..branchname
(このブランチがまだマージされていない場合、いくつかのログエントリが表示されます。マージされている場合、出力はありません。)
パッチが大きなサイクル(テストからリリースへ移動し、Linus によってプルされ、最終的にローカルの origin/master
ブランチに戻ってくる)を完了すると、この変更のためのブランチはもはや必要ありません。これは、
$ git log origin..branchname
の出力が空の場合に検出されます。この時点で、ブランチは削除できます。
$ git branch -d branchname
一部の変更は非常に些細なため、個別のブランチを作成してからテストブランチとリリースブランチのそれぞれにマージする必要はありません。これらの変更については、release
ブランチに直接適用し、その後 test
ブランチにマージするだけです。
作業を mytree
にプッシュした後、git-request-pull[1] を使用して Linus に送る「please pull」リクエストメッセージを準備できます。
$ git push mytree $ git request-pull origin mytree release
これらすべてをさらに簡素化するスクリプトをいくつか紹介します。
==== update script ==== # Update a branch in my Git tree. If the branch to be updated # is origin, then pull from kernel.org. Otherwise merge # origin/master branch into test|release branch 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 ==== # Merge a branch into either the test or release branch 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
履歴の書き換えとパッチシリーズの維持
通常、コミットはプロジェクトに追加されるだけで、削除されたり置き換えられたりすることはありません。Git はこの仮定に基づいて設計されており、これに違反すると Git のマージ機構などが誤動作する原因となります。
しかし、この仮定に違反することが有用な状況も存在します。
完璧なパッチシリーズの作成
大規模なプロジェクトに貢献していて、複雑な機能を追加したいとします。そして、その変更を他の開発者が簡単に読み、正確であることを確認し、なぜそれぞれの変更を行ったのかを理解できるように提示したいとします。
すべての変更を単一のパッチ(またはコミット)として提示した場合、一度に消化するには多すぎると感じるかもしれません。
間違い、修正、行き詰まりを含め、作業の全履歴を提示した場合、彼らは圧倒されてしまうかもしれません。
したがって、理想は通常、次のような一連のパッチを作成することです。
-
各パッチは順番に適用できること。
-
各パッチは単一の論理的な変更を含み、その変更を説明するメッセージが付随していること。
-
どのパッチもリグレッションを導入しないこと: シリーズの初期部分を適用した後も、結果のプロジェクトはコンパイルされ、動作し、以前にはなかったバグがないこと。
-
完全なシリーズは、あなた自身の(おそらくはるかに雑な!)開発プロセスと同じ最終結果を生み出すこと。
これを行うのに役立つツールを紹介し、その使用方法を説明し、その後、履歴を書き換えることによって生じる可能性のある問題について説明します。
git rebase を使用してパッチシリーズを最新に保つ
mywork
ブランチをリモート追跡ブランチ origin
上に作成し、その上にいくつかのコミットを作成したとします。
$ 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
というディレクトリに)され、mywork
が origin
の最新バージョンを指すように更新され、その後、保存された各パッチが新しい mywork
に適用されます。結果は以下のようになります。
o--o--O--o--o--o <-- origin \ a'--b'--c' <-- mywork
このプロセス中に、競合が発見されることがあります。その場合、Git は停止し、競合を修正できるようにします。競合を修正した後、git add
を使用してインデックスをその内容で更新し、その後 git commit
を実行する代わりに、単に
$ git rebase --continue
を実行すると、Git は残りのパッチの適用を続行します。
いつでも --abort
オプションを使用してこのプロセスを中止し、リベースを開始する前の mywork
の状態に戻すことができます。
$ git rebase --abort
ブランチ内の多数のコミットを並べ替えたり編集したりする必要がある場合、git rebase -i
を使用する方が簡単かもしれません。これは、コミットの並べ替えやスカッシュ、およびリベース中に個別に編集するためのマーク付けを可能にします。詳細については インタラクティブなリベースの使用 を、代替手段については パッチシリーズの並べ替えまたは選択 を参照してください。
単一のコミットの書き換え
履歴を書き換えることによる間違いの修正 で、最新のコミットを次のように置き換えることができることを見ました。
$ git commit --amend
これは、あなたの変更を組み込んだ新しいコミットで古いコミットを置き換え、最初に古いコミットメッセージを編集する機会を与えます。これは、最後のコミットのタイプミスを修正したり、ステージングが不十分なコミットのパッチ内容を調整したりするのに役立ちます。
履歴のより深い場所にあるコミットを修正する必要がある場合は、インタラクティブなリベースの edit
命令 を使用できます。
パッチシリーズの並べ替えまたは選択
履歴のより深い場所にあるコミットを編集したい場合があります。一つのアプローチは、git format-patch
を使用して一連のパッチを作成し、その後パッチの適用前の状態にリセットすることです。
$ git format-patch origin $ git reset --hard origin
その後、必要に応じてパッチを修正、並べ替え、または削除し、git-am[1] を使用して再度適用します。
$ git am *.patch
インタラクティブなリベースの使用
インタラクティブなリベースを使ってパッチシリーズを編集することもできます。これは 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
コメントで説明されているように、リストを編集することで、コミットを並べ替えたり、結合したり、コミットメッセージを編集したりできます。満足したら、リストを保存してエディタを閉じると、リベースが開始されます。
リベースは、pick
が edit
に置き換えられた場合、またはリストのステップが機械的に競合を解決できず、あなたの助けが必要な場合に停止します。編集や競合の解決が完了したら、git rebase --continue
で続行できます。事態が複雑になりすぎると判断した場合は、常に git rebase --abort
で中止できます。リベースが完了した後でも、reflog を使用して元のブランチを回復することができます。
手順と追加のヒントに関するより詳細な説明については、git-rebase[1] の「INTERACTIVE MODE」セクションを参照してください。
履歴の書き換えに関する問題
ブランチの履歴を書き換える際の主な問題はマージに関係しています。誰かがあなたのブランチをフェッチして自分のブランチにマージし、結果が次のようになったとします。
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
上位の開発ラインで、Z に存在する関数の一つがコミット X で意味が変更されたとします。Z から A に至るコミットは、関数の実装と、Z に存在するすべての呼び出し元サイト、および新しく追加された呼び出し元サイトの両方を変更し、一貫性を保ちます。A にバグはありません。
その間に下位の開発ラインで、誰かがコミット Y でその関数に対する新しい呼び出し元サイトを追加したとします。Z から B に至るコミットはすべて、その関数の古いセマンティクスを仮定しており、呼び出し元と呼び出し先は互いに一貫しています。B にもバグはありません。
さらに、2つの開発ラインが C できれいにマージされ、競合解決は必要なかったとします。
それにもかかわらず、C のコードは壊れています。なぜなら、下位の開発ラインで追加された呼び出し元が、上位の開発ラインで導入された新しいセマンティクスに変換されていないからです。したがって、D が不良で、Z が良好であり、git-bisect[1] が C を犯人と特定したことだけを知っている場合、このセマンティクスの変更が問題の原因であるとどのように判断するでしょうか?
git bisect
の結果がマージでないコミットである場合、通常はそのコミットだけを調べることで問題を特定できるはずです。開発者は、変更を小さく自己完結型のコミットに分割することで、これを容易にすることができます。しかし、上記の場合ではこれは役に立ちません。なぜなら、問題は単一のコミットの検査からは明らかではなく、代わりに開発全体のグローバルな視点が必要だからです。さらに悪いことに、問題のある関数のセマンティクスの変更は、上位の開発ラインにおける変更のごく一部にすぎない可能性があります。
一方で、C でマージする代わりに、Z から B までの履歴を A の上にリベースしていたら、次のような線形履歴が得られたでしょう。
---Z---o---X--...---o---A---o---o---Y*--...---o---B*--D*
Z と D* の間で bisect すると、単一の犯人コミット Y* に当たり、Y* がなぜ壊れていたのかを理解する方がおそらく簡単でしょう。
一部にはこの理由から、多くの経験豊富な Git ユーザーは、マージを多用するプロジェクトで作業している場合でも、公開する前に最新の上流バージョンに対してリベースすることで履歴を線形に保っています。
高度なブランチ管理
個別のブランチのフェッチ
git-remote[1] を使う代わりに、一度に1つのブランチだけを更新し、それを任意のローカル名で保存することもできます。
$ git fetch origin todo:my-todo-work
最初の引数 origin
は、Git に元のクローン元リポジトリからフェッチするよう指示するだけです。2番目の引数は、リモートリポジトリから todo
という名前のブランチをフェッチし、それをローカルに refs/heads/my-todo-work
という名前で保存するよう Git に指示します。
他のリポジトリからブランチをフェッチすることもできます。例えば、
$ git fetch git://example.com/proj.git master:example-master
は example-master
という新しいブランチを作成し、指定された URL のリポジトリから master
という名前のブランチをそこに保存します。すでに example-master
という名前のブランチがある場合、example.com の master ブランチによって与えられたコミットに fast-forward しようとします。より詳細に説明すると、
git fetch と 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
とラベル付けされたコミットを失う可能性があることに注意してください。
git fetch に non-fast-forward 更新を強制する
ブランチの新しいヘッドが古いヘッドの子孫でないために git fetch が失敗した場合、
$ git fetch git://example.com/proj.git +master:refs/remotes/example/master
で更新を強制できます。+
記号が追加されている点に注意してください。あるいは、-f
フラグを使用して、すべてのフェッチされたブランチの更新を強制することもできます。
$ git fetch -f origin
前のセクションで見たように、example/master
の古いバージョンが指していたコミットが失われる可能性があることに注意してください。
リモートトラッキングブランチの設定
前述のとおり、origin
は、最初にクローンしたリポジトリを参照するための単なるショートカットです。この情報は Git の設定変数に保存されており、git-config[1] を使用して確認できます。
$ git config -l core.repositoryformatversion=0 core.filemode=true core.logallrefupdates=true remote.origin.url=git://git.kernel.org/pub/scm/git/git.git remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* branch.master.remote=origin branch.master.merge=refs/heads/master
他にも頻繁に利用するリポジトリがある場合、同様の設定オプションを作成してタイプ入力を省略することができます。例えば、
$ git 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] を参照してください。
Git の概念
Git は、少数のシンプルながらも強力なアイデアに基づいて構築されています。これらを理解していなくても作業を進めることは可能ですが、理解していれば Git をはるかに直感的に感じることができます。
最も重要な オブジェクトデータベース と インデックス から始めます。
オブジェクトデータベース
履歴の理解: コミット で、すべてのコミットが40桁の「オブジェクト名」で保存されていることをすでに確認しました。実際、プロジェクトの履歴を表すのに必要なすべての情報は、そのような名前のオブジェクトに保存されています。それぞれの場合において、名前はオブジェクトの内容の SHA-1 ハッシュを取ることで計算されます。SHA-1 ハッシュは暗号学的ハッシュ関数です。これは私たちにとって、同じ名前を持つ2つの異なるオブジェクトを見つけることは不可能である、ということを意味します。これには多くの利点があり、中でも
-
Git は、名前を比較するだけで2つのオブジェクトが同一かどうかを迅速に判断できます。
-
オブジェクト名はどのリポジトリでも同じ方法で計算されるため、2つのリポジトリに保存された同じ内容は常に同じ名前で保存されます。
-
Git は、オブジェクトを読み取る際に、オブジェクトの名前がまだその内容の SHA-1 ハッシュであるかどうかを確認することで、エラーを検出できます。
(オブジェクトのフォーマットと SHA-1 計算の詳細については、オブジェクトストレージフォーマット を参照してください。)
オブジェクトには、「blob」、「tree」、「commit」、「tag」の4つの異なるタイプがあります。
-
「blob」オブジェクト はファイルデータを保存するために使用されます。
-
「tree」オブジェクト は1つ以上の「blob」オブジェクトをディレクトリ構造に結び付けます。さらに、tree オブジェクトは他の tree オブジェクトを参照することができ、これによりディレクトリ階層を作成します。
-
「commit」オブジェクト は、そのようなディレクトリ階層をリビジョンの 有向非巡回グラフ に結び付けます。各コミットには、コミット時のディレクトリ階層を指定するちょうど1つのツリーのオブジェクト名が含まれます。さらに、コミットは、そのディレクトリ階層にどのように到達したかの履歴を記述する「親」コミットオブジェクトを参照します。
-
「tag」オブジェクト は、他のオブジェクトを象徴的に識別し、署名するために使用できます。それは他のオブジェクトのオブジェクト名とタイプ、シンボリック名(もちろん!)、そしてオプションで署名を含みます。
オブジェクトのタイプをさらに詳しく説明します。
コミットオブジェクト
「コミット」オブジェクトは、ツリーの物理的な状態と、そこに至るまでの経緯、理由を記述したものとをリンクさせます。お気に入りのコミットを調べるには、git-show[1] または git-log[1] の --pretty=raw
オプションを使用してください。
$ 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>
ご覧のとおり、コミットは以下によって定義されます。
-
ツリー: ある時点のディレクトリの内容を表すツリーオブジェクトの SHA-1 名(以下で定義)。
-
親: プロジェクトの履歴における直前のステップを表す、いくつかのコミットの SHA-1 名。上記の例では親が1つですが、マージコミットは複数の親を持つ場合があります。親を持たないコミットは「ルート」コミットと呼ばれ、プロジェクトの最初の改訂を表します。各プロジェクトには少なくとも1つのルートが必要です。プロジェクトは複数のルートを持つことも可能ですが、それは一般的ではありません(また、必ずしも良い考えではありません)。
-
作者: この変更の責任者の名前と日付。
-
コミッター: 実際にコミットを作成した人の名前と日付。これは作者とは異なる場合があります。例えば、作者がパッチを作成して、それを使ってコミットを作成した人にメールで送った場合などです。
-
このコミットを説明するコメント。
コミット自体は、実際に何が変更されたかについての情報を含んでいないことに注意してください。すべての変更は、このコミットが参照するツリーの内容と、その親に関連付けられたツリーを比較することによって計算されます。特に、Git はファイルの名前変更を明示的に記録しようとはしませんが、変更されたパスで同じファイルデータが存在する場合に名前変更を示唆するケースを識別することができます(例えば、git-diff[1] の -M
オプションを参照)。
コミットは通常、git-commit[1] によって作成されます。これは、親が通常現在の HEAD であり、ツリーが現在インデックスに保存されている内容から取得されるコミットを作成します。
ツリーオブジェクト
常に多機能な 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 ...
ご覧のとおり、ツリーオブジェクトには、モード、オブジェクトタイプ、SHA-1 名、および名前を持つエントリのリストが含まれており、名前でソートされています。これは単一のディレクトリツリーの内容を表します。
オブジェクトタイプは、ファイルの内容を表す blob、またはサブディレクトリの内容を表す別のツリーである場合があります。ツリーと blob は、他のすべてのオブジェクトと同様に、その内容の SHA-1 ハッシュによって命名されるため、2つのツリーは、その内容(再帰的に、すべてのサブディレクトリの内容を含む)が同一である場合にのみ同じ SHA-1 名を持ちます。これにより、Git は同一のオブジェクト名を持つエントリを無視できるため、2つの関連するツリーオブジェクト間の違いを迅速に判断できます。
(注意:サブモジュールが存在する場合、ツリーはコミットをエントリとして持つこともあります。ドキュメントについては サブモジュール を参照してください。)
すべてのファイルのモードが 644 または 755 であることに注意してください。Git は実際に実行可能ビットのみに注意を払います。
ブロブオブジェクト
git-show[1] を使用して blob の内容を調べることができます。例えば、上記のツリーの COPYING
のエントリにある blob を見てみましょう。
$ git show 6ff87c4664 Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ...
「blob」オブジェクトは、データのあるバイナリ blob に過ぎません。他のものを参照したり、属性を持ったりすることはありません。
blob は完全にそのデータによって定義されるため、ディレクトリツリー内(またはリポジトリの異なる複数のバージョン内)の2つのファイルが同じ内容を持つ場合、それらは同じ blob オブジェクトを共有します。オブジェクトはディレクトリツリー内の場所とは完全に独立しており、ファイルの名前を変更してもそのファイルに関連付けられたオブジェクトは変更されません。
ツリーまたはブロブオブジェクトは、git-show[1] の <revision>:<path> 構文を使用して調べることができることに注意してください。これは、現在チェックアウトされていないツリーの内容をブラウズするのに役立つ場合があります。
信頼
あるソースから blob の SHA-1 名を受け取り、別の(信頼できない可能性のある)ソースからその内容を受け取った場合でも、SHA-1 名が一致する限り、その内容が正しいと信頼できます。これは、SHA-1 が同じハッシュを生成する異なる内容を見つけることが実行不可能であるように設計されているためです。
同様に、トップレベルのツリーオブジェクトの SHA-1 名だけを信頼すれば、それが参照するディレクトリ全体の内容を信頼できます。また、信頼できるソースからコミットの SHA-1 名を受け取った場合、そのコミットの親を介して到達可能なコミットの履歴全体と、それらのコミットが参照するツリーの内容すべてを簡単に検証できます。
したがって、システムに真の信頼を導入するために、唯一必要なことは、トップレベルコミットの名前を含む *1つだけ* の特別なメモにデジタル署名することです。あなたのデジタル署名は、そのコミットを信頼していることを他の人に示し、コミットの履歴の不変性は、履歴全体を信頼できることを他の人に伝えます。
言い換えれば、トップコミットの名前(SHA-1 ハッシュ)を人々に伝える単一の電子メールを送信し、GPG/PGP のようなものを使用してその電子メールにデジタル署名するだけで、アーカイブ全体を簡単に検証できます。
これを支援するために、Git はタグオブジェクトも提供しています…
タグオブジェクト
タグオブジェクトは、オブジェクト、オブジェクトタイプ、タグ名、タグを作成した人の名前(「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] は、タグオブジェクトではない「軽量タグ」を作成するためにも使用できることに注意してください。これは単に refs/tags/
で始まる名前を持つシンプルな参照です。)
Git がオブジェクトを効率的に保存する方法:パックファイル
新しく作成されたオブジェクトは、最初はオブジェクトの SHA-1 ハッシュに基づいて命名されたファイル(.git/objects
に保存)として作成されます。
残念ながら、このシステムはプロジェクトに多くのオブジェクトがある場合、非効率になります。古いプロジェクトでこれを試してみてください。
$ git count-objects 6930 objects, 47620 kilobytes
最初の数値は個別のファイルで保持されているオブジェクトの数です。2番目の数値は、それらの「ルーズ」オブジェクトが占めるスペースの量です。
これらのルーズオブジェクトを「パックファイル」に移動することで、スペースを節約し、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
を実行して、パックに含まれるようになった「ルーズ」オブジェクトを削除できます。これにより、参照されていないオブジェクト(例えば、git reset
を使用してコミットを削除したときに作成される可能性のあるもの)も削除されます。.git/objects
ディレクトリを確認するか、
$ git count-objects 0 objects, 0 kilobytes
オブジェクトファイルがなくなっても、それらのオブジェクトを参照するコマンドは以前とまったく同じように動作します。
git-gc[1] コマンドは、パッキング、プルーニングなどを行ってくれるため、通常はこれだけを実行すれば十分です。
ぶら下がりオブジェクト
git-fsck[1] コマンドは、ぶら下がりオブジェクトについて文句を言うことがありますが、これらは問題ではありません。
ぶら下がりオブジェクトの最も一般的な原因は、ブランチをリベースしたか、リベースされたブランチから誰かからプルしたことです(履歴の書き換えとパッチシリーズの維持 を参照)。その場合、元のブランチの古いヘッドはまだ存在し、それが指していたものすべてが存在します。ブランチポインタ自体は、別のものに置き換えられたため、存在しません。
ぶら下がりオブジェクトを引き起こす他の状況もあります。例えば、「ぶら下がり blob」は、ファイルを git add
したものの、実際にコミットして大きな全体の一部にする前に、そのファイル内の何かを別のものに変更して、その **更新された** ものをコミットした場合に発生する可能性があります。元々追加した古い状態は、どのコミットやツリーからも指されなくなり、ぶら下がり blob オブジェクトになります。
同様に、「ort」マージ戦略が実行され、交差するマージがあるため複数のマージベースが見つかる(これはかなり珍しいですが、実際に起こります)場合、一時的な中間ツリー(交差するマージが多数あり、マージベースが2つ以上ある場合はさらに多くなる可能性もあります)を一時的な内部マージベースとして生成します。これらも実際のオブジェクトですが、最終結果はこれらを指すことにならないため、リポジトリに「ぶら下がる」ことになります。
一般的に、ぶら下がりオブジェクトは心配するようなものではありません。むしろ、非常に役立つこともあります。何かを台無しにした場合、ぶら下がりオブジェクトは古いツリーを回復する方法になり得ます(例えば、リベースを行い、実際にはしたくなかったと気づいた場合、ぶら下がりオブジェクトを見て、ヘッドを古いぶら下がった状態にリセットすることを決定できます)。
コミットの場合は、単に以下を使用できます。
$ 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>
を実行して、blob の内容が何であったか(または、ツリーの場合、基本的にそのディレクトリの ls
が何であったか)を表示することができ、それがぶら下がりオブジェクトを残した操作が何であったかについての何らかの手がかりを与えてくれるかもしれません。
通常、ぶら下がり blob やツリーはあまり興味深いものではありません。ほとんどの場合、中間マージベースであるか(blob には、手動で修正した競合するマージの場合、しばしば競合マーカーが含まれていることさえあります)、あるいは単に git fetch
を ^C などで中断し、新しいオブジェクトの一部をオブジェクトデータベースに残してしまったが、ぶら下がっていて役に立たない状態になっているかの結果です。
いずれにしても、ぶら下がっている状態に興味がないと確信したら、到達不能なオブジェクトをすべてプルーニングするだけです。
$ git prune
そうすればそれらはなくなります。(git prune
は静止しているリポジトリでしか実行すべきではありません。これはファイルシステム fsck リカバリを行うようなものです。ファイルシステムがマウントされている間はそれを行いたくありません。git prune
は、リポジトリへの同時アクセスの場合でも害を引き起こさないように設計されていますが、混乱したり恐ろしいメッセージを受け取る可能性があります。)
リポジトリの破損からの復旧
設計上、Git は信頼されたデータを慎重に扱います。しかし、Git 自体のバグがない場合でも、ハードウェアやオペレーティングシステムのエラーによってデータが破損する可能性はあります。
そのような問題に対する最初の防御策はバックアップです。Git ディレクトリは、クローンを使用したり、単に cp、tar、または他のバックアップメカニズムを使用したりしてバックアップできます。
最後の手段として、破損したオブジェクトを検索し、手動で置き換えを試みることができます。このプロセスでさらに破損させる可能性があるので、試みる前にリポジトリをバックアップしてください。
問題は単一の欠落または破損した blob であると仮定します。これは解決可能な問題であることが時々あります。(欠落したツリー、特にコミットの復旧は **はるかに** 困難です。)
開始する前に、破損があることを確認し、git-fsck[1] でそれがどこにあるかを特定します。これは時間がかかる場合があります。
出力が次のようになると仮定します。
$ git fsck --full --no-dangling broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 to blob 4b9458b3786228369c63936db65827de3cc06200 missing blob 4b9458b3786228369c63936db65827de3cc06200
これで、blob 4b9458b3 が欠落しており、ツリー 2d9263c6 がそれを指していることがわかります。その欠落した blob オブジェクトのコピーを別のリポジトリなどで見つけることができれば、それを .git/objects/4b/9458b3...
に移動すれば完了です。見つからないと仮定しましょう。git-ls-tree[1] でそれを指していたツリーを調べることができます。これは次のようなものを出力するかもしれません。
$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore 100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap 100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING ... 100644 blob 4b9458b3786228369c63936db65827de3cc06200 myfile ...
これで、失われた blob が myfile
という名前のファイルのデータであることがわかりました。そして、ディレクトリも特定できる可能性が高いでしょう—例えば、somedirectory
にあるとします。運が良ければ、失われたコピーは、作業ツリーの somedirectory/myfile
にチェックアウトされているコピーと同じかもしれません。git-hash-object[1] を使ってそれが正しいかどうかテストできます。
$ git hash-object -w somedirectory/myfile
これは somedirectory/myfile
の内容で blob オブジェクトを作成して保存し、そのオブジェクトの SHA-1 を出力します。もし非常に運が良ければ、それが 4b9458b3786228369c63936db65827de3cc06200 であり、その場合あなたは正しく推測し、破損が修正されたことになります!
そうでなければ、もっと情報が必要です。どのバージョンのファイルが失われたかをどうやって判断するのでしょうか?
これを行う最も簡単な方法は、
$ git log --raw --all --full-history -- somedirectory/myfile
を使用することです。生出力を要求しているので、今度は次のようなものが得られるでしょう。
commit abc Author: Date: ... :100644 100644 4b9458b newsha M somedirectory/myfile commit xyz Author: Date: ... :100644 100644 oldsha 4b9458b M somedirectory/myfile
これは、ファイルの直後のバージョンが「newsha」であり、直前のバージョンが「oldsha」であったことを示しています。また、oldsha から 4b9458b へ、および 4b9458b から newsha への変更に伴うコミットメッセージもわかります。
十分に小さな変更をコミットしていれば、中間の状態 4b9458b の内容を再構築できる可能性が高まります。
それができれば、次のようにして失われたオブジェクトを再作成できます。
$ git hash-object -w <recreated-file>
これでリポジトリは再び正常になります!
(ところで、fsck
を無視して、代わりに
$ git log --raw --all
を実行し、その全体から欠落したオブジェクト(4b9458b)の SHA を探すこともできました。それはあなた次第です — Git は多くの情報 **を持って** いますが、特定の blob バージョンが1つ欠けているだけです。
インデックス
インデックスは、パス名のソートされたリストを含むバイナリファイル(通常は .git/index
に保存)で、それぞれにパーミッションと blob オブジェクトの SHA-1 が含まれています。git-ls-files[1] でインデックスの内容を表示できます。
$ git ls-files --stage 100644 63c918c667fa005ff12ad89437f2fdc80926e21c 0 .gitignore 100644 5529b198e8d14decbe4ad99db3f7fb632de0439d 0 .mailmap 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0 COPYING 100644 a37b2152bd26be2c2289e1f57a292534a51a93c7 0 Documentation/.gitignore 100644 fbefe9a45b00a54b58d94d06eca48b03d40a50e0 0 Documentation/Makefile ... 100644 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea 0 xdiff/xtypes.h 100644 2ade97b2574a9f77e7ae4002a4e07a6a38e46d07 0 xdiff/xutils.c 100644 d5de8292e05e7c36c4b68857c1cf9855e3d2f70a 0 xdiff/xutils.h
古いドキュメントでは、インデックスが「現在のディレクトリキャッシュ」または単に「キャッシュ」と呼ばれている場合があることに注意してください。これには3つの重要な特性があります。
-
インデックスには、単一の(一意に決定される)ツリーオブジェクトを生成するために必要なすべての情報が含まれています。
例えば、git-commit[1] を実行すると、このツリーオブジェクトがインデックスから生成され、オブジェクトデータベースに保存され、新しいコミットに関連付けられたツリーオブジェクトとして使用されます。
-
インデックスは、それが定義するツリーオブジェクトと作業ツリーとの間の高速な比較を可能にします。
これは、各エントリに追加データ(最終変更時刻など)を保存することで実現します。このデータは上記には表示されず、作成されたツリーオブジェクトにも保存されませんが、作業ディレクトリ内のどのファイルがインデックスに保存されたものと異なるかを迅速に判断するために使用でき、その結果、変更を検索するためにそのようなファイルからすべてのデータを読み取る必要がなくなります。
-
異なるツリーオブジェクト間のマージの競合に関する情報を効率的に表現でき、各パス名が関係するツリーに関する十分な情報と関連付けられることで、それらの間で3方向マージを作成できます。
マージ中の競合解決のヘルプ で、マージ中にインデックスが単一ファイルの複数のバージョン(「ステージ」と呼ばれる)を保存できることを見ました。git-ls-files[1] の上記の出力の3列目はステージ番号であり、マージ競合のあるファイルの場合は0以外の値を取ります。
インデックスは、作業中のツリーで満たされる一種の一時的なステージング領域です。
インデックスを完全に破棄しても、それが記述するツリーの名前がわかっている限り、通常は情報が失われることはありません。
サブモジュール
大規模なプロジェクトは、多くの場合、より小さな自己完結型のモジュールで構成されています。たとえば、組み込み Linux ディストリビューションのソースツリーには、ディストリビューション内のすべてのソフトウェアにいくつかのローカルな変更が加えられて含まれています。ムービープレーヤーは、特定の既知の動作するバージョンの解凍ライブラリに対してビルドする必要があるかもしれません。いくつかの独立したプログラムがすべて同じビルドスクリプトを共有しているかもしれません。
集中型リビジョン管理システムでは、これは多くの場合、すべてのモジュールを単一のリポジトリに含めることで実現されます。開発者はすべてのモジュール、または作業する必要があるモジュールのみをチェックアウトできます。また、物事を移動したり、API や翻訳を更新したりしながら、複数のモジュールにわたるファイルを単一のコミットで変更することもできます。
Git は部分的なチェックアウトを許可しないため、このアプローチを Git で再現すると、開発者は触れるつもりのないモジュールのローカルコピーを保持することを強いられます。膨大なチェックアウトでのコミットは、Git が変更を求めてすべてのディレクトリをスキャンする必要があるため、予想よりも遅くなります。モジュールに多くのローカル履歴がある場合、クローンに永遠の時間がかかります。
良い点としては、分散リビジョン管理システムは外部ソースとの統合がはるかに優れています。集中型モデルでは、外部プロジェクトの単一の任意のSNAPSHOTが独自のリビジョン管理からエクスポートされ、その後ベンダーブランチのローカルリビジョン管理にインポートされます。すべての履歴は隠されます。分散リビジョン管理では、外部の履歴全体をクローンでき、開発を追跡し、ローカルの変更を再マージすることがはるかに簡単になります。
Git のサブモジュールサポートにより、リポジトリはサブディレクトリとして外部プロジェクトのチェックアウトを含むことができます。サブモジュールは独自のアイデンティティを維持します。サブモジュールサポートは、サブモジュールのリポジトリの場所とコミットIDを保存するだけなので、コンテナプロジェクト(「スーパープロジェクト」)をクローンする他の開発者は、すべてのサブモジュールを同じリビジョンで簡単にクローンできます。スーパープロジェクトの部分的なチェックアウトも可能です。サブモジュールをまったくクローンしない、一部だけクローンする、またはすべてクローンするよう Git に指示できます。
git-submodule[1] コマンドは Git 1.5.3 以降で利用可能です。Git 1.5.2 を使用しているユーザーは、リポジトリ内のサブモジュールコミットを検索し、手動でチェックアウトできます。それ以前のバージョンではサブモジュールがまったく認識されません。
サブモジュールサポートがどのように機能するかを見るために、後でサブモジュールとして使用できる4つの例のリポジトリを作成します。
$ mkdir ~/git $ cd ~/git $ for i in a b c d do mkdir $i cd $i git init echo "module $i" > $i.txt git add $i.txt git commit -m "Initial commit, submodule $i" cd .. done
次に、スーパープロジェクトを作成し、すべてのサブモジュールを追加します。
$ mkdir super $ cd super $ git init $ for i in a b c d do git submodule add ~/git/$i $i done
注意
|
スーパープロジェクトを公開する予定がある場合は、ここでローカル URL を使用しないでください! |
git submodule
が作成したファイルを確認します。
$ ls -a . .. .git .gitmodules a b c d
git submodule add <repo> <path>
コマンドはいくつかのことを行います。
-
サブモジュールを
<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
注意
|
上記に示されたコミットオブジェクト名はあなたとは異なるかもしれませんが、それらはあなたのリポジトリの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 update
と git submodule add
の大きな違いは、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 status
と git 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
注意
|
変更はサブモジュールの reflog でまだ確認できます。 |
サブモジュールの作業ツリーに未コミットの変更がある場合、git submodule update
はそれらを上書きしません。代わりに、ダーティブランチから切り替えられないという通常の警告が表示されます。
Git の低レベル操作
多くの高レベルコマンドは、元々、より少ない数の低レベル Git コマンドを使用するシェルスクリプトとして実装されていました。これらは、Git で珍しいことを行う場合や、その内部動作を理解する方法として、依然として役立ちます。
オブジェクトへのアクセスと操作
git-cat-file[1] コマンドは任意のオブジェクトの内容を表示できますが、通常は高レベルの git-show[1] の方がより便利です。
git-commit-tree[1] コマンドは、任意の親とツリーを持つコミットを構築することを可能にします。
ツリーは git-write-tree[1] で作成でき、そのデータは git-ls-tree[1] でアクセスできます。2つのツリーは git-diff-tree[1] で比較できます。
タグは git-mktag[1] で作成され、署名は git-verify-tag[1] で検証できますが、通常は両方 git-tag[1] を使用する方が簡単です。
ワークフロー
git-commit[1] や git-restore[1] といった高レベルの操作は、ワーキングツリー、インデックス、オブジェクトデータベース間でデータを移動することで機能します。Git は、これらの各ステップを個別に行う低レベルの操作を提供します。
一般的に、すべての Git 操作はインデックスファイル上で動作します。一部の操作はインデックスファイル **のみ** で動作し(インデックスの現在の状態を表示)、ほとんどの操作はインデックスファイルとデータベースまたはワーキングディレクトリの間でデータを移動します。したがって、主に4つの組み合わせがあります。
ワーキングディレクトリ → インデックス
git-update-index[1] コマンドは、ワーキングディレクトリからの情報でインデックスを更新します。通常、更新したいファイル名を指定するだけでインデックス情報を更新します。
$ git update-index filename
ただし、ファイル名のグロビングなどによる一般的な間違いを避けるため、このコマンドは通常、まったく新しいエントリを追加したり、古いエントリを削除したりしません。つまり、通常は既存のキャッシュエントリを更新するだけです。
Git に、特定のファイルがもう存在しないこと、または新しいファイルを追加すべきであることを「はい、本当に理解している」と伝えるには、それぞれ --remove
および --add
フラグを使用する必要があります。
注! --remove
フラグは、それに続くファイル名が必ずしも削除されることを意味するわけではありません。ファイルがディレクトリ構造にまだ存在する場合、インデックスは削除されるのではなく、その新しいステータスで更新されます。--remove
が意味する唯一のことは、update-index が削除されたファイルを有効なものと見なし、ファイルが本当に存在しなくなった場合に、それに応じてインデックスを更新することです。
特殊なケースとして、git update-index --refresh
を実行することもできます。これは、各インデックスの「stat」情報を現在の stat 情報と一致するように更新します。オブジェクトの状態自体は更新せず、オブジェクトが古いバッキングストアオブジェクトとまだ一致するかどうかを迅速にテストするために使用されるフィールドのみを更新します。
以前に紹介した git-add[1] は、git-update-index[1] の単なるラッパーです。
インデックス → オブジェクトデータベース
現在のインデックスファイルを「ツリー」オブジェクトにプログラムで書き込みます。
$ git write-tree
これにはオプションは付属していません。現在のインデックスをその状態を記述する一連のツリーオブジェクトに書き出し、結果のトップレベルツリーの名前を返します。そのツリーを使用して、いつでも逆方向に進むことでインデックスを再生成できます。
オブジェクトデータベース → インデックス
オブジェクトデータベースから「ツリー」ファイルを読み取り、それを使用して現在のインデックスを埋め込みます(そして上書きします—後で復元したいかもしれない保存されていない状態がインデックスに含まれている場合はこれを行わないでください!)。通常の操作は次のとおりです。
$ git read-tree <SHA-1 of tree>
これで、あなたのインデックスファイルは以前保存したツリーと同等になります。しかし、それはあくまであなたの *インデックス* ファイルであって、ワーキングディレクトリの内容は変更されていません。
インデックス → ワーキングディレクトリ
ファイルを「チェックアウト」することで、ワーキングディレクトリをインデックスから更新します。これはあまり一般的な操作ではありません。なぜなら、通常はファイルを最新の状態に保ち、ワーキングディレクトリに書き込むのではなく、ワーキングディレクトリの変更をインデックスファイルに伝える(つまり、git update-index
)からです。
しかし、新しいバージョンにジャンプしたり、他の人のバージョンをチェックアウトしたり、以前のツリーを復元したりする場合、read-tree でインデックスファイルを生成し、その後、結果をチェックアウトする必要があります。
$ git checkout-index filename
または、インデックス全体をチェックアウトしたい場合は -a
を使用します。
注意! git checkout-index
は通常、古いファイルを上書きすることを拒否するため、既にツリーの古いバージョンをチェックアウトしている場合は、-f
フラグ(-a
フラグまたはファイル名の **前**)を使用して、チェックアウトを **強制** する必要があります。
最後に、純粋に表現を移行するだけではない、いくつかの雑多なものがあります。
すべてをまとめる
git write-tree
でインスタンス化したツリーをコミットするには、そのツリーとそれ以前の履歴、特に履歴で先行する「親」コミットを参照する「コミット」オブジェクトを作成します。
通常、「コミット」には親が1つあります。これは、特定の変更が行われる前のツリーの以前の状態です。しかし、時には親が2つ以上ある場合もあります。その場合、そのようなコミットは、他のコミットで表される2つ以上の以前の状態をまとめ(「マージ」し)るという事実から、「マージ」と呼ばれます。
言い換えれば、「ツリー」がワーキングディレクトリの特定のディレクトリ状態を表すのに対し、「コミット」はその時点での状態を表し、そこに至るまでの経緯を説明します。
コミット時の状態を記述するツリーと、親のリストを与えることでコミットオブジェクトを作成します。
$ git commit-tree <tree> -p <parent> [(-p <parent2>)...]
そして、コミットの理由を標準入力(パイプやファイルからのリダイレクト、または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 | +-----------+
データの調査
オブジェクトデータベースとインデックスに表現されているデータは、さまざまなヘルパーツールを使って調査できます。すべてのオブジェクトについて、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
トップコミットが何であったかを確認します。
複数のツリーのマージ
Gitは3方向マージを実行するのに役立ちます。これは、マージ手順を数回繰り返すことで、多方向マージにも使用できます。通常は、1回の3方向マージ(2つの履歴行を調停)を行って結果をコミットしますが、必要であれば、複数のブランチを一度にマージすることもできます。
3方向マージを実行するには、マージしたい2つのコミットから始め、それらの最も近い共通の親(3つ目のコミット)を見つけ、これらの3つのコミットに対応するツリーを比較します。
マージの「ベース」を取得するには、2つのコミットの共通の親を調べます。
$ git merge-base <commit1> <commit2>
これは、両方のコミットがベースとしているコミットの名前を出力します。次に、これらのコミットのツリーオブジェクトを調べる必要があります。これは次のように簡単に行えます。
$ git cat-file commit <commitname> | head -1
ツリーオブジェクト情報は常にコミットオブジェクトの最初の行にあるからです。
マージする3つのツリー(「オリジナル」ツリー、つまり共通ツリーと、2つの「結果」ツリー、つまりマージしたいブランチ)がわかったら、インデックスに「マージ」を読み込みます。これは、古いインデックスの内容を破棄しなければならない場合に文句を言いますので、それらをコミットしていることを確認する必要があります。実際、通常は常に最後のコミットに対してマージを行うでしょう(したがって、現在のインデックスにあるものと一致するはずです)。
マージを行うには、次のようにします。
$ git read-tree -m -u <origtree> <yourtree> <targettree>
これにより、すべての自明なマージ操作がインデックスファイルに直接行われ、結果をgit write-tree
で書き出すことができます。
複数のツリーのマージ、続き
残念ながら、多くのマージは自明ではありません。ファイルが追加、移動、削除された場合、または両方のブランチが同じファイルを変更した場合、インデックスツリーには「マージエントリ」が残ります。このようなインデックスツリーはツリーオブジェクトに書き出すことは**できません**。結果を書き出す前に、他のツールを使用してそのようなマージの衝突を解決する必要があります。
そのようなインデックスの状態はgit ls-files --unmerged
コマンドで調べることができます。例:
$ git read-tree -m $orig HEAD $target $ git ls-files --unmerged 100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello.c 100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello.c 100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello.c
git ls-files --unmerged
の各行は、blobモードビット、blob SHA-1、**ステージ番号**、およびファイル名で始まります。**ステージ番号**は、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ウェイマージプログラム、例えばdiff3
、merge
、またはGit自身のmerge-fileを、これら3つのステージからのblobオブジェクトに対して、次のように実行することで解決できます。
$ 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
パスが「unmerged」状態の場合、そのパスに対して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
が実装されている方法です。
Gitをハックする
この章では、Gitの実装の内部詳細を扱っており、おそらくGit開発者のみが理解する必要があるでしょう。
オブジェクトストレージフォーマット
すべてのオブジェクトは、オブジェクトのフォーマット(つまり、どのように使用され、他のオブジェクトをどのように参照できるか)を識別する、静的に決定された「タイプ」を持っています。現在、4つの異なるオブジェクトタイプがあります。「blob」、「tree」、「commit」、および「tag」です。
オブジェクトのタイプに関わらず、すべてのオブジェクトは次の特性を共有します。それらはすべてzlibでデフレートされ、タイプを指定するだけでなく、オブジェクト内のデータに関するサイズ情報も提供するヘッダーを持っています。オブジェクトの名前付けに使用されるSHA-1ハッシュは、元のデータとこのヘッダーのハッシュであることに注意する価値があります。したがって、`sha1sum *ファイル*`は*ファイル*のオブジェクト名と一致しません(Gitの初期バージョンはわずかに異なるハッシュ化を行っていましたが、結論は変わりません)。
これらのハッシュが手動でどのように生成されるかを示す短い例を次に示します。
単純な内容の小さなテキストファイルを想定しましょう。
$ echo "Hello world" >hello.txt
これで、Gitがこのファイルに使用するハッシュを手動で生成できます。
-
ハッシュを求めたいオブジェクトのタイプは「blob」で、サイズは12バイトです。
-
オブジェクトヘッダーをファイルコンテンツの先頭に追加し、これを
sha1sum
に供給します。
$ { printf "blob 12\0"; cat hello.txt; } | sha1sum 802992c4220de19a90767f3000a79a31b98d0df7 -
この手動で作成されたハッシュは、git hash-object
を使用して検証できます。もちろん、これはヘッダーの追加を隠しています。
$ git hash-object hello.txt 802992c4220de19a90767f3000a79a31b98d0df7
その結果、オブジェクトの一般的な整合性は、コンテンツやオブジェクトのタイプとは独立して常にテストできます。すべてのオブジェクトは、(a) ハッシュがファイルの内容と一致すること、および (b) オブジェクトが正常に展開されて<ascii-type-without-space> + <space> + <ascii-decimal-size> + <byte\0> + <binary-object-data>
のシーケンスを形成するバイトストリームになることを検証することで、検証できます。
構造化されたオブジェクトは、その構造と他のオブジェクトへの接続をさらに検証できます。これは通常、git fsck
プログラムで行われます。このプログラムは、すべてのオブジェクトの完全な依存関係グラフを生成し、その内部整合性を(ハッシュによる表面的な整合性の検証に加えて)検証します。
Gitソースコードの鳥瞰図
新しい開発者がGitのソースコードを理解するのは常に簡単ではありません。このセクションでは、どこから始めるべきかについて少し案内します。
開始するのに良い場所は、最初のコミットの内容です。
$ git switch --detach e83c5163
最初のリビジョンは、今日のGitが持つほぼすべてのものの基礎を築いています(一部の詳細は異なるかもしれませんが)、一度に読めるほど小さいです。
このリビジョン以降、用語が変更されていることに注意してください。たとえば、このリビジョンのREADMEでは、現在コミットと呼ぶものを記述するために「changeset」という単語が使用されています。
また、私たちはもはやそれを「キャッシュ」とは呼ばず、「インデックス」と呼んでいます。しかし、ファイル名は依然としてread-cache.h
です。
その最初のコミットのアイデアを理解したら、より新しいバージョンをチェックアウトし、read-cache-ll.h
、object.h
、commit.h
をざっと見てみてください。
初期の頃、Git(UNIXの伝統に従い)は非常にシンプルなプログラムの集まりであり、スクリプトで使用し、一方の出力を他方にパイプで送っていました。これは、新しいものをテストしやすかったため、初期開発には適していました。しかし、最近ではこれらの多くの部分が組み込み関数になり、コアの一部が「libified」、つまりパフォーマンス、移植性の理由、およびコードの重複を避けるためにlibgit.aに組み込まれました。
これで、インデックスが何であるか(そして対応するデータ構造がread-cache-ll.h
にあること)、そしてstruct object
から共通の構造を継承するいくつかのオブジェクトタイプ(blob、tree、commit、tag)があることがわかったはずです。つまり、例えば(struct object *)commit
をキャストすることで、&commit->object
と同じことを実現し、オブジェクト名とフラグにアクセスできます。
ここで休憩を取り、この情報を消化するのに良いでしょう。
次のステップ:オブジェクト命名に慣れること。コミット命名を読んでください。オブジェクト(およびリビジョンだけでなく!)を命名する方法はかなり多くあります。これらはすべてsha1_name.c
で処理されます。関数get_sha1()
をざっと見てください。多くの特別な処理はget_sha1_basic()
のような関数で行われます。
これは、Gitの最もライブラリ化された部分であるリビジョンウォーカーに慣れるためです。
基本的に、git log
の最初のバージョンはシェルスクリプトでした。
$ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \ LESS=-S ${PAGER:-less}
これは何を意味するのでしょうか?
git rev-list
はリビジョンウォーカーの元のバージョンで、**常に**リビジョンのリストを標準出力に出力していました。これは今でも機能しており、ほとんどの新しいGitコマンドがgit rev-list
を使用するスクリプトとして始まるため、必要です。
git rev-parse
はもはやそれほど重要ではありません。スクリプトによって呼び出されるさまざまなプラミングコマンドに関連するオプションをフィルタリングするためにのみ使用されていました。
git rev-list
が行っていたことのほとんどはrevision.c
とrevision.h
に含まれています。これはオプションをrev_info
という構造体にラップし、リビジョンがどのように、そして何が辿られるかを制御します。
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/<bla.c>
で定義され(Gitの古いバージョンではbuiltin-<bla>.c
にあったことに注意)、builtin.h
で宣言されています。 -
git.c
のcommands[]
配列のエントリと -
Makefile
のBUILTIN_OBJECTS
のエントリ。
時々、複数の組み込み関数が1つのソースファイルに含まれることがあります。例えば、cmd_whatchanged()
とcmd_log()
は両方ともbuiltin/log.c
にあります。これは、かなり多くのコードを共有しているためです。この場合、ファイル名と同じ名前ではないコマンドは、Makefile
のBUILT_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()
の関数シグネチャ内の変数sha1
はunsigned char *
ですが、実際にはunsigned char[20]
へのポインタであることが期待されます。この変数には、与えられたコミットの160ビットSHA-1が含まれます。SHA-1がunsigned char *
として渡される場合、それはバイナリ表現であり、char *
として渡されるASCIIの16進数表現とは対照的であることに注意してください。
これらの両方の事柄がコード全体に見られます。
さて、本題です。
case 0: buf = read_object_with_reference(sha1, argv[1], &size, NULL);
これがblob(実際にはblobだけでなく、あらゆるタイプのオブジェクト)を読み込む方法です。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が実際には最高のツールなのです!
Git用語集
Gitの解説
- 代替オブジェクトデータベース (alternate object database)
-
代替メカニズムを介して、リポジトリは、そのオブジェクトデータベースの一部を別のオブジェクトデータベース(「代替」と呼ばれる)から継承することができます。
- ベアリポジトリ (bare repository)
-
ベアリポジトリは、通常、適切な名前の
.git
接尾辞を持つディレクトリであり、リビジョン管理下のファイルのローカルチェックアウトコピーを持ちません。つまり、通常隠しディレクトリ.git
に存在するすべてのGit管理ファイルおよび制御ファイルが、代わりにrepository.git
ディレクトリに直接存在し、他のファイルは存在せずチェックアウトされていません。通常、公開リポジトリの提供者はベアリポジトリを提供します。 - ブロブオブジェクト (blob object)
-
型なしオブジェクト。例えば、ファイルの内容。
- ブランチ (branch)
-
「ブランチ」とは開発の線を表します。ブランチ上の最も新しいコミットは、そのブランチの先端(tip)と呼ばれます。ブランチの先端は、ブランチヘッドによって参照され、ブランチ上で追加の開発が行われると前方へ移動します。単一のGitリポジトリは任意の数のブランチを追跡できますが、あなたの作業ツリーはそのうちの1つ(「現在の」または「チェックアウトされた」ブランチ)にのみ関連付けられ、HEADはそのブランチを指します。
- キャッシュ (cache)
-
廃止済み: インデックス。
- チェーン (chain)
-
オブジェクトのリストで、リスト内の各オブジェクトがその次のオブジェクトへの参照を含みます(例えば、コミットの次のオブジェクトはその親の1つになることがあります)。
- チェンジセット (changeset)
-
BitKeeper/cvspsの用語で「コミット」を指します。Gitは変更ではなく状態を保存するため、Gitで「チェンジセット」という用語を使用するのは実際には意味がありません。
- チェックアウト (checkout)
-
オブジェクトデータベースからツリーオブジェクトまたはブロブを使用して、作業ツリーのすべてまたは一部を更新するアクション。作業ツリー全体が新しいブランチを指すようになった場合、インデックスおよびHEADも更新されます。
- チェリーピック (cherry-picking)
-
SCMの専門用語で、「チェリーピック」とは、一連の変更(通常はコミット)の中から変更のサブセットを選択し、それらを異なるコードベースの最上位に新しい変更のシリーズとして記録することを意味します。Gitでは、これは「git cherry-pick」コマンドによって実行され、既存のコミットによって導入された変更を抽出し、それを現在のブランチの先端をベースとして新しいコミットとして記録します。
- クリーン (clean)
-
作業ツリーが現在のヘッドによって参照されるリビジョンに対応している場合、その作業ツリーはクリーンであると言われます。「ダーティ」も参照。
- コミット (commit)
-
名詞として: Git履歴における単一の時点。プロジェクトの全履歴は、相互に関連するコミットの集合として表現されます。「コミット」という単語は、他のリビジョン管理システムが「リビジョン」または「バージョン」という単語を使用する場所で、Gitによってよく使用されます。コミットオブジェクトの略語としても使用されます。
- コミットグラフの概念、表現、および使用法 (commit graph concept, representations and usage)
-
オブジェクトデータベース内のコミットによって形成されるDAG構造の同義語で、ブランチの先端によって参照され、リンクされたコミットのチェーンを使用します。この構造は決定的なコミットグラフです。このグラフは、例えば「commit-graph」ファイルなど、他の方法でも表現できます。
- コミットグラフファイル (commit-graph file)
-
「コミットグラフ」(通常ハイフンで区切られる)ファイルは、コミットグラフの補助的な表現であり、コミットグラフの探索を高速化します。「コミットグラフ」ファイルは、.git/objects/infoディレクトリ、または代替オブジェクトデータベースのinfoディレクトリに保存されます。
- コミットオブジェクト (commit object)
-
特定のリビジョンに関する情報(親、コミッター、作者、日付、保存されたリビジョンのトップディレクトリに対応するツリーオブジェクトなど)を含むオブジェクト。
- コミットish (commit-ish) (committishとも)
-
コミットオブジェクト、またはコミットオブジェクトに再帰的にデリファレンスできるオブジェクト。以下のすべてがコミットishです:コミットオブジェクト、コミットオブジェクトを指すタグオブジェクト、コミットオブジェクトを指すタグオブジェクトを指すタグオブジェクト、など。
- コアGit (core Git)
-
Gitの基本的なデータ構造とユーティリティ。限られたソースコード管理ツールのみを公開しています。
- DAG
-
有向非巡回グラフ。コミットオブジェクトは有向非巡回グラフを形成します。なぜなら、それらは親を持ち(有向)、コミットオブジェクトのグラフは巡回しない(同じオブジェクトで始まり終わるチェーンがない)からです。
- ダングリングオブジェクト (dangling object)
-
他の到達不能なオブジェクトからも到達可能ではない到達不能なオブジェクト。リポジトリ内のどの参照からも、どのオブジェクトからも参照されていないオブジェクト。
- デリファレンス (dereference)
-
シンボリックリファレンスを参照する場合: シンボリックリファレンスが指す参照にアクセスするアクション。再帰的なデリファレンスとは、非シンボリックリファレンスが見つかるまで、結果の参照に対して前述のプロセスを繰り返すことを含みます。
タグオブジェクトを参照する場合: タグが指すオブジェクトにアクセスするアクション。タグは、結果が指定されたオブジェクトタイプ(該当する場合)または「タグ」以外のオブジェクトタイプになるまで、結果オブジェクトに対して操作を繰り返すことで再帰的にデリファレンスされます。タグの文脈における「再帰的なデリファレンス」の同義語は「剥がす」です。
コミットオブジェクトを参照する場合: コミットのツリーオブジェクトにアクセスするアクション。コミットは再帰的にデリファレンスすることはできません。
特に指定がない限り、Gitコマンドまたはプロトコルのコンテキストで「デリファレンス」という言葉が使用される場合、それは暗黙的に再帰的です。
- デタッチドHEAD (detached HEAD)
-
通常、HEADはブランチの名前を保存し、HEADが表す履歴を操作するコマンドは、HEADが指すブランチの先端に至る履歴を操作します。しかし、Gitでは、特定のブランチの先端であるとは限らない任意のコミットをチェックアウトすることもできます。このような状態のHEADは「デタッチド」と呼ばれます。
HEADがデタッチド状態でも、現在のブランチの履歴を操作するコマンド(例:その上に新しい履歴を構築するための
git commit
)は機能することに注意してください。これらは、ブランチに影響を与えることなく、HEADを更新された履歴の先端を指すように更新します。現在のブランチに関する情報を更新または問い合わせるコマンド(例:現在のブランチがどのリモート追跡ブランチと統合するかを設定するgit branch --set-upstream-to
)は、当然機能しません。この状態では問い合わせる(実際の)現在のブランチがないためです。 - ディレクトリ (directory)
-
"ls"で得られるリスト:-)
- ダーティ (dirty)
- エビルマージ (evil merge)
- ファストフォワード (fast-forward)
-
ファストフォワードは特殊なタイプのマージであり、あなたが持っているリビジョンに対して、あなたが持っているものの後続である別のブランチの変更を「マージ」します。このような場合、新しいマージコミットを作成するのではなく、あなたのブランチをマージしているブランチと同じリビジョンを指すように更新するだけです。これは、リモートリポジトリのリモートトラッキングブランチで頻繁に発生します。
- フェッチ (fetch)
-
ブランチをフェッチするとは、リモートリポジトリからそのブランチのヘッド参照を取得し、ローカルオブジェクトデータベースに欠落しているオブジェクトを見つけ、それらも取得することを意味します。git-fetch[1]も参照。
- ファイルシステム (file system)
-
リーナス・トーバルズは元々、Gitをユーザー空間ファイルシステム、つまりファイルやディレクトリを保持するためのインフラストラクチャとして設計しました。これにより、Gitの効率性と速度が確保されました。
- Gitアーカイブ (Git archive)
-
リポジトリの同義語(アーチの人向け)。
- gitfile
-
作業ツリーのルートにあるプレーンファイル
.git
で、実際のリポジトリであるディレクトリを指します。正しい使用法については、git-worktree[1]またはgit-submodule[1]を参照してください。構文については、gitrepository-layout[5]を参照してください。 - グラフト (grafts)
-
グラフトは、コミットの偽の祖先情報を記録することによって、それまで異なる2つの開発ラインを結合することを可能にします。これにより、Gitに、コミットが作成されたときに記録されたものとは異なる親のセットを持っているかのように振る舞わせることができます。
.git/info/grafts
ファイルで設定します。グラフトメカニズムは時代遅れであり、リポジトリ間のオブジェクト転送で問題を引き起こす可能性があることに注意してください。同じことを行うためのより柔軟で堅牢なシステムについては、git-replace[1]を参照してください。
- ハッシュ (hash)
-
Gitの文脈では、オブジェクト名の同義語。
- ヘッド (head)
-
ブランチの先端にあるコミットへの名前付き参照。ヘッドは
$GIT_DIR/refs/heads/
ディレクトリ内のファイルに保存されます(パックされた参照を使用する場合を除く。git-pack-refs[1]を参照)。 - HEAD
-
現在のブランチ。より詳細に言うと、あなたの作業ツリーは通常、HEADが参照するツリーの状態から派生します。HEADはあなたのリポジトリのヘッドのいずれかへの参照ですが、デタッチドHEADを使用している場合は、任意のコミットを直接参照します。
- ヘッド参照 (head ref)
-
ヘッドの同義語。
- フック (hook)
-
いくつかのGitコマンドの通常の実行中に、開発者が機能やチェックを追加できるように、オプションのスクリプトが呼び出されます。通常、フックはコマンドが事前に検証され、潜在的に中止されることを許可し、操作完了後の事後通知を許可します。フックスクリプトは
$GIT_DIR/hooks/
ディレクトリにあり、ファイル名から.sample
接尾辞を削除するだけで有効になります。Gitの以前のバージョンでは、実行可能にする必要がありました。 - インデックス (index)
-
統計情報を持つファイルのコレクションで、その内容はオブジェクトとして保存されます。インデックスはあなたの作業ツリーの保存されたバージョンです。実際には、マージ時に使用される、作業ツリーの2番目、さらには3番目のバージョンも含むことができます。
- インデックスエントリ (index entry)
-
インデックスに保存されている、特定のファイルに関する情報。マージが開始されたがまだ完了していない場合(つまり、インデックスにそのファイルの複数のバージョンが含まれている場合)、インデックスエントリはマージされていない状態になることがあります。
- マスター (master)
-
デフォルトの開発ブランチ。Gitリポジトリを作成すると、常に「master」という名前のブランチが作成され、アクティブなブランチになります。ほとんどの場合、これにはローカル開発が含まれますが、それは純粋に慣習によるものであり、必須ではありません。
- マージ (merge)
-
動詞として: 別のブランチ(外部リポジトリからの可能性もある)の内容を現在のブランチに取り込むこと。マージされるブランチが別のリポジトリからのものである場合、これはまずリモートブランチをフェッチし、その後結果を現在のブランチにマージすることで行われます。このフェッチとマージの操作の組み合わせはプルと呼ばれます。マージは、ブランチが分岐して以来行われた変更を識別し、それらの変更すべてをまとめて適用する自動プロセスによって実行されます。変更が競合する場合、マージを完了するために手動介入が必要になることがあります。
名詞として: fast-forwardでない限り、成功したマージは、マージの結果を表す新しいコミットの作成をもたらし、マージされたブランチの先端を親とします。このコミットは「マージコミット」または単に「マージ」と呼ばれます。
- オブジェクト (object)
-
Gitにおけるストレージの単位。その内容のSHA-1によって一意に識別されます。したがって、オブジェクトは変更できません。
- オブジェクトデータベース (object database)
-
「オブジェクト」のセットを格納し、個々のオブジェクトはオブジェクト名によって識別されます。オブジェクトは通常、
$GIT_DIR/objects/
に存在します。 - オブジェクト識別子 (oid) (object identifier (oid))
-
オブジェクト名の同義語。
- オブジェクト名 (object name)
-
オブジェクトの一意な識別子。オブジェクト名は通常、40文字の16進数文字列で表現されます。口語的にはSHA-1とも呼ばれます。
- オブジェクトタイプ (object type)
- オクトパス (octopus)
- 孤立 (orphan)
-
まだ存在しないブランチ(つまり、アンボーンブランチ)に乗る行為。このような操作の後、最初に作成されたコミットは親のないコミットとなり、新しい履歴が開始されます。
- オリジン (origin)
-
デフォルトのアップストリームリポジトリ。ほとんどのプロジェクトは、少なくとも1つの追跡対象アップストリームプロジェクトを持っています。デフォルトでは、その目的のためにoriginが使用されます。新しいアップストリームの更新は、
git branch -r
で確認できる、origin/name-of-upstream-branchという名前のリモートトラッキングブランチにフェッチされます。 - オーバーレイ (overlay)
-
作業ディレクトリにファイルを更新および追加するだけで、削除はしません。これは、
cp -R
が宛先ディレクトリの内容を更新する方法に似ています。チェックアウトでインデックスまたはツリーイッシュからファイルをチェックアウトする際のデフォルトモードです。対照的に、ノーオーバーレイモードは、ソースに存在しない追跡ファイルも削除します。これはrsync --delete
に似ています。 - パック (pack)
-
オブジェクトの集合で、1つのファイルに圧縮されています(スペースを節約するため、または効率的に転送するため)。
- パックインデックス (pack index)
-
パック内のオブジェクトの識別子やその他の情報のリストで、パックの内容に効率的にアクセスするのに役立ちます。
- パススペック (pathspec)
-
Gitコマンドでパスを制限するために使用されるパターン。
パススペックは、「git ls-files」、「git ls-tree」、「git add」、「git grep」、「git diff」、「git checkout」など多くのコマンドのコマンドラインで使用され、操作の範囲をツリーまたは作業ツリーのサブセットに制限します。パスが現在のディレクトリまたはトップレベルに対して相対的であるかどうかは、各コマンドのドキュメントを参照してください。パススペックの構文は以下の通りです。
-
任意のパスはそれ自身に一致します。
-
最後のスラッシュまでのパススペックはディレクトリプレフィックスを表します。そのパススペックのスコープはそのサブツリーに制限されます。
-
パススペックの残りの部分は、パス名の残りの部分に対するパターンです。ディレクトリプレフィックスに対する相対パスは、fnmatch(3)を使用してそのパターンと照合されます。特に、*と?はディレクトリ区切り文字に一致**する**ことができます。
例えば、Documentation/*.jpg は Documentation サブツリー内のすべての .jpg ファイルに一致し、Documentation/chapter_1/figure_1.jpg も含まれます。
コロン
:
で始まるパススペックは特別な意味を持ちます。短い形式では、先頭のコロン:
の後にゼロ個以上の「マジックシグネチャ」文字が続き(オプションで別のコロン:
で終端されます)、残りの部分がパスと照合するパターンになります。「マジックシグネチャ」は、英数字、グロブ、正規表現の特殊文字、コロンのいずれでもないASCII記号で構成されます。「マジックシグネチャ」を終端するオプションのコロンは、パターンが「マジックシグネチャ」記号セットに属さず、コロンでもない文字で始まる場合は省略できます。長い形式では、先頭のコロン
:
の後に左括弧(
、ゼロ個以上の「マジックワード」のカンマ区切りリスト、右括弧)
が続き、残りがパスと照合するパターンになります。コロンのみのパススペックは「パススペックがない」ことを意味します。この形式は他のパススペックと組み合わせて使用すべきではありません。
- トップ (top)
-
マジックワード
top
(マジックシグネチャ:/
)は、サブディレクトリ内でコマンドを実行している場合でも、作業ツリーのルートからパターンに一致させます。 - リテラル (literal)
-
パターン内のワイルドカード(
*
や?
など)は、リテラル文字として扱われます。 - icase
-
大文字小文字を区別しないマッチング。
- グロブ (glob)
-
Git はパターンを fnmatch(3) の FNM_PATHNAME フラグに適したシェルグロブとして扱います。パターン内のワイルドカードはパス名内の / には一致しません。例えば、"Documentation/*.html" は "Documentation/git.html" には一致しますが、"Documentation/ppc/ppc.html" や "tools/perf/Documentation/perf.html" には一致しません。
完全なパス名と照合されるパターン内の2つの連続したアスタリスク("
**
")は特別な意味を持つ場合があります。-
先頭の「
**
」にスラッシュが続く場合は、すべてのディレクトリに一致することを意味します。例えば、「**/foo
」は任意の場所にあるファイルまたはディレクトリ「foo
」に一致し、「foo
」パターンと同じです。「**/foo/bar
」はディレクトリ「foo
」の直下にある任意の場所のファイルまたはディレクトリ「bar
」に一致します。 -
末尾の「
/**
」は内部のすべてに一致します。例えば、「abc/**
」は.gitignore
ファイルの場所からの相対パスで、ディレクトリ「abc」内のすべてのファイルに無限の深さで一致します。 -
スラッシュの後に2つの連続するアスタリスク、そしてスラッシュが続く場合、0個以上のディレクトリに一致します。例えば、「
a/**/b
」は「a/b
」、「a/x/b
」、「a/x/y/b
」などに一致します。 -
その他の連続するアスタリスクは無効とみなされます。
グロブマジックはリテラルマジックと互換性がありません。
-
- 属性 (attr)
-
attr:
の後にスペース区切りの「属性要件」のリストが続きます。パスが一致と見なされるためには、これらすべてが満たされなければなりません。これは通常の非マジックなパススペックパターンマッチングに追加されます。gitattributes[5]を参照してください。パスの各属性要件は、以下のいずれかの形式を取ります。
-
「
ATTR
」は、属性ATTR
が設定されていることを要求します。 -
「
-ATTR
」は、属性ATTR
が設定されていないことを要求します。 -
「
ATTR=VALUE
」は、属性ATTR
が文字列VALUE
に設定されていることを要求します。 -
「
!ATTR
」は、属性ATTR
が指定されていないことを要求します。ツリーオブジェクトに対して照合を行う場合、属性は指定されたツリーオブジェクトからではなく、作業ツリーから取得されることに注意してください。
-
- 除外 (exclude)
-
パスが非除外パススペックに一致した後、すべての除外パススペック(マジックシグネチャ:
!
またはその同義語^
)を通じて実行されます。もし一致した場合、そのパスは無視されます。非除外パススペックがない場合、除外はパススペックなしで呼び出されたかのように結果セットに適用されます。
-
- 親 (parent)
-
コミットオブジェクトには、開発ラインにおける論理的な先行者(親)の(空の場合もある)リストが含まれています。
- 剥がす (peel)
- ピックアックス (pickaxe)
-
「ピックアックス」という用語は、特定のテキスト文字列の追加または削除を支援するdiffcoreルーチンのオプションを指します。
--pickaxe-all
オプションを使用すると、例えば特定の行のテキストを導入または削除した完全な変更セットを表示するために使用できます。git-diff[1]を参照。 - プラミング (plumbing)
-
コアGitの可愛い名前。
- ポーセリン (porcelain)
-
コアGitに依存するプログラムおよびプログラムスイートの可愛い名前で、コアGitへの高レベルなアクセスを提供します。ポーセリンは、プラミングよりもSCMインターフェースの多くを公開しています。
- ワークツリーごとの参照 (per-worktree ref)
-
グローバルではなく、ワークツリーごとの参照。現在、これはHEADおよび
refs/bisect/
で始まる任意の参照のみですが、将来的に他の珍しい参照を含む可能性があります。 - 擬似参照 (pseudoref)
-
通常の参照とは異なるセマンティクスを持つ参照。これらの参照は通常のGitコマンドで読み取れますが、git-update-ref[1]などのコマンドで書き込むことはできません。
Gitが認識している擬似参照は以下の通りです。
-
FETCH_HEAD
はgit-fetch[1]またはgit-pull[1]によって書き込まれます。複数のオブジェクトIDを参照する場合があります。各オブジェクトIDには、どこからフェッチされたか、およびそのフェッチステータスを示すメタデータが注釈として付けられます。 -
MERGE_HEAD
はgit-merge[1]がマージの競合を解決する際に書き込むものです。マージ中のすべてのコミットIDを含んでいます。
-
- プル (pull)
-
ブランチをプルするとは、それをフェッチしてマージすることを意味します。git-pull[1]も参照。
- プッシュ (push)
-
ブランチをプッシュするとは、リモートリポジトリからそのブランチのヘッド参照を取得し、それがブランチのローカルヘッド参照の祖先であるかどうかを確認し、その場合、ローカルヘッド参照から到達可能であり、リモートリポジトリに存在しないすべてのオブジェクトをリモートオブジェクトデータベースに入れ、リモートヘッド参照を更新することを意味します。リモートヘッドがローカルヘッドの祖先でない場合、プッシュは失敗します。
- 到達可能 (reachable)
-
与えられたコミットのすべての祖先は、そのコミットから「到達可能」であると言われます。より一般的には、あるオブジェクトが別のオブジェクトから到達可能であるとは、タグが指すもの、コミットがその親またはツリーを指すもの、ツリーがその中に含まれるツリーまたはブロブを指すものに従うチェーンによって、一方から他方に到達できる場合を指します。
- 到達可能性ビットマップ (reachability bitmaps)
-
到達可能性ビットマップは、パックファイルまたはマルチパックインデックス(MIDX)内の選択されたコミットセットの到達可能性に関する情報を格納し、オブジェクト検索を高速化します。ビットマップは「.bitmap」ファイルに格納されます。リポジトリでは最大1つのビットマップファイルを使用できます。ビットマップファイルは、1つのパック、またはリポジトリのマルチパックインデックス(存在する場合)のいずれかに属することができます。
- リベース (rebase)
- 参照 (ref)
-
オブジェクト名または別の参照(後者はシンボリック参照と呼ばれる)を指す名前。利便性のため、Gitコマンドの引数として使用される場合、参照は省略されることがあります。詳細については、gitrevisions[7]を参照してください。参照はリポジトリに保存されます。
参照名前空間は階層的です。参照名は
refs/
で始まるか、階層のルートに配置されている必要があります。後者の場合、その名前は以下のルールに従う必要があります。-
名前はすべて大文字の文字またはアンダースコアのみで構成されます。
-
名前は「
_HEAD
」で終わるか、「HEAD
」と等しいこと。階層のルートには、これらのルールに一致しないいくつかの不規則な参照が存在します。以下のリストは網羅的であり、将来的に拡張されることはありません。
-
AUTO_MERGE
-
BISECT_EXPECTED_REV
-
NOTES_MERGE_PARTIAL
-
NOTES_MERGE_REF
-
MERGE_AUTOSTASH
異なるサブ階層は異なる目的で使用されます。例えば、
refs/heads/
階層はローカルブランチを表すために使用され、refs/tags/
階層はローカルタグを表すために使用されます。
-
- リフロッグ (reflog)
-
リフロッグは参照のローカルな「履歴」を示します。言い換えれば、このリポジトリで3つ前のリビジョンが何だったか、そしてこのリポジトリで昨日午後9時14分に何が現在の状態だったかを教えてくれます。git-reflog[1]で詳細を確認してください。
- リフスペック (refspec)
-
「リフスペック」はフェッチとプッシュによって、リモート参照とローカル参照間のマッピングを記述するために使用されます。git-fetch[1]またはgit-push[1]で詳細を確認してください。
- リモートリポジトリ (remote repository)
-
同じプロジェクトを追跡するために使用されるが、別の場所に存在するリポジトリ。リモートとの通信については、フェッチまたはプッシュを参照してください。
- リモートトラッキングブランチ (remote-tracking branch)
-
別のリポジトリからの変更を追跡するために使用される参照。通常、refs/remotes/foo/bar(リモート名fooのブランチbarを追跡していることを示す)のように見え、設定されたフェッチrefspecの右側に一致します。リモートトラッキングブランチは直接的な変更やローカルコミットを含めるべきではありません。
- リポジトリ (repository)
-
参照の集合と、参照から到達可能なすべてのオブジェクトを含むオブジェクトデータベースで構成され、場合によっては1つ以上のポーセリンからのメタデータも伴います。リポジトリは、代替メカニズムを介して他のリポジトリとオブジェクトデータベースを共有できます。
- 解決 (resolve)
-
失敗した自動マージが残したものを手動で修正するアクション。
- リビジョン (revision)
-
コミット(名詞)の同義語。
- 巻き戻し (rewind)
- SCM
-
ソースコード管理(ツール)。
- SHA-1
-
「Secure Hash Algorithm 1」。暗号学的ハッシュ関数。Gitの文脈ではオブジェクト名の同義語として使用されます。
- シャロークローン (shallow clone)
-
ほとんどシャローリポジトリの同義語ですが、この表現は
git clone --depth=...
コマンドを実行して作成されたことをより明確に示します。 - シャローリポジトリ (shallow repository)
-
シャローリポジトリは不完全な履歴を持ち、そのコミットの一部は親が焼却されています(つまり、Gitは、それらの親がコミットオブジェクトに記録されていても、それらのコミットには親がないふりをすると言われます)。これは、実際の履歴がアップストリームで非常に大きい場合でも、プロジェクトの最近の履歴にのみ興味がある場合に役立つことがあります。シャローリポジトリは、git-clone[1]に
--depth
オプションを指定して作成され、その履歴は後でgit-fetch[1]で深くすることができます。 - スタッシュエントリ (stash entry)
- サブモジュール (submodule)
-
別のリポジトリ(後者はスーパープロジェクトと呼ばれる)内に、別のプロジェクトの履歴を保持するリポジトリ。
- スーパープロジェクト (superproject)
-
作業ツリーに他のプロジェクトのリポジトリをサブモジュールとして参照するリポジトリ。スーパープロジェクトは、含まれるサブモジュールのコミットオブジェクトの名前を知っていますが(コピーは保持していません)。
- シンボリック参照 (symref)
-
シンボリック参照: SHA-1 ID自体を含むのではなく、ref: refs/some/thingの形式であり、参照されると、この参照を再帰的にデリファレンスします。HEADはその典型的な例です。シンボリック参照はgit-symbolic-ref[1]コマンドで操作されます。
- タグ (tag)
-
refs/tags/
名前空間下の参照で、任意のタイプのオブジェクトを指します(通常、タグはタグまたはコミットオブジェクトのいずれかを指します)。ヘッドとは対照的に、タグはcommit
コマンドによって更新されません。GitのタグはLispのタグとは関係ありません(LispのタグはGitの文脈ではオブジェクトタイプと呼ばれます)。タグは、通常、コミット祖先チェーン内の特定の時点をマークするために最もよく使用されます。 - タグオブジェクト (tag object)
-
別のオブジェクトを指す参照を含むオブジェクトで、コミットオブジェクトのようにメッセージを含むことができます。また、(PGP)署名を含むこともでき、その場合は「署名付きタグオブジェクト」と呼ばれます。
- トピックブランチ (topic branch)
-
開発者が概念的な開発ラインを識別するために使用する通常のGitブランチ。ブランチは非常に簡単で安価なため、それぞれが非常によく定義された概念や、小さく漸進的でありながら関連する変更を含む複数の小さなブランチを持つことが望ましいことがよくあります。
- トレーラー (trailer)
-
キーと値のメタデータ。トレーラーはオプションでコミットメッセージの最後に記載されます。他のコミュニティでは「フッター」または「タグ」と呼ばれることもあります。git-interpret-trailers[1]を参照。
- ツリー (tree)
- ツリーオブジェクト (tree object)
-
ファイル名とモードのリスト、および関連するブロブオブジェクトやツリーオブジェクトへの参照を含むオブジェクト。ツリーはディレクトリに相当します。
- ツリーish (tree-ish) (treeishとも)
-
ツリーオブジェクト、またはツリーオブジェクトに再帰的にデリファレンスできるオブジェクト。コミットオブジェクトをデリファレンスすると、リビジョンのトップディレクトリに対応するツリーオブジェクトが得られます。以下はすべてツリーishです。コミットish、ツリーオブジェクト、ツリーオブジェクトを指すタグオブジェクト、ツリーオブジェクトを指すタグオブジェクトを指すタグオブジェクト、など。
- アンボーン (unborn)
-
HEADはまだ存在せず、まだコミットもないブランチを指すことができ、そのようなブランチはアンボーンブランチと呼ばれます。ユーザーがアンボーンブランチに出会う最も典型的な方法は、他の場所からクローンせずにリポジトリを新規作成することです。HEADはまだ生まれていないmain(または設定によってはmaster)ブランチを指すことになります。また、いくつかの操作ではorphanオプションを使用することでアンボーンブランチに移行できます。
- マージされていないインデックス (unmerged index)
-
マージされていないインデックスエントリを含むインデックス。
- 到達不能なオブジェクト (unreachable object)
- アップストリームブランチ (upstream branch)
-
対象のブランチにマージされる(または対象のブランチがリベースされる)デフォルトのブランチ。これはbranch.<name>.remoteおよびbranch.<name>.mergeで設定されます。Aのアップストリームブランチがorigin/Bである場合、時々「Aはorigin/Bを追跡している」と言います。
- 作業ツリー (working tree)
-
実際にチェックアウトされたファイルのツリー。作業ツリーには通常、HEADコミットのツリーの内容と、まだコミットしていないローカルの変更が含まれます。
- ワークツリー (worktree)
-
リポジトリにはゼロ(つまり、ベアリポジトリ)または1つ以上のワークツリーがアタッチされています。1つの「ワークツリー」は「作業ツリー」とリポジトリのメタデータで構成され、そのほとんどは単一のリポジトリの他のワークツリーと共有されますが、一部はワークツリーごとに個別に管理されます(例:インデックス、HEAD、MERGE_HEADなどの擬似参照、ワークツリーごとの参照、ワークツリーごとの設定ファイル)。
付録A:Gitクイックリファレンス
主要なコマンドの簡単なまとめです。前の章では、これらのコマンドがより詳細にどのように機能するかを説明しています。
新しいリポジトリの作成
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
ブランチの管理
$ 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
履歴を探索する
$ 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
回帰を検索する
$ 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.
変更を行う
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
または、1ステップでコミットを準備して作成します。
$ git commit d.txt # use latest content only of d.txt $ git commit -a # use latest content of all tracked files
マージ
$ 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
変更を共有する
パッチのインポートまたはエクスポート
$ 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
付録B:このマニュアルの注記とToDoリスト
ToDoリスト
これは作業進行中です。
基本的な要件
-
UNIXコマンドラインの基本的な理解があり、Gitに関する特別な知識を持たない知的な人であれば、最初から最後まで順番に読めるものでなければならない。必要であれば、他の前提条件は発生するたびに具体的に言及されるべきである。
-
可能な限り、セクションの見出しは、説明するタスクを、必要以上に知識を要しない言葉で明確に記述すべきである。例えば、「
git am
コマンド」ではなく、「プロジェクトへのパッチのインポート」のように。
人々が途中のすべてを必ずしも読むことなく重要なトピックにたどり着けるような、明確な章の依存関係グラフの作成方法を考える。
Documentation/
をスキャンして、他の見落とされたものを探す。特に
-
ハウツー
-
technical/
の一部? -
フック
-
git[1]のコマンドリスト
メールアーカイブをスキャンして、他の見落とされたものを探す
manページをスキャンして、このマニュアルが提供する以上の背景知識を仮定しているものがないか確認する。
より良い例を追加する。クックブック形式の例のみのセクションも良いアイデアかもしれない。「高度な例」セクションを章の標準的な最後に設けるのはどうだろうか。
適切であれば、用語集への相互参照を含めること。
CVS、Subversion、および一連のリリースtarballのインポートを含む、他のバージョン管理システムとの連携に関するセクションを追加する。
プラミングの使用とスクリプトの作成に関する章を書く。
オルタネート、クローン -リファレンスなど。