Gitにおける参照、リモート、操作の仕組み

リモートへの特殊なプッシュ形式: HEAD:refs/for/

git push origin HEAD:refs/for/masterというコマンドは、特定のワークフローで使用されます。この形式を分解すると以下のようになります。

  • git push: ローカルのコミットをリモートリポジトリにアップロードします。
  • origin: リモートリポジトリの短縮名(通常、clone時に自動的に設定されます)。
  • HEAD: 現在チェックアウトされているローカルブランチの最新コミットを指す特別なポインタです。
  • refs/for/master: リモート側のmasterブランチに対する、コードレビューを経由するための特別な参照先です。この形式はGerritなどのコードレビューシステムで主に用いられ、refs/heads/master(直接マージ)とは区別されます。
// 一般的な形式
git push <remote> <local_ref>:<remote_ref>

// レビューが必要なプッシュ例
git push origin feature-branch:refs/for/develop

参照(Refs)とブランチの関係

Gitでは、HEAD、ブランチ名、リモート参照は異なるレイヤーで機能します。

  • HEAD: 現在作業中のローカルブランチ(例:refs/heads/feature)を指すポインタです。git checkoutするたびに移動します。
  • ブランチ(例:master): コミット履歴の流れを表す可変の参照です。内部的にはrefs/heads/masterのようなパスを持ちます。
  • リモート追跡ブランチ: origin/masterのように表され、リモートリポジトリのブランチの状態を最後に取得した時点のコミットを指します。

refs/for/[branch]refs/heads/[branch]の違いは、前者がコードレビューのゲートを設けることをリモートサーバーに指示する点にあります。直接のマージを許可しないワークフローで利用されます。

originの実体とリモート操作

originは、リモートリポジトリのURLに付けられたデフォルトのエイリアス(名前)です。clone時に自動的に作成されます。

// 登録されているリモートリポジトリとそのURLを表示
git remote -v
// 出力例:
// origin  https://github.com/user/repo.git (fetch)
// origin  https://github.com/user/repo.git (push)

// すべてのリモートブランチをリスト表示
git branch -r

git push origin masterは、ローカルのmasterブランチをリモートoriginmasterブランチ(refs/heads/master)にプッシュする省略形です。ブランチを明示せずにgit pushのみを実行すると、カレントブランチと対応するリモートブランチの設定に依存するため、意図しないブランチにプッシュされる可能性があります。

リモートの状態でローカルを強制上書き

ローカルの変更を破棄し、リモートブランチの状態に完全に同期する手順です。

// 1. リモートのすべてのブランチの最新履歴をフェッチ(マージしない)
git fetch --all

// 2. カレントブランチのポインタをリモートブランチの最新コミットにリセットし、作業ディレクトリもそれに合わせる
git reset --hard origin/main  // 'main'は対象のリモートブランチ名

// 3. (必要に応じて)再度プルを実行(この時点では通常、変更は発生しない)
git pull

git reset --hardは、未コミットの作業ディレクトリの変更およびステージングエリアの内容をすべて破棄し、指定したコミットの状態にします。

フェッチとプルの内部動作

git fetchgit pullは関連するが異なる操作です。

// git fetch: リモートの変更履歴をローカルにダウンロードするのみ。作業ツリーは変更されない。
git fetch origin main
// フェッチした内容は一時的な参照 'FETCH_HEAD' に保存され、確認できる
git log --oneline FETCH_HEAD

// git pull = git fetch + git merge
// 以下の2コマンドの連続実行とほぼ同等
git fetch origin main        // リモートの'main'の最新を取得
git merge FETCH_HEAD         // 取得した内容を現在のブランチにマージ

pullはフェッチ後のマージを自動実行するため、コンフリクトが発生する可能性があります。状況を確認したい場合は、まずfetchしてからマージするかどうかを判断する流れが推奨されます。

ローカルとリモートブランチの追跡関係を設定

追跡関係(upstream)を設定すると、git pullgit pushでブランチ名を省略できるようになり、作業が効率化されます。

// 既存のローカルブランチに、対応するリモートブランチを追跡先として設定
git branch -u origin/develop my-local-feature
// または
git branch --set-upstream-to=origin/develop my-local-feature

// 新しいブランチをプッシュし、同時に追跡関係を設定する(よく使われる)
git push -u origin new-branch
// '-u' は '--set-upstream' のショートハンド

この設定後、そのブランチ上でgit pullと実行するだけで、設定したリモートブランチからマージが試みられます。

ステージングエリアと変更の取消しパターン

Gitの作業フローは、作業ディレクトリ、ステージングエリア(インデックス)、リポジトリの3段階で構成されます。変更の取消しは、変更がどの段階にあるかによってコマンドが異なります。

// シナリオA: 作業ディレクトリでファイルを修正したが、まだ git add していない
// 最後のコミット状態に戻す(変更を破棄)
git checkout -- modified-file.txt

// シナリオB: ファイルを修正し、git add でステージングエリアに追加した
// 1. ステージングエリアから作業ディレクトリに戻す(git add の取消し)
git reset HEAD modified-file.txt
// 2. その後、作業ディレクトリの変更を破棄(必要であれば)
git checkout -- modified-file.txt

// シナリオC: ステージングエリアにあるすべてのファイルの追加操作を取り消す
git reset

// 注意: git checkout -- [file] は未追跡(新規)ファイルには影響しない

git reset HEAD <file>は、そのファイルをステージングエリアから削除しますが、作業ディレクトリでの変更は保持されます。git checkout -- <file>は、作業ディレクトリの変更を最後にコミットまたはステージングした状態に戻します。

タグ: Git バージョン管理 リモートリポジトリ コードレビュー ブランチ戦略

8月20日 20:37 投稿