NO IMAGE

Git入門 — init/commit/branch/merge を Terraform コードで実践するハンズオン

NO IMAGE
📚 関連記事 — Git/GitHub × Terraform実践シリーズ


目次

Section 1: この記事について

1-1. シリーズ位置付け

本記事は「Git/GitHub × Terraform実践シリーズ」の第1弾です。

Terraform基礎・実践シリーズでは、AWS S3バケットやEC2インスタンスをTerraformコードで定義し、インフラをコード化(IaC: Infrastructure as Code)する方法を学びました。providers.tf で使用するプロバイダーを宣言し、main.tf にリソース定義を書き、terraform apply でAWSに反映する——このサイクルを手を動かして習得しました。

しかし「コードを書いた後」のことを考えると、いくつかの課題が浮かび上がります。

  • 先週から今日にかけて main.tf のどこを変えたか、誰が変えたか記録に残っていない
  • 本番環境に適用する前にコードの変更内容をチームでレビューしたいが、仕組みがない
  • 誤って重要なリソース定義を削除してしまい、以前のコードに戻したいが戻せない
  • 複数人で同じファイルを編集したら互いの変更が上書きされてしまった

これらすべてを解決するのが Git です。Git はファイルの変更履歴を管理するバージョン管理システムであり、「誰が・いつ・何を変更したか」を永続的に記録します。変更の巻き戻しも、複数人の変更の統合も、Git なら安全・確実に行えます。

本シリーズでは前シリーズで作成した Terraform コードを題材に、Git/GitHub を使ったIaCコードのバージョン管理・チーム協業・CI/CD 自動化までを一気通貫で学びます。

テーマ学ぶ主なスキル
第1弾(本記事)Git入門 — ローカル操作init / commit / branch / merge
第2弾GitHub入門 — リモート協業push / pull / Issue / PR / レビュー
第3弾ブランチ戦略 — チーム運用GitFlow / trunk-based / モジュール管理
第4弾セキュリティ — 事故防止.gitignore / secrets / pre-commit / tflint / tfsec
第5弾GitHub Actions × OIDCplan/apply の CI/CD 自動化

Terraform 基礎・実践を未学習の方は、上部の関連記事ボックスから先に読んでおくと Terraform 記法の理解がスムーズです。ただし本記事(第1弾)は AWSアカウント不要のローカル操作のみ で完結するため、Terraform 経験ゼロの方でも手を動かして試せます。


1-2. この記事で学ぶこと

本記事では terraform-git-handson/ というサンプルディレクトリを実際に作成しながら、以下の操作を習得します。

Section 3: Git初期設定(本 Section)

Git を使い始める前の一回限りの設定作業です。ユーザー情報・デフォルトブランチ名・エディタ・改行コードを設定します。設定は ~/.gitconfig に保存され、全リポジトリで共有されます。

Section 4: ファイル管理の基本(ashigaru2 担当)

git init でリポジトリを初期化し、git add でファイルをステージングエリアに追加、git commit でスナップショットを記録します。git status / git diff / git log で現在の状態・差分・履歴を確認するワークフローを一連の流れで体験します。

Section 5: ブランチ操作(ashigaru2 担当)

git branch / git switch でブランチを作成・切り替え、feature ブランチで独立した変更を加えます。git log --graph でブランチの分岐を視覚的に把握します。ブランチ命名規則(feature/ / fix/ / chore/)についても学びます。

Section 6: マージとコンフリクト解消(ashigaru2 担当)

fast-forward マージと no-ff マージの違いを理解し、実際に両方のマージを実行します。意図的にコンフリクトを発生させ、コンフリクトマーカー(<<<<<<< HEAD)を読んで手動解消する一連の流れを体験します。

Section 7〜9: 実践ワークフロー・早見表・まとめ(ashigaru3 担当)

日常の開発サイクル全体(feature → commit → merge)を空リポジトリからモジュール化完成まで一気通貫でデモします。よく使うコマンド早見表・トラブルシューティング・次のステップも収録します。


1-3. 前提条件

項目詳細
Terraform基礎・実践任意だが推奨(サンプルコードは Terraform 記法)
AWSアカウント不要 — 第1弾はすべてローカル操作のみ
OSmacOS / Linux / Windows WSL2 のいずれか
Git バージョン2.23以上(git switch / git restore を使うため)。2.28以上推奨
エディタVS Code 推奨(本記事の手順に沿って設定します)
ターミナルmacOS: Terminal / iTerm2、Linux: 任意、Windows: WSL2 Terminal

Gitのバージョンについて: git switch / git restore コマンドは Git 2.23 で導入されました。従来の git checkout でも同じ操作が可能ですが、本記事では機能が明確に分離された新しいコマンドを主として扱い、対応する git checkout の書き方を併記します。

WSL2ユーザーへ: コマンドはすべてLinux/macOS互換の記法で統一しています。WSL2のLinuxファイルシステム(~/)上で作業することを強く推奨します。Windows 側のファイルシステム(/mnt/c/)上での Git 操作はパフォーマンスが極端に低下します。


1-4. 学習の進め方

本記事では terraform-git-handson/ というサンプルディレクトリを手元に作成しながら進めます。Section ごとにリポジトリの状態を積み上げていくため、途中から読み始めた場合でも「ここまでのリポジトリ状態」の説明からキャッチアップできます。

最終的なディレクトリ構成は以下の通りです。各ファイルをどの Section で作成するかも記載しています。

terraform-git-handson/
├── .gitignore  # Section 4-3 で作成(*.tfstate / .terraform/ 等を除外)
├── README.md# Section 4-1 で作成
├── providers.tf# Section 4-1 で作成(AWS Provider 定義)
├── variables.tf# Section 4-1 で作成(変数定義)
├── main.tf  # Section 4-7 で追加(S3バケット定義)
└── modules/
 └── s3/
  ├── main.tf# Section 6-2 feature/add-s3 ブランチで追加
  ├── variables.tf # Section 6-2 feature/add-s3 ブランチで追加
  └── outputs.tf# Section 6-2 feature/add-s3 ブランチで追加

コマンドはすべてコピー&ペーストで実行できる完全版を記載しており、擬似コードや省略表記は使いません。実行結果の出力例もあわせて記載するため、手元の出力と比較しながら進められます。


1-5. 想定所要時間とコスト

項目内容
所要時間約120分(Section 1〜3: 30分 / Section 4〜6: 60分 / Section 7〜9: 30分)
AWS費用無料(AWSリソースを一切作成しない)
必要環境ローカルPC(Section 1〜3はインターネット接続不要)

Section ごとに学習内訳を示します。

Sectionテーマ目安時間担当
Section 1この記事について5分ashigaru1
Section 2Gitとは(概念理解)10分ashigaru1
Section 3Git初期設定ハンズオン15分ashigaru1
Section 4init/add/commit/status/diff/log25分ashigaru2
Section 5branch/switch ブランチ操作20分ashigaru2
Section 6merge / コンフリクト解消25分ashigaru2
Section 7実践ワークフロー(一気通貫デモ)10分ashigaru3
Section 8コマンド早見表・トラブルシューティング5分ashigaru3
Section 9まとめ・次のステップ5分ashigaru3

Section 2: Gitとは — 分散型バージョン管理の基本概念

Git の操作を始める前に、Git がどのような仕組みで動いているかを理解しておくことが重要です。コマンドの意味が直感的に把握でき、エラーが発生したときの原因特定が格段にスムーズになります。


2-1. バージョン管理システムとは

「バージョン管理システム(VCS: Version Control System)」とは、ファイルの変更履歴を記録・管理し、過去の状態に戻したり複数人の変更をマージしたりするためのシステムです。

バージョン管理システムが存在しなかった時代、エンジニアはファイルのコピーでバージョン管理を行っていました。

  • main_v1.tfmain_v2.tfmain_final.tfmain_final_final.tf
  • backup_20260415/ といったディレクトリを手動で作成してコピー
  • 誰かが書き換えても上書きされてしまい、元に戻せない
  • 「最新版はどれ?」「誰がいつ変えた?」が追跡不可能

このような属人的・手作業のバージョン管理には限界があります。VCS はこの問題を体系的に解決します。

バージョン管理システムは大きく 集中型(CVCS)分散型(DVCS) に分けられます。

比較項目集中型 VCS(例: SVN / CVS)分散型 VCS(例: Git)
リポジトリの場所中央サーバーのみ各開発者のローカルにも全履歴が存在
オフライン作業不可(サーバー接続必須)可(コミット・ブランチはローカルで完結)
障害耐性サーバー障害で全員作業停止ローカルに全履歴があるため継続可能
ブランチ操作重く低速(ファイルコピーが発生)軽量・高速(ポインタ操作のみ)
コミット速度サーバーとの通信が必要ローカル書き込みのみ(高速)
普及状況レガシーなエンタープライズシステムGitHub / GitLab / Bitbucket(現在の主流)

Git は分散型(DVCS)です。各開発者のローカルPCに全コミット履歴を含むリポジトリのコピーが存在します。このため、ネットワークなしでもコミット・ブランチ作成・履歴参照が行えます。

GitHubはこの Git リポジトリを「リモート」としてホスティングするウェブサービスです(詳細は第2弾で扱います)。

分散型のメリットを身近に感じる場面

Terraform リポジトリを管理する文脈で、分散型 VCS の恩恵を具体的に挙げます。

  • 出張先や在宅でのオフライン作業: Gitコミットはすべてローカルで完結するため、VPNが切れても git commit できます。
  • 高速なブランチ切り替え: feature/add-rds ブランチと main ブランチを瞬時に切り替えられます。数十ファイルのリポジトリでも git switch は1秒以内に完了します。
  • 安全な実験: ブランチで「試しにこのリソース定義を変えてみる」実験が低コストで行えます。問題があればブランチを削除するだけです。

2-2. なぜTerraformコードにGitが必要か

Terraform には terraform.tfstate というインフラの現在状態を管理するファイルがあります。しかしこれは「現在のAWSリソース状態のスナップショット」を記録するものであり、「コードの変更履歴」ではありません。

Git がない状態を想像してみてください。あなたが main.tf の S3 バケットのリージョンを ap-northeast-1 から us-east-1 に変更して terraform apply した翌日、本番環境に予期しない変更が発生したとします。「いつ誰が何を変えたか」の記録がなければ原因の特定に大変な時間がかかります。Git があれば git log --oneline で変更履歴を確認し、git diff <commit-hash> で差分を即座に特定できます。

tfstate と Git の役割分担

比較項目terraform.tfstateGit リポジトリ
管理する情報AWSの「現在の状態」(リソースID等)コードの「変更履歴」(誰がいつ何を変えたか)
変更履歴保持しない(上書き)すべてのコミットを永続的に保持
差分確認terraform plan で確認git diff でコードレベルの差分確認
複数人での利用Remote State(S3+DynamoDB)が必要git push / pull / PR でマージ
バックアップS3バージョニング等で別途管理git clone でどこでも全履歴を複製可能

Terraform コードを Git で管理する理由は主に4つあります。

① 変更の追跡と責任の明確化

誰が・いつ・どのリソース定義を変更したかが git log で一目瞭然になります。例えば「先月の本番障害はいつの変更が原因か」を git log --oneline で素早く特定できます。チームでの開発では、コミットに紐づくレビュー履歴(第2弾で扱う PR)も残ります。

コミット単位で変更内容とメッセージが記録されるため、「なぜこのリソースをこの設定にしたか」という意図も git log -p で追跡できます。

② 差分レビュー(安全な本番反映)

terraform apply の前に git diff main..feature/add-ec2 でコードの変更差分を確認し、意図しない変更が混入していないかを確認できます。terraform plan は実行時の差分を示しますが、コードレベルのレビューは Git が担当します。

実務では「コードレビュー(Git の diff)→ terraform plan 結果のレビュー→ terraform apply」という3段階の安全確認が標準的なワークフローです(第5弾で詳説)。

③ 並列開発とブランチによる安全な変更管理

複数人が同じリポジトリで作業する場合、ブランチを使って各自の変更を分離できます。main ブランチを常にデプロイ可能な状態に保ちながら、feature/add-rds ブランチで新リソースを開発・テスト・レビューしてから統合できます。

複数の機能を同時並行で進めながら、各ブランチが main に統合されるタイミングを独立して管理できます。

④ ロールバックの安全網

