Arc 6 Quest A4

The Artificer's Plans

Git for hardware, KiCad and electronics

In the depths of the Citadel, well below the archive halls, lie the underground workshops of the Artificers. Here, the flickering light of braziers reveals workbenches covered with mechanical blueprints, enchanted circuits and automaton models. The Artificers don't work with ordinary scrolls - their creations are physical artifacts, complex schematics that standard inks cannot differentiate.

The Master Artificer greets you with a nod. "You know how to version text. But can you version a printed circuit board? A mechanical schematic? A three-dimensional blueprint? That's a whole different challenge. And it's the one we'll tackle today."

The challenge of binary files

When you work with source code, Git excels: each file is text, and Git can calculate differences line by line. But in the world of hardware and electronics, the majority of files are binary:

  • Electronic schematics (.kicad_sch, .sch) - circuits and components
  • Printed circuit boards (.kicad_pcb, .brd) - physical routing
  • 3D models (.step, .stl, .FCStd) - enclosures and mechanical parts
  • Manufacturing files (.gbr, .drl) - Gerbers for the factory

The fundamental problem: when you run git diff on a binary file, Git simply responds Binary files differ. Impossible to know what changed.

# On a text file, git diff shows the changes:
git diff main.py
# -  print("Hello")
# +  print("Bonjour")

# On a binary file, git diff is silent:
git diff board.kicad_pcb
# Binary files a/board.kicad_pcb and b/board.kicad_pcb differ

Additionally, each modification of a binary file creates a complete copy in the Git history. A 5 MB PCB modified 20 times = 100 MB of history. The repository grows fast.

Hardware versioned with Git requires specific strategies: open formats when possible, visual diff tools, and rigorous repository organization.

KiCad + Git - the winning duo

KiCad is a free and open source electronic design automation (EDA) software. Its great strength for versioning: most of its files are in text format (S-expressions), which makes diffs possible.

Structure of a KiCad project

my-kicad-project/
β”œβ”€β”€ my-project.kicad_pro     # Project file (text, JSON)
β”œβ”€β”€ my-project.kicad_sch     # Electronic schematic (text, S-expr)
β”œβ”€β”€ my-project.kicad_pcb     # Printed circuit board (text, S-expr)
β”œβ”€β”€ sym-lib-table             # Symbol library table
β”œβ”€β”€ fp-lib-table              # Footprint library table
β”œβ”€β”€ my-project.kicad_prl     # Local preferences (DO NOT VERSION)
β”œβ”€β”€ fp-info-cache             # Footprint cache (DO NOT VERSION)
└── production/               # Generated manufacturing files
    β”œβ”€β”€ gerbers/
    └── bom/

Good news: .kicad_sch and .kicad_pcb files are structured text. Git can calculate diffs, even if reading them isn't always intuitive.

The complete KiCad .gitignore

# .gitignore for a KiCad project

# Backup files
*.kicad_sch-bak
*.kicad_pcb-bak
*-backups/
_autosave-*

# Local preferences (specific to each workstation)
*.kicad_prl

# Cache
fp-info-cache

# Netlist (generated, not source)
*.net

# Lock files
*.lck

# Manufacturing folder (generated, version separately if needed)
# production/gerbers/
# production/bom/

# Generated 3D files
*.wrl

# Misc
*~
\#*\#

Tip: .kicad_prl files contain your display preferences (zoom, active layer, etc.). They change constantly and should never be versioned - each developer has their own.

KiCad commit best practices

Since KiCad files are text, each save in KiCad can modify many lines (positions, internal identifiers). To keep a readable history:

  • One commit per logical change: "Add 3.3V voltage regulator", not "Various modifications"
  • Separate schematic and PCB: first the schematic, then the routing
  • Don't commit after every save: wait until you have a coherent state

Visual diff for schematics

Even though KiCad files are text, reading an S-expression diff isn't very meaningful. That's where visual diff tools come in: they generate images of each version and compare them visually.

kidiff

kidiff is the most popular tool. It generates PNG images of each version of the schematic or PCB, then displays them side by side in a web browser.

# Installation
pip install kidiff

# Compare the schematic between two commits
kidiff my-project.kicad_sch HEAD~1 HEAD

# Compare the PCB between a branch and main
kidiff my-project.kicad_pcb main feature-regulator

# Compare with the working directory (uncommitted changes)
kidiff my-project.kicad_sch HEAD

plotgitsch (KiCad 5 and earlier)

For older projects using KiCad 5, plotgitsch offers similar functionality:

# Installation (requires OCaml)
opam install plotgitsch

