Git refs: An overview
この包括的な Git トレーニングで、Git の基本を学習しましょう。
コミットを参照する多くの方法を理解することで、これらすべてのコマンドをより強力に使用することができるようになります。この章では、コミットを参照する多くの方法を示しながら、git checkout
、git branch
、git push
などの一般的なコマンドの内部動作について説明します。
また、 Git の reflog メカニズムを介して「失われた」ようにみえるコミットにアクセスして復活させる方法も学習します。
ハッシュ
コミットを参照する最も直接的な方法は、SHA-1 ハッシュを利用することです。これは、各コミットの一意の ID として機能します。git log
の出力からすべてのコミットのハッシュを見つけることができます。
commit 0c708fdec272bc4446c6cabea4f0022c2b616eba Author: Mary Johnson Date: Wed Jul 9 16:37:42 2014 -0500 Some commit message
他の Git コマンドにコミットを渡す場合に必要なことは、そのコミットを一意に特定できる十分な数の文字を指定することです。たとえば、次のコマンドを実行することで、git show
を使用して上記のコミットを検査できます。
git show 0c708f
場合によっては、ブランチ、タグ、または別の間接参照を対応するコミット ハッシュに変換する必要があります。これには、git rev-parse
コマンドを使用できます。次の例は、main
ブランチがポインターになっているコミットのハッシュを返します。
関連資料
Git リポジトリ全体を移動する方法
ソリューションを見る
Bitbucket Cloud での Git の使用方法についてのチュートリアルです。
git rev-parse main
これは、コミット参照を受け入れるカスタムスクリプトを記述するときに特に便利です。手動でコミット参照を解析する代わりに、git rev-parse
で入力を正規化させることができます。
Ref
ref は、コミットを参照するための間接的な方法です。コミットハッシュの使いやすい別名と考えることができます。これは、ブランチおよびタグを表す Git の内部機構です。
ref は .git/refs
ディレクトリに通常のテキストファイルとして保存されます。このディレクトリでは、.git
は通常 .git
と呼ばれます。リポジトリの1つで ref を調べるには、.git/refs
に移動します。次の構造が表示されるはずですが、リポジトリに含まれるブランチ、タグ、およびリモートに応じて異なるファイルが含まれます。
.git/refs/ heads/ main some-feature remotes/ origin/ main tags/ v0.9
heads
ディレクトリは、ご使用のリポジトリ内のすべてのローカル ブランチを定義します。各ファイル名は対応するブランチの名前と一致し、ファイル内にはコミット ハッシュがあります。このコミット ハッシュは、ブランチの先端の位置です。これを確認するには、Git リポジトリのルートから次の 2 つのコマンドを実行してみてください。
# Output the contents of `refs/heads/main` file: cat .git/refs/heads/main # Inspect the commit at the tip of the `main` branch: git log -1 main
cat
コマンドにより返されるコミット ハッシュは、git log
で表示されるコミット ID と一致する必要があります。
main
ブランチの場所を変更するために Git が実行する必要があるのは、refs/heads/master
ファイルのコンテンツの変更のみです。同様に、新しいブランチの作成とは、単に、新しいファイルに対してコミット ハッシュを記述することです。これは Git ブランチが SVN に比べて非常に軽量である理由の一つです。
tags
ディレクトリはまったく同じ方法で動作しますが、ブランチの代わりにタグが含まれています。remotes
ディレクトリには、git remote
で個別のサブディレクトリとして作成したすべてのリモートリポジトリが一覧表示されます。各リポジトリ内部に、ご使用のリポジトリにフェッチしたすべてのリモートブランチがあります。
Specifying refs
ref を Git コマンドに渡す場合、その ref のフルネームを定義するか、ショートネームを使用して一致する ref を Git に検索させます。, ref のショートネームは名前でブランチを参照するたびに使用するものなので、既に精通しているはずです。
git show some-feature
上記コマンドの some-feature
引数は実際にはブランチのショートネームです。Git はこれを使用する前に refs/heads/some-feature
に変換します。次のようにコマンド行で絶対 ref を指定することもできます。
git show refs/heads/some-feature
これは、ref の場所に関する曖昧さを回避します。たとえば、some-feature
と呼ばれるタグとブランチの両方が存在する場合などに必要です。ただし、適切な命名規則使用している場合、タグとブランチ間の曖昧さは一般的に問題にならないはずです。
Refspecs セクションでは、さらに多くの ref のフルネームを学習します。
Packed refs
大規模なリポジトリの場合、Git はパフォーマンスの効率を高めるために、定期的にガーベッジ・コレクションを実行して不要なオブジェクトを削除し、複数の ref を単一のファイルに圧縮します。この圧縮は、ガーベッジ・コレクションコマンドを使用して強制できます。
git gc
これにより、refs
フォルダ内の個々のブランチとタグファイルはすべて、.git
ディレクトリの最上部の packed-refs
という単一のファイルに移動します。このファイルを開くと、ref に対応するコミットハッシュのマッピングがあります。
00f54250cf4e549fdfcafe2cf9a2c90bc3800285 refs/heads/feature 0e25143693cfe9d5c2e83944bbaf6d3c4505eb17 refs/heads/main bb883e4c91c870b5fed88fd36696e752fb6cf8e6 refs/tags/v0.9
外部では、Git の通常の機能は何ら影響を受けません。しかし、.git/refs
フォルダーが空であることに戸惑うかもしれません。refs の場所は次のとおりです。
Special refs
refs
ディレクトリに加えて、.git
ディレクトリの最上部に特別な ref がいくつかあります。以下に一覧を示します。
HEAD
– 現在チェックアウト中のコミット/ブランチ。FETCH_HEAD
– リモートリポジトリからフェッチされた最新のブランチ。ORIG_HEAD
– 大幅な変更前のHEAD
へのバックアップ参照。MERGE_HEAD
–git merge
を使用して現在のブランチにマージしようとしているコミット。CHERRY_PICK_HEAD
– 選択したコミット
これらの ref はすべて必要に応じて Git により作成や更新が行われます。たとえば、git pull
コマンドは最初に git fetch
を実行し、これが FETCH_HEAD
参照を更新します。次に、git merge FETCH_HEAD
を実行して、リポジトリへのフェッチ済みブランチのプルを完了します。HEAD
で実行したことがあると思いますが、もちろん、これらはすべてその他の ref と同様に使用できます。
これらのファイルには、ファイルのタイプとリポジトリの状態に応じてさまざまなコンテンツが含まれます。HEAD
ref にはシンボリック ref (コミット ハッシュではなく、単に別の ref への参照) またはコミット ハッシュのいずれかを含めることができます。たとえば、main
ブランチにいる場合の HEAD
のコンテンツを調べてみましょう。
git checkout main cat .git/HEAD
これは、ref: refs/heads/main
を出力します。つまり、HEAD
は refs/heads/main
ref へのポインターです。このようにして、Git は main
ブランチが現在チェックアウト中であることを認識します。別のブランチに切り替えた場合、HEAD
のコンテンツは新しいブランチを反映するために更新されます。しかし、ブランチの代わりにコミットをチェックアウトした場合、HEAD
にはシンボリック ref の代わりにコミット ハッシュが含まれます。このようにして、Git は分離した HEAD 階層にいることを認識します。
ほとんどの場合、HEAD
は直接使用することになる参照にすぎません。それ以外は、一般的に Git の内部機構に接続する必要がある低レベルのスクリプトを記述する場合にのみ役立ちます。
Refspecs
refspec は、ローカルリポジトリのブランチをリモートリポジトリのブランチにマッピングします。これにより、ローカル Git コマンドを使用してリモートブランチを管理したり、ある種の高度な git push
や git fetch
の動作を設定したりすることが可能になります。
refspec は [+]
<src>
:
<dst>
として指定します。
パラメータはローカル リポジトリのソース ブランチであり、
パラメータはリモート リポジトリの宛先ブランチです。オプションの +
記号は、リモート リポジトリに強制的に非ファスト フォワード更新を実行させる場合に指定します。
refspec は git push
コマンドとともに使用して、リモート ブランチに別の名前を付けることができます。たとえば、次のコマンドは通常の git push
と同様に main
ブランチを origin
リモート リポジトリにプッシュしますが、origin
リポジトリのブランチの名前として qa-main
を使用します。これは、QA チームがリモート リポジトリに独自のブランチをプッシュする必要がある場合に役立ちます。
git push origin main:refs/heads/qa-main
You can also use refspecs for deleting remote branches. This is a common situation for feature-branch workflows that push the feature branches to a remote repo (e.g., for backup purposes). The remote feature branches still reside in the remote repo after they are deleted from the local repo, so you get a build-up of dead feature branches as your project progresses. You can delete them by pushing a refspec that has an empty parameter, like so:
git push origin :some-feature
リモートリポジトリにログインして手動でリモートブランチを削除する必要がないため、これは非常に便利です。ただし、Git v1.7.0 以降は、上記の方法の代わりに --delete
フラグを使用できます。次のコマンドは、上記のコマンドと同じ効果があります。
git push origin --delete some-feature
Git 設定ファイルに数行追加することで、refspec を使用して git fetch
の動作を変更できます。既定では、git fetch
はリモートリポジトリのブランチをすべてフェッチします。その理由は、.git/config
ファイルの次のセクションにあります。
[remote "origin"] url = https://git@github.com:mary/example-repo.git fetch = +refs/heads/*:refs/remotes/origin/*
fetch
行は、git fetch
に origin
リポジトリからブランチをすべてダウンロードするように指示します。しかし、一部のワークフローはすべてのブランチが必要なわけではありません。たとえば、多くの継続的インテグレーション ワークフローでは、main
ブランチのみが重要です。main
ブランチのみをフェッチするには、fetch
行を次のように変更します。
[remote "origin"] url = https://git@github.com:mary/example-repo.git fetch = +refs/heads/main:refs/remotes/origin/main
同様の方法で、git push
も設定できます。たとえば、(上記で実行したように) 常に main
ブランチを origin
リモートの qa-main
にプッシュする必要がある場合、構成ファイルを次のように変更します。
[remote "origin"] url = https://git@github.com:mary/example-repo.git fetch = +refs/heads/main:refs/remotes/origin/main push = refs/heads/main:refs/heads/qa-main
Refspec を使用すると、様々な Git コマンドがリポジトリ間でブランチを転送する方法について完全に制御できます。ローカルリポジトリからブランチ名の変更やブランチの削除を行ったり、別の名前でブランチをフェッチ/プッシュしたり、git push
と git fetch
を設定して、必要なブランチのみで作業したりすることができます。
Relative refs
別のコミットに関連するコミットを参照することもできます。~
文字を使用すると、親コミットに移動できます。たとえば、次の例は、HEAD
の祖父母を表示します。
git show HEAD~2
しかし、マージコミットを使用して作業すると、問題は多少複雑になります。マージコミットには2つ以上の親があるため、追跡できるパスが複数になります。3 段階のマージの場合、最初の親はマージの実行時にあなたがいたブランチから派生します。2 番目の親は、git merge
コマンドに渡したブランチから派生します。
~
文字は、常にマージコミットの最初の親の後に続きます。別の親の後にする必要がある場合、^
文字を使用して対象の親を指定する必要があります。たとえば、HEAD
がマージコミットの場合、次の例は HEAD
の 2 番目の親を返します。
git show HEAD^2
^
文字を複数使用して複数の世代を移動できます。たとえば、マージコミットの場合、以下は HEAD
の祖父母を表示します。この HEAD は 2 番目の親から生じています。
git show HEAD^2^1
~
と ^
の動作を明確にするために、相対参照を使用して A
から生じるいずれかのコミットに移動する方法を次の図に示します。場合によっては、コミットに移動する方法が複数あります。
相対 ref には、通常の ref で使用できるコマンドと同じコマンドを使用できます。たとえば、次のコマンドはすべて相対参照を使用します。
# Only list commits that are parent of the second parent of a merge commit git log HEAD^2 # Remove the last 3 commits from the current branch git reset HEAD~3 # Interactively rebase the last 3 commits on the current branch git rebase -i HEAD~3
The reflog
reflog は Git のセーフティ ネットです。スナップショットをコミットしたかどうかにかかわらず、リポジトリで行うほとんどすべての変更を記録します。ローカル リポジトリで行ったあらゆることが時系列に沿って記録されている履歴だと考えることができます。reflog を表示するには、git reflog
コマンドを実行します。次のような出力が得られるはずです。
400e4b7 HEAD@{0}: checkout: moving from main to HEAD~2 0e25143 HEAD@{1}: commit (amend): Integrate some awesome feature into `main` 00f5425 HEAD@{2}: commit (merge): Merge branch ';feature'; ad8621a HEAD@{3}: commit: Finish the feature
これは、次のように変換されます。
HEAD~2
を今チェックアウトしました。- その前にコミットメッセージを修正しました。
- その前に、
フィーチャー
ブランチをmain
にマージしました。 - その前にスナップショットをコミットしました。
HEAD{
構文により、reflog に保存されているコミットを参照できます。これは、前のセクションの HEAD~
参照と同様にいろいろと役立ちますが、
はコミット履歴ではなく reflog 内のエントリーを表します。
これを使用して、ある状態が失われてしまわないようにその状態に戻すことができます。たとえば、たった今 git reset
を使用して新しいフィーチャーを破棄したとしましょう。あなたの reflog は次のようになります。
ad8621a HEAD@{0}: reset: moving to HEAD~3 298eb9f HEAD@{1}: commit: Some other commit message bbe9012 HEAD@{2}: commit: Continue the feature 9cb79fa HEAD@{3}: commit: Start a new feature
git reset
の前の3つのコミットは中ぶらりんの状態です。つまり、reflog を使用しない限り、これらを参照する方法はないということです。ここで、あなたは作業のすべてを破棄すべきではなかったことに気付きます。あなたがやらなければならないことは、HEAD@{1}
コミットをチェックアウトして git reset
を実行する前のリポジトリの状態に戻ることのみです。
git checkout HEAD@{1}
これにより、あなたは分離した HEAD
の階層に移動します。ここから、新しいブランチを作成して自分のフィーチャーで作業を続行できます。
要約
これで、皆さんは Git リポジトリ内のコミットを快適に参照できるようになりました。ここでは、ブランチとタグが .git
サブディレクトリに ref として保存される仕組み、packed-refs
ファイルを読む込む方法、HEAD
の表現方法、高度なプッシュおよびフェッチのための refspec の使用方法、~
および ^
比較演算子を使用してブランチ履歴をトラバースする方法を学習しました。
また、reflog についても検討します。これは、他のどんな方法でも利用できないコミットを参照する方法です。些細な「しまった、こんなことしなければよかった」という状況から復旧する優れた方法です。
ここまでの要点は、特定の開発シナリオで必要なコミットを正確に選別できるようになることでした。最も一般的なコマンドの一部は、ref を引数として受け入れるため、既存の Git の知識があれば、この記事で学習したスキルを非常に容易に活用することができます。このようなコマンドには、git log
、git show
、git checkout
、git reset
、git revert
、git rebase
などの多くのコマンドがあります。
この記事を共有する
次のトピック
おすすめコンテンツ
次のリソースをブックマークして、DevOps チームのタイプに関する詳細や、アトラシアンの DevOps についての継続的な更新をご覧ください。