git revert <commit-hash> を使えば問題のある変更を安全に取り消せます(変更履歴を保持したまま巻き戻す)。terraform.tfstate だけでは実現できないコードレベルのロールバックが可能です。

緊急時に「昨日の terraform apply 前の状態に戻したい」場合も、該当コミットのコードを確認してすぐに巻き戻せます。


2-3. Gitの3エリア — Working / Staging / Repository

Git がファイルを管理する仕組みの核心は「3エリア」の概念です。この概念を理解することで、git addgit commit が何をしているのかが直感的に把握できます。

エリア別名説明移動コマンド
Working Tree作業ディレクトリ実際のファイルが存在する作業領域。main.tf の編集や ec2.tf の新規作成はここで行うgit add <file> で Staging Area へ
Staging Areaインデックスコミット対象を選択する中間領域。git add で Working Tree の変更をここに積むgit commit で Repository へ
Repository.git/ 以下コミット済みスナップショットが永続化される領域。全履歴が保存されるgit restore <file> で Working Tree へ戻す

Working Tree(作業ディレクトリ)

実際にファイルを編集する場所です。main.tf を書き換えたり、新しいファイル ec2.tf を作成したりする操作はすべてここで行います。

Working Tree の変更は Git に「まだ記録されていない変更」として認識されます。git status を実行すると赤字で modified: main.tfUntracked files: ec2.tf と表示されます。

Staging Area(ステージングエリア / インデックス)

次のコミットに含めるファイルを「仮置き」する場所です。git add <ファイル名> を実行すると、Working Tree の変更が Staging Area に移ります。

Staging Area の存在意義は「コミットに含める変更を選別できる」点にあります。例えば main.tfvariables.tf の両方を編集していても、git add main.tf だけ実行すれば main.tf の変更だけをコミットできます。git add -p を使えばさらに細かく、1ファイルの変更を hunk(差分ブロック)単位で選択できます。

Terraform リポジトリでは「S3バケット定義の追加」と「変数の整理」を1回のコミットに混在させず、別々のコミットとして記録することが推奨されます。Staging Area がその選別を可能にします。

Repository(リポジトリ)

コミット済みのスナップショットが永続化される場所です。.git/ ディレクトリの中(正確には .git/objects/)に格納されます。git commit を実行すると Staging Area の内容がスナップショットとして記録され、一意のハッシュ値(例: a1b2c3d4)が付与されます。

操作コマンド移動方向
変更をステージングに追加git add <file>Working Tree → Staging Area
ステージングをコミットgit commit -m "message"Staging Area → Repository
ステージングから取り消しgit restore --staged <file>Staging Area → Working Tree
コミット済みを作業ディレクトリに展開git restore <file>Repository → Working Tree
変更の確認(未ステージ)git diffWorking Tree と Staging Area の差分
変更の確認(ステージ済み)git diff --stagedStaging Area と Repository の差分

2-4. コミット・ブランチ・マージの概念

コミット(Commit)

コミットとは、ある時点のプロジェクト全体の「スナップショット」です。各コミットには一意のSHA-1ハッシュ値(例: a1b2c3d4e5f6...、通常は先頭7文字 a1b2c3d で参照)が付与されます。

コミットには以下の情報が含まれます。

  • ファイルシステム全体のスナップショット(変更差分ではなく全体)
  • 作者名・メールアドレス(user.name / user.email
  • タイムスタンプ
  • コミットメッセージ
  • 親コミットのハッシュ値(履歴グラフを形成する参照)

コミットは変更差分ではなく全体のスナップショットとして記録されるため、任意の時点の状態を高速に復元できます。ただし Git 内部では効率化のため変更があったファイルのみを新しいオブジェクトとして作成し、変更のないファイルは前のコミットのオブジェクトを参照する仕組みになっています。

コミット間の繋がりは「親コミットへの参照」で形成されます。各コミットは1つ(または2つ以上)の親コミットを指しており、この繋がりがコミット履歴の「グラフ(有向非循環グラフ: DAG)」を構成します。

ブランチ(Branch)

ブランチとは、特定のコミットを指す「軽量なポインタ(参照)」です。ブランチを作成しても、ファイルのコピーは一切発生しません。単に「このコミットを指すラベルが追加された」だけです。このためブランチの作成・切り替えが非常に高速です。

HEAD は「現在作業中のブランチ(またはコミット)」を指す特殊なポインタです。git switch feature/add-ec2 を実行すると、HEADfeature/add-ec2 ブランチを指すように変わります。

新しいコミットをすると、現在の HEAD が指すブランチのポインタが新しいコミットに自動で進みます。これが「ブランチが成長する」仕組みです。

典型的な開発フローでは次のようにブランチが活用されます。

  • main ブランチ: 本番環境と同期。常にデプロイ可能な状態を維持
  • feature/add-s3-module ブランチ: S3モジュール開発用。main から派生し、完成後にマージ
  • fix/s3-bucket-acl ブランチ: バグ修正用。軽微な修正でも独立したブランチで管理

git log --oneline --graph --all でブランチの分岐と合流を視覚的に確認できます(Section 5 で実際に実行します)。

* a1b2c3d (HEAD -> main) Merge feature/add-s3-module into main
|\
| * 9f8e7d6 (feature/add-s3-module) Add s3 module main.tf
| * 5c4b3a2 Add modules/s3 directory structure
* | 2d1e0f9 Update variables.tf: add bucket_prefix variable
|/
* 1a2b3c4 Initial commit: providers.tf and variables.tf

このグラフ形式で「どのブランチがどのコミットで分岐し、どこでマージされたか」が一目で把握できます。

マージ(Merge)

マージとは、2つのブランチの変更を統合する操作です。Git のマージには主に2種類あります。

  • fast-forward マージ: マージ先ブランチ(main)の先端コミットが、マージ元ブランチ(feature)の祖先コミットである場合、マージコミットを作らず単純に main ポインタを feature の先端に進める。履歴が直線的になる。
  • no-ff マージ: --no-ff オプションを付けることで、fast-forward できる状況でも強制的にマージコミットを作成する。feature ブランチで行った作業のまとまりが履歴に明示的に残るため、チーム開発では推奨される。

両者の詳細とハンズオンは Section 6 で扱います。


2-5. GitとGitHubの違い

初学者が混乱しやすいポイントを整理します。

項目GitGitHub
種類バージョン管理ツール(ソフトウェア)Webホスティングサービス
インストールローカルPCに git コマンドをインストールブラウザからアクセス(アカウント作成が必要)
動作場所ローカルPCクラウド(GitHub社のサーバー)
主な役割バージョン管理・ブランチ・マージリモートリポジトリ・PR・Issue・Actions
費用無料(オープンソース)基本無料(プライベートリポジトリも無料枠あり)
開発・管理Linux Foundation 傘下のオープンソースMicrosoft 傘下のサービス
代替サービスGitLab / Bitbucket / AWS CodeCommit

第1弾(本記事)では Git のみを扱います。 GitHub を使ったリモートリポジトリへの git push / git pull / プルリクエスト(PR)のワークフローは第2弾「GitHub入門」で詳しく解説します。

本記事で作成する terraform-git-handson/ リポジトリはすべてローカルPC上に存在します。第2弾では、この同じリポジトリを GitHub にプッシュして、リモートリポジトリとの同期・協業フローに進みます。

補足: Git はリーナス・トーバルズが2005年にLinuxカーネル開発のために作成したオープンソースソフトウェアです。GitHub は2008年に設立された別会社のサービスであり、2018年にMicrosoftが買収しました。「GitとGitHubは別物」という認識は、後々のサービス選択(GitLabを使うか、Bitbucketを使うか)の判断にも役立ちます。


Section 3: Git初期設定ハンズオン

本 Section では Git を使い始める前に必要な初期設定を行います。設定は1回だけ実施すれば、以降に作成するすべてのリポジトリに自動で適用されます。

設定はすべて git config --global コマンドで行い、結果は ~/.gitconfig ファイルに保存されます。

本 Section の設定を完了すると、Section 4 以降のハンズオンをスムーズに進められます。


3-1. インストール確認

まず Git がインストールされているか確認します。ターミナルを開いて以下を実行してください。

# バージョン確認(2.x.x が返ればOK、2.23以上が必須、2.28以上推奨)
git --version
# 期待する出力例: git version 2.45.2

# インストール場所の確認
which git
# 期待する出力例: /usr/bin/git または /opt/homebrew/bin/git

git version 2.x.x と表示されれば Git はインストール済みです。バージョンが 2.23 未満の場合は以下の方法でアップデートしてください。

macOS — Homebrew でインストール / アップデート

# Homebrew が未インストールの場合は先にインストール
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Git のインストール(または最新版へのアップデート)
brew install git

# シェルを再起動後、バージョン確認
git --version
# git version 2.45.2

macOS では Xcode Command Line Tools に同梱された Git(/usr/bin/git)が古いままになりがちです。brew install git 後は /opt/homebrew/bin/git が優先されるよう PATH が設定されているか確認してください。which git/usr/bin/git を指している場合は ~/.zshrcexport PATH="/opt/homebrew/bin:$PATH" を追加します。

Linux (Ubuntu / Debian) — apt でインストール

sudo apt update
sudo apt install git -y
git --version

Linux (Amazon Linux / CentOS / RHEL) — yum でインストール

sudo yum install git -y
git --version

Windows WSL2 — apt でインストール

# WSL2 内の Ubuntu ターミナルで実行
sudo apt update
sudo apt install git -y
git --version

WSL2 では Windows ネイティブの git.exe/mnt/c/ 配下)ではなく、WSL2 内の Linux の git/usr/bin/git)を使うことを確認してください。which git/usr/bin/git が返れば問題ありません。


3-2. ユーザー情報設定

Git のコミットには「誰が作成したか」の情報(名前・メールアドレス)が自動で付与されます。git commit を実行する前に必ず設定してください。未設定のままコミットしようとすると以下のエラーが発生します。

Author identity unknown

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

ユーザー情報を設定します。

# グローバルユーザー情報の設定
git config --global user.name "Taro Yamada"
git config --global user.email "taro.yamada@example.com"

# 設定の確認
git config --global user.name
# 出力: Taro Yamada
git config --global user.email
# 出力: taro.yamada@example.com

--global フラグを付けると設定が ~/.gitconfig に保存され、そのユーザーのすべてのリポジトリに適用されます。

メールアドレスのプライバシー設定(GitHub利用時の注意)

第2弾で GitHub に push すると、コミットに含まれるメールアドレスが GitHub の Web UI で公開される場合があります。プライバシーが気になる場合は、GitHub の「Settings → Emails」で「Keep my email addresses private」を有効にし、GitHub が提供するダミーアドレス(XXXXXXXX+username@users.noreply.github.com)をコミット用メールアドレスとして設定できます。

特定リポジトリだけメールアドレスを変えたい場合

個人PCで会社プロジェクトを扱う場合など、リポジトリごとにメールアドレスを切り替えたいことがあります。その場合は対象リポジトリのディレクトリ内で --global を省いて実行します。リポジトリ固有の .git/config に保存され、グローバル設定より優先されます。

# (参考)特定リポジトリだけ設定を上書きする場合
# cd /path/to/work-project
# git config user.email "work@company.com"
# git config user.name "Taro Yamada (Work)"

3-3. init.defaultBranch = main 設定

Git 2.28以降では、git init 実行時に作成されるデフォルトブランチ名を init.defaultBranch で設定できます。

git config --global init.defaultBranch main

# 設定確認
git config --global init.defaultBranch
# 出力: main

なぜ main に設定するのか

Git 2.27以前では git init すると master ブランチが作成されました。しかし「master / slave」という用語が持つ歴史的・社会的な文脈への配慮から、GitHubは2020年10月、GitLabは2021年に新規リポジトリのデフォルトブランチを main に変更しました。

現在では GitHub や GitLab のデフォルトが main であるため、この設定を行っておくことでローカルで git init したときも main ブランチが作成されます。第2弾で GitHub へ git push する際にブランチ名が一致し、余計なブランチ名変更作業が不要になります。

Git 2.27以前のブランチ名変更方法(参考)

既存のリポジトリで master ブランチを main にリネームしたい場合は以下の手順で対応できます。

# (参考)既存リポジトリの master → main リネーム
# git branch -m master main

この操作は GitHub への push 後の場合さらにステップが必要です(git push origin --delete master 等)。詳細は第2弾で扱います。

