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.
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 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 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:
- Create the recommended folder structure
- Add a KiCad-adapted .gitignore
- Create a README and a CHANGELOG
- Simulate a first "schematic" commit and a second "PCB routing" commit
- 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."