Table of Contents
Gitは高速分散型リビジョン管理システムです。
この説明書は、基本的なUNIXコマンドラインスキルを持っているが、Gitの予備知識がない人でも読めるように設計されています。
Chapter 1, Repositories and Branches と Chapter 2, Exploring Git history は、gitを使用してプロジェクトをフェッチして調査する方法を説明しています。これらの章を読んで、ソフトウェアプロジェクトの特定のバージョンをビルド、テスト、デグレを探す方法を学んで下さい。
実際の開発を行う必要がある人は、 Chapter 3, Gitでの開発作業 と Chapter 4, Sharing development with others も読みたいと思うでしょう。
それ以降の章では、より専門的なトピックについて説明します。
包括的なリファレンスドキュメントは、マニュアルページまたは git-help(1) コマンドから入手できます。 たとえば、コマンド git clone <repo>
の場合、次のいずれかを使用できます:
$ man git-clone
または:
$ git help clone
後者では、選択したマニュアルビューアを使用できます。 詳細については git-help(1) を参照してください。
詳しい説明なしでのGitコマンドの概要については、 Appendix A, Git Quick Reference も参照してください。
最後に、このマニュアルをより完全にするためにあなたが貢献する方法については、 Appendix B, Notes and todo list for this manual を参照してください。
Table of Contents
このマニュアルを読むときに、Gitリポジトリを試してみると便利です。
これを取得する最良の方法は、 git-clone(1) コマンドを使用して既存のリポジトリのコピーをダウンロードすることです。あなたがプロジェクトをまだ考えていない場合は、いくつかの興味深い例を以下に示します:
# Git itself (approx. 40MB download): $ git clone git://git.kernel.org/pub/scm/git/git.git # the Linux kernel (approx. 640MB download): $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
大規模なプロジェクトでは、最初のクローン作成に時間がかかる場合がありますが、クローン作成が必要になるのは1回だけです。
cloneコマンドは、プロジェクトにちなんで名付けられた新しいディレクトリを作成します(上記の例では git
または linux
)。このディレクトリにcdすると、 working tree と呼ばれるプロジェクトファイルのコピーと、プロジェクトの歴史について全ての情報を含む .git
という名前の特別なトップレベルディレクトリが含まれていることがわかります。
Gitは、ファイルのコレクションの履歴を保存するためのツールとして最もよく考えられています。プロジェクトのコンテンツの相互に関連するスナップショットの圧縮されたコレクションとして履歴を保存します。 Gitでは、このような各バージョンは commit と呼ばれます。
これらのスナップショットは、必ずしもすべてが古いものから新しいものへと一列に配置されているわけではなく、作業は、 branches と呼ばれる並行する開発ラインに沿って同時に進行する場合があります。これは、マージおよび分岐する場合があります。
単一のGitリポジトリで、複数のブランチの開発を追跡できます。これは、各ブランチの最新のコミットを参照する heads のリストを保持することによって行われます。 git-branch(1) コマンドは、ブランチヘッド(branch heads)を一覧表示します。
$ git branch * master
新しくクローンされたリポジトリには、デフォルトで「master」という名前の単一のブランチヘッドが含まれ、作業ディレクトリはそのブランチヘッドによって参照されるプロジェクトの状態に初期化されます。
ほとんどのプロジェクトは tags も使用します。タグはヘッドのようであり、プロジェクトの履歴への参照であり、 git-tag(1) コマンドを使用して一覧表示できます。
$ git tag -l v2.6.11 v2.6.11-tree v2.6.12 v2.6.12-rc2 v2.6.12-rc3 v2.6.12-rc4 v2.6.12-rc5 v2.6.12-rc6 v2.6.13 ...
タグは常にプロジェクトの同じバージョンを指すことが期待されますが、開発が進むにつれてヘッドは前進することが期待されます。
これらのバージョンのいずれかを指す新しいブランチヘッドを作成し、 git-switch(1) を使用してチェックアウトします:
$ git switch -c new v2.6.13
作業ディレクトリは、v2.6.13 のタグが付けられたときにプロジェクトが持っていた内容を反映し、 git-branch(1) は2つのブランチを表示し、アスタリスクは現在チェックアウトされているブランチを示します。
$ git branch master * new
あなたがバージョン 2.6.17 を使用することにした場合は、以下のようにして、代わりに v2.6.17 を指すように現在のブランチを変更できます。
$ git reset --hard v2.6.17
注意: 現在のブランチヘッドが履歴内の特定のポイントへの唯一の参照である場合、そのブランチをリセットすると、それが指していた履歴を見つける方法がなくなる可能性があることに注意してください。したがって、このコマンドは慎重に使用してください。
プロジェクトの履歴のすべての変更は、コミットによって表されます。 git-show(1) コマンドは、現在のブランチでの最新のコミットを表示します:
$ git show commit 17cf781661e6d38f737f15f53ab552f1e95960d7 Author: Linus Torvalds <torvalds@ppc970.osdl.org.(none)> Date: Tue Apr 19 14:11:06 2005 -0700 Remove duplicate getenv(DB_ENVIRONMENT) call Noted by Tony Luck. diff --git a/init-db.c b/init-db.c index 65898fa..b002dc6 100644 --- a/init-db.c +++ b/init-db.c @@ -7,7 +7,7 @@ int main(int argc, char **argv) { - char *sha1_dir = getenv(DB_ENVIRONMENT), *path; + char *sha1_dir, *path; int len, i; if (mkdir(".git", 0755) < 0) {
ご覧のとおり、コミットは、誰が最新の変更を行ったか、何をしたか、そしてその理由を示しています。
すべてのコミットには16進40桁のIDがあります。それは「オブジェクト名」または「SHA-1 ID」と呼ばれることもあり、「git show」出力の最初の行に表示されます。通常、タグやブランチ名などの短い名前でコミットを参照できますが、この長い名前も役立ちます。最も重要なことは、これはこのコミットにとってグローバルに一意の名前です。したがって、他の誰かにオブジェクト名を(たとえば電子メールで)伝えると、その名前が自分のリポジトリで行うのと同じコミットを参照することが保証されます(彼らのリポジトリにそのコミットがあると仮定します)。オブジェクト名はコミットの内容に対するハッシュとして計算されるため、名前が変更されてなければコミットも変更されてない事が保証されます。
実際、 Chapter 7, Gitの概念 では、ファイルデータやディレクトリの内容など、Gitの履歴に保存されているすべてのものが、その内容のハッシュである名前が付いたオブジェクトに保存されていることがわかります。
(プロジェクトの最初のコミットを除く)すべてのコミットには、このコミットの前に何が起こったかを示す親コミットもあります。親の連鎖をたどると、最終的にはプロジェクトの最初に戻ります。
ただし、コミットは単純なリストを形成しません。 Gitを使用すると、開発ラインが分岐してから再収束します。2つの開発ラインが再収束するポイントは「マージ」と呼ばれます。したがって、マージを表すコミットは1つ以上の親を持つことができ、各親はそれぞれがそのポイントにつながる開発ラインの1つで最も最近のコミットを表します。
これがどのように働くを確認する最良の方法は、 gitk(1) コマンドを使用することです。現在Gitリポジトリでgitkを実行し、マージコミットを探すと、Gitが履歴を整理する方法を理解するのに役立ちます。
以下、コミットXがコミットYの祖先である場合、コミットXはコミットYから「到達可能」(reachable)であると言います。同様に、YはXの子孫である、コミットYからコミットXにつながる親のチェーンがあると言えます。
以下のような図を使用して、Gitの履歴を表す場合があります。コミットは「o」として表示され、コミット間の繋がりは、 -
および /
および \
で描かれた線で示されます。時間は左から右に進みます:
o--o--o <-- Branch A / o--o--o <-- master \ o--o--o <-- Branch B
特定のコミットについて話す必要がある場合は、文字「o」を別の文字または数字に置き換えることができます。
正確にする必要がある場合は、「ブランチ」という言葉を使用して開発ラインを意味し、「ブランチヘッド」(branch head)(または単に「ヘッド」(head))という言葉を使用して、ブランチでの最新のコミットへの参照を意味します。上記の例では、「A」という名前のブランチヘッドは、1つの特定のコミットへのポインターですが、その時点までの3つのコミットの連なった線をすべて「ブランチA」の一部として参照します。
ただし、混乱が生じない場合は、ブランチとブランチヘッドの両方に「ブランチ」という用語を使用することがよくあります。
ブランチの作成、削除、および変更は、素早く・簡単です。コマンドの概要は以下のとおりです:
git branch
git branch <branch>
<branch>
という名前の新しいブランチを作成します。
git branch <branch> <start-point>
<start-point>
を参照して、 <branch>
という名前の新しいブランチを作成します。これは、ブランチ名やタグ名を使用するなど、任意の方法で指定できます。
git branch -d <branch>
git branch -D <branch>
<branch>
を削除します。
git switch <branch>
<branch>
にし、 <branch>
によって参照されるバージョンを反映するように作業ディレクトリを更新します。
git switch -c <new> <start-point>
<start-point>
を参照する新しいブランチ <new>
を作成し、それをチェックアウトします。
特殊シンボル記号「HEAD」は、常に現在のブランチを参照するために使用できます。実際、Gitは .git
ディレクトリにある HEAD
という名前のファイルを使用して、現在のブランチを記憶しています。
$ cat .git/HEAD ref: refs/heads/master
git switch
コマンドは通常、ブランチヘッドを想定していますが、 --detach
を伴って呼び出されると任意のコミットも受け入れます。たとえば、タグによって参照されるコミットをチェックアウトできます:
$ git switch --detach v2.6.17 Note: checking out 'v2.6.17'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another switch. (あなたは「切り離されたHEAD」状態にあります。あなたは周りを見回し、 実験的な変更を加えてコミットすることができます。また、別のswitchを実行することで、 他のブランチに影響を与えることなく、この状態で行ったコミットを破棄できます。) If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command again. Example: (作成したコミットを保持するために新しいブランチを作成する場合は、 switchコマンドで `-c` を再度使用して、(現在または後で)作成できます。例:) git switch -c new_branch_name HEAD is now at 427abfa Linux v2.6.17
次に、HEADはブランチではなくコミットのSHA-1を参照し、 git branch
は、あなたがもはやブランチ上にいないことを示します:
$ cat .git/HEAD 427abfa28afedffadfca9dd8b067eb6d36bac53f $ git branch * (detached from v2.6.17) master
この場合、HEADは「切り離されている」(detached)と言います。
これは、新しいブランチの名前を作成しなくても、 特定のバージョンをチェックアウトする簡単な方法です。 必要に応じて、後でこのバージョンの新しいブランチ(またはタグ)を作成することもできます。
クローン作成時に作成された「master」ブランチは、クローン作成元のリポジトリにあるHEADのコピーです。ただし、そのリポジトリには他のブランチも含まれている可能性があり、ローカルリポジトリには、リモート追跡ブランチ(remote-tracking branch)と呼ばれる、これらのリモートブランチのそれぞれを追跡するブランチが保持されます。これは、 git-branch(1) の -r
オプションを使用して表示できます:
$ git branch -r origin/HEAD origin/html origin/maint origin/man origin/master origin/next origin/seen origin/todo
この例では、「origin」はリモートリポジトリ、または略して「リモート」(remote)と呼ばれます。このリポジトリのブランチは、私たちの観点からは「リモートブランチ」と呼ばれます。上記のリモート追跡ブランチは、クローン時にリモートブランチに基づいて作成され、 git fetch
(つまり git pull
でも)と git push
によって更新されます。 詳細については、 the section called “Updating a repository with git fetch” を参照してください。
タグの場合と同じように、これらのリモート追跡ブランチの1つをあなた独自のブランチに構築することをお勧めします:
$ git switch -c my-todo-copy origin/todo
origin/todo
を直接チェックアウトして調べたり、1回限りのパッチを作成したりすることもできます。 detached head を参照してください。
注意: 「origin」という名前は、clone元のリポジトリを参照するためにGitがデフォルトで使用する名前にすぎないことに注意してください。
ブランチやリモート追跡ブランチやタグはすべてコミットへの参照です。すべての参照は、 refs
で始まるスラッシュ(/
)で区切られたパス名で名前が付けられます。これまで使用してきた名前は、実際には省略形です:
test
は refs/heads/test
の略です。
v2.6.18
は refs/tags/v2.6.18
の略です。
origin/master
は refs/remotes/origin/master
の略です。
フルネームでの指定は、たとえば、同じ名前のタグとブランチが存在する場合に役立つことがあります。
(新しく作成されたrefは、実際にはその名前で指定されたパスで .git/refs
ディレクトリに保存されます。ただし、効率上の理由から、単一のファイルにまとめてパックすることもできます。 git-pack-refs(1) を参照してください。)
もう1つの便利な短縮形として、リポジトリの「HEAD」は、そのリポジトリの名前を使用するだけで参照できます。したがって、たとえば、「origin」は通常、「リポジトリ origin のHEADブランチ」の短縮形です。
Gitが参照を調べるするパスの完全なリスト、および同じ省略名を持つ複数の参照がある場合に選択するために使用する順序については、 gitrevisions(7) の「SPECIFYING REVISIONS」セクションを参照してください。
リポジトリのクローンを作成し、独自の変更をいくつかコミットした後、あなたは元のリポジトリで更新を確認することをお勧めします。
引数を指定しない 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 <remote>
を実行すると、 <remote>
という名前のリモート追跡ブランチが更新されます。
ファイル .git/config
を調べると、Gitが新しい一節を追加したことがわかります:
$ cat .git/config ... [remote "staging"] url = git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git fetch = +refs/heads/*:refs/remotes/staging/* ...
これが、Gitがリモートのブランチを追跡できる理由です。 テキストエディタで .git/config
を編集することにより、これらの設定オプションを変更または削除できます。 (詳細については、 git-config(1) の「CONFIGURATION FILE」セクションを参照してください。)
Table of Contents
Gitは、ファイルのコレクションの履歴を保存するためのツールとして最もよく考えられています。これは、ファイル階層のコンテンツの圧縮されたスナップショットを、これらのスナップショット間の関係を示す「コミット」とともに保存することによって行われます。
Gitは、プロジェクトの履歴を探索するための非常に柔軟で高速なツールを提供します。
私達は、プロジェクトにバグを導入したコミットを見つけるのに役立つ、ある専用ツールから始めることにします。
あなたのプロジェクトのバージョン2.6.18は機能していしたが、「master」のバージョンがクラッシュしたとします。このようなデグレの原因を見つける最良の方法は、プロジェクトの履歴を総当たりで検索して、問題の原因となった特定のコミットを見つけることです。 git-bisect(1) コマンドは、あなたがこれを行う助けとなります:
$ git bisect start $ git bisect good v2.6.18 $ git bisect bad master Bisecting: 3537 revisions left to test after this [65934a9a028b88e83e2b0f8b36618fe503349f8e] BLOCK: Make USB storage depend on SCSI rather than selecting it [try #6]
この時点で git branch
を実行すると、Gitが一時的に あなたを "(no branch)" に移動させたことがわかります。HEADはブランチから切り離され、「master」からは到達可能であるが v2.6.18 からは到達できないコミット(コミットID 65934)を直接指します。コンパイルしてテストし、クラッシュするかどうかを確認します。クラッシュするとします。ならばあなたは以下のようにします:
$ git bisect bad Bisecting: 1769 revisions left to test after this [7eff82c8b1511017ae605f0c99ac275a7e21b867] i2c-core: Drop useless bitmaskings
より古いバージョンをチェックアウトします。このように作業を続けて、各ステップで提供されるバージョンが良いか悪いかをGitに伝え、テストするために残されたリビジョンの数が毎回約半分に削減されることに注目してください。
(今回は)約13回のテストの後、有罪コミット(guilty commit)のコミットIDが出力されました。あなたは、次に、 git-show(1) を使用してコミットを調べ、誰がコミットを作成したかを調べ、コミットIDを使用してバグレポートをメールで送信できます。そして最後に、あなたは以下を実行します:
$ git bisect reset
そうすると、あなたが以前にいたブランチに戻ります。
注意: git bisect
が各時点でチェックアウトするバージョンは単なる提案であり、そっちの方が良い考えであると思った場合は、別のバージョンを自由に試すことができることに注意してください。 たとえば、関係のない何かを壊したコミットにたどり着くことがあったりします。このようにしたいときは、以下のようにします。
$ git bisect visualize
これはgitkを実行し、選択したコミットに「bisect」というマーカーでラベルを付けます。近くで安全に見えるコミットを選択し、そのコミットIDをメモして、以下のコマンドでチェックアウトします:
$ git reset --hard fb47ddb2db
そしてテストし、必要に応じて「bisect good」または「bisect bad」を実行して続行します。
git bisect visualize
と git reset --hard fb47ddb2db
を行う代わりに、現在のコミットをスキップすることをGitに伝えたい場合があります:
$ git bisect skip
ただし、この場合、Gitは、最初にスキップされたコミットとその後の不正なコミットの間で、最初の不正なコミットを最終的に判断できない可能性があります。
良いコミットと悪いコミットを区別できるテストスクリプトがある場合は、bisecするプロセスを自動化する方法もあります。この機能およびその他の git bisect
機能の詳細については、 git-bisect(1) を参照してください。
コミットに名前を付けるいくつかの方法をすでに見てきました:
その他いろいろ: リビジョンに名前を付ける方法の完全なリストについては、 gitrevisions(7) のマニュアルページの「SPECIFYING REVISIONS」セクションを参照してください。 例をいくつか:
$ git show fb47ddb2 # オブジェクト名の最初の数文字は、 # 通常、一意に指定するのに十分です。 $ git show HEAD^ # HEADコミットの親 $ git show HEAD^^ # 祖父母(grandparent;親の親) $ git show HEAD~4 # 曽祖父母(great-great-grandparent)
マージコミットには複数の親が存在する可能性があることを思い出してください。 デフォルトでは、 ^
と ~
はそのコミットにリストされている最初の親を辿りますが、以下のように選択することもできます:
$ git show HEAD^1 # HEADの(複数ある)親のうち1番目の親 $ git show HEAD^2 # HEADの複数ある親のうち2番目の親
HEADに加えて、コミットには他にもいくつかの特別な名前があります:
マージ(後で説明します)、および現在チェックアウトされているコミットを変更する git reset
などの操作では、通常、 ORIG_HEAD を、「現在の操作の前にHEADが持っていた値」に設定します。
git fetch
操作は、常に、最後にフェッチされたブランチのヘッドをFETCH_HEADに格納します。 たとえば、以下のように、操作のターゲットとしてローカルブランチを指定せずに git fetch
を実行した場合
$ git fetch git://example.com/proj.git theirbranch
フェッチされたコミットは、引き続きFETCH_HEADから利用できます。
マージについて説明すると、現在のブランチにマージしている他のブランチを指す特別な名前MERGE_HEADもあります。
git-rev-parse(1) コマンドは、コミットの名前をそのコミットのオブジェクト名に変換するのに役立つことがある低レベルのコマンドです。
$ git rev-parse origin e05db0fd4f31dde7005f075a84f96b360d05984b
特定のコミットを参照するタグを作成することもできます。以下を実行すると
$ git tag stable-1 1b2e1d63ff
あなたは stable-1
を使用して、コミット 1b2e1d63ff
を参照できます。
これにより、「軽量」タグ(lightweight tag)が作成されます。タグにコメントを含め、場合によっては暗号で署名する場合は、代わりにタグオブジェクトを作成する必要があります。詳細については、 git-tag(1) のマニュアルページを参照してください。
git-log(1) コマンドは、コミットのリストを表示できます。それ自体で、親コミットから到達可能なすべてのコミットが表示されますが、より具体的なリクエストを行うこともできます。
$ git log v2.5.. # commits since (not reachable from) v2.5 $ git log test..master # commits reachable from master but not test $ git log master..test # ...reachable from test but not master $ git log master...test # ...reachable from either test or master, # but not both $ git log --since="2 weeks ago" # commits from the last 2 weeks $ git log Makefile # commits which modify Makefile $ git log fs/ # ... which modify any file under fs/ $ git log -S'foo()' # commits which add or remove any file data # matching the string 'foo()'
そしてもちろん、これらすべてを組み合わせることができます。 以下は、 Makefile
または fs
ディレクトリより下の任意のファイル、を触った v2.5 以降のコミットを検索します:
$ git log v2.5.. Makefile fs/
パッチを表示するようにgit logに依頼することもできます:
$ git log -p
その他の表示オプションについては、 git-log(1) のマニュアルページの --pretty
オプションを参照してください。
git log は最新のコミットから始まり、親を介して逆方向に機能することに注意してください。 ただし、Gitの履歴には複数の独立した開発ラインが含まれる可能性があるため、コミットがリストされる順序は多少前後する可能性があります。
git-diff(1) を使用して、任意の2つのバージョン間の差分(diff)を生成できます。
$ git diff master..test
これにより、2つのブランチの先端の間にdiffが生じます。共通の祖先からのdiffを見つけてテストしたい場合は、2つではなく3つのドットを使用できます:
$ git diff master...test
しばしば、あなたが必要なのは差分の代わりにパッチのセットである場合があります。これには、 git-format-patch(1) を使用できます。
$ git format-patch master..test
これは、masterからではなく、testから到達可能な各コミットのパッチを含むファイルを生成します。
あなたは、最初に正しいリビジョンをチェックアウトするだけで、いつでも古いバージョンのファイルを表示できます。ただし、何もチェックアウトせずに古いバージョンの単一ファイルを表示できる方が便利な場合もあります。以下のコマンドはそれを行います:
$ git show v2.5:fs/locks.c
コロン(:
)の前にはコミットを指定するものがあり、その後にはGitによって追跡されているファイルへのパスがあります。
あなたが origin
から分岐して以来、 mybranch
に対して行ったコミットの数を知りたいとします:
$ git log --pretty=oneline origin..mybranch | wc -l
あるいは、この種のことは、指定されたすべてのコミットのSHA-1を一覧表示するだけの低レベルのコマンド git-rev-list(1) で行われることがよくあります:
$ git rev-list origin..mybranch | wc -l
あなたが、2つのブランチが履歴の同じポイントを指しているかどうかを確認したいとします。
$ git diff origin..master
プロジェクトの内容が2つのブランチで同じであるかどうかがわかります。ただし、理論的には、同じプロジェクトの内容が2つの異なる履歴ルートによって到達された可能性があります。あなたはオブジェクト名を比較できます:
$ git rev-list origin e05db0fd4f31dde7005f075a84f96b360d05984b $ git rev-list master e05db0fd4f31dde7005f075a84f96b360d05984b
または、 ...
演算子は、一方の参照または他方の参照から到達可能なすべてのコミットを選択しますが、両方は選択しないことを思い出してください。ということで以下を実行すると
$ git log origin...master
この2つのブランチが等しい場合、コミットは返されません。
コミット e05db0fd が特定の問題を修正したことを知っているとします。その修正を含む最も古いタグ付きリリースを見つけたいと思います。
ええ、もちろん、複数の答えがあるかもしれません。コミット e05db0fd の後に履歴が分岐した場合、複数の「最も早い」タグ付きリリースが存在する可能性があります。
あなたは e05db0fd 以降のコミットを視覚的に検査できます:
$ gitk e05db0fd..
または、あなたは git-name-rev(1) を使用できます。これにより、コミットの子孫の1つを指すタグが見つかった場合は、そのコミットに名前が与えられます(訳注:コミットの子孫の1つを指すタグが tags/v1.5.0-rc1 であったのでそこから辿って e05db0fd は tags/v1.5.0-rc1^0~23 という「名前」になった):
$ git name-rev --tags e05db0fd e05db0fd tags/v1.5.0-rc1^0~23
git-describe(1) コマンドは逆のことを行い、指定されたコミットの基になっているタグを使用してリビジョンに名前を付けます:
$ git describe e05db0fd v1.5.0-rc0-260-ge05db0f
これは、特定のコミット後にどのタグが来るかを推測するのに役立つ場合があります。
あなたが特定のタグ付きバージョンに特定のコミットが含まれているかどうかを確認したいだけの場合は、 git-merge-base(1) を使用できます:
$ git merge-base e05db0fd v1.5.0-rc1 e05db0fd4f31dde7005f075a84f96b360d05984b
merge-baseコマンドは、指定されたコミットの共通の祖先を検索し、一方が他方の子孫である場合は常にどちらか一方を返します。したがって、上記の出力は、e05db0fdが実際にはv1.5.0-rc1の祖先であることを示しています。
あるいは、以下のように
$ git log v1.5.0-rc1..e05db0fd
とすると、v1.5.0-rc1にe05db0fdが含まれている場合のみ、空の出力が生成されます。これは、v1.5.0-rc1から到達できないコミットのみを出力するためです。
さらに別の方法として、 git-show-branch(1) コマンドは、指定の引数から到達可能なコミットを一覧表示し、その各行の左側にそのコミットが指定のどの引数から到達可能かを示す表示を行います。よって、あなたが以下のようなものを実行する場合
$ git show-branch e05db0fd v1.5.0-rc0 v1.5.0-rc1 v1.5.0-rc2 ! [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if available ! [v1.5.0-rc0] GIT v1.5.0 preview ! [v1.5.0-rc1] GIT v1.5.0-rc1 ! [v1.5.0-rc2] GIT v1.5.0-rc2 ...
そしてこれに続く行は以下のようになります
+ ++ [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if available
これは、e05db0fdが、v1.5.0-rc0からではなく、v1.5.0-rc1とv1.5.0-rc2から到達可能であることを示しています。
master
という名前のブランチヘッドから到達可能なすべてのコミットを確認したいが、リポジトリ内の他のヘッドからは到達できないと仮定します。
git-show-ref(1) を使用して、このリポジトリ内のすべてのヘッドを一覧表示できます。
$ git show-ref --heads bf62196b5e363d73353a9dcf094c59595f3153b7 refs/heads/core-tutorial db768d5504c1bb46f63ee9d6e1772bd047e05bf9 refs/heads/maint a07157ac624b2524a059a3414e99f6f44bebc1e7 refs/heads/master 24dbc180ea14dc1aebe09f14c8ecf32010690627 refs/heads/tutorial-2 1e87486ae06626c2f31eaa63d26fc0fd646c8af2 refs/heads/tutorial-fixes
標準のユーティリティcutとgrepを使用して、ブランチヘッド名だけを取得し、 master
を削除できます:
$ git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master' refs/heads/core-tutorial refs/heads/maint refs/heads/tutorial-2 refs/heads/tutorial-fixes
そして、masterから到達可能だけども他のヘッドからは到達できない全てのコミットを確認するように依頼できます:
$ gitk master --not $( git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master' )
このようにすれば、明らかに、無限のバリエーションが可能です。 たとえば、リポジトリ内のタグからではなく、あるヘッドから到達可能なすべてのコミットを表示するには、以下のようにします:
$ gitk $( git show-ref --heads ) --not $( git show-ref --tags )
( --not
などのコミット選択構文の説明については、 gitrevisions(7) を参照してください。)
git-archive(1) コマンドは、プロジェクトの任意のバージョンからtarまたはzipアーカイブを作成できます。例えば:
$ git archive -o latest.tar.gz --prefix=project/ HEAD
HEADを使用して、各ファイル名の前に project/
が付いたgzip圧縮されたtarアーカイブを作成します。出力ファイル形式は、可能であれば出力ファイル拡張子から推測されます。詳細については git-archive(1) を参照してください。
1.7.7より古いバージョンのGitは、 `tar.gz`形式を認識していません。明示的にgzipを使用する必要があります:
$ git archive --format=tar --prefix=project/ HEAD | gzip >latest.tar.gz
ソフトウェアプロジェクトの新しいバージョンをリリースする場合は、リリースアナウンスに含める変更ログを同時に作成することをお勧めします。
Linus Torvaldsは、たとえば、タグを付けて実行することにより、新しいカーネルリリースを作成します:
$ release-script 2.6.12 2.6.13-rc6 2.6.13-rc7
ここで、release-script は以下のようなシェルスクリプトです:
#!/bin/sh stable="$1" last="$2" new="$3" echo "# git tag v$new" echo "git archive --prefix=linux-$new/ v$new | gzip -9 > ../linux-$new.tar.gz" echo "git diff v$stable v$new | gzip -9 > ../patch-$new.gz" echo "git log --no-merges v$new ^v$last > ../ChangeLog-$new" echo "git shortlog --no-merges v$new ^v$last > ../ShortLog" echo "git diff --stat --summary -M v$last v$new > ../diffstat-$new"
そしてそれから、シェルスクリプトが出力したコマンドが正常に見えることを確認した後、出力されたコマンドをカットアンドペーストします。
誰かがあなたにファイルのコピーを渡し、与えられた内容を含むようにファイルを変更したコミットを、そのコミットの前と後のどちらで行ったかを尋ねます。 それを知るには、以下のようにします:
$ git log --raw --abbrev=40 --pretty=oneline | grep -B 1 `git hash-object filename`
なぜこれが機能するのかを理解することは、(上級)学生への演習として残されています。 git-log(1) や git-diff-tree(1) や git-hash-object(1) のマニュアルページが役に立つ事でしょう。
Table of Contents
コミットを作成する前に、あなた自身を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つのステップがあります:
実際には、あなたが望むかぎりステップ1と2を何度でも行き来したり繰り返したりすることができます。ステップ3でコミットしたい内容を追跡するために、Gitは「インデックス」(index)と呼ばれる特別なステージング領域にツリーのコンテンツのスナップショットを保持します。
最初は、インデックスの内容はHEADの内容と同じになります。したがって、HEADとインデックスの違いを示すコマンド git diff --cached
は、その時点では何も出力を生成しないはずです。
インデックスの変更は簡単です:
新規または変更されたファイルの内容でインデックスを更新するには、以下を使用します
$ git add path/to/file
インデックスと作業ツリーからファイルを削除するには、以下を使用します
$ git rm path/to/file
各ステップの後、あなたは以下のようにして確認できます
$ git diff --cached
常にHEADとインデックスファイルの違い示します(つまり、今、この時点でコミットを作成した場合、何がコミットされる事になるのかを知る事ができます)
$ git diff
これは、作業ツリーとインデックスファイルの違いを示しています。
注意: git add
は常に、ファイルのその時点での内容をインデックスに追加するだけであることに注意してください。同じファイルに対して git add
を再度実行しない限り、そのファイルへのその後の編集変更は無視されます。
準備ができたら、あとは実行するだけです
$ git commit
Gitはコミットメッセージの入力を求め、新しいコミットを作成します。以下のようにして、それがあなたが期待したもののように見えることをチェックしてください
$ git show
特別な省略形として、以下があります
$ git commit -a
これは、変更または削除したファイルでインデックスを更新し、コミットを作成します。それらを1ステップで実行します。
あなたの行ったコミットを追跡するために、いくつかのコマンドが役立ちます
$ git diff --cached # HEADとインデックス間の差異。 # 今、コミットを実行したら何がコミットされるかを知る。 $ git diff # インデックスファイルと作業ディレクトリの違い。 # 今、コミットを実行したら、 # 何がそのコミットに「含まれない」かを知る。 $ git diff HEAD # HEADと作業ツリーの違い。 # 今、 `commit -a` を実行したら何がコミットされるかを知る。 $ git status # 上記のファイルごとの簡単な要約。
git-gui(1) を使用して、コミットを作成し、インデックスと作業ツリーファイルの変更を表示し、インデックスに含めるdiffハンクを個別に選択することもできます(diffハンクを右クリックして「Stage Hunk For Commit」を選択します)。
必須ではありませんが、変更を要約した1行の短い行(50文字未満)でコミットメッセージを開始し、その後に空行を続け、さらに詳細な説明を続けることをお勧めします。 コミットメッセージの最初の空行までのテキストはコミットタイトルとして扱われ、そのタイトルはGit全体で使用されます。たとえば、 git-format-patch(1) はコミットを電子メールに変換し、コミットタイトルを件名に使用し、コミットタイトル以降を本文として使用します。
プロジェクトは、Gitで追跡したくないファイルを生成することがよくあります。これには通常、ビルドプロセスによって生成されたファイル、またはエディターによって作成された一時バックアップファイルが含まれます。もちろん、Gitでファイルを追跡しないのは、ファイルに対して「git add」を呼び出さないだけの問題です。しかし、これらの追跡されていないファイルがたくさん横たわっているのはすぐにうっとおしくなります。例えば それらは git add .
を実質的に役に立たなくし、 git status
の出力に表示され続けます。
あなたは、作業ディレクトリの最上位に .gitignore
というファイルを作成し、以下のような内容で特定のファイルを無視するようにGitに指示できます:
# '#'で始まる行はコメントと見なされます。 # foo.txtという名前のファイルはすべて無視してください。 foo.txt # (生成された) htmlファイルを無視します。 *.html # 但し、手作業で管理されているfoo.htmlを除きます(無視しません)。 !foo.html # オブジェクト(*.o)とアーカイブ(*.a)を無視します。 *.[oa]
構文の詳細な説明については、 gitignore(5) を参照してください。作業ツリーの他のディレクトリに .gitignore ファイルを配置することもでき、それらはそれらのディレクトリとその下のサブディレクトリに適用されます。 .gitignore
ファイルは、他のファイルと同じようにリポジトリに追加できます
.(通常どおり git add .gitignore
と git commit
を実行するだけです)。
これは、除外パターン(ビルド出力ファイルに一致するパターンなど)の場合に便利です。リポジトリのクローンを作成する他のユーザーにとっても意味があります。<
除外パターンが(特定のプロジェクトのすべてのリポジトリではなく、)特定のリポジトリにのみ影響するようにする場合は、代わりに、リポジトリ内の .git/info/exclude
という名前のファイル、または core.excludesFile
構成変数で指定されたファイルに除外パターンを配置できます。一部のGitコマンドは、コマンドラインで直接除外パターンを指定することもできます。詳細については、 gitignore(5) を参照してください。
git-merge(1)を使用して、2つに分岐したブランチを再結合できます:
$ git merge branchname
ブランチ branchname
を現在のブランチにマージします。
マージは、 branchname
で行われた変更と、履歴が分岐してから現在のブランチで最新のコミットまでに行われた変更とを組み合わせることによって行われます。作業ツリーは、この結合がクリーンに実行された場合はマージの結果によって上書きされ、この結合が競合を引き起こした場合は半マージ(half-merged)された結果によって上書きされます。したがって、マージの影響を受けたファイルと同じファイルに変更を加えたコミットされていない変更がある場合、Gitは続行を拒否します。ほとんどの場合、マージする前に変更をコミットする必要があります。コミットしない場合は、 git-stash(1) を使用すると、マージ中にこれらの変更を一旦取り除いて、後で再適用できます。
変更が十分に独立している場合、Gitは自動的にマージを完了し、結果をコミットします(または、fast-forwardの場合は、既存のコミットを再利用します。これについては以下を参照してください)。 一方、競合がある場合—たとえば、リモートブランチとローカルブランチで同じファイルが異なる方法で変更された場合—は警告が表示されます。出力は以下のようになります:
$ git merge next 100% (4/4) done Auto-merged file.txt CONFLICT (content): Merge conflict in file.txt Automatic merge failed; fix conflicts and then commit the result.
問題のあるファイルには競合マーカーが残ります(訳注:つまり そのファイルは git merge が編集したものです)。あなたは競合を手動で解決した後(訳注:当該ファイルを手動で編集してセーブして)、新しいファイルを作成するときと同じように、インデックスを更新してGit commitを実行できます。
あなたがgitkを使用して結果のコミットを調べると、2つの親があり、1つは現在のブランチの先頭を指し、もう1つはもう一方のブランチの先頭を指していることがわかるはずです。
マージが自動的に解決されない場合、Gitはインデックスと作業ツリーを特別な状態のままにして、マージの解決に必要なすべての情報を提供します。
特に、競合のあるファイルはインデックスでマークされるため、問題を解決してインデックスを更新するまで、 git-commit(1) は失敗します:
$ git commit file.txt: needs merge
また、 git-status(1) は、これらのファイルを "unmerged" としてリストし、競合のあるファイルの内容には、以下のように競合マーカー(conflict marker)が追加されます(訳注:ファイルの内容が以下のように変更されます):
<<<<<<< HEAD:file.txt Hello world ======= Goodbye >>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
あなたしなければならないことは、競合を解決するためにファイルを編集することです。そしてその後、以下のようにします。
$ git add file.txt $ git commit
注意: コミットメッセージには、マージに関するいくつかの情報がすでに入力されていることに注意してください。 通常、このデフォルトのメッセージは変更せずに使用できますが、必要に応じて独自のコメントを追加することもできます。
単純なマージを解決するために知っておく必要があるのは、上記ですべてです。 ただし、Gitは、競合の解決に役立つ詳細情報も提供します:
Gitが自動的にマージできたすべての変更は、すでにインデックスファイルに追加されているため、 git-diff(1) は競合のみを表示します。それは普通でない構文を使用します:
$ git diff diff --cc file.txt index 802992c,2b60207..0000000 --- a/file.txt +++ b/file.txt @@@ -1,1 -1,1 +1,5 @@@ ++<<<<<<< HEAD:file.txt +Hello world ++======= + Goodbye ++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
この競合を解決した後にコミットされるコミットには、通常の親ではなく2つの親があることを思い出してください。1つの親は現在のブランチの先端であるHEADになります。もう一方は、MERGE_HEADに一時的に保存されるもう一方のブランチの先端になります。
マージ中、インデックスは各ファイルについて3つのバージョンを保持します。これらの3つの「ファイルステージ」(file stages)はそれぞれ、ファイルの異なるバージョンを表します。
$ git show :1:file.txt # 両方のブランチの共通の祖先にあるファイル $ git show :2:file.txt # the version from HEAD. $ git show :3:file.txt # the version from MERGE_HEAD.
git-diff(1) に競合を表示するように要求すると、競合するマージ結果間で3方向のdiffが実行され、ステージ2と3の作業ツリーで、内容がステージ2と3の両方があるハンクのみが混合されて表示されます(つまり、ハンクのマージ結果がステージ2からのみ取得される場合、その部分は競合せず、表示されません。ステージ3についても同じで、ハンクのマージ結果がステージ3からのみ取得される場合、その部分は競合せず、表示されません)。
上記の差分は、file.txt の作業ツリーバージョンと、ステージ2バージョンと、ステージ3バージョンとの間の違いを示しています。したがって、各行の前に単一の +
または -
を付ける代わりに、2つの桁を使用するようになりました。最初の桁は最初の親と作業ディレクトリのコピーの違いに使用され、2番目の列は2番目の親と 作業ディレクトリのコピーの違いに使用されます。(形式の詳細については、 linkgit: git-diff-files[1] の「COMBINED DIFF FORMAT」セクションを参照してください。)
あなたが編集するなどして、ちゃんと競合を解決した後(ただし、インデックスを更新する前)でdiffを実行してみると以下のようになります:
$ git diff diff --cc file.txt index 802992c,2b60207..0000000 --- a/file.txt +++ b/file.txt @@@ -1,1 -1,1 +1,1 @@@ - Hello world -Goodbye ++Goodbye world
これは、解決されたバージョンが最初の親から「Hello world」を削除し、2番目の親から「Goodbye」を削除し、以前は両方に存在しなかった「Goodbye world」を追加したことを示しています。
いくつかの特別なdiffオプションを使用すると、これらのステージのいずれかに対して作業ディレクトリとdiffを取ることができます:
$ git diff -1 file.txt # diff against stage 1 $ git diff --base file.txt # same as the above $ git diff -2 file.txt # diff against stage 2 $ git diff --ours file.txt # same as the above $ git diff -3 file.txt # diff against stage 3 $ git diff --theirs file.txt # same as the above.
(デフォルトの)「ort」マージ戦略を使用する場合、 Git は、マージの結果で作業ツリー(working tree)を更新する前に、 書き込もうとしているツリーの状態を反映する AUTO_MERGE という名前の特別な ref を書き込みます。 自動的にマージができなかったテキストの競合を伴う競合パスは、 作業ツリーと同様に、 競合マーカーとともにこのツリーに書き込まれます。 したがって、AUTO_MERGE を git-diff(1) とともに使用すると、 競合を解決するためにこれまでに行った変更を表示できます。 上記と同じ例を使用して競合を解決すると、以下のようになります:
$ git diff AUTO_MERGE diff --git a/file.txt b/file.txt index cd10406..8bf5ae7 100644 --- a/file.txt +++ b/file.txt @@ -1,5 +1 @@ -<<<<<<< HEAD:file.txt -Hello world -======= -Goodbye ->>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt +Goodbye world
diffを見ると、 競合マーカー(conflict markers)と両方のバージョンの内容行(content line)が削除され、 代わりに「Goodbye world」と書き込まれたことがわかります。
git-log(1) や gitk(1) コマンドも、マージのためにに特別な援助を提供します:
$ git log --merge $ gitk --merge
これらは、HEADまたはMERGE_HEADにのみ存在し、マージされていないファイルにアクセスするすべてのコミットを表示します。
あなたは git-mergetool(1) を使用することもできます。これにより、Emacsやkdiff3などの外部ツールを使用してマージされていないファイルをマージできます。
ファイル内の競合を解決するたびに、以下のようにしてインデックスを更新します:
$ git add file.txt
そうすると、そのファイルのさまざまなステージが「折りたたまれ」(collapsed)、その後、 git diff
は(デフォルトでは)そのファイルのdiffを表示しなくなります。
行き詰まり、あきらめて、混乱全体を捨てることにした場合は、いつでもマージ前の状態に戻ることができます
$ git merge --abort
または、あなたが破棄したいマージをすでにコミットしちゃってる場合は以下です
$ git reset --hard ORIG_HEAD
ただし、この最後のコマンドは危険な場合があります。コミット自体が別のブランチにマージされている可能性がある場合は、既にコミット済のコミットを破棄しないでください。これを行うと、さらにマージが混乱する可能性があります。
上記に記載されていない特殊なケースが1つあり、それは異なる方法で処理されます。通常、マージは、マージされた2つの開発ラインのそれぞれを指す2つの親を持つマージコミットをもたらします。
けれども、現在のブランチが他のブランチの祖先である場合(したがって、現在のブランチに存在するすべてのコミットは、すでに他のブランチに含まれています)、Gitは「fast-forward」(早送り)を実行するだけです。現在のブランチのヘッドは、新しいコミットが作成されることなく、マージされたブランチのヘッドを指すように前方に移動されます。
作業ツリーを台無しにしたが、まだ間違いをコミットしていない場合は、作業ツリー全体を最後にコミットした状態に戻すことができます
$ git restore --staged --worktree :/
コミットしたのを後悔した時、問題を解決するためには、根本的に異なる2つの方法があります:
以前の変更を元に戻す新しいコミットを作成するのは非常に簡単です。 git-revert(1) コマンドに不正なコミットへの参照を渡すだけです。たとえば、最新のコミットを元に戻すには、以下のようにします:
$ git revert HEAD
これにより、HEADの変更を元に戻す新しいコミットが作成されます。そしてあなたには新しいコミットのコミットメッセージを編集する機会が与えられます。
以前の変更を元に戻すこともできます。たとえば、最後から2番目を指定します:
$ git revert HEAD^
この場合、Gitは古い変更を元に戻そうとしますが、それ以降に行われた変更はそのまま残します。最近の変更が元に戻す変更と重複する場合は、resolving a mergeの場合と同様に、競合を手動で修正するように求められます。
問題のあるコミットが最新のコミットであり、そのコミットをまだ公開していない場合は、git reset
を使用して破棄することができます。
あるいは、create a new commitを実行する場合と同じように、作業ディレクトリを編集してインデックスを更新し、間違いを修正することもできます。
$ git commit --amend
これにより、古いコミットが変更を組み込んだ新しいコミットに置き換えられ、最初に古いコミットメッセージの方を編集する機会が与えられます。
繰り返しになりますが、すでに別のブランチにマージされている可能性のあるコミットに対してこれを実行しないでください。 その場合は、代わりに git-revert(1) を使用してください。
履歴のさらに古いコミットを置き換えることも可能ですが、これは別の章で扱う高度なトピックです。
以前の悪い変更を元に戻すプロセスでは、 git-restore(1) を使用して特定のファイルの古いバージョンをチェックアウトすると便利な場合があります。
$ git restore --source=HEAD^ path/to/file
これは path/to/fileを、コミットHEAD^にある内容に置き換え、それに一致するようにインデックスを更新します。ブランチは変更されません。
作業ディレクトリを変更せずに古いバージョンのファイルを確認したい場合は、 git-show(1) を使用して確認できます:
$ git show HEAD^:path/to/file
これにより、指定のバージョンのファイルが表示されます。
何か複雑なことに取り組んでいる最中に、無関係であるが明白で些細なバグを見つけたとします。取り組みを続行する前にちょいと修正したいと思います。 git-stash(1) を使用して作業の現在の状態を保存(stash;隠す)し、バグを修正した後(または、オプションで別のブランチで修正してから戻ってきた後)、保存(stash)しておいた進行中の変更を取り戻し(unstash)ます。
$ git stash push -m "work in progress for foo feature"
このコマンドは、変更を stash
に保存し、現在のブランチの先端に一致するように作業ツリーとインデックスをリセットします。その後、通常どおりに(ちょいと)修正を行うことができます。
... edit and test ... $ git commit -a -m "blorpl: typofix"
その後、 git stash pop
で作業していたのに戻ることができます:
$ git stash pop
大規模なリポジトリでは、Gitは圧縮に頼って、履歴情報がディスクまたはメモリ内のスペースを占有しすぎないようにします。 一部のGitコマンドは git-gc(1) を自動的に実行する可能性があるため、手動で実行することを心配する必要はありません。 ただし、大規模なリポジトリの圧縮には時間がかかる場合があるため、都合の悪いときに自動圧縮が開始されないように、明示的に gc
を呼び出すことをお勧めします。
git-fsck(1) コマンドは、リポジトリに対していくつかの自己整合性チェックを実行し、問題があれば報告します。これには時間がかかる場合があります。
$ git fsck dangling commit 7281251ddd2a61e38657c827739c57015671a6b3 dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63 dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5 dangling blob 218761f9d90712d37a9c5e36f406f92202db07eb dangling commit bf093535a34a4d35731aa2bd90fe6b176302f14f dangling commit 8e4bec7f2ddaa268bef999853c25755452100f8e dangling tree d50bb86186bf27b681d25af89d3b5b68382e4085 dangling tree b24c2473f1fd3d91352a624795be026d64c8841f ...
宙ぶらりんオブジェクト(dangling objects)に関するinfoメッセージが表示されます。これらはリポジトリにまだ存在しているが、どのブランチからも参照されていないオブジェクトであり、しばらくすると gc
を使用して削除できます(削除される予定です)。 git fsck --no-dangling
を実行してこれらのinfoメッセージを抑制し、errorメッセージだけを表示することができます。
git reset --hard
を使用してブランチを変更し、そのブランチが履歴内のその時点への唯一の参照であることに気付いたとします。
幸い、Gitには、各ブランチの以前の値のすべてを保持する「reflog」と呼ばれるログがあります。したがって、この場合でも、たとえば、以下を使用して古い履歴を見つけることができます。
$ git log master@{1}
これは、以前のバージョンの master
ブランチヘッドから到達可能なコミットを一覧表示します。 この構文は、 git log
だけでなく、コミットを受け入れる任意のGitコマンドで使用できます。 他のいくつかの例:
$ git show master@{2} # See where the branch pointed 2, $ git show master@{3} # 3, ... changes ago. $ gitk master@{yesterday} # See where it pointed yesterday, $ gitk master@{"1 week ago"} # ... or last week $ git log --walk-reflogs master # show reflog entries for master
HEAD用には個別のreflogが保持されるため、以下のようにできます
$ git show HEAD@{"1 week ago"}
現在のブランチが1週間前に指し示したものではなく、HEADが1週間前に指し示したものを表示します。これにより、チェックアウトした内容の履歴を確認できます。
reflogはデフォルトで30日間保持され、その後、たぶん刈り取られます(prune)。この刈り取りを制御する方法については、 git-reflog(1) や linkgit:git-gc [1] を参照してください。詳細については、 gitrevisions(7)の「SPECIFYING REVISIONS」セクションを参照してください。
注意: reflogの履歴は、通常のGitの履歴とは大きく異なることに注意してください。通常の履歴は同じプロジェクトで動作するすべてのリポジトリで共有されますが、reflogの履歴は共有されません。reflogは、ローカルリポジトリのブランチが時間の経過とともにどのように変化したかについてのみ通知します。
状況によっては、reflogで保存できない場合があります。たとえば、ブランチを削除して、そこに含まれる履歴が必要であることに気付いたとします。reflogも削除されます。ただし、リポジトリをまだ刈り取りしていない場合でも、 git fsck
が報告する宙ぶらりんオブジェクトで失われたコミットを見つけることができる場合があります。 詳細については、 the section called “Dangling objects(宙ぶらりんオブジェクト)”を参照してください。
$ git fsck dangling commit 7281251ddd2a61e38657c827739c57015671a6b3 dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63 dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5 ...
あなたは、たとえば、以下のようにして、これらの宙ぶらりんコミットの1つを調べることができます。
$ gitk 7281251ddd --not --all
これを言葉で説明すると、つまり、宙ぶらりんコミットによって記述されたコミット履歴を表示したいが、既存のすべてのブランチとタグによって記述された履歴は表示したくないということです。したがって、失われたコミットから到達可能な履歴を正確に取得できます。(そして、それが1つのコミットだけではない可能性があることに注意してください。これは「開発ラインの先端」(tip of the line)が宙ぶらりんだと報告するだけです。がしかし、全体に渡る深く複雑なコミット履歴が削除された可能性があります。)
履歴を元に戻したい場合は、いつでもそれを指す新しい参照を作成できます。たとえば、新しいブランチを作成できます:
$ git branch recovered-branch 7281251ddd
他のタイプの宙ぶらりんオブジェクト(ブロブとツリー)でもこれは可能であり、宙ぶらりんオブジェクトは他の状況でも発生する可能性があります。
Table of Contents
リポジトリのクローンを作成し、あなた独自の変更をいくつかコミットした後、元のリポジトリで更新を確認し、それらをあなた独自の作業にマージすることをお勧めします。
git-fetch(1) を使用したリモート追跡ブランチを最新の状態に保つ方法や、2つのブランチをマージする方法についてはすでに説明しました。したがって、元のリポジトリのmasterブランチからの変更を以下のようにマージできます:
$ git fetch $ git merge origin/master
しかしながら、 git-pull(1) コマンドは、これを1ステップで実行する方法を提供します:
$ git pull origin master
実際、 あなたが master
をチェックアウトしている場合、このブランチは、元のリポジトリのHEADブランチから変更を取得するように git clone
実行時に構成済です。多くの場合、以下のように単純なもので上記を達成できます
$ git pull
このコマンドは、リモートブランチからリモート追跡ブランチ origin/*
に変更をフェッチし、そのデフォルトブランチを現在のブランチにマージします。
より一般的には、リモート追跡ブランチから作成されたブランチは、デフォルトでそのリモート追跡ブランチからプルされます。これらのデフォルトを制御する方法を学ぶためには、 git-config(1) の branch.<name>.remote
や branch.<name>.merge
オプションの説明と、 git-checkout(1) の --track
オプションの説明を参照してください。
git pull
はキーストロークを節約するだけでなく、プル元のブランチとリポジトリを文書化したデフォルトのコミットメッセージを生成するのにも役立ちます。
(注意: ただし、fast-forwardの場合、そのようなコミットは作成されないことに注意してください。代わりに、アップストリームブランチからの最新のコミットを指すようにブランチが更新されます。)
git pull
コマンドに「リモートリポジトリ」として .
を指定することもできます。この場合、現在のリポジトリからのブランチにマージされます。
$ git pull . branch $ git merge branch
よってこれらは、ほぼ同等です。
変更がいくつかある場合、それらを送信する最も簡単な方法は、パッチとして電子メールで送信することです:
まずは git-format-patch(1) の使用例です:
$ git format-patch origin
そうすると、現在のディレクトリに番号付きの一連のファイルを生成します。現在のブランチのパッチごとに1つですが、 origin/HEAD
についてのモノはありません。
git format-patch
は、最初に送付状(cover letter)を付けることができます。 format-patch
がコミットメッセージの後、パッチ自体の前に配置する3つの破線(---
)の後に、個々のパッチに関するコメントを挿入できます。送付状の素材を追跡するために git notes
を使用する場合、git format-patch --notes
は同様の方法でコミットのnoteを含みます。
次に、これらをメールクライアントにインポートして、手動で送信できます。 ただし、一度に送信するものが多い場合は、 git-send-email(1) スクリプトを使用してプロセスを自動化することをお勧めします。まずはプロジェクトのメーリングリストを参照して、パッチを送信するための要件を決定してください。
Gitは、このような電子メールで送信された一連のパッチをインポートするための git-am(1) (amは「apply mailbox」の略)と呼ばれるツールも提供します。パッチを含むすべてのメッセージを順番に1つのメールボックスファイル(patches.mbox
など)に保存してから、以下を実行します
$ git am -3 patches.mbox
Gitは各パッチを順番に適用します。競合が見つかった場合は停止し、Resolving a mergeの説明に従って競合を修正できます。( -3
オプションは、Gitにマージを実行するように指示します。ツリーとインデックスを中止してそのままにしておく場合は、そのオプションを省略できます。)
競合解決の結果でインデックスが更新されたら、新しいコミットを作成する代わりに、以下を実行するだけです。
$ git am --continue
こうすると、Gitはコミットを作成し、メールボックスから残りのパッチを適用し続けます。
最終的な結果は、元のメールボックスのパッチごとに1つずつ、一連のコミットになります。各パッチを含むメッセージから作者とコミットログメッセージが取得されます。
プロジェクトに変更を送信する別の方法は、 git-pull(1) を使用して、リポジトリから変更をプルするようにそのプロジェクトのメンテナに指示することです。 「Getting updates with git pull
」のセクションでは、これを「メイン」リポジトリから更新を取得する方法として説明しましたが、 他の向きでも同様に機能します。
あなたとメンテナの両方が同じマシン上にアカウントを持っている場合は、お互いのリポジトリから直接変更をプルすることができます。リポジトリURLを引数として受け入れるコマンドは、ローカルディレクトリ名も受け入れます:
$ git clone /path/to/repository $ git pull /path/to/other/repository
または ssh URL を指定できます:
$ git clone ssh://yourhost/~you/repository
開発者が少ないプロジェクトの場合、または少数のプライベートリポジトリを同期する場合は、これで十分な場合があります。
けれども、これを行うためのより一般的な方法は、他のユーザーが変更をプルできるように、別のパブリックリポジトリ(通常は別のホスト上)を維持することです。これは通常、より便利であり、進行中の私的な作業を公に見える作業からきれいに分離することができます。
あなたは個人リポジトリで日常業務を継続しますが、定期的に個人リポジトリから公開リポジトリに変更を「プッシュ」して、他の開発者がそのリポジトリからプルできるようにします。したがって、パブリックリポジトリを持つ他の開発者が1人いる状況での変更のフローは、以下のようになります:
you push your personal repo ------------------> your public repo ^ | | | | you pull | they pull | | | | | they push V their public repo <------------------- their repo
これを行う方法については、次のセクションで説明します。
あなたの個人リポジトリがディレクトリ ~/proj
にあると仮定します。 まず、リポジトリの新しいクローンを作成し、それがパブリックであることを意図していることを git daemon
に伝えます:
$ git clone --bare ~/proj proj.git $ touch proj.git/git-daemon-export-ok
結果のディレクトリ proj.git には、ベアgitリポジトリ(bare;生の、むき出しの)が含まれています。これは、 .git
ディレクトリの内容であり、周囲にファイルがチェックアウトされていません。
次に、パブリックリポジトリをホストする予定のサーバーに proj.git
をコピーします。 scpやrsyncなど、あなたが最も便利と思うツールを使う事ができます。
これが推奨の方法です。
もし、他の誰かがサーバーを管理している場合は、リポジトリを配置するディレクトリと、リポジトリが表示される git://
URLを教えてもらってください。そして、以下の「Pushing changes to a public repository」のセクションまでスキップできます。
それ以外の場合は、あなたは git-daemon(1)を開始する必要があります。 ポート9418でリッスンします。デフォルトでは、Gitディレクトリのように見え、マジックファイルgit-daemon-export-okを含む任意のディレクトリへのアクセスが許可されます。一部のディレクトリパスを git daemon
の引数として渡すと、さらにそれらのパスへのエクスポートが制限されます。
`gitdaemon`をinetdサービスとして実行することもできます。 詳細については、 git-daemon(1) のマニュアルページを参照してください。(特に「examples」セクションを参照してください。)
Gitプロトコルを使用した方がパフォーマンスと信頼性が向上しますが、Webサーバーが設定済のホストでは、HTTPエクスポートの設定が簡単な場合があります。
あなたは、新しく作成したベアGitリポジトリをWebサーバーによってエクスポートされるディレクトリに配置し、Webクライアントに必要な追加情報を提供するためにいくつかの調整を行うだけです:
$ mv proj.git /home/you/public_html/proj.git $ cd proj.git $ git --bare update-server-info $ mv hooks/post-update.sample hooks/post-update
(最後の2行の説明については、 git-update-server-info(1) と githooks(5) を参照してください。)
proj.git
のURLを公表します。 そうすれば、他の誰もが、たとえば以下のようなコマンドラインを使用して、そのURLからクローンを作成したりプルしたりできるようになります:
$ git clone http://yourserver.com/~you/proj.git
(HTTPでプッシュすることもできるWebDAVを使用した少し洗練されたセットアップについては、 setup-git-server-over-http も参照してください。)
上記の2つの手法(httpまたはgitを介したエクスポート)では、他のメンテナが最新の変更をフェッチできますが、書き込みアクセスは許可されないことに注意してください。これらはあなたのプライベートリポジトリで作成された最新の変更であなたがパブリックリポジトリを更新する必要があります。
これを行う最も簡単な方法は、 git-push(1) とsshを使用することです。 master
という名前のリモートブランチを master
という名前のブランチの最新の状態で更新するには、以下のコマンドを実行します
$ git push ssh://yourserver.com/~you/proj.git master:master
または単に
$ git push ssh://yourserver.com/~you/proj.git master
git fetch
と同様に、これがfast-forwardにならない場合、 git push
は文句を言います。 この場合の処理の詳細については、次のセクションを参照してください。
注意: push
のターゲットは通常、ベアリポジトリであることに注意してください。チェックアウトされた作業ツリーがあるリポジトリにプッシュすることもできますが、混乱を防ぐために、現在チェックアウトされているブランチを更新するためのプッシュはデフォルトで拒否されます。詳細については、 git-config(1)の receive.denyCurrentBranch オプションの説明を参照してください。
git fetch
と同様に、入力を節約するための構成オプションを設定することもできます。たとえば:
$ git remote add public-repo ssh://yourserver.com/~you/proj.git
とすると、 .git/config
は以下のようになります:
[remote "public-repo"] url = yourserver.com:proj.git fetch = +refs/heads/*:refs/remotes/example/*
そうすると、以下のようにするだけで上記と同じプッシュをする事ができます
$ git push public-repo master
詳細については、 git-config(1)の remote.<name>.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
を使用して、すでに公開されているコミットを置き換えようとした(the section called “履歴を巻き戻す事によって作業ミスを修正する”のように)
git rebase
を使用して、すでに公開されているコミットをリベースしようとした(the section called “Keeping a patch series up to date using git rebase”のように)
あなたはブランチ名の前にプラス記号(+
)を付けることで、 git push
に強制的に更新を実行させることができます:
$ git push ssh://yourserver.com/~you/proj.git +master
注意: 上記では +
記号が追加されていることに注意してください。または、以下のように、 -f
フラグを使用してリモート更新を強制することもできます:
$ git push -f ssh://yourserver.com/~you/proj.git master
通常、パブリックリポジトリのブランチヘッドが変更されると、以前にポイントしたコミットの子孫を指すように変更されます。この状況でプッシュを強制することにより、あなたはその慣習を破ることになります。(the section called “履歴の書き換えに関する問題”を参照してください。)
それでも、これは進行中のパッチシリーズを公開する簡単な方法を必要とする人々にとって一般的な方法であり、これがブランチの管理方法であることを他の開発者に警告する限り、許容できる妥協案です。
他の人が同じリポジトリにプッシュする権利を持っている場合、この方法でプッシュが失敗する可能性もあります。その場合、正しい解決策は、最初にプル、またはフェッチとそれに続くリベースのいずれかの作業を更新した後にプッシュを再試行することです。詳細については、次のセクション および gitcvs-migration(7) を参照してください。
共同作業するもう1つの方法は、CVSで一般的に使用されているモデルと同様のモデルを使用することです。このモデルでは、特別な権限を持つ複数の開発者がすべて、単一の共有リポジトリにプッシュおよびプルします。これを設定する方法については、 gitcvs-migration(7) を参照してください。
けれども、共有リポジトリに対するGitのサポートに問題はありませんが、Gitがサポートする、パッチをやり取りし公開リポジトリからプルすることによる共同作業モードは中央共有リポジトリに比べて非常に多くの利点があるため、この操作モードは通常は推奨されません:
git pull
は、そのメンテナがこのジョブを他のメンテナに委任するための簡単な方法を提供しますが、それでも着信変更のオプションのレビューを許可します。
gitweb cgiスクリプトを使用すると、Gitをインストールしなくても、プロジェクトのリビジョン、ファイルの内容、ログを簡単に参照できます。 RSS/Atomフィードやblame/annotationの詳細などの機能をオプションで有効にすることができます。
git-instaweb(1) コマンドは、gitwebを使用してリポジトリの閲覧を開始する簡単な方法を提供します。 instawebを使用する場合のデフォルトのサーバーはlighttpdです。
CGIまたはPerl対応サーバーを使用した永続的なインストールのセットアップの詳細については、Gitソースツリーのファイル gitweb/INSTALL および gitweb(1) を参照してください。
履歴が切り捨てられたshallow cloneは、プロジェクトの最近の履歴のみに関心があり、アップストリームから完全な履歴を取得するのにコストがかかる場合に役立ちます。
shallow cloneは、 git-clone(1) に --depth
スイッチを指定することで作成されます。深さは後で git-fetch(1) の --depth
スイッチで変更するか、または、完全な履歴を --unshallow
で復元することができます。
shallow clone内でのマージは、マージベースが取得してきた最近の履歴にある限り機能します。そうでなければ、無関係な履歴をマージするようなものになり、大きな競合が発生する可能性があります。この制限により、このようなリポジトリはマージベースのワークフローでの使用に適さなくなる可能性があります。
ここでは、Tony LuckがLinuxカーネルのIA64アーキテクチャーのメンテナーとしてGitをどのように使用しているかを説明します。
彼は2つのパブリックブランチを使用しています:
彼はまた、パッチの論理グループをそれぞれ含む一時的なブランチ(トピックブランチ)の組を使用します。
これを設定するには、最初にLinusのパブリックツリーのクローンを作成して作業ツリーを作成します。
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git work $ cd work
Linusのツリーは、 origin/master という名前のリモート追跡ブランチに保存され、 git-fetch(1) を使用して更新できます。 git-remote(1) を使用して "remote" を設定し、 git-fetch(1) を使用して他のパブリックツリーを追跡し、それらを最新の状態に保つことができます。Chapter 1, Repositories and Branches を参照してください。
そして今、作業するブランチを作成します。 これらは現在の origin/master ブランチの先端から始まり、デフォルトでLinusからの変更をマージするように設定する必要があります(git-branch(1)の --track
オプションを使用)。
$ git branch --track test origin/master $ git branch --track release origin/master
これらは、 git-pull(1) を使用して簡単に最新の状態に保つことができます。
$ git switch test && git pull $ git switch release && git pull
重要な注意点! これらのブランチにローカルの変更がある場合、このマージは履歴にコミットオブジェクトを作成します(ローカルの変更なしで、Gitは単に「fast-forward」マージを実行します)。 多くの人がこれがLinuxの歴史に生み出す「ノイズ」を嫌います。つまり、Linusにリリースブランチからプルするように依頼すると、これらのノイズの多いコミットが永続的な履歴の一部になるため、 release
ブランチでこれを気まぐれに行うことは避けてください。
いくつかの構成変数(git-config(1) を参照)を使用すると、両方のブランチをパブリックツリーに簡単にプッシュできます。(the section called “Setting up a public repository” を参照してください。)
$ cat >> .git/config <<EOF [remote "mytree"] url = master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux.git push = release push = test EOF
次に、 git-push(1) を使用して、testツリーとreleaseツリーの両方をプッシュできます:
$ git push mytree
または、以下を使用してtestブランチとreleaseブランチの1つだけをプッシュします:
$ git push mytree test
または
$ git push mytree release
次に、コミュニティからいくつかのパッチを適用します。 このパッチ(または関連するパッチのグループ)を保持するブランチの短い名前を考えて、Linusのブランチの最近の安定したタグから新しいブランチを作成します。あなたのブランチの為に安定したベースを選択すると、以下のようになります:
1) 無関係でおそらく軽くテストされた変更を含めることを避けるのは、あなた自身への助けになります。
2) git bisect
を使用して問題を見つける将来のバグハンターを支援する
$ git switch -c speed-up-spinlocks v2.6.35
次に、パッチを適用し、いくつかのテストを実行して、変更をコミットします。 パッチがマルチパートシリーズの場合は、それぞれを個別のコミットとしてこのブランチに適用する必要があります。
$ ... patch ... test ... commit [ ... patch ... test ... commit ]*
この変更の状態に満足したら、公開する準備として「test」ブランチにマージできます:
$ git switch test && git merge speed-up-spinlocks
ここで競合が発生する可能性はほとんどありません…ただし、この手順にしばらく時間を費やし、アップストリームから新しいバージョンをプルした場合は、競合が発生する可能性があります。
しばらくして、十分な時間が経過してテストが完了したら、同じブランチを「release」ツリーにプルして、アップストリームに進む準備をすることができます。ここで、各パッチ(またはパッチシリーズ)を独自のブランチに保持することの価値を確認できます。 これは、パッチを任意の順序で「release」ツリーに移動できることを意味します。
$ git switch release && git merge speed-up-spinlocks
しばらくすると、いくつかのブランチが作成され、それぞれに適切に選択された名前があるにもかかわらず、ブランチの目的や状態を忘れてしまう可能性があります。特定のブランチにどのような変更があるか思い出すには以下を使用します:
$ git log linux..branchname | git shortlog
testブランチまたはreleaseブランチにすでにマージされているかどうかを確認するには、以下を使用します:
$ git log test..branchname
または
$ git log release..branchname
(このブランチがまだマージされていない場合は、いくつかのログエントリが表示されます。マージされている場合、出力はありません。)
パッチがその大きなサイクルを完了すると(testからreleaseに移動し、Linusによってプルされ、最後にローカルの origin/master
ブランチに戻るというサイクル)、この、変更のためのブランチは不要になります。あなたは以下の出力時にそれを悟ります:
$ git log origin..branchname
これの出力が空です。この時点で、このブランチを削除できます:
$ git branch -d branchname
一部の変更は非常に簡単なので、個別のブランチを作成してから、testブランチとreleaseブランチのそれぞれにマージする必要はありません。これらの変更については、 release
ブランチに直接適用してから、それを test
ブランチにマージしてください。
作業を mytree
にプッシュした後、 git-request-pull(1) を使用して、Linusに送信するための、「please pull」リクエストメッセージを準備できます:
$ git push mytree $ git request-pull origin mytree release
これをさらに単純化するスクリプトのいくつかを以下に示します。
==== update script ==== # Gitツリーのブランチを更新します。更新するブランチがoriginの場合は、kernel.orgからプルします。それ以外の場合は、origin/masterブランチを test ブランチ または releaseブランチにマージします case "$1" in test|release) git checkout $1 && git pull . origin ;; origin) before=$(git rev-parse refs/remotes/origin/master) git fetch origin after=$(git rev-parse refs/remotes/origin/master) if [ $before != $after ] then git log $before..$after | git shortlog fi ;; *) echo "usage: $0 origin|test|release" 1>&2 exit 1 ;; esac
==== merge script ==== # ブランチをtestブランチまたはreleaseブランチにマージします pname=$0 usage() { echo "usage: $pname branch test|release" 1>&2 exit 1 } git show-ref -q --verify -- refs/heads/"$1" || { echo "Can't see branch <$1>" 1>&2 usage } case "$2" in test|release) if [ $(git log $2..$1 | wc -c) -eq 0 ] then echo $1 already merged into $2 1>&2 exit 1 fi git checkout $2 && git pull . $1 ;; *) usage ;; esac
==== status script ==== # report on status of my ia64 Git tree gb=$(tput setab 2) rb=$(tput setab 1) restore=$(tput setab 9) if [ `git rev-list test..release | wc -c` -gt 0 ] then echo $rb Warning: commits in release that are not in test $restore git log test..release fi for branch in `git show-ref --heads | sed 's|^.*/||'` do if [ $branch = test -o $branch = release ] then continue fi echo -n $gb ======= $branch ====== $restore " " status= for ref in test release origin/master do if [ `git rev-list $ref..$branch | wc -c` -gt 0 ] then status=$status${ref:0:1} fi done case $status in trl) echo $rb Need to pull into test $restore ;; rl) echo "In test" ;; l) echo "Waiting for linus" ;; "") echo $rb All done $restore ;; *) echo $rb "<$status>" $restore ;; esac git log origin/master..$branch | git shortlog done
Table of Contents
通常、コミットはプロジェクトにのみ追加され、削除または置換されることはありません。 Gitはこの仮定に基づいて設計されており、これに違反すると、(たとえば、)Gitのマージ機構が間違ったことを実行することになります。
しかしながら、この仮定に違反することが役立つ場合があります。
例えば、あなたが大規模なプロジェクトの貢献者で、複雑な機能を追加したとき、他の開発者があなたの変更を読んでそれらが正しいかどうかを確認するために、あなたがそれぞれの変更を行った理由を理解しやすい方法で、提示したいとします。
すべての変更をそれぞれ単一のパッチ(またはコミット)として提示すると、一度にすべてを消化するには多すぎると思える事があります。
あなたの仕事の全履歴を提示し、その間違い・訂正・行き詰まりでいっぱいいっぱいにしたら、それを見る人達は閉口するかもしれません。
したがって、理想なのは、通常、以下のような一連のパッチを作成することです:
これを行うのに役立ついくつかのツールを紹介し、それらの使用方法を説明してから、履歴を書き換えているために発生する可能性のあるいくつかの問題について説明します。
リモート追跡ブランチ origin
にブランチ mywork
を作成し、その先頭にいくつかのコミットを作成するとします:
$ git switch -c mywork origin $ vi file.txt $ git commit $ vi otherfile.txt $ git commit ...
myworkへのマージは実行されていないため、 origin
の先頭にあるパッチの単純な線形シーケンスです:
o--o--O <-- origin \ a--b--c <-- mywork
上流のプロジェクトでさらに興味深い作業が行われ、 origin
が前進歩しました:
o--o--O--o--o--o <-- origin \ a--b--c <-- mywork
この時点で、 pull
を使用してあなたの変更をマージして戻すことができます。 その結果、次のような新しいマージコミットが作成されます:
o--o--O--o--o--o <-- origin \ \ a--b--c--m <-- mywork
しかしながら、myworkの履歴をマージせずに単純な一連のコミットを保持したい場合は、代わりに git-rebase(1) を使用することを選択できます:
$ git switch mywork $ git rebase origin
これにより、各コミットがmyworkから削除され、一時的にパッチとして( .git/rebase-apply
という名前のディレクトリに)保存され、最新バージョンのoriginを指すようにmyworkが更新され、保存された各パッチが新しいmyworkに適用されます。結果は以下のようになります:
o--o--O--o--o--o <-- origin \ a'--b'--c' <-- mywork
その過程で、競合が見つかる可能性があります。その場合、rebaseは停止し、競合を修正できるようになります。 競合を修正した後は、それらのコンテンツに対して git add
を使用してインデックスを更新します。そして ` git commit` を実行する代わりに以下を実行します:
$ git rebase --continue
そうすると、Gitは残りのパッチを適用し続けます。
あなたは、いつでも --abort
オプションを使用してこの作業を中止し、リベースを開始する前の状態にmyworkを戻すことができます:
$ git rebase --abort
ブランチ内の多数のコミットを並べ替えたり編集したりする必要がある場合は、 git rebase -i
を使用する方が簡単な場合があります。これにより、コミットを並べ替えたり押しつぶしたり(squash)、リベース中に個別に編集できるようにマークを付けたりできます。詳細については the section called “Using interactive rebases” を、代替案については the section called “Reordering or selecting from a patch series” を参照してください。
the section called “履歴を巻き戻す事によって作業ミスを修正する”で、以下のようにして最新のコミットを置き換えることができることを確認しました
$ git commit --amend
これにより、古いコミットが変更を組み込んだ新しいコミットに置き換えられ、最初に古いコミットメッセージを編集する機会が与えられます。これは、最後のコミットでのタイプミスを修正したり、ステージングが不十分なコミットのパッチの内容を調整したりする場合に役立ちます。
履歴のより深いところのコミットを修正する必要がある場合は、対話的rebaseの「edit」命令を使用できます。
時々、あなたはあなたの歴史のより深いところにコミットを編集したいです。 1つのアプローチとしては、 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
で続行できます。途中で物事があまりにもスパゲッティ(hairy)になっていると判断した場合は、いつでも 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つの(古いものと新しい)開発ラインをマージしようとします。結果は予想外の可能性があります。
あなたは、履歴が書き換えられたブランチを公開することを選択することもできます。他の人がそれらのブランチを調べたりテストしたりするためにそれらのブランチをフェッチできると便利な場合がありますが、そのようなブランチを自分の作業に引き込もうとしないでください。
適切なマージをサポートする、本当に分散開発を行っている場合、公開されたブランチを書き直してはいけません。
git-bisect(1) コマンドは、マージコミットを含む履歴を正しく処理します。ただし、検出されたコミットがマージコミットである場合、ユーザーは、そのコミットが問題を引き起こした理由を理解するために、通常よりも慎重に作業する事を心がけて下さい。
以下の履歴があるとします:
---Z---o---X---...---o---A---C---D \ / o---o---Y---...---o---B
開発の(図の)上側のラインで、コミットXでZに存在する関数の1つの意味が変更されたとします。AにつながるZからのコミットは、関数の実装とZに存在するすべての呼び出し側の両方を変更し、一貫性を保つために、追加するする新しい呼び出し側も同様にします。Aにはバグはありません。
その間に、開発の(図の)下側のラインで、誰かがコミットYでその関数の新しい呼び出し側を追加するとします。BにつながるZからのコミットはすべて、その関数の古いセマンティクスを想定しており、呼び出し元と呼び出し先は互いに一貫しています。Bにもバグはありません。
さらに、この上下2つの開発ラインがCできれいにマージされるため、競合の解決は必要ないとします。
それにもかかわらず、開発の下側のラインで追加された呼び出し側は、開発の上側ラインに導入された新しいセマンティクスに変換されていないため、Cのコードは壊れています。したがって、Dがbad、Zがgoodで、git-bisect(1) がCを原因として特定していることだけを知っている場合、あなたは問題がこのセマンティクスの変更によるものであることをどのようにして解き明かしますか?
git bisect
の結果が非マージコミットである場合、通常はそのコミットだけを調べることで問題を発見できるはずです。開発者は、変更を小さな自己完結型のコミットに分割することで、これを簡単に行うことができます。 ただし、上記の場合は、単一のコミットを調べても問題が明らかではないため、これは役に立ちません。代わりに、開発の全体的な俯瞰が必要です。さらに悪いことに、問題のある関数のセマンティクスの変更は、開発の上側ラインの変更のほんの一部にすぎない可能性があります。
一方、Cでマージする代わりに、Aの上にZからBまでの履歴をリベースした場合、以下の線形履歴が得られます:
---Z---o---X--...---o---A---o---o---Y*--...---o---B*--D*
ZとD*の間のbisectは、単一の犯人コミットY*にヒットし、Y*が壊れた理由を理解するのはおそらく簡単でしょう。
多くの経験豊富なGitユーザーが、他の方法でマージが多いプロジェクトで作業している場合でも、公開する前に最新のアップストリームバージョンにリベースすることで履歴を線形に保つのは、これが理由の一つです。
Table of Contents
git-remote(1) を使用する代わりに、一度に1つのブランチを更新し、それを任意の名前でローカルに保存することもできます:
$ git fetch origin todo:my-todo-work
最初の引数 origin
は、最初にクローンを作成したリポジトリからフェッチするようにGitに指示するだけのものです。2番目の引数は、Gitに todo
という名前のブランチをリモートリポジトリからフェッチし、それを refs/heads/my-todo-work
という名前でローカルに保存するように指示します。
以下のように、他のリポジトリからブランチをフェッチすることもできます。
$ git fetch git://example.com/proj.git master:example-master
これは example-master
という名前の新しいブランチを作成し、指定されたURLのリポジトリの master
という名前のブランチをその中に保存します。example-master
という名前のブランチがすでにある場合は、 example.com
の masterブランチによって指定されたコミットにfast-forwardしようとします。これについて詳細は次のセクションを参照下さい:
前の例では、既存のブランチを更新するときに、 git fetch
は、新しいコミットを指すようにブランチのコピーを更新する前に、リモートブランチの最新のコミットがブランチのコピーの最新のコミットの子孫であることを確認します。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が失敗した場合は、以下のように更新を強制できます:
$ 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) を参照してください。
Table of Contents
Gitは、少数のシンプルで強力なアイデアに基づいて構築されています。これらのアイデアを理解しなくても物事を成し遂げることは可能ですが、理解した上での方がGitをはるかに直感的に理解できるようになります。
私達は、最も重要なオブジェクト データベースとインデックスから始めることにします。
私達はすでにthe section called “Understanding History: Commits”で、すべてのコミットが40桁の「オブジェクト名」で保存されているのを見ました。実際、プロジェクトの履歴を表すために必要なすべての情報は、そのような名前のオブジェクトに格納されています。いずれの場合も、名前はオブジェクトのコンテンツのSHA-1ハッシュを使用して計算されます。SHA-1ハッシュは暗号化ハッシュ関数です。それが私たちにとって意味することは、同じ名前の2つの異なるオブジェクトを見つけることは不可能であるということです。これには多くの利点があります:
(オブジェクトのフォーマットとSHA-1計算の詳細については、the section called “Object storage format”を参照してください。)
オブジェクトには "blob"、"tree"、"commit"、"tag" の4種類があります。
オブジェクトタイプの詳細:
「commit」オブジェクトは、ツリーの物理的な状態を、そこに到達した方法とその理由の説明にリンクします。 --pretty=raw
オプションを伴った git-show(1) または git-log(1) を使用し、あなたのお気に入りのコミットを調べてみます:
$ git show -s --pretty=raw 2be7fcb476 commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4 tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf parent 257a84d9d02e90447b149af58b271c19405edb6a author Dave Watson <dwatson@mimvista.com> 1187576872 -0400 committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700 Fix misspelling of 'suppress' in docs Signed-off-by: Junio C Hamano <gitster@pobox.com>
コミットは以下のように定義されています:
注意: コミット自体には、実際に何が変更されたかに関する情報は含まれていないことに注意してください。すべての変更は、このコミットによって参照されるツリーの内容を、その親に関連付けられているツリーと比較することによって計算されます。特に、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 ...
ご覧のとおり、ツリーオブジェクトにはエントリのリストが含まれており、各エントリにはモード(mode)とオブジェクトタイプ(object type)とSHA-1名(SHA-1 name)と名前(name)があり、名前でソートされています。これは、単一のディレクトリツリーの内容を表します。
オブジェクトタイプは、ファイルの内容を表すブロブ(blob)でも、サブディレクトリの内容を表す別のツリーでもかまいません。 ツリー(tree)とブロブ(blob)は、他のすべてのオブジェクトと同様に、その内容のSHA-1ハッシュによって名前が付けられるため、内容(再帰的にすべてのサブディレクトリの内容を含む)が同一である場合に限り、2つのツリーは同じSHA-1名を持ちます。これにより、Gitは、同じオブジェクト名を持つエントリを無視できるため、2つの関連するツリーオブジェクト間の違いをすばやく判断できます。
(注意: サブモジュールが存在する場合、ツリーにはエントリとしてコミットが含まれる場合もあります。その文書については、Chapter 8, Submodules を参照してください。)
注意: すべてのファイルのモードが644または755であることに注意してください。Gitは実際には実行可能ビット(executable bit)にのみ注意を払います。
あなたは git-show(1) を使用して、ブロブ(blob)の内容を調べることができます。たとえば、上のツリーから COPYING
のエントリのブロブを取得します:
$ git show 6ff87c4664 Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ...
「ブロブ」(blob)オブジェクトは、データのバイナリブロブ(バイナリの小さい塊)に他なりません。それは他のものを参照したり、いかなる種類の属性も持っていません。
ブロブはそのデータによって完全に定義されるため、ディレクトリツリー(またはリポジトリの複数の異なるバージョン)内の2つのファイルの内容が同じである場合、それらは同じブロブオブジェクトを共有します。オブジェクトはディレクトリツリー内の場所から完全に独立しており、ファイルの名前を変更しても、ファイルが関連付けられているオブジェクトは変更されません。
注意: <revision>:<path> 構文で git-show(1) を使用すると、任意のツリーまたはブロブオブジェクトを調べることができることに注意してください。これは、現在チェックアウトされていないツリーの内容を参照する場合に役立つことがあります。
あなたが、とあるソースからブロブの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)を使用して、「軽量タグ」(lightweight tags)を作成することもできます。これは、タグオブジェクトではなく、名前が refs/tags/
で始まる単純な参照です)。
新しく作成されたオブジェクトは、最初にオブジェクトのSHA-1ハッシュにちなんで名付けられたファイル名のファイルとして作成されます( .git/objects
に保存されます)。
残念ながら、プロジェクトに多くのオブジェクトがあると、このシステムは非効率になります。 古いプロジェクトで以下を試してください:
$ git count-objects 6930 objects, 47620 kilobytes
最初の数は、個々のファイルに保持されているオブジェクトの数です。 2つ目は、これらの「緩い」オブジェクト("loose" objects)が占めるスペースの量です。
あなたは、これらの緩いオブジェクトを「パックファイル」(pack file)に移動することで、スペースを節約し、Gitを高速化できます。このファイルには、オブジェクトのグループが効率的な圧縮形式で保存されます。 パックファイルのフォーマットの詳細については、 gitformat-pack(5) をご覧ください。
緩いオブジェクトをパックに入れるには、 git repack を実行するだけです:
$ git repack Counting objects: 6020, done. Delta compression using up to 4 threads. Compressing objects: 100% (6020/6020), done. Writing objects: 100% (6020/6020), done. Total 6020 (delta 4070), reused 0 (delta 0)
これにより、現在アンパックされているすべてのオブジェクトを含む単一の「パックファイル」が .git/objects/pack/ に作成されます。 あなたは、その後、以下を実行できます:
$ git prune
これは、今やパックに格納済の緩いオブジェクト("loose" objects)をすべて削除します。これにより、参照されていないオブジェクトも削除されます(たとえば、 git reset
を使用してコミットを削除すると作成される可能性があります)。 .git/objects
ディレクトリを確認するか、以下を実行することで、緩んだオブジェクトがなくなったことを確認できます。
$ git count-objects 0 objects, 0 kilobytes
オブジェクトファイルはなくなりましたが、それらのオブジェクトを参照するコマンドは、以前とまったく同じように機能します。
git-gc(1) コマンドは、パッキング(packing)やプルーニング(pruning)を実行します。このため、通常、あなたに必要な高レベルのコマンドはこのコマンドだけです。
git-fsck(1) コマンドは、宙ぶらりんオブジェクト(dangling objects)について文句を言うことがあります。それらは問題ではありません。
宙ぶらりんオブジェクトの最も一般的な原因は、ブランチをリベースしたか、ブランチをリベースした他の誰かからプルしたことです。 Chapter 5, Rewriting history and maintaining patch series を参照してください。 その場合、元のブランチの古いヘッドは、それが指しているすべてのものと同様に、まだ存在しています。ブランチポインタ自体は、別のポインタに置き換えたため、存在していません。
宙ぶらりんオブジェクトを引き起こす他の状況もあります。たとえば、ファイルに「git add」を実行したために「宙ぶらりんブロブ」が発生する可能性があります。実際にコミットして全体像の一部にする前に、そのファイル内の別の何かを変更し、その更新されたものをコミットした場合—この時最初に追加した古い状態は、コミットまたはツリーによってポイントされないため、宙ぶらりんブロブオブジェクトになります。
同様に、 "ort" マージ戦略が実行され、交差マージ(criss-cross merge)があり、したがって複数のマージベースがあることがわかった場合(これはかなり珍しいですが、実際に発生します)、一時的な内部マージベースとして1つの一時的な中間ツリーを生成します。そして、それらは実際のオブジェクトですが、最終結果はそれらを指すことにはならないため、リポジトリ内で「宙ぶらりん」になります(または、交差マージが多数あり、マージベースが3つ以上ある場合は、さらに多くの可能性があります)。
一般的に、宙ぶらりんオブジェクトは心配する必要はありません。それらは非常に役立つことさえあります。あなたが何かを台無しにした場合、宙ぶらりんオブジェクトはあなたがあなたの古いツリーを回復する方法になることができます(たとえば、リベースを行った後で、本当はリベースをやりたくなかった気づきました。持っている宙ぶらりんオブジェクトを確認して、ヘッドを古い宙ぶらりん状態にリセットすることができます)。
コミットの場合は、あなたは以下を使用できます:
$ gitk <dangling-commit-sha-goes-here> --not --all
これは、指定されたコミットから到達可能なすべての履歴を要求しますが、ブランチ、タグ、またはその他の参照からは要求しません。 必要なものであると判断した場合は、いつでも新しい参照を作成できます。たとえば、
$ git branch recovered-branch <dangling-commit-sha-goes-here>
ブロブとツリーの場合、同じことはできませんが、それでも調べることができます。 あなたはただ以下のようにするだけです。
$ git show <dangling-blob/tree-sha-goes-here>
ブロブの内容が何であるか(または、ツリーの場合、基本的にそのディレクトリの ls
したのが何であるか)を示すために、それはあなたに宙ぶらりんオブジェクトを残した操作が何であったかについてのいくらかの考えを与えるかもしれません。
通常、宙ぶらりんのブロブやツリーはあまり興味深くはありません。ほとんどの場合、それらは中途のマージベースであるか(手動で修正した競合するマージがあった場合、ブローブにはマージからの競合マーカーが含まれることがよくあります)、または、git fetch
で、あなたが ^C か何かで中断すると、オブジェクトデータベースに新しいオブジェクトの一部を残す事が発生しますが、それらは役に立たない単なる宙ぶらりんです。
とにかく、あなたは宙ぶらりん状態に興味がないと確信したら、あなたは到達できないすべてのオブジェクトを削除できます:
$ git prune
そしてそれらは消え去ります。(あなたは、静止した、活動していないリポジトリでのみ git prune
を実行すべきです。これは、ファイルシステムのfsckリカバリを実行するようなものです。あなたはfsckを、ファイルシステムがマウントされている間は実行したくないでしょう。同様に git prune
は、リポジトリへの同時アクセスの場合でも害を及ぼさないように設計されていますが、紛らわしいメッセージや恐ろしいメッセージを受け取る可能性があります。)
設計上、Gitは信頼できるデータを慎重に扱います。ただし、Git自体にバグがない場合でも、ハードウェアまたはオペレーティングシステムのエラーによってデータが破損する可能性があります。
このような問題に対する最初の防御策はバックアップです。cloneを使用するか、cp、tar、またはその他のバックアップメカニズムを使用して、Gitディレクトリをバックアップできます。
最後の手段として、破損したオブジェクトを検索し、手動で置き換えることができます。その処理でさらに破損してしまう場合に備えて、これを試行する前にリポジトリをバックアップしてください。
私達は、問題は単一の欠落または破損したブロブであると想定しますが、これは解決可能な問題である場合があります。 (欠落しているツリー、特にコミットを回復することは*非常に*困難です)。
開始する前に、破損があることを確認し、 git-fsck(1) を使用して破損がどこにあるかを把握します。これにはけっこう時間がかかる場合があります。
出力が次のようになっているとします:
$ git fsck --full --no-dangling broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 to blob 4b9458b3786228369c63936db65827de3cc06200 missing blob 4b9458b3786228369c63936db65827de3cc06200
これで、ブロブ 4b9458b3 が欠落しており、ツリー 2d9263c6 がそれを指していることがわかります。欠落しているブロブブジェクトのコピーが1つだけ見つかった場合は、おそらく他のリポジトリで、それを .git/objects/4b/9458b3...
に移動して実行できます。 しかし、あなたはそれができなかったということにしましょう。そうするってぇと、あなたは git-ls-tree(1) を使用して、それを指しているツリーを調べることができます。これにより、以下のような出力が得られるかもしれません:
$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore 100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap 100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING ... 100644 blob 4b9458b3786228369c63936db65827de3cc06200 myfile ...
これで、欠落しているブロブが myfile
という名前のファイルのデータであることがわかりました。 また、ディレクトリを特定できる可能性もあります。たとえば、「somedirectory」にあるとします。 運が良ければ、不足しているコピーは、作業ツリーの somedirectory/myfile
でチェックアウトしたコピーと同じである可能性があります。 git-hash-object(1) を使用して、それが正しいかどうかをテストできます:
$ git hash-object -w somedirectory/myfile
これにより、 somedirectory/myfile の内容を含むブロブオブジェクトが作成および保存され、そのオブジェクトのSHA-1が出力されます。 運が良ければ、4b9458b3786228369c63936db65827de3cc06200 になる可能性があります。その場合は、あなたの推測が正しかったということで、破損が修正されます。
それ以外の場合は、より多くの情報が必要です。あなたはファイルのどのバージョンが失われたかをどのように知ればよいのでしょうか?
これを行う最も簡単な方法は以下のとおりです:
$ git log --raw --all --full-history -- somedirectory/myfile
あなたは生の出力を問い合わせたので、あなたは以下のようなものを得ます。
commit abc Author: Date: ... :100644 100644 4b9458b newsha M somedirectory/myfile commit xyz Author: Date: ... :100644 100644 oldsha 4b9458b M somedirectory/myfile
これは、ファイルの直前のバージョンが「newsha」であり、直前のバージョンが「oldsha」であったことを示しています。 また、oldshaから4b9458bへの変更、および4b9458bからnewshaへの変更に伴うコミットメッセージも分かります。
十分に小さな変更をコミットしている場合は、中間状態4b9458bの内容が再構築するのに適している可能性があります。
もしそれが可能でであれば、あなたは不足しているオブジェクトを以下のように再作成できます。
$ git hash-object -w <recreated-file>
そしてあなたのリポジトリは再び正常になります。
(ところで、あなたは fsck
を無視する事がでます。以下の通り始めます。
$ git log --raw --all
そして、その全体の中で行方不明のオブジェクト(4b9458b)のshaを探しました。そうするかどうかはあなた次第です—Gitは多くの情報を*持っていて*、それはただ1つの特定のブロブバージョンを失っているだけです。
インデックスは、ソートされたパス名のリストを含むバイナリファイル(通常は .git/index
に保持されます)であり、それぞれパーミッションとブロブオブジェクトのSHA-1を含んでいます。 git-ls-files(1) は、インデックスの内容を表示できます。
$ git ls-files --stage 100644 63c918c667fa005ff12ad89437f2fdc80926e21c 0 .gitignore 100644 5529b198e8d14decbe4ad99db3f7fb632de0439d 0 .mailmap 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0 COPYING 100644 a37b2152bd26be2c2289e1f57a292534a51a93c7 0 Documentation/.gitignore 100644 fbefe9a45b00a54b58d94d06eca48b03d40a50e0 0 Documentation/Makefile ... 100644 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea 0 xdiff/xtypes.h 100644 2ade97b2574a9f77e7ae4002a4e07a6a38e46d07 0 xdiff/xutils.c 100644 d5de8292e05e7c36c4b68857c1cf9855e3d2f70a 0 xdiff/xutils.h
注意: 古いドキュメントでは、「現在のディレクトリキャッシュ」または単に「キャッシュ」と呼ばれるインデックスが表示される場合があることに注意してください。 これには3つの重要な特性があります:
インデックスには、単一の(一意に決定された)ツリーオブジェクトを生成するために必要なすべての情報が含まれています。
たとえば、 git-commit(1) を実行すると、インデックスからこのツリーオブジェクトが生成され、オブジェクトデータベースに保存され、新しいコミットに関連付けられたツリーオブジェクトとして使用されます。
インデックスを使用すると、インデックスが定義するツリーオブジェクトと作業ツリーをすばやく比較できます。
これは、エントリごとにいくつかの追加データ(最終変更時刻など)を保存することによって行われます。このデータは上に表示されず、作成されたツリーオブジェクトに保存されませんが、作業ディレクトリ内のどのファイルがインデックスに保存されているものと異なるかをすばやく判断するために使用できるため、Gitはそのようなファイルからすべてのデータを読み取って変更を探す必要がありません。
異なるツリーオブジェクト間のマージの競合に関する情報を効率的に表すことができるため、各パス名を、関連するツリーに関する十分な情報に関連付けて、それらの間で3方向のマージを作成できます。
私達はマージ中に、インデックスが1つのファイルの複数のバージョン(「ステージ」と呼ばれる)を格納できることを the section called “マージ作業中に競合解決の助けを得る” で確認しました。上記の git-ls-files(1) 出力の3番目の列はステージ番号であり、マージの競合があるファイルの場合は0以外の値を取ります。
したがって、インデックスは一種の一時的なステージング領域であり、作業中のツリーで埋められます。
あなたがインデックスを完全に吹き飛ばしても、それが記述されたツリーの名前を持っている限り、通常、情報は失われていません。
Table of Contents
大規模なプロジェクトは、多くの場合、より小さな自己完結型のモジュールで構成されます。たとえば、組み込みLinuxディストリビューションのソースツリーには、いくつかのローカルな変更を加えたディストリビューション内のすべてのソフトウェアが含まれます。例えば動画プレーヤーは、特定の既知の動作バージョンの解凍ライブラリに対して構築する必要がある場合があります。複数の独立したプログラムがすべて同じビルドスクリプトを共有する場合があります。
一元化されたリビジョン管理システムでは、これは多くの場合、すべてのモジュールを1つのリポジトリに含めることで実現されます。 開発者は、すべてのモジュールをチェックアウトすることも、操作する必要のあるモジュールのみをチェックアウトすることもできます。 物事を移動したり、APIや翻訳を更新したりしながら、1回のコミットで複数のモジュール間でファイルを変更することもできます。
Gitでは部分的なチェックアウトが許可されていないため、Gitでこのアプローチを複製すると、開発者は触れたくないモジュールのローカルコピーを保持する必要があります。巨大なチェックアウトでのコミットは、Gitがすべてのディレクトリをスキャンして変更を確認する必要があるため、あなたが期待するよりもずっと遅くなります。モジュールに多くのローカル履歴がある場合、クローン操作は永遠に終わりません。
プラス面として、分散型リビジョン管理システムは、外部ソースとはるかにうまく統合できます。 一元化されたモデルでは、外部プロジェクトの単一の任意のスナップショットが独自のリビジョン管理からエクスポートされ、ベンダーブランチのローカルリビジョン管理にインポートされます。すべての歴史は隠されています。分散リビジョン管理を使用すると、外部履歴全体のクローンを作成し、開発をより簡単に追跡して、ローカルの変更を再マージできます。
Gitはサブモジュールのサポートにより、リポジトリに外部プロジェクトのチェックアウトをサブディレクトリとして含めることができます。サブモジュールは独自のIDを維持します。サブモジュールサポートは、サブモジュールリポジトリの場所とコミットIDを格納するだけなので、包含するプロジェクト("superproject")を複製する他の開発者は、同一リビジョンのすべてのサブモジュールを簡単に複製できます。スーパープロジェクトの部分的なチェックアウトができます。あなたはサブモジュールのクローンを作成しない、一部、またはすべてを複製するようにGitに指示できます。
git-submodule(1) コマンドは、Git1.5.3以降で使用できます。Git 1.5.2を使用しているユーザーは、リポジトリでサブモジュールのコミットを検索し、手動でチェックアウトできます。それ以前のバージョンはサブモジュールをまったく認識しません。
サブモジュールのサポートがどのように機能するかを確認するには、ここで、後でサブモジュールとして使用できる4つのリポジトリ例を作成します:
$ mkdir ~/git $ cd ~/git $ for i in a b c d do mkdir $i cd $i git init echo "module $i" > $i.txt git add $i.txt git commit -m "Initial commit, submodule $i" cd .. done
次に、スーパープロジェクトを作成し、すべてのサブモジュールを追加します:
$ mkdir super $ cd super $ git init $ for i in a b c d do git submodule add ~/git/$i $i done
!スーパープロジェクトを公開する予定がある場合は、ここでローカルURLを使用してはいけません!
git submodule
が作成したファイルをご覧下さい:
$ ls -a . .. .git .gitmodules a b c d
git submodule add <repo> <path>
コマンドは以下のことを行います:
<repo>
から現在のディレクトリの下の指定された <path>
に複製し、デフォルトでmasterブランチをチェックアウトします。
スーパープロジェクトをコミットします:
$ 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
の大きな違いの1つは、 git submodule update
がブランチの先端ではなく、特定のコミットをチェックアウトすることです。これは、タグをチェックアウトするようなものです。ヘッドが切り離されているため、あなたはブランチで作業していません。
$ git branch * (detached from d266b98) master
サブモジュール内で変更を加えたい場合で、ヘッドが切り離されている場合は、ブランチを作成またはチェックアウトし、変更を加え、サブモジュール内で変更を公開してから、スーパープロジェクトを更新して新しいコミットを参照する必要があります:
$ git switch master
または
$ git switch -c fix-up
してから
$ echo "adding a line again" >> a.txt $ git commit -a -m "Updated the submodule from within the superproject." $ git push $ cd .. $ git diff diff --git a/a b/a index d266b98..261dfac 160000 --- a/a +++ b/a @@ -1 +1 @@ -Subproject commit d266b9873ad50488163457f025db7cdd9683d88b +Subproject commit 261dfac35cb99d380eb966e102c1197139f7fa24 $ git add a $ git commit -m "Updated submodule a." $ git push
サブモジュールも更新する場合は、 git pull
の後に git submodule update
を実行する必要があります。
サブモジュールの変更を参照するスーパープロジェクトに変更を公開する前に、必ずサブモジュールの変更を公開してください。サブモジュールの変更を公開するのを忘れると、他の人はリポジトリのクローンを作成できなくなります:
$ cd ~/git/super/a $ echo i added another line to this file >> a.txt $ git commit -a -m "doing it wrong this time" $ cd .. $ git add a $ git commit -m "Updated submodule a again." $ git push $ cd ~/git/cloned $ git pull $ git submodule update error: pathspec '261dfac35cb99d380eb966e102c1197139f7fa24' did not match any file(s) known to git. Did you forget to 'git add'? Unable to checkout '261dfac35cb99d380eb966e102c1197139f7fa24' in submodule path 'a'
古いバージョンのGitでは、サブモジュールで新しいファイルや変更されたファイルをコミットすることを簡単に忘れてしまう可能性があります。これにより、サブモジュールの変更をプッシュしないのと同様の問題が発生します。 Git 1.7.0以降、スーパープロジェクトの git 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
はそれらを上書きしません。 代わりに、ダーティブランチから切り替えることができないという通常の警告が表示されます。
Table of Contents
高レベルのコマンドの多くは、かつては低レベルの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操作はインデックスファイルで機能します。一部の操作はインデックスファイルに対して*純粋に*機能します(インデックスの現在の状態を示す)が、ほとんどの操作は、インデックスファイルとデータベースまたは作業ディレクトリの間でデータを移動します。 したがって、Git操作には4つの主要な組み合わせがあります:
git-update-index(1) コマンドは、作業ディレクトリからの情報でインデックスを更新します。 通常、以下のように、更新するファイル名を指定するだけでインデックス情報を更新します:
$ git update-index filename
ただし、ファイル名のグロブなどでよくある間違いを避けるために、コマンドは通常、まったく新しいエントリを追加したり、古いエントリを削除したりしません。つまり、通常は既存のキャッシュエントリを更新するだけです。
つまり、特定のファイルがもう存在しないこと、または新しいファイルを追加する必要があることをGitに伝えるには、それぞれ --remove
フラグと --add
フラグを使用する必要があります。
注意! --remove
フラグは、そのフラグの後ろに記述されたファイル名が必ずしも削除されることを意味しません。ファイルがディレクトリ構造にまだ存在する場合、インデックスは削除されずに新しいステータスで更新されます。 --remove
が意味する唯一のことは、update-indexが削除されたファイルを有効なものと見なし、ファイルが実際に存在しなくなった場合、それに応じてインデックスを更新することです。
特別な場合として、 git update-index --refresh
を実行することもできます。これにより、各インデックスの「統計」情報が現在の統計情報と一致するように更新されます。オブジェクトのステータス自体は「更新されません」。オブジェクトが古いバッキングストアオブジェクトとまだ一致するかどうかをすばやくテストするために使用されるフィールドのみが更新されます。
以前紹介した git-add(1) は、 git-update-index(1) の単なるラッパーです。
あなたは、プログラムを使用して、現在のインデックスファイルを「ツリー」オブジェクトに書き込みます
$ 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
フラグ または filename より前に指定する)とチェックアウトを強制します。
最後に、これまでの範疇にない半端な話題をいくつか:
git write-tree
でインスタンス化したツリーをコミットするには、そのツリーとその背後にある履歴(最も注目すべきは、履歴上、その前にあった「親」のコミットです)を参照する「commit」オブジェクトを作成します。
通常、「コミット」には1つの親があります。つまり、特定の変更が行われる前のツリーの以前の状態です。ただし、2つ以上の親コミットを持つ場合もあります。その場合、そのようなコミットは、他のコミットによって表される2つ以上の以前の状態をまとめる(「マージする」)ため、「マージ」と呼びます。
つまり、「ツリー」は作業ディレクトリの特定のディレクトリ状態を表し、「コミット」は時間内のその状態を表し、どのようにしてそこに到達したかを説明します。
コミット時の状態を説明するツリーと親のリストをオブジェクトに与えることで、コミットオブジェクトを作成します:
$ git commit-tree <tree> -p <parent> [(-p <parent2>)...]
次に、stdinでコミットの理由を示します(パイプまたはファイルからのリダイレクト、またはttyで入力するだけ)。
git commit-tree
は、そのコミットを表すオブジェクトの名前を返します。あなたはそれを後で使用できるように保存しておくべきです。 通常、新しい HEAD
状態をコミットし、Gitはその状態に関するメモをどこに保存するかを気にしませんが、実際には、最後にコミットされた状態が何であったかを常に確認できるように、 .git/HEAD
が指すファイルに結果を書き込む傾向があります。
以下は、さまざまな部分がどのように組み合わされるかを示す図です:
commit-tree commit obj +----+ | | | | V V +-----------+ | Object DB | | Backing | | Store | +-----------+ ^ write-tree | | tree obj | | | | read-tree | | tree obj V +-----------+ | Index | | "cache" | +-----------+ update-index ^ blob obj | | | | checkout-index -u | | checkout-index stat | | blob obj V +-----------+ | Working | | Directory | +-----------+
あなたはさまざまなヘルパーツールを使用して、オブジェクトデータベースとインデックスに表示されているデータを調べることができます。すべてのオブジェクトについて、 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方向マージを実行するのに役立ちます。これは、マージ手順を数回繰り返すことで、多方向マージに使用できます。 通常の状況では、3方向マージ(2ラインの履歴の調整)を1回だけ実行して結果をコミットしますが、必要に応じて、一度に複数のブランチをマージできます。
3方向マージを実行するには、マージする2つのコミットから開始し、最も近い共通の親(3番目のコミット)を見つけて、これら3つのコミットに対応するツリーを比較します。
マージの「ベース」を取得するには、2つのコミットの共通の親を検索します:
$ git merge-base <commit1> <commit2>
これにより、両方が基づいているコミットの名前が出力されます。これらのコミットのツリーオブジェクトを検索する必要があります。これは、以下の方法で簡単に実行できます
$ git cat-file commit <commitname> | head -1
これは、ツリーオブジェクト情報は常にコミットオブジェクトの最初の行にからです。
マージする3つのツリー(1つの「元の」ツリー(別名共通ツリー)と2つの「結果」ツリー(別名マージするブランチ))が分かったら、インデックスに「マージ」読み取りを行います。 古いインデックスの内容を破棄する必要がある場合には文句を言うので、それらをコミット済であることをことを確認する必要があります。実際、通常は常に最後のコミットに対してマージを実行します(したがって、とにかく現在のインデックスにあるものと一致する必要があります)。
マージを行うには以下を行います
$ git read-tree -m -u <origtree> <yourtree> <targettree>
これにより、とるにたりない些細な3方向マージ操作(trivial merge operations)の全てがインデックスファイルに対して直接実行され、 git write-tree
を使用して結果を書き出すことができます。(訳注:trivial;ラテン語「3 本の道が合する所,ありふれたこと」の意がある)
悲しいことに、多くのマージはとるにたりないものでもなく些細なものでもありません。 追加、移動、または削除されたファイルがある場合、または両方のブランチが同じファイルを変更した場合は、「マージエントリ」を含むインデックスツリーが残ります。 このようなインデックスツリーはツリーオブジェクトに書き出すことはできません。結果を書き出す前に、他のツールを使用してこのようなマージの衝突を解決する必要があります。
このようなインデックスの状態は、 git ls-files --unmerged
コマンドで調べることができます。 たとえば:
$ git read-tree -m $orig HEAD $target $ git ls-files --unmerged 100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello.c 100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello.c 100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello.c
git ls-files --unmerged
出力の各行は、ブロブモードビット、ブロブ SHA-1、「ステージ番号」、ファイル名 です。 「ステージ番号」は、Gitがどのツリーから来たかを示す方法です。ステージ1は「$orig」ツリーに対応し、ステージ2は「HEAD」ツリーに対応し、ステージ3は「$target」ツリーに対応します。
ささいなマージは git read-tree -m
内で行われると先に述べました。 たとえば、ファイルが $orig
から、 HEAD
または $target
に変更されなかった場合、または同様に、ファイルが $orig
から HEAD
に変更され、かつ、 $orig
が $target
に変更された場合、最終的な結果は明らかに HEAD
にあるものです。上記の例が示すのは、ファイル hello.c
が $orig
から HEAD
に、かつ、 $ orig
から $target
に別の方法で変更されたことです。これは、以下のように、あなたのお気に入りの3方向マージプログラム、例えば、これら3つのステージのブロブオブジェクトに対する diff3
または merge
またはGit独自の merge-file を実行することで解決できます:
$ git cat-file blob 263414f >hello.c~1 $ git cat-file blob 06fa6a2 >hello.c~2 $ git cat-file blob cc44c73 >hello.c~3 $ git merge-file hello.c~2 hello.c~1 hello.c~3
これにより、競合がある場合は競合マーカーとともに、マージ結果が hello.c~2
ファイルに残ります。マージ結果が理にかなっていることを確認した後、あなたは、このファイルの最終的なマージ結果が何であるかをGitに伝えることができます:
$ mv -f hello.c~2 hello.c $ git update-index hello.c
パスが「マージされていない」状態の場合、そのパスに対して git update-index
を実行すると、Gitにパスを解決済みとしてマークするように指示します。
上記は、内部で概念的に何が起こるかを理解するのに役立つ、最下位レベルでのGitマージの説明です。 実際には、Git自体でさえも、このために git cat-file
を3回実行する人は誰もいません。ステージを一時ファイルに抽出し、その上で「マージ」スクリプトを呼び出す git merge-index
プログラムがあります:
$ git merge-index git-merge-one-file hello.c
そして、これが、より高いレベルの git merge -s resolve
の実装になっています。
Table of Contents
この章では、おそらくGit開発者だけが理解する必要があるGit実装の内部の詳細について説明します。
すべてのオブジェクトには、オブジェクトの形式(つまり、オブジェクトの使用方法や他のオブジェクトの参照方法)を識別する、静的に決定された「タイプ」( "type" )があります。 現在、「blob」、「tree」、「commit」、「tag」の4つの異なるオブジェクトタイプがあります。
オブジェクトタイプに関係なく、すべてのオブジェクトは次の特性を共有します: すべてzlibで圧縮され、タイプを指定するだけでなく、オブジェクト内のデータに関するサイズ情報も提供するヘッダーがあります。オブジェクトの名前付けに使用されるSHA-1ハッシュは、 元のデータ+ヘッダー のハッシュであるため、 sha1sum
file は file のオブジェクト名と一致しないことに注意してください。
その結果、オブジェクトの一般的な整合性は、オブジェクトの内容やタイプに関係なく常にテストできます。すべてのオブジェクトは、 (a)ハッシュがファイルの内容と一致、かつ、 (b)オブジェクトが、 <ascii type without space> + <space> + <ascii decimal size> + <byte\0> + <binary object data>
というシーケンスのバイトストリームへちゃんと解凍できることで検証できます。
構造化オブジェクトは、その構造と他のオブジェクトへの接続をさらに検証できます。 これは通常、すべてのオブジェクトの完全な依存関係グラフを生成し、(ハッシュを介して表面的な一貫性を検証するだけでなく)内部の一貫性を検証する gitfsck
プログラムを使用して行われます。
新しい開発者がGitのソースコードを見つけるのは必ずしも簡単ではありません。この節では、あなたがどこから始めればよいかを示すちょっとしたガイダンスを提供します。
開始するのに適した場所は、最初のコミットの内容から始めることです:
$ git switch --detach e83c5163
最初のリビジョンは、Gitが今日持っているほとんどすべての基礎を築きますが、一度に読むには十分小さくまとまっています。
そのリビジョン以降、用語が変更されていることに注意してください。 たとえば、そのリビジョンのREADMEは、 "changeset" という単語を使用して、今、 commit と呼んでいるものを説明しています。
また、 私たちはそれを「キャッシュ」(cache)と呼ぶのではなく、「インデックス」(index)と呼びます。 ただし、ファイルの名前は依然として read-cache.h
です。
あなたが最初のコミットでGitの考え方を把握したなら、あなたはより新しいバージョンをチェックして、 read-cache-ll.h
と object.h
と commit.h
を読み下すべきです。
初期のGitは、(UNIXの伝統でである)非常に単純なプログラムの集まりであり、スクリプトで使用して、出力を別のプログラムにパイプしていました。 新しいものをテストするのが簡単だったので、これは初期開発に適していました。ただし、最近、これらのパーツの多くが組み込みになり、コアの一部が「解放」( "libified" )されました。つまり、パフォーマンス、移植性、およびコードの重複を避けるために libgit.a に配置されました。
今や、 あなたはインデックスが何であるかを知り(そして、それに対応するデータ構造は read-cache-ll.h
にある)、 その最初のメンバーである struct object
から共通の構造を継承するオブジェクトタイプ(blobs と trees と commits と tags)が幾つかあることを知りました(したがって、 たとえば (struct object *)commit
とキャストして、 &commit->object
と全く同様の事を行う事ができます。 つまり、 オブジェクト名とフラグを取得を取得できるのです)。
ここで、この情報を吸収するために一服するとしましょう。
次のステップ: オブジェクトの命名に慣れてください。 the section called “Naming commits” を読んでください。 オブジェクトに名前を付ける方法はたくさんあります(リビジョンだけではありません!)。 これらはすべて sha1_name.c
で処理されます。 関数 get_sha1()
をざっと見てください。特別な処理の多くは、 get_sha1_basic()
などの関数によって実行されます。
リビジョンウォーカー。これはGitの最も解放(libified)された部分にあなたがうまく適応できるようにします。
基本から行きましょう。 git log
の初期バージョンは、シェルスクリプトでした:
$ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \ LESS=-S ${PAGER:-less}
これはどういう意味か?
git rev-list
は、リビジョンウォーカーのオリジナルバージョンであり、「常に」stdoutにリビジョンのリストを出力します。ほとんどの新しいGitコマンドは git rev-list
を使用するスクリプトとして開始されるため、これは引き続き機能し続ける必要があります。
gitrev-parse
はもはやそれほど重要ではありません。これは、スクリプトによって呼び出されたさまざまな配管コマンドに関連するオプションを除外するためにのみ使用されました。
git rev-list
が行ったことのほとんどは、 revision.c
と revision.h
に含まれています。 オプションを rev_info
という名前の構造体でラップします。この構造体は、どのリビジョンをどのように歩くか(walk)などを制御します。
git rev-parse`の元々の仕事は、今や関数 `setup_revisions()
によって実行されるようになりました。この関数は、リビジョンとリビジョンウォーカーの一般的なコマンドラインオプションを解析します。この情報は、後で使用できるように構造体 rev_info
に保存されます。 あなたは setup_revisions()
を呼び出した後、独自のコマンドラインオプション解析を行うことができます。 その後、初期化のために prepare_revision_walk()
を呼び出す必要があります。その後は、関数 get_revision()
を使用してコミットを1つずつ取得できます。
リビジョンウォーキング処理の詳細に興味があるなら cmd_log()
の最初の実装をご覧ください。 git show v1.3.0~155^2~4
を呼び出し、その関数までスクロールダウンします( setup_pager()
を直接呼び出す必要がなくなったことに注意してください)。
今日では git log
は組み込みです。つまり、コマンド git
に含まれていることを意味します。ビルトインのソースは、
cmd_<bla>
と呼ばれる関数で、通常は builtin.h
で宣言され、builtin/<bla.c>
で定義されます(Gitの古いバージョンでは、代わりに builtin-<bla>.c
に含まれていたことに注意してください)。
git.c
の commands[]
配列のエントリ、および
Makefile
の BUILTIN_OBJECTS
エントリ。
しばしば1つのソースファイルに複数のビルトインが含まれている場合があります。たとえば、 cmd_whatchanged()
と cmd_log()
はどちらも、かなりのコードを共有しているため、 builtin/log.c
にあります。その場合、それらが存在する .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()
の関数シグネチャの変数 sha1
は unsigned char *
ですが、実際には unsigned char[20]
へのポインタであることが期待されています。この変数には、指定されたコミットの160ビットのSHA-1が含まれます。SHA-1が unsigned char *
として渡される場合は常に、 char *
として渡される16進文字のASCII表現とは対照的に、バイナリ表現であることに注意してください。
コード全体でこれらの両方が表示されます。
さて、肝心の部分は:
case 0: buf = read_object_with_reference(sha1, argv[1], &size, NULL);
これはブロブ(実際にはブロブだけでなくあらゆるタイプのオブジェクト)を読み取る方法です。 関数 read_object_with_reference()
が実際にどのように機能するかを知るには、(Gitのリポジトリで git grep read_object_with | grep ":[a-z]"
などして)そのソースコードを探して拝んでください。
結果の使用方法を確認するには、 cmd_cat_file()
を読んでください:
write_or_die(1, buf, size);
機能を探す場所がわからない場合があります。このような多くの場合、 git log
の出力を検索してから、対応するコミットを git show
すると役に立ちます。
例: git bundle
のテストケースがあったことはわかっているが、それがどこにあったか覚えていない場合(はいもちろん、あなたは git grep bundle t/
できますが、そーゆー事を言いたい訳じゃない):
$ git log --no-merges t/
ページャー(less
)の中で、 "bundle" を検索し、数行前に戻って、それがコミット 18449ab0 にあることを確認します。このオブジェクト名をコピーして、コマンドラインに貼り付けます。
$ git show 18449ab0
できた!
別の例: スクリプトを組み込みにするために何をすべきかを調べます:
$ git log --no-merges --diff-filter=A builtin/*.c
ほらこのとおり。Gitは実際、Git自体のソースを見つけるのに最適なツールです!
Table of Contents
.git
で、リビジョン管理下にあるファイルのローカルでチェックアウトされたコピーはありません。つまり、通常は非表示の .git
サブディレクトリに存在するすべてのGit管理ファイルと制御ファイルは、代わりに repository.git
ディレクトリに直接存在し、他のファイルは存在せず、チェックアウトされません。通常、公開リポジトリの発行者は、ベアリポジトリを利用可能にします。
名詞として: Gitの履歴における一つのポイント。プロジェクトの履歴全体は、相互に関連する一連のコミットとして表されます。「コミット」という言葉は、他のリビジョン管理システムが「リビジョン」または「バージョン」という言葉を使用するのと同じ場所で、Gitによってよく使用されます。 コミットオブジェクト の省略形としても使用されます。
動詞として(コミットする): インデックスの現在の状態を表す新しいコミットを作成し、その新しいコミットをポイントするようにHEADを進めることにより、プロジェクトの状態の新しいスナップショットをGit履歴に保存する操作。
通常、HEADはブランチの名前を格納し、HEADが示す履歴を操作するコマンドは、HEADが指すブランチの先端につながる履歴を操作します。 ただし、Gitでは、必ずしも特定のブランチの先端ではない任意のコミットをチェックアウトすることもできます。このような状態のHEADを「detached」(切り離されている、デタッチされている)と呼びます。
注意: 現在のブランチの履歴を操作するコマンド(たとえば、その上に新しい履歴を構築するための git commit
)は、HEADがデタッチされている間も機能することに注意してください。それらは、ブランチに影響を与えることなく、更新された履歴の先端を指すようにHEADを更新します。現在のブランチに関する情報を更新または照会するコマンド(たとえば、現在のブランチが統合するリモートトラッキングブランチを設定する git branch --set-upstream-to
)は、この状態で問い合わせる(実際の)現在のブランチがないため、明らかに機能しません。
.git
graftsは、コミットの偽の祖先情報を記録することで、他の点では異なる2つの開発ラインを結合できます。こうすることで、あるコミットが持つ親の組を、コミット作成時に記録されたものとは異なるものとして Git に見せかけることができるのです。これは .git/info/grafts
ファイルを介して構成されます。
注意: graftsメカニズムは時代遅れであり、リポジトリ間でオブジェクトを転送する際に問題が発生する可能性があることに注意してください。 同じことを行うためのより柔軟で堅牢なシステムについては、 git-replace(1) を参照してください。
$GIT_DIR/refs/heads/
ディレクトリのファイルに保存されます。 (linkgit: git-pack-refs[1] を参照してください。)
$GIT_DIR/hooks/
ディレクトリにあり、ファイル名から .sample
サフィックスを削除するだけで有効になります。以前のバージョンのGitでは、それらを実行可能にする必要がありました。
動詞として: 別のブランチ(あるいは外部のリポジトリから)の内容を現在のブランチに取り込むこと。マージされたブランチが別のリポジトリからのものである場合、これは最初にリモートブランチをフェッチし、次に結果を現在のブランチにマージすることによって行われます。このフェッチ操作とマージ操作の組み合わせは、プル(pull)と呼ばれます。マージは、ブランチが分岐してから行われた変更を識別し、それらすべての変更を一緒に適用する自動プロセスによって実行されます。変更が競合する場合は、マージを完了するために手動による介入が必要になる場合があります。
名詞として: fast-forwardでない限り、マージ成功の結果として、マージされたブランチの先端を親に持つ新しいコミットが作成されます。このコミットは「マージコミット」と呼ばれます。または単に「マージ」と呼ばれることもあります。
$GIT_DIR/objects/
にあります。
git branch-r
を使用して確認できます。
cp -R
が宛先ディレクトリの内容を更新するのと同様に、ファイルを更新して作業ディレクトリに追加するのみで削除を行いません。これは、インデックスまたはツリー風の何か(tree-ish)からファイルをチェックアウトするときのcheckoutのデフォルトモードです。対照的に、オーバーレイなしモード(no-overlay mode)では、 rsync --delete
と同様に、ソース側に存在しない追跡ファイルは削除されます。
(パススペック):Gitコマンドでパスを制限するために使用されるパターン。
pathspec は、「git ls-files」や「git ls-tree」や「git add」や「git grep」や「git diff」や「git checkout」や、ツリーまたは作業ツリー(working tree)のサブセットへの操作の為にスコープを制限する他の多くのコマンドの、コマンドラインで使用されます。 パスが現在のディレクトリまたはトップレベルのどちらを基準にしているかについては、 各コマンドのドキュメントを参照してください。 pathspec の構文は以下のとおりです:
*
と ?
はディレクトリ区切り文字と一致させる事ができます。
たとえば、 Documentation/*.jpg
は、 Documentation/chapter_1/figure_1.jpg
を含む、Documentationサブツリー内のすべての .jpg
ファイルと一致します。
コロン(:
)で始まるpathspecには特別な意味があります。短い形式では、先頭のコロン(:
)の後に0個以上の「魔法記号」(magic signature)(オプションで別のコロン(:
)で終了)が続き、残りはパスと照合するパターンです。「魔法記号」は、英数字、グロブ、正規表現の特殊文字でもコロンでもないASCII記号で構成されます。パターンが「魔法記号」シンボルセットに属さず、コロンではない文字で始まる場合、「魔法記号」を終了するオプションのコロンは省略できます。
長い形式では、先頭のコロン(:
)の後に開き括弧((
) 、0個以上の「魔法単語」(magic words)のコンマ区切りリスト、および閉じ括弧()
) が続き、残りは次のパターンです。パスと一致します。
コロンのみのpathspecは、「pathspecが無い」ことを意味します。 この形式は、他のpathspecと組み合わせないでください。
top
(魔法記号: /
)は、サブディレクトリ内からコマンドを実行している場合でも、作業ツリーのルートからパターンを一致させます。
*
または ?
などのパターンのワイルドカードはリテラル文字として扱われます。
Gitはパターンを、 FNM_PATHNAMEフラグを指定した fnmatch(3) に消費されるのに適したシェルグロブとして扱います。パターン内のワイルドカードは、パス名内の / と一致しません。 たとえば、「Documentation/*.html」は「Documentation/git.html」と一致しますが、「Documentation/ppc/ppc.html」または「tools/perf/Documentation/perf.html」とは一致しません。
フルパス名と一致するパターンの2つの連続するアスタリスク **
は、特別な意味を持つ場合があります:
**
の後にスラッシュが続く場合は、すべてのディレクトリで一致することを意味します。たとえば、 **/foo
は、パターン foo
と同じように、ファイルまたはディレクトリ foo
のどこにでも一致します。 **/foo/bar
は、ディレクトリ foo
の直下にあるファイルまたはディレクトリ bar
と一致します。
/**
は、内部のすべてに一致します(matches everything inside)。たとえば、 abc/**
は、 .gitignore
ファイルの場所を基準にして、ディレクトリ "abc" 内のすべてのファイルと無限の深さで一致します。
a/**/b
は a/b
、 a/x/b
、a/x/y/b
などと一致します。
他の連続するアスタリスクは無効と見なされます。
グロブ魔法はリテラル魔法と互換性がありません。
attr:
の後には、スペースで区切られた「属性要件」(attribute requirements)のリストがあります。パスが一致すると見なされるには、これらすべてを満たす必要があります。これは、通常の非魔法pathspecパターンマッチングに追加されます。 gitattributes(5) 参照。
パスの各属性要件は、以下のいずれかの形式を取ります:
ATTR
では、属性 ATTR
を設定する必要があります。
-ATTR
では、属性 ATTR
が設定されていない必要があります。
ATTR = VALUE
では、属性 ATTR
を文字列 VALUE
に設定する必要があります。
!ATTR
では、属性 ATTR
が指定されていない必要があります。
注意: ツリーオブジェクトと照合する場合、属性は、指定されたツリーオブジェクトからではなく、作業ツリーから取得されることに注意してください。
!
またはその同義語 ^
)が実行されます。一致する場合、パスは無視されます。非除外pathspecがない場合、pathspecなしで呼び出されたかのように、除外が結果セットに適用されます。
--pickaxe-all
オプションを使用すると、特定のテキスト行などを導入または削除した完全なチェンジセットを表示するために使用できます。 git-diff(1) を参照してください。
refs/bisect/
で始まるすべてのrefのみですが、今後、他の普通でないrefが含まれる可能性があります。
$GIT_DIR
の下にあるファイルのクラスであり、rev-parseしたときrefのように動作しますが、それはgitによって特別扱されます。疑似参照はすべて大文字の名前を持ち、かつ、常にSHA-1とそれに続く空白(whitespace)で構成される行で始まります。したがって、HEADは疑似参照ではありません。なぜならHEADはシンボリック参照である場合があるためです。オプションで、いくつかの追加データが含まれる場合があります。例としては MERGE_HEAD
と CHERRY_PICK_HEAD
があります。 per-worktree refs とは異なり、これらのファイルはシンボリックrefにすることはできず、reflogを含めることはできません。 また、通常のref更新機構を使用して更新することもできません。代わりに、ファイルに直接書き込むことによって更新されます。ただし、それらはrefであるかのように読み取ることができるため、 git rev-parse MERGE_HEAD
は機能します。
オブジェクト名または別のrefを指す refs/
で始まる名前(例: refs/heads/master
;別のrefを指すrefは シンボリックref と呼ぶ)。便宜上、Gitコマンドの引数として使用する場合は refs/
を省略できる場合があります。詳細については gitrevisions(7) を参照してください。 refs は repository に保存されます。
ref名前空間は階層的です。さまざまなサブ階層がさまざまな目的で使用されます(たとえば、 refs/heads/
階層はローカルブランチを表すために使用されます)。
refs/
で始まらない特別な目的のrefがいくつかあります。最も注目すべき例は HEAD
です。
refs/remotes/foo/bar
のように見え(「foo」という名前のリモートで「bar」という名前のブランチを追跡することを示します)、構成されたフェッチrefspecの右側(right-hand-side)に一致します。リモート追跡ブランチには、直接の変更を含めたり、ローカルコミットを行ったりしないでください。
git clone --depth=...
コマンドを実行して作成されたこと明言したものです。
--depth
オプションを指定することで作成され、その履歴は後で git-fetch(1) で深めることができます。
<<def_HEAD,HEAD>>
はsymrefの代表的な例です。シンボリックrefは git-symbolic-ref(1) コマンドで操作されます。
refs/tags/
名前空間の下のref(通常、タグは タグ または コミットオブジェクト のいずれかを指します)。headとは対照的に、タグは commit
コマンドによって更新されません。Gitタグは、Lispタグとは何の関係もありません(Git界隈では、それはオブジェクトタイプと呼ばれます)。タグは通常、コミットの祖先チェーンの特定のポイントをマークするために使用されます。
branch.<name>.remote
や branch.<name>.merge
を介して構成されます。Aのアップストリームブランチが origin/B
の場合、「Aは origin/B
を追跡しています」と言うことがあります。
Table of Contents
これは主要なコマンドの簡単な要約です。ここまでの章を読めば、これらがどのように機能するか、より詳細に説明してあります。
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
デグレ(regression)を探します:
$ git bisect start $ git bisect bad # current version is bad $ git bisect good v2.6.13-rc2 # last known good revision Bisecting: 675 revisions left to test after this # test here, then: $ git bisect good # if this revision is good, or $ git bisect bad # if this revision is bad. # repeat until done.
Gitにあなたが誰なのか教えてやってください:
$ cat >>~/.gitconfig <<\EOF [user] name = Your Name Comes Here email = you@yourdomain.example.com EOF
次のコミットに含めるファイルの内容を選択してから、コミットします:
$ git add a.txt # updated file $ git add b.txt # new file $ git rm c.txt # old file $ git commit
または、ワンステップでコミットを準備・作成します:
$ git commit d.txt # use latest content only of d.txt $ git commit -a # use latest content of all tracked files
$ 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
パッチの輸出入(inport/export):
$ git format-patch origin..HEAD # format a patch for each commit # in HEAD but not in origin $ git am mbox # import patches from the mailbox "mbox"
別のGitリポジトリからブランチをフェッチしてから、現在のブランチにマージします:
$ git pull git://example.com/project.git theirbranch
現在のブランチにマージする前に、フェッチしたブランチをローカルブランチに保存します:
$ git pull git://example.com/project.git theirbranch:mybranch
ローカルブランチでコミットを作成した後、あなたのコミットでリモートブランチを更新します:
$ git push ssh://example.com/project.git mybranch:theirbranch
リモートブランチとローカルブランチの両方に "test" という名前が付けられている場合:
$ git push ssh://example.com/project.git test
頻繁に使用されるリモートリポジトリのショートカットバージョン:
$ git remote add example ssh://example.com/project.git $ git push example test
Table of Contents
これらは未だ作業中です。
基本的な要件:
git am
command" ではなく "importing patches into a project" です。
明確な章の依存関係グラフを作成する方法を考えてください。これにより、人々は必ずしもその間のすべてを読むことなく、重要なトピックにたどり着くことができます。
省略されている他のものについては、 Documentation/
を浚ってください。特に:
technical/
のいくつかと
メールアーカイブをスキャンして、省略されている他のものを探します
マニュアルページをスキャンして、このマニュアルが提供するよりも多くの背景を想定しているものがあるかどうかを確認します。
より良い例を追加します。 クックブックの例だけのセクションを記述するのは良い考えです。 たいてい「高度な例」セクションを標準の章の最後のセクションにしますよね?
必要に応じて、用語集への相互参照を含めます。
CVSやSubversionや一連のリリースtarballのインポートなど、他のバージョン管理システムの操作に関するセクションを追加します。
配管コマンドの使用とスクリプトの作成に関する章を作成します。
代替、clone -reference、等。
リポジトリの破損からの回復の詳細は、以下: https://lore.kernel.org/git/Pine.LNX.4.64.0702272039540.12485@woody.linux-foundation.org/ https://lore.kernel.org/git/Pine.LNX.4.64.0702141033400.3604@woody.linux-foundation.org/