Git 2.28以降で git init を実行した際に以下のような案内が表示された場合、本節の設定で解消されます。

hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:git config --global init.defaultBranch <name>

3-4. core.editor / core.autocrlf の設定

core.editor — コミットメッセージエディタの設定

git commit-m オプションなし)を実行したり、git merge --no-ff でマージコミットメッセージを入力したりする際にテキストエディタが起動します。デフォルトは vi(またはシステムの $VISUAL / $EDITOR 環境変数)ですが、使い慣れたエディタに変更することを推奨します。

# VS Code をエディタに設定("code --wait" で保存・終了まで Git が待機)
git config --global core.editor "code --wait"

# vim が好みの場合
# git config --global core.editor "vim"

# nano が好みの場合(初心者に操作しやすい)
# git config --global core.editor "nano"

# 設定確認
git config --global core.editor
# 出力: code --wait

VS Code を使う場合、code --wait--wait フラグが重要です。このフラグがないと Git がエディタの終了を待機せずに「空のコミットメッセージ」でエラーになります。code コマンドが使えるか確認してください。使えない場合は VS Code の「コマンドパレット(Cmd+Shift+P / Ctrl+Shift+P)」→「Shell Command: Install ‘code’ command in PATH」を実行します。

core.autocrlf — 改行コード自動変換の設定

Windows は CRLF(\r\n)、macOS/Linux は LF(\n)を改行コードとして使用します。異なるOS間でリポジトリを共有すると、改行コードの違いによって「コード内容が変わっていないのに git diff で大量の差分が出る」問題が発生することがあります。

# macOS / Linux / WSL2 の場合: コミット時に CRLF → LF へ変換、チェックアウト時は変換しない
git config --global core.autocrlf input

# 設定確認
git config --global core.autocrlf
# 出力: input

core.autocrlf の設定値の意味は以下の通りです。

設定値チェックアウト時コミット時推奨OS
trueLF → CRLFCRLF → LFWindows ネイティブ(Git Bash)
input変換しないCRLF → LFmacOS / Linux / WSL2
false変換しない変換しない統一環境(すべてUnix系)の場合

macOS / Linux / WSL2 で開発する場合は input を設定しておけば問題ありません。チーム全員が .editorconfig.gitattributes で改行コードを統一する方法もありますが、個人設定の第一歩として core.autocrlf input を設定しておきましょう。


3-5. ~/.gitconfig ファイル確認

これまでの設定がすべて ~/.gitconfig に保存されているか確認します。

cat ~/.gitconfig

以下のような内容が表示されれば正常です。

[user]
 name = Taro Yamada
 email = taro.yamada@example.com
[init]
 defaultBranch = main
[core]
 editor = code --wait
 autocrlf = input

各セクション([user][init][core])に対応する設定が記録されています。~/.gitconfig は通常のテキストファイルなので、エディタで直接編集することも可能です(ただし git config コマンド経由の方がミスが少なく推奨)。

次のステップで core.excludesfile を追加するため、この時点では上記4項目が揃っていれば問題ありません。


3-6. グローバル .gitignore_global 設定

OS や IDE が自動生成するファイル(macOS の .DS_Store、VS Code の .vscode/ 等)は、特定のプロジェクトに関係なくリポジトリに含めたくないファイルです。

これらを各プロジェクトの .gitignore に毎回書くのは非効率です。~/.gitignore_global(ファイル名は任意)に一度書いておけば、すべてのリポジトリで自動的に除外されます。

# グローバル gitignore ファイルを作成
cat > ~/.gitignore_global << 'EOF'
# macOS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes

# Windows
Thumbs.db
Desktop.ini
ehthumbs.db

# IDE - VS Code
.vscode/
*.code-workspace

# IDE - JetBrains (IntelliJ, PyCharm 等)
.idea/
*.iml
*.ipr
*.iws

# Terraform(プロジェクト .gitignore にも明示推奨)
.terraform/
*.tfstate
*.tfstate.backup
.terraform.lock.hcl

# 一時ファイル
*.log
*.tmp
*.swp
*~
EOF

# Gitにグローバル excludesfile として登録
git config --global core.excludesfile ~/.gitignore_global

# 登録確認
git config --global core.excludesfile
# 出力: /Users/yourname/.gitignore_global

core.excludesfile に設定されたファイルは、すべてのリポジトリで .gitignore と同様に機能します。各プロジェクトの .gitignore には「そのプロジェクト固有の除外ルール」(例: Terraform の *.tfvars、Node.js の node_modules/)を書く、という役割分担が理想的です。

Terraform ユーザーへの注意: *.tfvars ファイルには aws_access_key_id などの機密値が含まれる場合があります。グローバルで除外するとともに、各プロジェクトの .gitignore にも明示的に記載することを強く推奨します。機密値を誤ってコミットしてしまった場合の対処法は第4弾「セキュリティ」で詳しく解説します。

プロジェクト固有の .gitignore との役割分担

ファイル管理場所対象
~/.gitignore_globalホームディレクトリ(Git管理外)OS/IDE固有ファイル(全プロジェクト共通)
.gitignoreプロジェクトルート(Git管理下)プロジェクト固有の除外ルール

Section 4 では Terraform プロジェクト向けの .gitignore を実際に作成します。グローバルの設定と組み合わせることで、必要なファイルだけをリポジトリに含める制御が完成します。


3-7. 全設定の確認 — git config –list –show-origin

最後に、すべての設定がどのファイルから読み込まれているかを一覧で確認します。

git config --list --show-origin

以下のような出力が得られます。

file:/Users/yourname/.gitconfig user.name=Taro Yamada
file:/Users/yourname/.gitconfig user.email=taro.yamada@example.com
file:/Users/yourname/.gitconfig init.defaultbranch=main
file:/Users/yourname/.gitconfig core.editor=code --wait
file:/Users/yourname/.gitconfig core.autocrlf=input
file:/Users/yourname/.gitconfig core.excludesfile=/Users/yourname/.gitignore_global

--show-origin フラグにより、各設定が「どのファイルで定義されているか」が左列に表示されます。

設定の優先順位

Git の設定は3つのスコープに分かれており、より狭いスコープの設定が優先されます。

スコープフラグ保存先適用範囲優先順位
リポジトリ--local.git/config特定のリポジトリのみ最高
ユーザー--global~/.gitconfigそのユーザーの全リポジトリ
システム--system/etc/gitconfigシステム全ユーザー最低

例えば個人PCで会社プロジェクトを扱う際、そのリポジトリ内で git config user.email "work@company.com" を実行すれば、そのリポジトリだけ会社メールアドレスが使われます(グローバル設定の個人メールアドレスは他のリポジトリで引き続き使われます)。

git config --list --show-origin で同じキーが複数のファイルで定義されている場合、最後に表示されたもの(ローカル設定)が有効になります。


これで Git の初期設定は完了です。

設定内容をまとめると、以下の6項目を設定しました。

設定項目コマンド目的
ユーザー名git config --global user.name "..."コミットの作者情報
メールアドレスgit config --global user.email "..."コミットの作者情報
デフォルトブランチgit config --global init.defaultBranch maingit initmain ブランチを作成
エディタgit config --global core.editor "code --wait"コミットメッセージ入力エディタ
改行コードgit config --global core.autocrlf inputCRLF → LF 変換(macOS/Linux)
グローバル除外git config --global core.excludesfile ~/.gitignore_globalOS/IDE 自動生成ファイルを全リポジトリで除外

よくある初期設定のトラブルと対処法

設定後に遭遇しやすい問題とその対処を整理します。

症状原因対処法
git commit 時に Please tell me who you are.user.name/email 未設定git config --global user.name/email を再実行
VS Code が起動するが Git がすぐに終わる--wait フラグ漏れgit config --global core.editor "code --wait" に修正
git init しても master ブランチになるGit 2.28未満 or defaultBranch未設定Git をアップデート後 git config --global init.defaultBranch main
~/.gitconfig に変更が反映されない--global 漏れ、またはリポジトリ設定が上書きgit config --list --show-origin で優先される設定を確認
code コマンドが見つからない(macOS)PATH に VS Code が未登録VS Code → コマンドパレット →「Install ‘code’ command in PATH」

3-8. (オプション)便利なエイリアス設定

毎日使う長いコマンドをエイリアスで短縮できます。Terraform リポジトリ作業で特に役立つものを紹介します。

# git st → git status -s (short format)
git config --global alias.st "status -s"

# git lg → git log --oneline --graph --all(ブランチ分岐の可視化)
git config --global alias.lg "log --oneline --graph --all --decorate"

# git sw → git switch(ブランチ切り替えの短縮)
git config --global alias.sw "switch"

# git cm → git commit -m(コミットの短縮)
git config --global alias.cm "commit -m"

設定後は git st だけで git status -s と同等の出力が得られます。エイリアスは ~/.gitconfig[alias] セクションに保存されます。

[alias]
 st = status -s
 lg = log --oneline --graph --all --decorate
 sw = switch
 cm = commit -m

エイリアスは好みに合わせてカスタマイズしてください。本記事の以降の手順では明示的な完全コマンドを使うため、エイリアスの設定は任意です。


3-9. pull.rebase の設定(チーム開発前の推奨設定)

第2弾で GitHub を使った協業ワークフローに入る前に、もう1つ設定しておくことをお勧めします。

# git pull 時にリベースではなくマージを使う(チーム開発で履歴を分かりやすく保つ)
git config --global pull.rebase false

# 設定確認
git config --global pull.rebase
# 出力: false

Git 2.27以降では git pull を初めて実行したとき、以下のヒントが表示されます。

hint: Pulling without specifying how to reconcile divergent branches is
hint: discouraged. You can squelch this message by running one of the following
hint: commands sometime before your next pull:
hint:
hint:git config --global pull.rebase false  # merge (the default strategy)
hint:git config --global pull.rebase true# rebase
hint:git config --global pull.ff only # fast-forward only

pull.rebase false(マージ戦略)を設定すると、このヒントが表示されなくなり、git pull の動作が一貫します。rebase vs merge の詳細な比較は第3弾「ブランチ戦略」で扱います。初学者には false(マージ戦略)から始めることをお勧めします。


これで Git の初期設定は完了です。

設定内容をまとめると、以下の項目を設定しました。

設定項目コマンド目的
ユーザー名git config --global user.name "..."コミットの作者情報
メールアドレスgit config --global user.email "..."コミットの作者情報
デフォルトブランチgit config --global init.defaultBranch maingit initmain ブランチを作成
エディタgit config --global core.editor "code --wait"コミットメッセージ入力エディタ
改行コードgit config --global core.autocrlf inputCRLF → LF 変換(macOS/Linux)
グローバル除外git config --global core.excludesfile ~/.gitignore_globalOS/IDE 自動生成ファイルを全リポジトリで除外
pull 戦略git config --global pull.rebase falsegit pull 時にマージ戦略を使用

Section 4 では実際に terraform-git-handson/ リポジトリを作成し、ファイルの追加・コミット・履歴確認をハンズオン形式で進めます。


Section 4 — ファイル管理ハンズオン

Section 3 で Git の初期設定が完了しました。いよいよ実際のリポジトリを作成して、ファイルの追加・変更・コミットを体験しましょう。このセクションでは Terraform の設定ファイルを題材に、Git の「3エリア(Working Tree / Staging Area / Repository)」がどのように機能するかをハンズオンで確認します。


4-1. サンプル Terraform リポジトリの作成

まず、ハンズオンで使うディレクトリとファイルを作成します。このリポジトリは本記事全体を通じて使い続けるので、場所を覚えておいてください。

# ホームディレクトリ配下に作成(任意の場所でも可)
mkdir -p ~/terraform-git-handson/modules/s3
cd ~/terraform-git-handson

ディレクトリ構成は以下のとおりです。

パス役割
.gitignoreGit 管理から除外するファイル/ディレクトリを定義
README.mdプロジェクト概要(今回は簡易版)
providers.tfTerraform プロバイダー設定(AWS)
variables.tf変数定義(バケット名・リージョン)
main.tfメインリソース定義(S3バケット)
modules/s3/main.tfS3モジュールのメインリソース
modules/s3/variables.tfS3モジュールの変数定義
modules/s3/outputs.tfS3モジュールの出力値定義

各ファイルを作成します。

# README.md
cat > README.md << 'EOF'
# terraform-git-handson