# Compare between two commits
plotgitsch -ik my-project.sch HEAD~3 HEAD

kicad-diff

kicad-diff is another tool that integrates directly into Git as an external diff tool:

# Configuration in .gitconfig
git config --global diff.kicad.command kicad-diff

# Then in the project's .gitattributes
# *.kicad_sch diff=kicad
# *.kicad_pcb diff=kicad

# Then, git diff automatically uses the visual diff
git diff my-project.kicad_sch

Visual diff is essential for hardware code review. Without it, it's practically impossible to verify that a schematic change is correct by reading raw text.

Gerber and BOM files

Production files are those you send to the PCB manufacturer. The question arises: should you version them?

Gerber files

Gerbers (.gbr, .drl) are the manufacturing files for the printed circuit board. Each PCB layer (copper, solder mask, silkscreen, drill holes) has its own file.

  • Advantage of versioning them: you know exactly which Gerbers were sent for each PCB version
  • Disadvantage: they are generated from the PCB, so they are derived data

Best practice: generate Gerbers at release time and attach them as artifacts to the Git release, rather than committing them to the history.

The BOM (Bill of Materials)

The BOM is the list of required components. In KiCad, you can export it as CSV - a text format perfect for versioning.

# Example BOM in CSV (versioned in Git)
# Reference, Value, Footprint, Quantity
# R1, 10k, 0805, 1
# R2, 4.7k, 0805, 1
# C1, 100nF, 0805, 1
# U1, ATmega328P, TQFP-32, 1

# Generate BOM from KiCad CLI
kicad-cli sch export bom my-project.kicad_sch -o production/bom/bom.csv

Tip: Always version the BOM as CSV in the repository. It's text, diffs are perfect, and you can see exactly which components changed between two versions.

Mechanical CAD and Git

Hardware isn't limited to electronics. Enclosures, mechanical parts, assemblies - all of this needs to be versioned too.

FreeCAD (.FCStd = ZIP archive)

FreeCAD is a free and open source 3D CAD software. Its native format .FCStd is actually a ZIP archive containing XML files. Git treats it as binary, but there are tricks:

# A .FCStd file is a ZIP
unzip -l enclosure.FCStd
# Archive:  enclosure.FCStd
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#     12543  2026-03-09 14:30   Document.xml
#      2341  2026-03-09 14:30   GuiDocument.xml
#      8923  2026-03-09 14:30   Brep/Part__Feature.brp

# Trick: use a Git filter to extract the XML
# .gitattributes
# *.FCStd diff=fcstd

# .gitconfig
# [diff "fcstd"]
#   textconv = unzip -p -c Document.xml

OpenSCAD (.scad = pure text)

OpenSCAD is a versioner's dream: .scad files are pure source code. You describe your 3D part with code, and Git handles diffs perfectly.

# OpenSCAD example - enclosure for a PCB
// enclosure.scad - Enclosure for electronic board v1.2

pcb_width = 50;
pcb_length = 80;
pcb_thickness = 1.6;
clearance = 2;  // Gap around the PCB
wall_thickness = 2;

module bottom_enclosure() {
    difference() {
        // Exterior
        cube([pcb_width + 2*clearance + 2*wall_thickness,
              pcb_length + 2*clearance + 2*wall_thickness,
              15]);
        // Interior (hollow)
        translate([wall_thickness, wall_thickness, wall_thickness])
            cube([pcb_width + 2*clearance,
                  pcb_length + 2*clearance,
                  15]);
    }
}

bottom_enclosure();

With OpenSCAD, a git diff shows you exactly what changed: "clearance went from 2 mm to 3 mm". Crystal clear.

Fusion 360 and cloud tools

Fusion 360 (Autodesk) stores files in the cloud. Versioning is managed by the platform itself, not by Git. If you use Fusion 360:

  • Regularly export to STEP (.step) for archiving in Git
  • STEP is an open standard, readable by all CAD software
  • Version the STEP exports as reference "snapshots"

Organizing a hardware repo

A well-organized hardware project in Git follows a clear structure that separates sources, firmware, documentation and production files:

my-hw-project/
β”œβ”€β”€ hw/                          # Everything hardware
β”‚   β”œβ”€β”€ electronics/             # Electronic design
β”‚   β”‚   β”œβ”€β”€ my-project.kicad_pro
β”‚   β”‚   β”œβ”€β”€ my-project.kicad_sch
β”‚   β”‚   β”œβ”€β”€ my-project.kicad_pcb
β”‚   β”‚   β”œβ”€β”€ sym-lib-table
β”‚   β”‚   β”œβ”€β”€ fp-lib-table
β”‚   β”‚   └── libs/                # Custom libraries
β”‚   β”‚       β”œβ”€β”€ symbols/
β”‚   β”‚       └── footprints/
β”‚   └── mechanical/              # Mechanical parts
β”‚       β”œβ”€β”€ enclosure.scad       # or .FCStd
β”‚       └── exports/             # Exported STEP, STL
β”‚           β”œβ”€β”€ enclosure-v1.step
β”‚           └── enclosure-v1.stl
β”œβ”€β”€ firmware/                    # Embedded code
β”‚   β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ include/
β”‚   └── platformio.ini           # or Makefile, CMakeLists.txt
β”œβ”€β”€ docs/                        # Documentation
β”‚   β”œβ”€β”€ architecture.md
β”‚   β”œβ”€β”€ pinout.md
β”‚   └── images/
β”œβ”€β”€ production/                  # Manufacturing files
β”‚   β”œβ”€β”€ gerbers/
β”‚   β”œβ”€β”€ bom/
β”‚   β”‚   └── bom.csv
β”‚   └── pick-and-place/
β”œβ”€β”€ .gitignore
β”œβ”€β”€ LICENSE
β”œβ”€β”€ CHANGELOG.md
└── README.md

Tip: Separate hw/ and firmware/ at the top level. This allows a firmware developer to do a sparse checkout without downloading the electronic design files (often bulky).

Managing hardware releases

In software, a release is a version of the code. In hardware, a release corresponds to a physical version of the PCB - once manufactured, you can't modify it with a simple commit.

Tag convention for hardware

Git tags are perfect for marking PCB versions:

# First PCB version sent to manufacturing
git tag -a v1.0-rev-a -m "PCB v1.0 Rev A - First prototype"

# Bug fixes (missing trace, resistor value)
git tag -a v1.1-rev-b -m "PCB v1.1 Rev B - Power supply fix"

# Major change (new microcontroller, layout redesign)
git tag -a v2.0-rev-a -m "PCB v2.0 Rev A - Migration to STM32"

The vX.Y-rev-Z convention is common in the industry:

  • X (major): architecture change, new main component
  • Y (minor): bug fixes, value adjustments
  • rev-Z (revision): PCB revision letter (a, b, c...)

Manufacturing files in releases

Rather than committing Gerbers to the history, attach them to releases:

# Generate Gerbers
kicad-cli pcb export gerbers my-project.kicad_pcb -o production/gerbers/

# Generate drill files
kicad-cli pcb export drill my-project.kicad_pcb -o production/gerbers/

# Create a manufacturing archive
cd production/gerbers/
zip ../../my-project-v1.0-rev-a-gerbers.zip *.gbr *.drl
cd ../..

# Create the tag and release
git tag -a v1.0-rev-a -m "PCB v1.0 Rev A"
git push origin v1.0-rev-a

# On GitHub, create a release and attach the Gerber ZIP
gh release create v1.0-rev-a my-project-v1.0-rev-a-gerbers.zip \
  --title "PCB v1.0 Rev A - First prototype" \
  --notes "First PCB version sent to manufacturing."

Hardware changelog

A CHANGELOG.md is even more important in hardware than in software - you need to know exactly what changed between two physical revisions:

# CHANGELOG.md

## [v1.1-rev-b] - 2026-03-15
### Fixed
- Resistor R12: 10k -> 4.7k (incorrect voltage divider)
- Missing trace between U3 pin 7 and C15
- J2 footprint corrected (2.0mm pitch instead of 2.54mm)

### Added
- Diagnostic LED D4 on GPIO PA5
- Test point TP3 on 3.3V rail

## [v1.0-rev-a] - 2026-03-01
### Initial version
- STM32F103 microcontroller
- 5V USB power supply + 3.3V regulator
- 4 analog inputs, 2 PWM outputs

In hardware, a Git tag = a physical version. The changelog is vital because it documents the differences between objects that exist in the real world and cannot be "updated" like software.

Best practices - summary

Practice Why
Use KiCad (text format) Readable diffs, no proprietary lock-in
Prefer OpenSCAD for mechanical parts Source code = perfect diffs
Install a visual diff tool Essential for schematic review
Separate hw/, firmware/, docs/ Clarity and sparse checkout possible
Version the BOM as CSV Text = clear diffs on components
Tags = PCB versions Clear link between code and physical object
Gerbers in releases, not in history Avoid inflating the repository
Detailed hardware changelog Know what changed between two physical revisions
Rigorous .gitignore Don't version caches, backups, local preferences
Export proprietary 3D models as STEP Open standard format, long-term archiving

