開発・デバッグ・テスト

バージョン管理(Git)

ソースの変更履歴を管理。

概要

バージョン管理(Version Control)とは、ソースコードや設定ファイルなどの変更履歴を記録・管理するシステムです。誰が・いつ・何を変更したかを追跡し、過去のバージョンへの復帰、複数人での並行開発(ブランチ)、変更内容の比較(diff)などを可能にします。

現代の組み込み開発では、Git(Linus Torvaldsが2005年に開発)が事実上の標準バージョン管理システムとなっています。GitHubやGitLabなどのホスティングサービスと組み合わせることで、チームでの協調開発、コードレビュー(Pull Request/Merge Request)、CI/CDパイプラインの自動化が実現します。

組み込み開発では、ファームウェアのバイナリ管理、リンカスクリプトデバイスツリーなどのテキスト管理、ドキュメント・テスト結果の管理にも活用されます。

歴史・背景

1970〜80年代:SCCS(Source Code Control System、1972年)やRCS(Revision Control System、1982年)など初期のファイル単位バージョン管理システムが登場しました。

1990年代:CVS(Concurrent Versions System)が普及し、複数人での並行開発が容易になりました。しかしCVSのブランチ・マージの扱いにくさから、SVN(Subversion、2000年)が登場しCVSを置き換えました。

2005年:Linus TorvaldsがLinuxカーネル開発のためにGitを開発。分散型バージョン管理(各ユーザーがリポジトリの完全なコピーを持つ)により高速性・柔軟性を実現しました。

2008年:GitHubがサービス開始。Pull Requestベースのコードレビューフローを普及させ、オープンソース開発のスタイルを変えました。

2010年代:GitHubのフォーク・Pull Requestモデルがオープンソースだけでなく企業内開発にも普及。GitLabがセルフホスト型CI/CDを強化し、組み込み開発での採用が広がりました。

2020年代:GitHub Actionsの登場(2019年)により、リポジトリとCI/CDの統合が極めて容易になりました。組み込みファームウェアのビルド・テスト・リリースを全てGit/GitHubで管理するプラクティスが普及しています。

技術仕様

Git の基本概念

ワーキングディレクトリ(作業ツリー)
    ↓ git add
ステージングエリア(インデックス)
    ↓ git commit
ローカルリポジトリ(.git/)
    ↓ git push
リモートリポジトリ(GitHub/GitLab等)

組み込み開発での典型的なリポジトリ構成

my-firmware/
├── .git/
├── .github/
│   └── workflows/
│       ├── build.yml          # ビルド自動化
│       └── test.yml           # テスト自動化
├── src/
│   ├── main.c
│   ├── drivers/
│   └── hal/
├── include/
├── test/                      # ユニットテスト
├── docs/                      # ドキュメント
├── tools/                     # ビルドスクリプト
├── third_party/               # サードパーティライブラリ(gitサブモジュール)
├── CMakeLists.txt
├── linker_script.ld
├── .gitignore
└── README.md

.gitignoreの設定(組み込み開発用)

# ビルド成果物
build/
*.o
*.elf
*.bin
*.hex
*.map
*.lst

# IDEの設定ファイル(プロジェクト固有のものはコミット、ユーザー設定は除外)
.vscode/settings.json
.vscode/launch.json
*.suo
*.user
Debug/
Release/

# STM32CubeIDE 固有
.metadata/
.settings/
.cproject
RemoteSystemsTempFiles/

# Keil MDK
*.uvguix.*
*.uvoptx
*.bak

# 一時ファイル・OS固有
.DS_Store
Thumbs.db
*.swp
*~

# gcovカバレッジファイル(CIでは保持するが通常は除外)
*.gcda
*.gcno

動作原理

Gitの内部構造

コミットオブジェクトの構造:
commit abc1234
├── tree: def5678   ← そのコミット時のファイルツリー
├── parent: bcd9012 ← 前のコミット
├── author: 田中太郎 <t@example.com>
└── message: "Fix sensor timeout bug"

tree def5678
├── blob: 111aaaa src/main.c      ← ファイルの内容
├── blob: 222bbbb src/sensor.c
└── tree: 333cccc include/        ← サブディレクトリ

各コミットはSHA-1/SHA-256ハッシュで識別され
内容が1bitでも違えば異なるハッシュになる
→ 改ざん検知・整合性確保

ブランチ戦略(組み込み開発向け)

Git Flow(複雑な製品向け)

main ────────────────────────────────► (リリース済み安定版)

         merge
         |