Git × Terraform ハンズオン用サンプルリポジトリ。
AWS S3 バケットを Terraform で管理する最小構成。
EOF

# providers.tf
cat > providers.tf << 'EOF'
terraform {
  required_version = ">= 1.5"
  required_providers {
 aws = {
source  = "hashicorp/aws"
version = "~> 5.0"
 }
  }
}

provider "aws" {
  region = var.aws_region
}
EOF

# variables.tf
cat > variables.tf << 'EOF'
variable "aws_region" {
  description = "AWSリージョン"
  type  = string
  default  = "ap-northeast-1"
}

variable "bucket_name" {
  description = "S3バケット名(グローバル一意)"
  type  = string
}
EOF

# main.tf
cat > main.tf << 'EOF'
module "s3" {
  source= "./modules/s3"
  bucket_name = var.bucket_name
}
EOF

# modules/s3/main.tf
cat > modules/s3/main.tf << 'EOF'
resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name

  tags = {
 ManagedBy = "Terraform"
  }
}
EOF

# modules/s3/variables.tf
cat > modules/s3/variables.tf << 'EOF'
variable "bucket_name" {
  description = "S3バケット名"
  type  = string
}
EOF

# modules/s3/outputs.tf
cat > modules/s3/outputs.tf << 'EOF'
output "bucket_id" {
  description = "作成された S3 バケットの ID"
  value = aws_s3_bucket.this.id
}

output "bucket_arn" {
  description = "作成された S3 バケットの ARN"
  value = aws_s3_bucket.this.arn
}
EOF

ファイルが揃ったか確認します。

find . -type f | sort

出力例:

./README.md
./main.tf
./modules/s3/main.tf
./modules/s3/outputs.tf
./modules/s3/variables.tf
./providers.tf
./variables.tf

これで Terraform の最小構成が整いました。次に Git リポジトリとして初期化します。


4-2. git init -b main でリポジトリ初期化

git init -b main

出力例:

Initialized empty Git repository in /home/user/terraform-git-handson/.git/

-b main オプションにより、最初のブランチ名が main になります(Section 3-3 で設定した init.defaultBranch = main と同じ効果ですが、明示的に指定することで意図が明確になります)。

.git/ ディレクトリが作成されたことを確認します。

ls -la

出力例(抜粋):

drwxr-xr-x  9 user user 4096 Apr 15 10:00 .git
-rw-r--r--  1 user user82 Apr 15 09:58 README.md
-rw-r--r--  1 user user  110 Apr 15 09:58 main.tf
drwxr-xr-x  3 user user 4096 Apr 15 09:58 modules
-rw-r--r--  1 user user  200 Apr 15 09:58 providers.tf
-rw-r--r--  1 user user  150 Apr 15 09:58 variables.tf

.git/ ディレクトリには Git の内部データ(オブジェクトデータベース、設定、参照情報)がすべて格納されます。このディレクトリを削除すると Git 管理が失われるので注意してください。


4-3. .gitignore の作成

Terraform プロジェクトでは、管理下に置くべきでないファイルがいくつかあります。.gitignore ファイルに記述することで、それらを Git の追跡対象から外せます。

# Terraform 実行ディレクトリ(terraform init で生成)
.terraform/

# tfstate ファイル(実インフラの状態。ローカル開発時はコミット不要)
*.tfstate
*.tfstate.backup

# 依存関係ロックファイル(プロジェクト方針による)
# チームで Terraform バージョンを固定したい場合はコメントを外してコミットする
# .terraform.lock.hcl

# 機密情報を含む可能性のある変数ファイル
*.tfvars

# クラッシュログ
crash.log
crash.*.log

# Terraform が生成する一時ファイル
*.tfplan
override.tf
override.tf.json
*_override.tf
*_override.tf.json

補足: .terraform.lock.hcl のコミット方針

.terraform.lock.hcl はプロバイダーのバージョンとハッシュを記録するファイルです。チームメンバー全員が同じプロバイダーバージョンを使うためにコミットを推奨する場合も多いです。本記事では Git 学習に集中するため除外していますが、実務では上記コメントを外してコミットしてください。

.gitignore を作成します。

cat > .gitignore << 'EOF'
.terraform/
*.tfstate
*.tfstate.backup
# .terraform.lock.hcl
*.tfvars
crash.log
crash.*.log
*.tfplan
override.tf
override.tf.json
*_override.tf
*_override.tf.json
EOF

4-4. git status で現在の状態を確認

git status は最も頻繁に使うコマンドです。Working Tree(作業ディレクトリ)と Staging Area(インデックス)の状態を表示します。

git status

出力例:

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
  .gitignore
  README.md
  main.tf
  modules/
  providers.tf
  variables.tf

nothing added to commit but untracked files present (use "git add" to start tracking)

「Untracked files」とは、Git がまだ追跡していない新規ファイルです。これらは Working Tree に存在しますが、Staging Area にも Repository にも登録されていません。


4-5. git add でステージングし、git status -s で確認

まず providers.tf だけをステージングに追加してみます。

git add providers.tf
git status -s

-s オプションは短縮形式(short format)で表示します。

出力例:

A  providers.tf
?? .gitignore
?? README.md
?? main.tf
?? modules/
?? variables.tf

各行の先頭2文字はステータスコードです。

コード意味
AStaging Area に新規追加(Added)
??Untracked(Git管理外)
MStaging Area で変更済み(Modified)
MWorking Tree で変更済み(未ステージ)
DStaging Area で削除済み(Deleted)

providers.tfA となり、ステージングに追加されたことがわかります。

残りのファイルをすべてステージングに追加します。

git add .gitignore README.md main.tf variables.tf modules/
git status -s

出力例:

A  .gitignore
A  README.md
A  main.tf
A  modules/s3/main.tf
A  modules/s3/outputs.tf
A  modules/s3/variables.tf
A  providers.tf
A  variables.tf

すべてのファイルが Staging Area に追加されました。


4-6. git commit で初回コミット

ステージングされた変更を Repository に記録します。

git commit -m "feat: Terraform S3バケット管理の初期構成を追加"

出力例:

[main (root-commit) a1b2c3d] feat: Terraform S3バケット管理の初期構成を追加
 8 files changed, 72 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 README.md
 create mode 100644 main.tf
 create mode 100644 modules/s3/main.tf
 create mode 100644 modules/s3/outputs.tf
 create mode 100644 modules/s3/variables.tf
 create mode 100644 providers.tf
 create mode 100644 variables.tf

root-commit は「このリポジトリの最初のコミット」であることを示します。コミットハッシュ(a1b2c3d)は実行環境ごとに異なります。

コミット後に git status を実行すると:

git status

出力例:

On branch main
nothing to commit, working tree clean

Working Tree がクリーンな状態になりました。


4-7. main.tf を変更して git diff で差分確認

main.tf にタグ設定を追加して、差分確認のコマンドを体験します。

cat > main.tf << 'EOF'
module "s3" {
  source= "./modules/s3"
  bucket_name = var.bucket_name
}

locals {
  common_tags = {
 Project= "terraform-git-handson"
 ManagedBy = "Terraform"
 Env = "dev"
  }
}
EOF

git diff でWorking Tree と最後のコミット(HEAD)の差分を確認します。

git diff

出力例:

diff --git a/main.tf b/main.tf
index 3c2a1b0..f7d8e2a 100644
--- a/main.tf
+++ b/main.tf
@@ -1,4 +1,11 @@
 module "s3" {
source= "./modules/s3"
bucket_name = var.bucket_name
 }
+
+locals {
+  common_tags = {
+ Project= "terraform-git-handson"
+ ManagedBy = "Terraform"
+ Env = "dev"
+  }
+}

+ 行が追加された行です。git diff はデフォルトで「Staging Area に未追加(Unstaged)の変更」を表示します。

次に、ステージングしてから --staged オプションを試します。

git add main.tf
git diff --staged

git diff --staged(または git diff --cached)は「Staging Area に追加された変更と最後のコミットの差分」を表示します。先ほどと同じ差分が表示されるはずです。

使い分けの整理

コマンド比較対象用途
git diffWorking Tree vs Staging Area未ステージの変更を確認
git diff --stagedStaging Area vs HEADコミット前の最終確認
git diff HEADWorking Tree vs HEADすべての変更を一括確認

4-8. git add -p によるハンク単位のステージング

git add -p--patch)は、ファイルの変更を「ハンク(hunk)」と呼ばれる塊ごとに選択してステージングできる高度なコマンドです。1つのファイルで複数の独立した変更をした場合に、論理的な単位でコミットを分けるときに使います。

体験するため、variables.tf に2か所の変更を加えます。

cat > variables.tf << 'EOF'
variable "aws_region" {
  description = "AWSリージョン(デフォルト: 東京)"
  type  = string
  default  = "ap-northeast-1"
}

variable "bucket_name" {
  description = "S3バケット名(グローバル一意)"
  type  = string
}

variable "environment" {
  description = "環境名(dev/stg/prod)"
  type  = string
  default  = "dev"
}
EOF

git add -p variables.tf を実行すると、変更がハンクごとに表示され、対話的に選択できます。

git add -p variables.tf

表示例(実際の出力は環境により異なります):