Practical exercise - Initialize a hardware repo

Create a Git repository for a fictional electronics project (a temperature sensor) with the right structure and best practices:

  1. Create the recommended folder structure
  2. Add a KiCad-adapted .gitignore
  3. Create a README and a CHANGELOG
  4. Simulate a first "schematic" commit and a second "PCB routing" commit
  5. Mark the version with a hardware tag

Step 1 - Create the structure

# Create the repository
mkdir temperature-sensor
cd temperature-sensor
git init -b main

# Create the hardware directory tree
mkdir -p hw/electronics/libs/symbols
mkdir -p hw/electronics/libs/footprints
mkdir -p hw/mechanical/exports
mkdir -p firmware/src
mkdir -p firmware/include
mkdir -p docs/images
mkdir -p production/gerbers
mkdir -p production/bom
mkdir -p production/pick-and-place

Step 2 - The .gitignore

# Create the .gitignore
cat > .gitignore << 'EOF'
# KiCad
*.kicad_sch-bak
*.kicad_pcb-bak
*-backups/
_autosave-*
*.kicad_prl
fp-info-cache
*.net
*.lck
*.wrl
*~
\#*\#

# Firmware (PlatformIO)
.pio/
.vscode/

# OS
.DS_Store
Thumbs.db
EOF

git add .gitignore
git commit -m "Add KiCad + firmware .gitignore"

Step 3 - README and CHANGELOG

# Create the README
cat > README.md << 'EOF'
# Temperature Sensor

Electronic board for DS18B20 temperature sensor.

## Structure
- hw/ - Electronic and mechanical design
- firmware/ - Embedded code (PlatformIO)
- docs/ - Documentation
- production/ - Manufacturing files
EOF

# Create the CHANGELOG
cat > CHANGELOG.md << 'EOF'
# Changelog

## [v1.0-rev-a] - 2026-03-09
### Initial version
- Schematic: MCU + DS18B20 sensor + USB
- PCB: 40x30mm board, 2 layers
EOF

git add README.md CHANGELOG.md
git commit -m "Add README and CHANGELOG"

Step 4 - Simulate source files

# Simulate a schematic file (a real one would be generated by KiCad)
cat > hw/electronics/sensor.kicad_sch << 'EOF'
(kicad_sch (version 20230121) (generator eeschema)
  (uuid "example-uuid-schema")
  (paper "A4")
  (title_block (title "Temperature Sensor") (rev "A"))
)
EOF

git add hw/
git commit -m "Add electronic schematic - DS18B20 sensor"

# Simulate a PCB file
cat > hw/electronics/sensor.kicad_pcb << 'EOF'
(kicad_pcb (version 20221018) (generator pcbnew)
  (general (thickness 1.6))
  (paper "A4")
  (layers (0 "F.Cu" signal) (31 "B.Cu" signal))
)
EOF

git add hw/electronics/sensor.kicad_pcb
git commit -m "PCB routing - 2-layer 40x30mm board"

Step 5 - Mark the version

# Create the hardware release tag
git tag -a v1.0-rev-a -m "PCB v1.0 Rev A - First temperature sensor prototype"

# Verify
git log --oneline --decorate
# abc1234 (HEAD -> main, tag: v1.0-rev-a) PCB routing - 2-layer 40x30mm board
# def5678 Add electronic schematic - DS18B20 sensor
# ghi9012 Add README and CHANGELOG
# jkl3456 Add KiCad + firmware .gitignore

Concept summary

Concept Description
Binary files Non-text files that Git cannot diff line by line
KiCad Free electronic design software, text format files
kidiff Visual diff tool for KiCad schematics and PCBs
Gerbers Manufacturing files sent to the PCB manufacturer
BOM Bill of Materials - component list (version as CSV)
FreeCAD (.FCStd) ZIP archive containing XML - binary for Git
OpenSCAD (.scad) Parametric CAD in code - text format, perfect diffs
STEP Open standard format for 3D models, ideal for archiving
Hardware tags vX.Y-rev-Z convention for PCB versions
Hardware changelog Documentation of changes between physical revisions

The Master Artificer examines your repository with attention. He nods, satisfied. "You understand now that versioning doesn't stop at code. Every circuit, every mechanical part, every manufacturing file deserves to be protected by history."

"Artificers who work without Git waste hours searching for 'the right version' of a schematic. They send the wrong Gerbers to the manufacturer. They no longer remember why that resistor value changed. You will know. Your history is your memory."

He hands you a brass key engraved with a miniature printed circuit. "Keep it. It's the symbol of the Versioning Artificers. You've earned your place in the workshop."