develop ─────────────────────────────► (開発中)
         ↑         ↑
      feature/   feature/
      sensor-v2  can-protocol

Trunk-Based Development(CI/CD重視)

main ────────────────────────────────► (常にビルド可能)
  ↑   ↑   ↑   ↑
  short-lived feature branches
  (最大1〜2日で main にマージ)

組み込みでは長期サポート(LTS)版の管理も重要です:

# v1.x と v2.x を並行サポートする例
git branch release/v1.x
git branch release/v2.x

# バグ修正を両方に適用
git checkout release/v1.x
git cherry-pick abc1234  # バグ修正コミットを適用

サブモジュールの管理

組み込み開発ではサードパーティライブラリをgit submoduleで管理することが多いです:

# サブモジュールを追加
git submodule add https://github.com/FreeRTOS/FreeRTOS.git third_party/freertos

# クローン時にサブモジュールも取得
git clone --recursive https://github.com/your-org/firmware.git

# 既存クローン後にサブモジュール初期化
git submodule update --init --recursive

# サブモジュールを特定バージョンに固定
cd third_party/freertos
git checkout V10.5.1
cd ../..
git add third_party/freertos
git commit -m "freertos: Update to V10.5.1"

用途・ユースケース

ファームウェアのタグ管理とリリース

# バージョンタグの付与
git tag -a v1.2.3 -m "Release v1.2.3: Fixed I2C timeout, Added BLE support"
git push origin v1.2.3

# GitHub Releases と連携してバイナリを配布
# .github/workflows/release.yml で自動化
# タグ作成時に firmware.bin/hex を自動ビルドして添付
# .github/workflows/release.yml
name: Release
on:
  push:
    tags:
      - 'v*'
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive
      - name: Install toolchain
        run: sudo apt-get install -y gcc-arm-none-eabi
      - name: Build firmware
        run: make release
      - name: Create GitHub Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            build/firmware.bin
            build/firmware.hex

コードレビューとPull Request

# フィーチャーブランチを作成して作業
git checkout -b feature/add-watchdog-timer

# 変更をコミット
git add src/watchdog.c include/watchdog.h
git commit -m "Add software watchdog timer for task monitoring

- 各タスクのハートビートを1000ms間隔で監視
- タイムアウト時はシステムリセットを実行
- FreeRTOS対応

Closes #42"

# Push してPull Request作成
git push origin feature/add-watchdog-timer
# → GitHub/GitLab でPull Request作成
# → CI/CDが自動でビルド・テスト
# → レビュー後にマージ

バイナリ差分の管理(LFS)

大きなバイナリファイル(プリビルドライブラリ、テストベクタ等)はGit LFS(Large File Storage)で管理します:

# Git LFSのインストールと設定
git lfs install
git lfs track "*.bin" "*.hex" "*.a"
git add .gitattributes

# 以後 *.bin, *.hex, *.a は LFS で管理される

実装・開発のポイント

コミットメッセージの規約

組み込みプロジェクトでは変更の種類を明確にするため、Conventional Commits形式が有用です:

<type>(<scope>): <subject>

<body>

<footer>

type: feat | fix | docs | style | refactor | test | chore | perf
scope: sensor | comm | rtos | hal | bootloader など

例:

fix(i2c): Fix ACK timeout handling for slow sensors

HAL_I2C_Master_Transmit のタイムアウト値が短すぎたため、
一部の低速センサー(BMP280)でNACKが検出されていた。
タイムアウトを100msから500msに変更。

Fixes #123
Tested on: STM32F407VG with BMP280 @ 100kHz

バイナリとソースの分離

# フラッシュメモリのイメージ(バイナリ)はGit LFS
# または releases に添付し、Gitには含めない

# .gitignore でビルド成果物を除外
echo "*.bin" >> .gitignore
echo "*.elf" >> .gitignore

# 代わりにビルドスクリプトとツールチェーンバージョンをコミット
# → 再現性のあるビルド環境を保証

他技術との比較

比較項目GitSVN(Subversion)CVS
分散/集中分散型(ローカルに完全なコピー)集中型集中型
ブランチコスト非常に低い(ポインタ操作)高い(コピー)高い
オフライン作業可能(ローカルコミット)不可不可
マージ能力強力(3-way merge)弱い非常に弱い
バイナリ対応LFSで対応対応弱い
学習コスト中(概念が多い)
CI/CDエコシステム非常に豊富一部対応ほぼなし

CI/CDとの統合において、Gitのブランチ戦略とタグ管理がビルド・テスト・デプロイフローの基盤となります。

関連用語

参考リンク