diff --git a/variables.tf b/variables.tf
index abc1234..def5678 100644
--- a/variables.tf
+++ b/variables.tf
@@ -1,9 +1,16 @@
 variable "aws_region" {
-  description = "AWSリージョン"
+  description = "AWSリージョン(デフォルト: 東京)"
type  = string
default  = "ap-northeast-1"
 }

 variable "bucket_name" {
...
(1/2) Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

主な入力オプション:

キー動作
yこのハンクをステージング
nこのハンクをスキップ
sこのハンクをさらに分割
q対話を終了
?ヘルプを表示

本記事では git add -p の存在と目的の理解で十分です。実際のチーム開発で「1コミット1目的」の原則を守るとき、このコマンドが役立ちます。


4-9. git commit --amend でコミットメッセージを修正

直前のコミットのメッセージを修正したい場合、--amend を使います。

まず現在の状態をコミットしておきます(-p 操作の後の状態で実行してください)。

# ステージングされていない変更を含め全ファイルを追加してコミット
git add -A
git commit -m "fix: variablesにenvironment変数追加"

コミットメッセージに誤字があったと仮定して修正します。

git commit --amend -m "feat: variables.tf に environment 変数を追加"

出力例:

[main f9e1a2b] feat: variables.tf に environment 変数を追加
 Date: Tue Apr 15 10:05:00 2026 +0900
 1 file changed, 7 insertions(+), 2 deletions(-)

重要な注意点

--amend は直前のコミットを書き換えます。コミットハッシュが変わるため、すでにリモート(GitHub)にプッシュ済みのコミットに対して使うと、他のメンバーの履歴と食い違いが生じます--amend は「まだプッシュしていないローカルの直前コミットだけ」に使う習慣をつけてください。


4-10. git log でコミット履歴を確認

git log

出力例:

commit f9e1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f (HEAD -> main)
Author: Your Name <you@example.com>
Date:Tue Apr 15 10:05:00 2026 +0900

 feat: variables.tf に environment 変数を追加

commit a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b
Author: Your Name <you@example.com>
Date:Tue Apr 15 09:58:00 2026 +0900

 feat: Terraform S3バケット管理の初期構成を追加

git log --oneline --graph を使うと、より簡潔に履歴を確認できます。

git log --oneline --graph

出力例:

* f9e1a2b (HEAD -> main) feat: variables.tf に environment 変数を追加
* a1b2c3d feat: Terraform S3バケット管理の初期構成を追加

--oneline はコミットを1行で表示し、--graph はブランチの分岐・合流をテキストで可視化します(現時点では直線状)。


Section 5 — ブランチ操作ハンズオン

Section 4 でコミット履歴の基本を習得しました。このセクションでは Git の強力な機能である「ブランチ」を操作します。ブランチを使うことで、本体(main)への影響を出さずに新機能の開発や修正を進められます。


5-1. ブランチとは — ポインタの概念

ブランチとは、コミットを指す「ポインタ(参照)」です。以下のように理解すると直感的です。

コミットとブランチポインタの関係

概念実体役割
コミットGit オブジェクト(不変)変更のスナップショット
ブランチコミットへのポインタ(可変)「どのコミットが最新か」を指し示す
HEAD現在チェックアウト中のブランチへのポインタ「今どこにいるか」を示す

具体例で説明します。

コミット履歴:  A → B → C
  ↑
 main (ブランチポインタ)
  ↑
 HEAD
  • main ブランチは最新コミット C を指しています
  • HEAD は「現在作業中のブランチ」を指しており、現在は main を指しています

新しいブランチを作成するとはどういうことか:

コミット履歴:  A → B → C
  ↑
 main
  ↑
  feature/new ← 同じコミットを指す新しいポインタ

ブランチを作成しただけでは、コミット自体はコピーされません。ポインタが増えるだけです。feature ブランチで新たにコミットすると:

コミット履歴:  A → B → C → D
  ↑↑
 mainfeature/new
  • main は引き続き C を指す
  • feature/new は新しいコミット D に進む

この「独立したポインタ」の仕組みにより、作業を並列で進められます。


5-2. git branch でブランチ一覧を確認

git branch

出力例:

* main

* が現在作業中のブランチを示します。現時点では main のみが存在します。


5-3. git switch -c でブランチ作成と切替

EC2 インスタンスリソースを追加する feature ブランチを作成します。

git switch -c feature/add-ec2

出力例:

Switched to a new branch 'feature/add-ec2'

-c--create の短縮形です。ブランチの作成と切替を1コマンドで行います。

ブランチ一覧を確認します。

git branch

出力例:

* feature/add-ec2
  main

*feature/add-ec2 に移動しました。現在このブランチで作業していることを示します。

git checkout -b との比較

git switch -c feature/add-ec2 は、以前の git checkout -b feature/add-ec2 と同じ動作をします。

コマンドバージョン推奨度
git switch -c <branch>Git 2.23以降推奨(ブランチ専用)
git checkout -b <branch>全バージョン非推奨(多機能で混乱しやすい)

git switch はブランチ操作専用コマンドとして Git 2.23 で導入されました。git checkout はブランチ切替だけでなくファイルの復元にも使えるため多機能で初学者が混乱しやすい、という理由で switch / restore への分割が行われました。本記事では git switch を主として使います。


5-4. feature ブランチで ec2.tf を追加・コミット

feature ブランチ上で EC2 インスタンスリソースを定義します。

cat > ec2.tf << 'EOF'
resource "aws_instance" "web" {
  ami  = "ami-0d52744d6551d851e"  # Amazon Linux 2023 (ap-northeast-1)
  instance_type = "t3.micro"

  tags = {
 Name= "web-server"
 ManagedBy = "Terraform"
  }
}
EOF

ステージングしてコミットします。

git add ec2.tf
git commit -m "feat: EC2インスタンス(t3.micro)リソースを追加"

出力例:

[feature/add-ec2 3c4d5e6] feat: EC2インスタンス(t3.micro)リソースを追加
 1 file changed, 11 insertions(+)
 create mode 100644 ec2.tf

5-5. git switch main で main ブランチに戻り、ブランチの独立性を確認

git switch main

出力例:

Switched to branch 'main'

ディレクトリの内容を確認します。

ls

出力例:

README.md  main.tf  modules  providers.tf  variables.tf

ec2.tf が消えました。

ポイント: ブランチは独立した作業空間

ec2.tffeature/add-ec2 ブランチ上のコミットにのみ存在します。main ブランチには影響が一切ありません。git switch main した瞬間に Working Tree の内容が main の状態に切り替わります。

これが Git の「ブランチ = ポインタ」という仕組みの実例です。ファイルを物理的にコピーしているわけではなく、どのコミットを参照するかが切り替わっているだけです。大規模なリポジトリでも高速にブランチを切り替えられる理由はここにあります。


5-6. git log --oneline --graph --all でブランチの分岐を可視化

git log --oneline --graph --all

出力例:

* 3c4d5e6 (feature/add-ec2) feat: EC2インスタンス(t3.micro)リソースを追加
* f9e1a2b (HEAD -> main) feat: variables.tf に environment 変数を追加
* a1b2c3d feat: Terraform S3バケット管理の初期構成を追加

--all を付けることで、現在チェックアウトしていないブランチのコミットも含めて表示されます。feature/add-ec2main の1コミット先にあることが視覚的にわかります。


5-7. git branch -d / -D でブランチを削除

マージ済みのブランチを削除する場合は -d を使います。

# ブランチ一覧確認
git branch

# マージ済みブランチを削除(マージ済みでない場合はエラー)
git branch -d feature/add-ec2

マージ前に強制削除する場合は -D を使います(変更が失われるので注意)。

# 強制削除(未マージでも削除)
git branch -D feature/add-ec2

注意: ブランチを削除してもコミットオブジェクト自体はしばらく残りますが、ポインタ(参照)が消えるため通常の方法では参照できなくなります。重要なブランチを誤って削除した場合は git reflog で復元できることがありますが、基本的には慎重に操作してください。

Section 6 のマージハンズオンで使うため、今回は feature/add-ec2 ブランチは削除しません。再作成します。

# 先ほど削除した場合は再作成
git switch -c feature/add-ec2
# ec2.tf を再作成してコミット(削除していない場合はスキップ)
cat > ec2.tf << 'EOF'
resource "aws_instance" "web" {
  ami  = "ami-0d52744d6551d851e"
  instance_type = "t3.micro"

  tags = {
 Name= "web-server"
 ManagedBy = "Terraform"
  }
}
EOF
git add ec2.tf
git commit -m "feat: EC2インスタンス(t3.micro)リソースを追加"
git switch main

5-8. ブランチ命名規則

チーム開発では一貫したブランチ名の付け方が重要です。プレフィックスで目的を明示するのが一般的です。

プレフィックス用途
feature/新機能・新リソースの追加feature/add-ec2, feature/add-rds
fix/バグ修正・設定ミスの修正fix/s3-policy-typo, fix/sg-rule
chore/ビルド・CI/CD・ドキュメントなどの雑務chore/update-provider-version
refactor/機能変更を伴わないコードの整理refactor/extract-vpc-module
docs/ドキュメントのみの変更docs/update-readme

ブランチ名に含めると便利な情報:

  • 課題番号: feature/issue-42-add-ec2(チケット管理と連動)
  • 担当者: チームによっては feature/yamada/add-ec2 のように担当者を含める

本記事では feature/fix/ の2種類を中心に使います。


Section 6 — マージハンズオン

ブランチで開発した変更を main に取り込む操作が「マージ」です。このセクションでは fast-forward マージと no-ff マージの2種類、そしてコンフリクトの解消を体験します。


6-1. fast-forward マージの仕組み

fast-forward マージは、最もシンプルなマージ方法です。以下の条件が揃ったときに発生します。

fast-forward が発生する条件

main ブランチの最新コミットが、マージ対象ブランチの分岐点と一致する(つまり main が先に進んでいない)場合。

概念をテーブルで整理します。

状態コミット位置
マージ前mainCfeatureC → D
fast-forward後mainD に前進。feature と同じコミットを指す

fast-forward の特徴

  • マージコミットが生成されない(新しいコミットオブジェクトが作られない)
  • コミット履歴が直線状に保たれる
  • 「いつブランチが存在したか」が履歴から消える

小さな修正やチケット単位の変更では fast-forward が使いやすいですが、「このまとまりはどのブランチで開発されたか」を追いたいチームでは no-ff を選ぶことが多いです。


6-2. [ハンズオン] fast-forward マージ

feature/add-s3 ブランチを作成し、S3 モジュールを拡張してから main に fast-forward マージします。

# feature/add-s3 ブランチを作成して切替
git switch -c feature/add-s3

# modules/s3/main.tf にバージョニング設定を追加
cat > modules/s3/main.tf << 'EOF'
resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name

  tags = {
 ManagedBy = "Terraform"
  }
}

resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id
  versioning_configuration {
 status = "Enabled"
  }
}
EOF

# コミット
git add modules/s3/main.tf
git commit -m "feat: S3バケットのバージョニングを有効化"

main に戻ってマージを実行します。

git switch main
git merge feature/add-s3

出力例(fast-forward が実行された場合):

Updating f9e1a2b..7d8e9f0
Fast-forward
 modules/s3/main.tf | 9 +++++++++
 1 file changed, 9 insertions(+)

Fast-forward の文字が確認できます。マージコミットは生成されず、main のポインタが前進しただけです。

git log --oneline --graph --all

出力例:

* 7d8e9f0 (HEAD -> main, feature/add-s3) feat: S3バケットのバージョニングを有効化
* f9e1a2b feat: variables.tf に environment 変数を追加
* a1b2c3d feat: Terraform S3バケット管理の初期構成を追加

mainfeature/add-s3 が同じコミットを指しており、グラフが直線状です。


6-3. no-ff マージの仕組み(マージコミット生成)

--no-ff(no fast-forward)オプションをつけると、fast-forward が可能な状況でも必ずマージコミットを生成します。

項目fast-forward–no-ff
マージコミット生成されない必ず生成される
履歴の形状直線状分岐・合流が記録される
ブランチ追跡不可(履歴から消える)可能(グラフに残る)
git revert個別コミットごとに必要マージコミット1つで戻せる

no-ff を選ぶ場面

  • 機能ブランチ・リリースブランチの統合(「この機能がいつマージされたか」を追いたい)
  • コードレビュー後の統合(PR マージ = no-ff マージが一般的)
  • git revert でまとめて戻せるようにしたい

6-4. [ハンズオン] --no-ff マージ

feature/add-ec2 ブランチを main--no-ff でマージします。

git merge --no-ff feature/add-ec2 -m "Merge: EC2インスタンスリソースを追加 (#feature/add-ec2)"

出力例:

Merge made by the 'ort' strategy.
 ec2.tf | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 ec2.tf

6-5. git log --graph で fast-forward と no-ff の履歴差を比較

git log --oneline --graph --all

出力例:

*b1c2d3e (HEAD -> main) Merge: EC2インスタンスリソースを追加 (#feature/add-ec2)
|\
| * 3c4d5e6 (feature/add-ec2) feat: EC2インスタンス(t3.micro)リソースを追加
|/
* 7d8e9f0 (feature/add-s3) feat: S3バケットのバージョニングを有効化
* f9e1a2b feat: variables.tf に environment 変数を追加
* a1b2c3d feat: Terraform S3バケット管理の初期構成を追加

グラフの見方:

  • *b1c2d3e — マージコミット(2つの親を持つ)
  • |\ — 分岐を表す(上が main、右が feature/add-ec2
  • | *feature/add-ec2 上のコミット
  • |/ — 合流を表す

feature/add-s3 の fast-forward マージは直線状(グラフが * のみ)であるのに対し、feature/add-ec2 の no-ff マージは |\ |/ で分岐・合流が記録されています。


6-6. コンフリクト発生シナリオ

コンフリクト(競合)は、2つのブランチが同じファイルの同じ箇所を異なる内容に変更したときに発生します。Git は自動的にどちらの変更を採用すべきか判断できないため、ユーザーに解決を求めます。

コンフリクトを意図的に起こしてみます。

シナリオ: mainfeature/change-instance-type の両方で ec2.tfinstance_type を変更する。

まず、feature/change-instance-type ブランチを作成して変更します。

git switch -c feature/change-instance-type

cat > ec2.tf << 'EOF'
resource "aws_instance" "web" {
  ami  = "ami-0d52744d6551d851e"
  instance_type = "t3.small"

  tags = {
 Name= "web-server"
 ManagedBy = "Terraform"
  }
}
EOF

git add ec2.tf
git commit -m "feat: EC2インスタンスタイプを t3.small に変更"

main ブランチに戻り、同じ箇所を別の値に変更します。

git switch main

cat > ec2.tf << 'EOF'
resource "aws_instance" "web" {
  ami  = "ami-0d52744d6551d851e"
  instance_type = "t3.medium"

  tags = {
 Name= "web-server"
 ManagedBy = "Terraform"
  }
}
EOF

git add ec2.tf
git commit -m "feat: EC2インスタンスタイプを t3.medium に変更"

この状態でマージを試みます。

git merge --no-ff feature/change-instance-type

出力例(コンフリクト発生):

Auto-merging ec2.tf
CONFLICT (content): Merge conflict in ec2.tf
Automatic merge failed; fix conflicts and then commit the result.

コンフリクトが発生しました。


6-7. コンフリクトマーカーの読み方

git status でコンフリクトが発生しているファイルを確認します。

git status

出力例:

On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
  both modified:ec2.tf

no changes added to commit (use "git add" and/or "git commit -a")

ec2.tf を開くと、コンフリクトマーカーが挿入されています。

resource "aws_instance" "web" {
  ami  = "ami-0d52744d6551d851e"
<<<<<<< HEAD
  instance_type = "t3.medium"
=======
  instance_type = "t3.small"
>>>>>>> feature/change-instance-type

  tags = {
 Name= "web-server"
 ManagedBy = "Terraform"
  }
}

コンフリクトマーカーの意味:

マーカー意味
<<<<<<< HEAD現在チェックアウトしているブランチ(main)の変更ここから
=======2つの変更の区切り
>>>>>>> feature/change-instance-typeマージしようとしているブランチの変更ここまで

HEADmain)は t3.mediumfeature/change-instance-typet3.small に変更していたことがわかります。

コンフリクトは怖くない — 落ち着いて解消しよう

コンフリクトが起きても慌てる必要はありません。Git はどちらの変更を採用するかをユーザーに委ねているだけです。次の手順で冷静に対処してください。

1. git status でコンフリクトファイルを把握
2. 各ファイルを開き、マーカーを探して内容を理解
3. 正しい状態に手動で編集(マーカーをすべて削除)
4. git add で解決済みとしてマーク
5. git commit でマージを完了

「どちらを採用するか」は技術的な判断です。チームメンバーと相談しながら決めることも多いです。


6-8. コンフリクトの解消フロー

今回は t3.small を採用することにします(より小さいインスタンスタイプ)。

Step 1: エディタで ec2.tf を編集

マーカーをすべて削除し、採用する内容だけを残します。

resource "aws_instance" "web" {
  ami  = "ami-0d52744d6551d851e"
  instance_type = "t3.small"

  tags = {
 Name= "web-server"
 ManagedBy = "Terraform"
  }
}

<<<<<<< HEAD=======>>>>>>> feature/change-instance-type の3行をすべて削除することを忘れないでください。

Step 2: 解決済みをステージング

git add ec2.tf

Step 3: git status で確認

git status

出力例:

On branch main
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
  modified:ec2.tf

「All conflicts fixed」と表示されれば解消成功です。

Step 4: マージコミットを作成

git commit -m "Merge: feature/change-instance-type — t3.small を採用"

出力例:

[main e2f3a4b] Merge: feature/change-instance-type — t3.small を採用

コンフリクトの解消が完了しました。


6-9. git merge --abort でマージを取り消す

コンフリクト解消中に「やはりマージ自体をやめたい」という場合は --abort で元の状態に戻せます。

# コンフリクト発生後、解消前に実行(解消後は使えません)
git merge --abort

出力例:

(プロンプトに戻る)

git status で確認すると、マージ前の状態に戻っています。

git status

出力例:

On branch main
nothing to commit, working tree clean

--abort はコンフリクト解消の途中でのみ有効です。git commit でマージを完了させた後は git revertgit reset を使います。


6-10. 実務でのマージ方針の使い分け

どちらのマージ方法が正解かはチームやプロジェクトの方針次第です。以下を参考にしてください。

状況推奨マージ方式理由
機能ブランチ → main--no-ffブランチの存在を履歴に残したい
細かい修正(typo修正など)fast-forward履歴をシンプルに保ちたい
リリースブランチ → main--no-ffリリース単位で git revert できるようにしたい
一時的な実験ブランチfast-forward実験結果だけ残したい
GitHub/GitLab の PR マージ--no-ff(デフォルト)PR の存在を履歴に残す

実務でよく採用されるルール

  1. GitHub Flow: main への PR は --no-ff でマージ。feature/ ブランチは最終的に削除
  2. Git Flow: develop へのマージは --no-ff、リリースブランチは --no-ff でタグ付け
  3. Squash Merge: PR 内のコミットを1つにまとめてマージ(GitHub の「Squash and merge」)

本シリーズ第3弾「ブランチ戦略」でこれらのワークフロー全体を詳しく解説します。まずは --no-ff を基本として、チームで相談しながら判断してください。


git log --oneline --graph --all でここまでの全履歴を確認しましょう。

git log --oneline --graph --all

出力例:

*e2f3a4b (HEAD -> main) Merge: feature/change-instance-type — t3.small を採用
|\
| * 9a0b1c2 (feature/change-instance-type) feat: EC2インスタンスタイプを t3.small に変更
* | c8d9e0f feat: EC2インスタンスタイプを t3.medium に変更
|/
*b1c2d3e Merge: EC2インスタンスリソースを追加 (#feature/add-ec2)
|\
| * 3c4d5e6 (feature/add-ec2) feat: EC2インスタンス(t3.micro)リソースを追加
|/
* 7d8e9f0 (feature/add-s3) feat: S3バケットのバージョニングを有効化
* f9e1a2b feat: variables.tf に environment 変数を追加
* a1b2c3d feat: Terraform S3バケット管理の初期構成を追加

複数のブランチの分岐・コンフリクト解消・マージが履歴として記録されています。

Section 7 では GitHub にこのリポジトリをプッシュし、リモートとの連携を学びます。


Section 7: 実践ワークフロー — 一気通貫デモ

Section 1〜6 では Git の概念から初期設定、ファイル管理の基本、ブランチ操作、マージとコンフリクト解消まで、個々の操作を丁寧に学んできました。Section 7 では、これまでに習得したコマンドを組み合わせ、空のリポジトリから Terraform モジュール化完成までを一気通貫でデモします。実際の開発現場で繰り返す日常サイクルを体感してください。


7-1. 日常ワークフロー(feature branch → commit → merge の反復サイクル)

Terraform リポジトリでの日常作業は、以下のサイクルを繰り返します。feature ブランチで変更を加え、コミットで記録し、main ブランチへマージする——この反復がチームの安全な協業を支えます。

ステップコマンド説明
① ブランチ作成git switch -c feature/xxxmain から feature ブランチを切る
② ファイル編集(エディタで作業)Terraform コードを追加・修正する
③ ステージングgit add <file> または git add -p変更をコミット対象として選択する
④ コミットgit commit -m "feat: ..."スナップショットを記録する
⑤ 状態確認git status / git log --oneline現在の状態と履歴を把握する
⑥ マージgit switch main && git merge --no-ff feature/xxxfeature ブランチを main に統合する
⑦ ブランチ削除git branch -d feature/xxxマージ済みブランチを削除する
→ ①へ次の機能開発へサイクルを繰り返す

このサイクルを Terraform コードに適用する際の原則は 「1つの論理的変更 = 1ブランチ = 複数の小さなコミット」 です。providers.tf の追加、variables.tf の追加、VPC モジュールの追加は、それぞれ別の feature ブランチで管理します。

なぜ feature ブランチを使うのか?

main ブランチに直接コミットし続けると、複数の機能変更が入り混じった履歴になりやすく、問題の特定や変更の切り戻しが困難になります。feature ブランチを使うメリットは3つです。

  1. 安全性: main ブランチを常に「動作する状態」に保てる。feature ブランチでの実験的な変更が main を壊さない
  2. レビューしやすさ: 第2弾で学ぶ Pull Request は「feature ブランチから main への変更申請」がベース。1つのブランチが1つのテーマに集中していると、レビュワーがレビューしやすい
  3. rollback の容易さ: 問題のある変更がどの feature ブランチ由来かを特定して、そのマージコミットだけを git revert で戻せる(第2弾で詳説)

Terraform 文脈での feature ブランチ命名規則

プレフィックス意味
feature/新しいリソース追加feature/vpc / feature/s3-tfstate / feature/ecs-cluster
fix/既存コードの修正・バグ修正fix/security-group-cidr / fix/variable-type-mismatch
chore/設定・ドキュメント整備chore/update-gitignore / chore/add-readme
refactor/機能変更なしのコード整理refactor/extract-network-module

7-2. [一気通貫デモ] 空リポジトリから Terraform モジュール化完成まで

以下のデモでは、空ディレクトリから始めて Terraform コードを段階的に追加し、feature ブランチでの開発とマージを繰り返しながら履歴を積み上げていきます。Section 4〜6 で学んだ操作の総復習です。

STEP 1: リポジトリの初期化と .gitignore の設定

# 作業ディレクトリを作成してリポジトリを初期化
mkdir terraform-full-demo && cd terraform-full-demo
git init -b main
# 出力: Initialized empty Git repository in /path/to/terraform-full-demo/.git/

# .gitignore を作成(Section 7-6 の詳細版より抜粋)
cat > .gitignore << 'EOF'
# Terraform ローカルキャッシュ・プラグイン(コミット不要)
.terraform/
.terraform.lock.hcl

# tfstate(ローカル実行時の生成物、リモートbackendを使用する場合は不要)
*.tfstate
*.tfstate.*

# 機密情報(絶対コミットしない)
*.tfvars
!terraform.tfvars.example

# プラン結果ファイル
*.tfplan

# OS自動生成ファイル
.DS_Store
Thumbs.db
EOF

# .gitignore をコミット
git add .gitignore
git commit -m "chore: add .gitignore for Terraform project"
# 出力例:
# [main (root-commit) a1b2c3d] chore: add .gitignore for Terraform project
#  1 file changed, 18 insertions(+)
#  create mode 100644 .gitignore

STEP 2: providers.tf の追加

# providers.tf を作成
cat > providers.tf << 'EOF'
terraform {
  required_version = ">= 1.9"
  required_providers {
 aws = {
source  = "hashicorp/aws"
version = "~> 5.0"
 }
  }
}

provider "aws" {
  region = var.aws_region
}
EOF

# variables.tf を作成
cat > variables.tf << 'EOF'
variable "aws_region" {
  description = "AWS region to deploy resources"
  type  = string
  default  = "ap-northeast-1"
}

variable "project_name" {
  description = "Project name used as resource name prefix"
  type  = string
}
EOF

# ステージングしてコミット
git add providers.tf variables.tf
git commit -m "feat: add provider configuration and variables"
# 出力例:
# [main b2c3d4e] feat: add provider configuration and variables
#  2 files changed, 22 insertions(+)
#  create mode 100644 providers.tf
#  create mode 100644 variables.tf

# 現在の履歴確認
git log --oneline
# a1b2c3d (HEAD -> main) chore: add .gitignore for Terraform project
# b2c3d4e feat: add provider configuration and variables

STEP 3: feature/vpc ブランチで VPC モジュールを追加

# feature/vpc ブランチを作成して切り替え
git switch -c feature/vpc
# 出力: Switched to a new branch 'feature/vpc'

# modules/vpc/ ディレクトリと各ファイルを作成
mkdir -p modules/vpc

cat > modules/vpc/main.tf << 'EOF'
resource "aws_vpc" "this" {
  cidr_block  = var.vpc_cidr
  enable_dns_support= true
  enable_dns_hostnames = true

  tags = {
 Name = "${var.project_name}-vpc"
 Project = var.project_name
  }
}

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidrs)
  vpc_id= aws_vpc.this.id
  cidr_block  = var.public_subnet_cidrs[count.index]
  availability_zone = data.aws_availability_zones.available.names[count.index]

  tags = {
 Name = "${var.project_name}-public-${count.index + 1}"
 Project = var.project_name
  }
}

data "aws_availability_zones" "available" {
  state = "available"
}
EOF

cat > modules/vpc/variables.tf << 'EOF'
variable "project_name" {
  description = "Project name"
  type  = string
}

variable "vpc_cidr" {
  description = "VPC CIDR block"
  type  = string
  default  = "10.0.0.0/16"
}

variable "public_subnet_cidrs" {
  description = "List of public subnet CIDR blocks"
  type  = list(string)
  default  = ["10.0.1.0/24", "10.0.2.0/24"]
}
EOF

cat > modules/vpc/outputs.tf << 'EOF'
output "vpc_id" {
  description = "VPC ID"
  value = aws_vpc.this.id
}

output "public_subnet_ids" {
  description = "List of public subnet IDs"
  value = aws_vpc.this.*.id
}
EOF

# 段階的にコミット(1論理変更 = 1コミット)
git add modules/vpc/variables.tf
git commit -m "feat: add vpc module variable definitions"

git add modules/vpc/outputs.tf
git commit -m "feat: add vpc module output definitions"

git add modules/vpc/main.tf
git commit -m "feat: add vpc module main resource (aws_vpc + aws_subnet)"

# feature/vpc ブランチの履歴確認
git log --oneline
# d4e5f6g (HEAD -> feature/vpc) feat: add vpc module main resource
# c3d4e5f feat: add vpc module output definitions
# b2c3d4e feat: add vpc module variable definitions
# a1b2c3d feat: add provider configuration and variables
# 9a8b7c6 chore: add .gitignore for Terraform project

STEP 4: main ブランチへマージし、履歴を確認

# main ブランチへ切り替え
git switch main
# 出力: Switched to branch 'main'

# --no-ff でマージ(ブランチの存在を履歴に残す)
git merge --no-ff feature/vpc -m "Merge: add VPC module (modules/vpc)"
# 出力例:
# Merge made by the 'ort' strategy.
#  modules/vpc/main.tf| 35 +++++++++++++++++++++++++++
#  modules/vpc/outputs.tf| 10 ++++++++
#  modules/vpc/variables.tf | 18 ++++++++++++++
#  3 files changed, 63 insertions(+)

# マージ済みブランチを削除
git branch -d feature/vpc
# 出力: Deleted branch feature/vpc (was d4e5f6g).

# グラフ付き履歴でブランチの分岐・統合を確認
git log --oneline --graph --all
# *e5f6g7h (HEAD -> main) Merge: add VPC module (modules/vpc)
# |\
# | * d4e5f6g feat: add vpc module main resource
# | * c3d4e5f feat: add vpc module output definitions
# | * b2c3d4e feat: add vpc module variable definitions
# |/
# * a1b2c3d feat: add provider configuration and variables
# * 9a8b7c6 chore: add .gitignore for Terraform project

git log --oneline --graph の出力から、feature/vpc ブランチで 3 回のコミットを積み上げ、main へ no-ff マージされた流れが一目で把握できます。| がブランチを、* がコミットを、|\|/ がブランチの分岐・合流を示しています。


7-3. commit 粒度のベストプラクティス

Terraform コードでは、コミット粒度が後からのコード読解・rollback 容易性に直結します。「何をどこまでコミットするか」の判断基準を整理します。

良いコミット粒度の例(Terraform 文脈)

コミットメッセージ含む変更理由
feat: add S3 bucket for tfstate backends3.tf 新規追加のみ1リソース = 1コミット。rollback が容易
fix: correct S3 bucket name variable referencevariables.tf の1行修正バグ修正は小さく記録。原因特定が容易
chore: add .gitignore for Terraform.gitignore のみ設定ファイルは機能と分離
feat: add VPC subnet configurationmodules/vpc/ の複数ファイルモジュール単位での追加は同一コミットが自然

避けるべきコミット粒度

避けるべき例問題点
いろいろ変更変更内容が不明。git log で何を変えたか分からない
WIP作業途中の状態をコミット。他者が参照したとき壊れた状態を見る
10ファイルを一度にコミット問題が起きたとき、どのファイルが原因か特定困難
fix に機能追加を混在バグ修正と機能追加が同一コミットで、テスト・レビューが困難

コミット前のセルフチェック

「このコミットメッセージを見て、変更内容が 1 文で説明できるか?」——説明が複数必要なら、コミットを分割するサインです。git add -p(パッチ単位でステージング)を使えば、同じファイル内の変更を複数のコミットに分けることもできます。


7-4. コミットメッセージ規約(Conventional Commits)

チーム開発では、コミットメッセージに一定のフォーマットを採用することで、履歴の可読性と自動化(CHANGELOG 生成・セマンティックバージョニング)が向上します。Conventional Commits は広く採用されている規約です。

基本フォーマット

コミットメッセージは以下の構造に従います。

<type>[optional scope]: <description>
(空行)
[optional body](任意・変更の背景・理由を詳述)
(空行)
[optional footer](任意・Issue 番号・Breaking Change の注記)

type の一覧と Terraform 文脈での使い分け

type意味Terraform での例
feat新機能・新リソースの追加feat: add RDS instance for production
fixバグ修正・設定ミスの修正fix: correct security group ingress port
chore設定・ツール・ドキュメント整備chore: update .gitignore for tfplan files
refactor機能変更なしのコード整理refactor: extract network config to module
docsドキュメントのみの変更docs: add README setup instructions
testテストコードの追加・修正test: add terratest for vpc module
ciCI/CD パイプラインの変更ci: add GitHub Actions workflow for plan

scope(任意)の使い方

scope はカッコで囲み、変更対象のモジュール・ファイル・コンポーネントを示します。

scope の例メッセージ例
(vpc)feat(vpc): add private subnets with NAT gateway
(s3)fix(s3): enable versioning on tfstate bucket
(network)refactor(network): extract subnet config to local variable

コミットメッセージの完全な例

1行目: feat(rds): add Aurora PostgreSQL cluster for production
本文: - Add aws_rds_cluster with Multi-AZ configuration / - Add aws_rds_cluster_instance (2 instances for HA) / - Add aws_db_subnet_group using private subnets from vpc module
フッター: Closes #42

Conventional Commits の完全な仕様は https://www.conventionalcommits.org/ja/v1.0.0/ を参照してください。


7-5. Terraform plan 結果をコミットメッセージ・PR に活用

第2弾(GitHub 入門)では、Pull Request(PR)のレビューワークフローを学びます。その布石として、terraform plan の結果を PR の本文に貼り付ける習慣 を紹介します。

terraform plan はコードの変更がインフラに与える影響を事前に確認するコマンドです。PR に plan 結果を添付することで、レビュワーがコードを読むだけでなく実際の変更内容(追加・変更・削除されるリソース数)を把握できます。

# plan 結果をファイルに保存(バイナリ形式のプランファイル)
terraform plan -out=tfplan.binary

# 人間が読める形式でも出力(PR 本文に貼り付け用)
terraform plan -no-color 2>&1 | tee plan_output.txt

# コミットメッセージに plan サマリを含める例
git commit -m "feat: add S3 bucket for tfstate backend

terraform plan output:
  + aws_s3_bucket.tfstate_backend
  + aws_s3_bucket_versioning.tfstate_backend
  + aws_s3_bucket_server_side_encryption_configuration.tfstate_backend
  + aws_dynamodb_table.terraform_lock

Plan: 4 to add, 0 to change, 0 to destroy."

# 注意: tfplan.binary と plan_output.txt はコミットしない(.gitignore に *.tfplan を追加済み)

PR 本文のテンプレート(第2弾への布石)

第2弾では PR 本文に以下の構成を使います(Markdown 形式)。

  • ## 変更内容 セクション: 追加・変更したリソースを箇条書きで列挙
  • ## terraform plan 結果 セクション: Plan: 4 to add, 0 to change, 0 to destroy. などの plan サマリを貼り付け
  • ## 確認事項 セクション: .gitignore の記載確認・バケット名のグローバル一意性確認などのチェックリスト

この習慣は第5弾(GitHub Actions × OIDC)で GitHub Actions による 自動 plan 実行 → PR コメント投稿 として自動化します。


7-6. .gitignore の Terraform 向け決定版

Section 4-3 では基本的な .gitignore を作成しました。ここでは コメント付きの完全版 を紹介します。プロジェクト開始時にそのまま使えます。

.terraform.lock.hcl をコミットすべきかどうかは判断が必要なため、末尾に判断基準を記載します。

# =============================================================
# Terraform 向け .gitignore 決定版
# =============================================================

# ----- Terraform ローカルキャッシュ -------------------------
# プロバイダープラグインのバイナリ(terraform init で自動生成)
# 大容量かつ OS/アーキテクチャ依存のため除外
.terraform/

# ----- tfstate ファイル -------------------------------------
# ローカル実行時の状態ファイル(リモートbackendを使う場合は生成されない)
# 平文でリソースIDや機密値が含まれるため絶対コミットしない
*.tfstate
*.tfstate.*
terraform.tfstate.backup

# ----- プランファイル ----------------------------------------
# terraform plan -out=xxx で生成されるバイナリ
*.tfplan
tfplan

# ----- 変数ファイル(機密値を含む可能性) --------------------
# terraform.tfvars は AWS アクセスキーや DB パスワードを含みがち
# サンプルファイル(*.tfvars.example)はコミットしてよい
*.tfvars
!*.tfvars.example

# ----- クラッシュログ ----------------------------------------
# terraform が異常終了した際に生成されるログ
crash.log
crash.*.log

# ----- ローカル上書き設定 ------------------------------------
# terraform.tfvars.json はプロダクション値を含む場合があるため除外
terraform.tfvars.json

# ----- OS 自動生成ファイル -----------------------------------
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Thumbs.db
ehthumbs.db
Desktop.ini

# ----- エディタ・IDE 設定 ------------------------------------
.vscode/
.idea/
*.swp
*.swo
*~

# ----- CI/CD 一時ファイル ------------------------------------
.terraform-version

.terraform.lock.hcl をコミットすべきか?

状況判断理由
チーム開発(複数人・CI/CD あり)コミットするプロバイダーバージョンをチームで固定し、terraform init の冪等性を保つ
個人プロジェクト・学習用どちらでもよいバージョン固定の恩恵が小さく、更新のたびにコミットが必要になる
CI/CD で terraform init を毎回実行するコミットするロックファイルがないと毎回最新バージョンが取得され不安定になる
Terraform バージョンアップ作業中更新後にコミットterraform init -upgrade 後に新しいロックファイルをコミット

本記事(第1弾)では .terraform.lock.hcl.gitignore に含めていませんが、プロジェクト方針に合わせて判断してください。


Section 8: よく使う git コマンド早見表 + トラブルシューティング

Section 8 では、日常的によく使う Git コマンドをカテゴリ別に一覧化し、よくあるミスとその対処法を集約します。手元に置いてリファレンスとして使ってください。


8-1. コマンド早見表(5カテゴリ)

カテゴリ 1: 状態確認

コマンド用途
git status変更されたファイルの一覧(詳細表示)
git status -s変更されたファイルの一覧(短縮表示)
git diffWorking Tree の変更内容(未ステージ)
git diff --stagedStaging Area の変更内容(ステージ済み)
git diff HEAD直前のコミットとの全差分

カテゴリ 2: 履歴確認

コマンド用途
git logコミット履歴(詳細)
git log --onelineコミット履歴(1行表示)
git log --oneline --graph --allブランチの分岐を含む全履歴
git log --oneline -10直近10件のコミット
git show <commit>特定コミットの変更内容
git blame <file>ファイルの各行を最後に変更したコミット

カテゴリ 3: ステージング・コミット

コマンド用途
git add <file>特定ファイルをステージング
git add .現在ディレクトリ以下の全変更をステージング
git add -p変更をハンク単位で選択してステージング
git commit -m "..."メッセージ付きでコミット
git commitエディタを開いてコミットメッセージを入力

カテゴリ 4: ブランチ操作

コマンド用途
git branchローカルブランチ一覧
git branch -aリモートを含む全ブランチ一覧
git switch <branch>ブランチを切り替え
git switch -c <branch>ブランチを作成して切り替え
git branch -d <branch>マージ済みブランチを削除
git branch -m <old> <new>ブランチ名を変更

カテゴリ 5: マージ・統合

コマンド用途
git merge <branch>ブランチをマージ(fast-forward 可)
git merge --no-ff <branch>マージコミットを必ず作成
git merge --abortマージ中断(コンフリクト時)
git rebase <branch>現在のブランチを指定ブランチの先頭に付け替え(第3弾で詳説)

8-2. 直前のコミットを取り消したい

コミット後に「やっぱり変更を加えたい」「コミットするファイルを間違えた」という場合は git reset を使います。2種類のオプションを使い分けます。

# --soft: コミットのみ取り消し(変更は Staging Area に残る)
# → コミットメッセージを修正したいとき、もう少し変更を加えてからコミットしたいとき
git reset --soft HEAD^

git status
# Changes to be committed:
#(use "git restore --staged <file>..." to unstage)
#modified:providers.tf

# --mixed(デフォルト): コミットとステージングを取り消し(変更は Working Tree に残る)
# → ステージングからやり直したいとき
git reset --mixed HEAD^
# または単に:
git reset HEAD^

git status
# Changes not staged for commit:
#(use "git add <file>..." to update what will be committed)
#modified:providers.tf

注意: git push 済みのコミットに対して git reset を使うことは危険です。リモートリポジトリの履歴と食い違いが生じ、チームの作業に影響します。push 済みのコミットを取り消す方法(git revert)は第2弾で詳説します。


8-3. コミットメッセージを修正したい

直前のコミットメッセージにタイポがある場合や、Conventional Commits のフォーマットに直したい場合は git commit --amend を使います。

# 直前のコミットメッセージを修正(エディタが開く)
git commit --amend

# メッセージを直接指定して修正(エディタを開かない)
git commit --amend -m "feat: add provider configuration and variables"

# ファイルの変更もあわせてコミットに含める場合
git add <forgot-to-add-file>
git commit --amend --no-edit  # メッセージは変更しない

# 修正後の確認
git log --oneline -3

注意: git commit --amend は新しいコミットを作成してコミット ID(ハッシュ)を書き換えます。git push 済みのコミットに対して amend するとリモートとの差分が生じ、git push --force が必要になります。push 済みコミットの amend はチームに影響するため、push 前のみ使用してください。


8-4. 間違ってファイルを add した

git add でステージングしたファイルを、コミット前に取り消したい場合は git restore --staged を使います(Git 2.23 以降推奨)。

# 特定ファイルをステージングから取り消す
git restore --staged providers.tf

# 全ファイルをステージングから取り消す
git restore --staged .

# 確認
git status
# Changes not staged for commit:
#(use "git add <file>..." to update what will be committed)
#modified:providers.tf

# 旧コマンド(Git 2.23 未満)
# git reset HEAD providers.tf

git restore --staged はファイルの内容(Working Tree)は変更せず、ステージングエリアからの除外のみ行います。誤って git add した機密ファイルもこのコマンドで安全に取り消せます。


8-5. ブランチ名を変更したい

ブランチを切った後で命名ミスに気づいた場合は git branch -m で変更できます。

# 現在いるブランチの名前を変更
git branch -m feature/my-feature feature/add-vpc-module

# 別のブランチの名前を変更(切り替えなくてもよい)
git branch -m old-branch-name new-branch-name

# 確認
git branch
# * feature/add-vpc-module
#main

# push 済みの場合:リモートブランチの名前変更(第2弾で詳説)
# git push origin --delete old-branch-name
# git push -u origin new-branch-name

注意: git push 済みのブランチをリネームする場合は、リモートの旧ブランチを削除して新名前でプッシュし直す必要があります。チームに対して事前に周知することを推奨します。


8-6. コミット整理(git rebase -i の入口)

「push する前にコミット履歴をきれいにしたい」という場合、git rebase -i(インタラクティブリベース)が強力なツールです。複数のコミットを1つにまとめたり、順番を並び替えたりできます。

# 直近3件のコミットをインタラクティブに編集
git rebase -i HEAD~3

# エディタに以下のような画面が表示される:
# pick a1b2c3d feat: add vpc module variable definitions
# pick b2c3d4e feat: add vpc module output definitions
# pick c3d4e5f feat: add vpc module main resource
#
# コマンドを変更する:
# pick  → そのまま使う
# squash → 直前のコミットに統合する
# reword → コミットメッセージを変更する
# drop→ コミットを削除する

インタラクティブリベースの詳細な使い方(squash・reword・fixup の使い分け)は 第3弾「ブランチ戦略 — チーム運用」 で詳説します。まずは「push 前にコミット履歴を整理できるツールが存在する」ことを覚えておいてください。

注意: git push 済みのコミットに対して git rebase -i を使うと履歴の書き換えが発生します。ローカルのみ・push 前の段階でのみ使用してください。


8-7. 機密値を commit してしまった

AWS アクセスキーや DB パスワードなどの機密値を誤ってコミットしてしまった場合は、以下の対応が必要です。

push 前であれば: git reset --soft HEAD^ または git commit --amend で機密値を含むコミットを取り消し、.gitignore に対象ファイルを追加してから再コミットします。

push 済みであれば: 機密値がリモートリポジトリに公開されているため、対応は二段階必要です。

  1. 即時: 該当する AWS アクセスキー・トークン・パスワードをすべて無効化・ローテーションする(公開されたと仮定して行動する)
  2. 履歴削除: git filter-repo(旧 git filter-branch)または BFG Repo Cleaner で履歴から該当コミットを完全に除去する

機密値の誤コミット対策(pre-commit フック・git-secrets・GitLeaks)と履歴からの完全除去手順は 第4弾「セキュリティ — 事故防止」 で詳説します。

機密値の誤コミットを防ぐ日常習慣(先行紹介)

習慣効果
git diff --staged をコミット前に必ず確認するステージングした変更に機密値が含まれていないか目視確認
*.tfvars.gitignore に追加する変数ファイルの誤コミットをシステム的に防ぐ
terraform.tfvars.example にサンプル値のみ記載するチームにテンプレートを共有しながら実値はコミットしない
シークレット管理を AWS Secrets Manager / Parameter Store に移行するコードに機密値を書かない設計が根本的な解決策

これらの実践的な対策は第4弾で一気に学びます。第1弾のうちは「コミット前に git diff --staged で確認する」習慣だけを身につけておけば十分です。

⚠️ 重要: 機密値は push した瞬間から漏洩リスクが発生します
GitHub では push された内容を自動スキャンし、検出した機密値を AWS などのプロバイダーに通知する仕組みがあります(Secret Scanning)。push が完了した時点でアクセスキーは即時ローテーションしてください。「すぐ削除したから大丈夫」という判断は危険です。

Section 9: まとめ・次のステップ・参考リンク

Section 1 から Section 8 まで、Terraform コードを題材に Git のローカル操作を一通り学びました。最後に習得したスキルを振り返り、次のステップへの道筋を示します。


9-1. 本記事で習得したスキル一覧

Sectionテーマ習得した主なコマンド・知識
1この記事についてシリーズ全体像・前提条件・学習の進め方
2Gitとは分散型VCSの概念・Working Tree / Staging Area / Repository の3エリア
3Git初期設定git config --global / ~/.gitconfig / init.defaultBranch / エディタ設定
4ファイル管理の基本git init / git add / git commit / git status / git diff / git log
5ブランチ操作git switch -c / git branch / git log --graph / ブランチ命名規則
6マージとコンフリクトgit merge --no-ff / git merge --abort / コンフリクトマーカー解消手順
7実践ワークフロー日常サイクル / 一気通貫デモ / コミット粒度 / Conventional Commits / .gitignore 完全版
8コマンド早見表git reset --soft / git commit --amend / git restore --staged / git branch -m

身についた実践スキル

  • Terraform プロジェクトに特化した .gitignore を設定できる
  • feature ブランチを切って開発し、--no-ff マージで履歴を整理できる
  • Conventional Commits のフォーマットで意味のあるコミットメッセージを書ける
  • コミットミス(wrong file / wrong message)を push 前に安全に取り消せる
  • git log --oneline --graph でブランチの分岐と統合の流れを把握できる
  • terraform plan 結果の活用方法(コミットメッセージ・PR 本文への記載)を知っている
  • 機密値の誤コミットが起きた場合の初動対応(即時ローテーション)の重要性を理解している

この記事で作成したリポジトリの最終状態

Section 7 の一気通貫デモを完走すると、以下の構成のリポジトリが完成しています。

ファイル作成 Section内容
.gitignoreSection 7-2 STEP 1Terraform 向け除外設定(Section 7-6 完全版)
providers.tfSection 7-2 STEP 2AWS Provider 定義
variables.tfSection 7-2 STEP 2プロジェクト共通変数
modules/vpc/main.tfSection 7-2 STEP 3aws_vpc + aws_subnet リソース定義
modules/vpc/variables.tfSection 7-2 STEP 3VPC 固有変数
modules/vpc/outputs.tfSection 7-2 STEP 3vpc_id / subnet_ids 出力

git log --oneline --graph --all で確認すると、chore: add .gitignorefeat: add provider configuration → feature/vpc ブランチでの3コミット → Merge: add VPC module という流れが視覚的に確認できます。第2弾ではこの履歴を持ったリポジトリを GitHub にプッシュするところから始めます。


9-2. 次のステップ(第2弾への橋渡し)

ローカルでの Git 操作は完璧に身につきました。しかしチーム開発では、ローカルの変更をリモートリポジトリ(GitHub)と同期し、Pull Request を使ってコードレビューを行う ことが不可欠です。

第2弾「GitHub 入門 — push/pull/PR/レビュー」では以下を学びます。

テーマ学ぶ内容
リモートリポジトリGitHub でリポジトリを作成し、git remote add で連携する
push / pullgit push でローカルの変更をリモートへ送り、git pull でリモートの変更を取り込む
Pull Requestfeature ブランチの変更を PR として提出し、差分をチームでレビューする
Issue 連携コミットメッセージ・PR で Issue 番号を参照し、自動クローズを使う
fork & cloneOSS コントリビューションの基本フロー(fork → clone → PR)
push 済みコミットの安全な取り消しgit revert で履歴を書き換えずに取り消しコミットを追加する

第1弾で作成した terraform-git-handson/ リポジトリをそのまま使い、GitHub にプッシュするところから始めます。

第2弾を始める前のチェックリスト

第1弾の内容が身についているかを以下で確認してください。すべて「できる」になっていれば第2弾の準備完了です。

チェック項目確認方法
git init で新しいリポジトリを作成できるgit log --oneline でコミット履歴が確認できるか
.gitignore.terraform/*.tfstate を設定したcat .gitignore で内容を確認
feature ブランチを作成してコミットし、main にマージできるgit log --oneline --graph でブランチ履歴が見えるか
コミットメッセージに Conventional Commits を使っているgit log --oneline でメッセージを確認
git reset --soft HEAD^ でコミットを取り消せるpush 前のコミットで試してみる

9-3. シリーズロードマップ

📚 Git/GitHub × Terraform実践シリーズ — 全5弾ロードマップ

  • 第1弾(この記事): Git入門 — init / commit / branch / merge をローカルで習得
  • 第2弾(近日公開): GitHub入門 — push / pull / Issue / PR / レビューで協業
  • 第3弾: ブランチ戦略 — GitFlow / trunk-based / Terraform モジュール管理
  • 第4弾: セキュリティ — .gitignore / secrets / pre-commit / tflint / tfsec で事故防止
  • 第5弾: GitHub Actions × OIDC — plan/apply の CI/CD 完全自動化

各弾は独立した記事として読めますが、第1弾 → 第2弾 → 第5弾 の順に読み進めると、「ローカル操作 → GitHub 協業 → CI/CD 自動化」という実務の成長ステップをスムーズに追体験できます。第3弾・第4弾はそれぞれのテーマに特化した深掘り記事です。


9-4. 参考公式ドキュメント

本記事で参照した公式ドキュメントと推奨リソースを一覧にまとめます。

リソースURL内容
Git 公式リファレンスhttps://git-scm.com/doc全コマンドのリファレンスドキュメント
Pro Git ブック(日本語)https://git-scm.com/book/ja/v2Git の仕組みから応用まで網羅した無料電子書籍
Conventional Commitshttps://www.conventionalcommits.org/ja/v1.0.0/コミットメッセージ規約の完全仕様
GitHub Docshttps://docs.github.com/jaGitHub の操作ガイド(第2弾以降で参照)
Terraform .gitignorehttps://github.com/github/gitignore/blob/main/Terraform.gitignoreGitHub 公式の Terraform 向け .gitignore テンプレート

さらに深く学びたい方へ

  • 「Learn Git Branching」(https://learngitbranching.js.org/?locale=ja): ブランチ操作をビジュアルインタラクティブに学べる無料サービス。git rebase・cherry-pick まで体験できます
  • 「Oh My Git!」: カード型の Git 学習ゲーム。コマンドの意味と結果を視覚的に体験できます
  • 「Atlassian Git Tutorial」(https://www.atlassian.com/ja/git/tutorials): Bitbucket 運営のアトラシアンが提供するチュートリアル。Gitの内部構造(オブジェクトモデル)まで丁寧に解説されており、Pro Git ブックと合わせて読むと理解が深まります
  • 「GitHub Skills」(https://skills.github.com/): GitHub が提供するインタラクティブなハンズオンコース。第2弾の学習と並行して進めると相乗効果があります

次弾: GitHub入門 — push/pull/PR/レビューで協業 →(近日公開)

本記事ではローカルの Git 操作をTerraform コードで一通り実践しました。第2弾では GitHub を使ったリモート協業——git push でコードを共有し、Pull Request でチームレビューを行う流れを学びます。本記事で作成した terraform-git-handson/ リポジトリをそのまま持ち越して GitHub にプッシュするところから始めます。引き続きシリーズをお楽しみください。