This tutorial walks you through integrating git-perf into your GitHub project for automated performance tracking, regression detection, and reporting.
- A Git repository (local or on GitHub)
- Git version 2.43.0 or higher
- For GitHub Actions integration: A GitHub repository with Actions enabled
- Basic familiarity with Git and YAML (for GitHub Actions setup)
Check your Git version:
git --version
# Should output: git version 2.43.0 or higherIf you need to upgrade Git:
Ubuntu/Debian:
sudo add-apt-repository ppa:git-core/ppa
sudo apt update && sudo apt install gitmacOS:
brew upgrade gitWindows: Download the latest version from git-scm.com
Shell Installer (Recommended for Linux/macOS):
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/kaihowl/git-perf/releases/latest/download/git-perf-installer.sh | shFor other installation methods (pre-built binaries, cargo install, building from source), see the Installation section in the README.
Verify Installation:
git perf --versionNavigate to your project repository and add a measurement:
cd /path/to/your/project
# Add a measurement (e.g., build time in seconds)
# You'll typically get this value from your build or test process
git perf add 42.5 -m build_time
# Generate an HTML report (creates output.html by default)
git perf report
# Or specify a custom output location
git perf report -o my-report.htmlNote: The git perf report command generates an HTML file (default: output.html) and produces no terminal output. Open the HTML file in a browser to view your performance data with interactive charts.
Create a .gitperfconfig file in your repository root. See the Configuration section in the README for all available options.
Example configuration:
# Default settings for all measurements
[measurement]
min_relative_deviation = 5.0
dispersion_method = "mad"
unit = "ms" # Default unit for all measurements
# Measurement-specific settings
[measurement."build_time"]
min_relative_deviation = 10.0
dispersion_method = "mad"
unit = "seconds" # Override default unit for build_time
[measurement."binary_size"]
min_relative_deviation = 2.0
dispersion_method = "stddev"
unit = "bytes"
[measurement."test_duration"]
unit = "ms"Unit Configuration: Units are displayed in audit output, HTML reports, and CSV exports. They help make your performance data more readable and professional:
- Configure units for each measurement in
.gitperfconfig - Units are applied at display time (not stored with measurement data)
- Existing measurements automatically display with units once configured
- Measurements without units continue to work normally (backward compatible)
git add .gitperfconfig
git commit -m "chore: add git-perf configuration"Verify your local setup is working correctly:
# Check status of pending measurements
git perf status
# Check that measurements were recorded
git perf report -o -
# Generate and view a report
git perf report -o test-report.html
# Open test-report.html in your browser to verify the report displays correctlyExpected output from git perf status:
Pending measurements:
1 commit with measurements
1 unique measurement
Measurement names:
- build_time
(use "git perf reset" to discard pending measurements)
(use "git perf push" to publish measurements)
Create a workflow that uses the git-perf install action to measure your builds automatically.
Create .github/workflows/performance-tracking.yml:
name: Performance Tracking
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
measure-performance:
runs-on: ubuntu-latest
permissions:
contents: write # Required to push measurements
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Fetch all history for git-perf
# Install git-perf
- name: Install git-perf
uses: kaihowl/git-perf/.github/actions/install@latest
with:
release: latest
# Configure git identity (required for git-perf to commit measurements)
- name: Configure git identity
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
# Example: Measure build time using the measure command
- name: Build project and measure
run: |
git perf measure -m build_time -- cargo build --release
# Example: Measure binary size
- name: Measure binary size
run: |
binary_size=$(stat -c%s target/release/your-binary)
git perf add "$binary_size" -m binary_size
# Push measurements back to the repository
- name: Push measurements
run: git perf pushImportant Notes:
- The
fetch-depth: 0fetches full history; you can use a specific depth (e.g.,fetch-depth: 50) matching the-nflag used inreportorauditcommands - The
contents: writepermission is needed to push measurement data - Git identity configuration is required because git-perf stores measurements as git-notes (git commits)
- The
git perf measurecommand automatically times the execution of the supplied command - Push is unconditional to ensure measurements are always saved
After pushing your workflow, verify it runs successfully:
# Push the workflow file
git add .github/workflows/performance-tracking.yml
git commit -m "ci: add performance tracking workflow"
git push
# Check the workflow status
gh run list --workflow=performance-tracking.yml --limit 5
# View the most recent run
gh run view --log
# Verify measurements were pushed
git perf pull
git perf status # Should show "No pending measurements" after pull
git perf reportExpected Result: The workflow should complete successfully and push measurements to git-notes. The git perf pull command retrieves the measurements, git perf status should show no pending measurements (since they were already pushed by CI), and git perf report displays them.
The report generation happens as a step in your workflow using the report action. Update your workflow to include report generation:
# Update your .github/workflows/performance-tracking.yml
jobs:
measure-and-report:
runs-on: ubuntu-latest
permissions:
contents: write
pages: write
pull-requests: write # Required for PR comments
# Concurrency control prevents race conditions when multiple workflows
# try to update the gh-pages branch simultaneously. Without this, you may
# encounter "failed to push" errors or lost reports when multiple PRs or
# commits trigger the workflow at the same time.
concurrency:
group: gh-pages-${{ github.ref }} # One deployment per branch at a time
cancel-in-progress: false # Queue jobs instead of canceling
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install git-perf
uses: kaihowl/git-perf/.github/actions/install@latest
with:
release: latest
- name: Configure git identity
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
- name: Build project and measure
run: git perf measure -m build_time -- cargo build --release
- name: Measure binary size
run: |
binary_size=$(stat -c%s target/release/your-binary)
git perf add "$binary_size" -m binary_size
- name: Push measurements
run: git perf push
- name: Generate performance report
uses: kaihowl/git-perf/.github/actions/report@latest
with:
depth: 40
github-token: ${{ secrets.GITHUB_TOKEN }}Important: The first workflow run will intentionally fail with clear setup instructions. This is expected behavior.
First Run (Expected to Fail):
When you push the workflow for the first time:
- ✅ The workflow will successfully generate your performance report
- ✅ The workflow will create and push the
gh-pagesbranch - ❌ The workflow will fail at "Get Pages URL" with detailed instructions
After First Run, Configure GitHub Pages:
-
Go to Settings → Pages in your repository
- Direct link:
https://github.com/<owner>/<repo>/settings/pages
- Direct link:
-
Under "Build and deployment":
- Source: Select "Deploy from a branch"
- Branch: Select
gh-pagesand/ (root) - Click "Save"
-
Wait a few minutes for GitHub Pages to initialize
-
Re-run the workflow:
- Go to the Actions tab
- Find the failed workflow run
- Click "Re-run all jobs"
-
✅ The workflow will now succeed and your report will be available at:
https://<username>.github.io/<repository>/
Note: The gh-pages branch is automatically created by the report action on the first run. If you don't see it in the dropdown immediately, refresh the Settings page.
Verify GitHub Pages is working correctly:
# Check that gh-pages branch exists
git ls-remote origin gh-pages
# View the GitHub Pages deployment status
gh api repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/pages
# Visit your report URL (replace with your details)
# https://<username>.github.io/<repository>/<branch-name>.htmlExpected Result: GitHub Pages should show as "built and deployed" and your report should be accessible via the URL.
To prevent your repository from growing indefinitely with measurement data, use the cleanup action:
Create .github/workflows/cleanup-measurements.yml:
name: Cleanup Old Measurements
on:
schedule:
# Run weekly on Sundays at 2 AM UTC
- cron: '0 2 * * 0'
workflow_dispatch: # Allow manual triggering
jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Cleanup measurements and reports
uses: kaihowl/git-perf/.github/actions/cleanup@latest
with:
retention-days: 90
cleanup-reports: true
reports-retention-days: 30Configuration Options:
retention-days: How long to keep measurement data (default: 90 days)cleanup-reports: Whether to also cleanup old reports (default: true)reports-retention-days: How long to keep reports (default: 30 days)dry-run: Preview what would be deleted without actually deleting (default: false)
Configure audit settings in your .gitperfconfig. See the Audit System section in the README for complete details.
# Default settings
[measurement]
min_relative_deviation = 5.0
dispersion_method = "mad"
# Measurement-specific settings
[measurement."build_time"]
min_relative_deviation = 10.0
dispersion_method = "mad"
[measurement."binary_size"]
min_relative_deviation = 2.0
dispersion_method = "stddev"Update your workflow to run audits and fail on regressions. You can also integrate audit into the report action:
# Option 1: Add audit as a separate step
- name: Run audit for regressions
run: |
# Pull latest measurements to ensure we have historical data
git perf pull || true
# Run audit for specific measurements
git perf audit -m build_time
git perf audit -m binary_size
# Option 2: Integrate audit with report generation
- name: Generate report with audit
uses: kaihowl/git-perf/.github/actions/report@latest
with:
depth: 40
audit-args: '-m build_time -m binary_size -d 4.0 --min-measurements 5'
github-token: ${{ secrets.GITHUB_TOKEN }}The report action will automatically comment on pull requests with both the report URL and audit results.
When your audit detects a performance change that is intentional and expected (e.g., after a refactoring or optimization), you can use epochs to accept this change without future audits treating it as a regression.
Epochs are boundaries in measurement history that mark intentional performance changes. Each measurement includes an epoch identifier (a commit SHA), and the audit system only compares measurements within the same epoch. This allows you to:
- Accept expected performance changes (like major refactorings)
- Reset the performance baseline for specific measurements
- Prevent false positives when you deliberately change performance characteristics
Use the git perf bump-epoch command when:
- Intentional optimization: You've improved performance and want to accept the new baseline
- Major refactoring: Architecture changes that legitimately alter performance
- Algorithm changes: Switching to a different approach with different performance profile
- Dependency updates: External library changes that affect measurements
# Scenario: You refactored code and performance legitimately changed
# Run audit and see it fails
git perf audit -m build_time
# Output: ❌ 'build_time' - HEAD measurement 85.3 is outside acceptable range
# Accept this as the new baseline
git perf bump-epoch -m build_time
# This updates .gitperfconfig with the current commit SHA:
# [measurement."build_time"]
# epoch = "abc12345" # First 8 chars of HEAD commit
# Commit the configuration change
git add .gitperfconfig
git commit -m "perf: accept build_time increase from refactoring"
# Future measurements will be compared against this new baseline
git perf add 85.3 -m build_time
git perf audit -m build_time
# Output: ✅ 'build_time' - Within acceptable rangeEpochs are stored in .gitperfconfig (not in git-notes) for important reasons:
- Team coordination: Changes require pull request review, preventing silent baseline changes
- Merge conflicts: Multiple developers bumping the same epoch triggers conflicts, forcing discussion
- History tracking: Git history shows who approved performance changes and why
- Visibility: Changes appear in PRs, not hidden in git-notes
Bump multiple epochs at once:
git perf bump-epoch -m metric1 -m metric2 -m metric3View current epochs:
# Check .gitperfconfig for epoch values
cat .gitperfconfig | grep epochIn CI workflows: When an audit fails due to a legitimate change:
- Review the performance change locally
- Determine if it's acceptable
- Bump the epoch and commit the config
- Include rationale in commit message
- Push and let CI verify the new baseline
For complete details on epochs, including how they affect statistical analysis, see the Epochs section in the README.
git-perf supports multiple dispersion methods for regression detection. See the Audit System section in the README for details on choosing between stddev and mad.
Configure per measurement:
[measurement."build_time"]
dispersion_method = "mad" # Robust to outliers
[measurement."memory_usage"]
dispersion_method = "stddev" # More sensitiveOr override via CLI:
git perf audit -m build_time --dispersion-method mad
git perf audit -m build_time -D stddev # Short formTrack measurements across different environments using key-value pairs:
# In your workflow
- name: Measure performance (development)
run: |
git perf measure -m build_time -k env=dev -- cargo build
- name: Measure performance (production)
run: |
git perf measure -m build_time -k env=prod -- cargo build --releaseFilter in reports:
git perf report -m build_time -k env=dev
git perf report -m build_time -k env=prodAudit specific environments:
# Audit development environment only
git perf audit -m build_time -s env=dev
# Audit production environment only
git perf audit -m build_time -s env=prodSymptom: Workflow fails with "Author identity unknown" or "empty ident name not allowed"
Error Message:
Error: Permanent failure while adding note line to head
Caused by:
Git failed to execute.
stderr:
Author identity unknown
*** Please tell me who you are.
Solution: Add git identity configuration before any git-perf measurement commands:
- name: Configure git identity
run: |
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"Why: Git-perf stores measurements as git-notes, which require git commits. All commits need a configured user identity.
Symptom: git perf push fails with authentication errors
Solutions:
- Ensure
contents: writepermission is set in the workflow - Use
fetch-depth: 0when checking out the repository - Verify the branch is not protected (or add exception for Actions)
Symptom: Workflow fails at "Get Pages URL" step with "Not Found (HTTP 404)"
Error Message:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📄 GitHub Pages Setup Required
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
This is Expected on First Run!
The workflow intentionally fails with detailed instructions on how to configure GitHub Pages.
Solution:
- The
gh-pagesbranch has already been created by the workflow - Go to Settings → Pages in your repository
- Select
gh-pagesbranch and/ (root)folder - Click "Save"
- Re-run the workflow
See the Enable GitHub Pages section for complete instructions.
Symptom: Audit reports regressions for normal variations
Solutions:
- Increase thresholds in
.gitperfconfig:[measurement."build_time"] min_relative_deviation = 10.0 # Percentage (0..100), unset by default
- Use MAD instead of stddev for more robust detection:
[measurement."build_time"] dispersion_method = "mad"
- Increase sigma threshold via CLI:
git perf audit -m build_time -d 6.0 # Default is 4.0
Symptom: Important historical data is being removed
Solutions:
- Increase retention days in cleanup workflow:
retention-days: 180 # Keep 6 months
- Use dry-run mode first:
dry-run: true
- Disable report cleanup if needed:
cleanup-reports: false
Symptom: Workflow completes but measurements don't show up in reports
Debug Steps:
-
Check if notes were actually pushed:
git ls-remote origin refs/notes/perf-v3
-
Fetch notes manually:
git fetch origin refs/notes/perf-v3:refs/notes/perf-v3
-
Verify measurements exist locally:
git perf report -o -
-
Check workflow logs for push errors:
gh run view --log | grep -A 10 "Push measurements"
Common Causes:
- Git identity not configured (measurements can't be committed)
- Insufficient permissions (
contents: writemissing) - Protected branch rules blocking git-notes push
Symptom: "Resource not accessible by integration" or similar permission errors
Error Message:
Error: Resource not accessible by integration
Solutions:
-
Verify workflow permissions in YAML:
permissions: contents: write pages: write pull-requests: write
-
Check repository Settings → Actions → General → Workflow permissions:
- Ensure "Read and write permissions" is enabled
- Or grant specific permissions in the workflow file
-
For organization repositories, check organization-level permissions
Do:
- Measure discrete, meaningful metrics (build time, test duration, binary size)
- Configure units in
.gitperfconfigfor clarity - Use consistent units across related measurements
- Measure on the same hardware/environment for comparability
Don't:
- Measure every small operation (too much noise)
- Change units for the same measurement over time (creates confusion)
- Mix units (record bytes but configure as "MB", or vice versa)
- Measure on different runner types without noting the environment
Unit Configuration Example:
[measurement."build_time"]
unit = "seconds"
[measurement."binary_size"]
unit = "bytes"
[measurement."memory_peak"]
unit = "MB"Units will automatically appear in:
- Audit output:
✓ build_time: 42.5 seconds (within acceptable range) - HTML reports: Legend entries like "build_time (seconds)"
- CSV exports: Dedicated unit column with configured units
Do:
- Start with lenient thresholds and tighten over time
- Use MAD for more stable metrics
- Require multiple samples before auditing (min_samples ≥ 5)
Don't:
- Set thresholds too tight initially (causes false positives)
- Audit metrics with high natural variation
- Fail CI on audit failures without investigation
Do:
- Keep at least 90 days of measurement data
- Archive old reports separately if needed
- Schedule cleanup during low-traffic times
Don't:
- Delete all historical data (defeats trending analysis)
- Run cleanup too frequently (weekly is usually enough)
- Skip dry-runs before production cleanup
Do:
- Push measurements before generating reports
- Use the report action for automated reporting and PR comments
- Include concurrency control to prevent gh-pages conflicts
- Use workflow dispatch for manual triggers
Don't:
- Skip the push step (measurements won't be saved)
- Skip permissions declarations
- Forget concurrency control when publishing to gh-pages
Before deploying to your main branch, test the integration on a feature branch:
-
Create a test branch:
git checkout -b test-git-perf-integration
-
Add workflow files and configuration:
git add .github/workflows/ .gitperfconfig git commit -m "test: add git-perf integration" git push -u origin test-git-perf-integration -
Verify the workflow runs successfully:
# Watch the workflow run gh run watch # Check for errors gh run list --branch test-git-perf-integration --limit 5
-
Test with manual workflow dispatch first before enabling automatic triggers:
on: workflow_dispatch: # Only manual triggering initially # push: # Enable after testing # branches: [main]
-
Make small, incremental changes:
- Start with just measurement collection (Step 3)
- Then add reporting (Step 4)
- Then add cleanup (Step 5)
- Finally add audit (Step 6)
-
Once verified, merge to main:
gh pr create --title "feat: add performance tracking with git-perf" # After review and approval gh pr merge --squash
Benefits of Testing First:
- Catch configuration errors before they affect main branch
- Experiment with settings without polluting production data
- Understand the full workflow before team-wide rollout
- Avoid breaking CI/CD for the entire team
Here's a complete, production-ready workflow combining all best practices:
name: Performance CI
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 0' # Weekly reports
jobs:
measure-and-report:
runs-on: ubuntu-latest
permissions:
contents: write
pages: write
pull-requests: write
concurrency:
group: gh-pages-${{ github.ref }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: kaihowl/git-perf/.github/actions/install@latest
- name: Build and measure
run: |
git perf measure -m build_time -- cargo build --release
size=$(stat -c%s target/release/my-app)
git perf add "$size" -m binary_size
- name: Test and measure
run: git perf measure -m test_duration -- cargo test --release
- name: Push measurements
run: git perf push
- name: Generate report with audit
uses: kaihowl/git-perf/.github/actions/report@latest
with:
depth: 40
audit-args: '-m build_time -m binary_size -m test_duration -d 4.0 --min-measurements 5'
github-token: ${{ secrets.GITHUB_TOKEN }}Here's what a complete, successful git-perf integration looks like in action:
-
You make a code change that affects performance (e.g., optimize a function)
-
Create a pull request:
git checkout -b optimize-parser git commit -am "perf: optimize JSON parser" git push -u origin optimize-parser gh pr create --title "perf: optimize JSON parser"
-
GitHub Actions automatically runs:
- ✅ Checks out code with full history (
fetch-depth: 0) - ✅ Installs git-perf
- ✅ Configures git identity
- ✅ Builds project and measures
build_time - ✅ Runs tests and measures
test_duration - ✅ Measures
binary_size - ✅ Pushes measurements to
refs/notes/perf-v3 - ✅ Generates interactive HTML report
- ✅ Publishes report to GitHub Pages
- ✅ Runs audit to check for regressions
- ✅ Comments on your PR with results
- ✅ Checks out code with full history (
-
You receive a PR comment with performance analysis:
## Performance Report ⏱ [Performance Results](https://username.github.io/repo/abc123def.html) ## Audit Results ✅ 'build_time' z-score (stddev): ↓ 2.62 Head: μ: 38.2s σ: 0ns MAD: 0ns n: 1 Tail: μ: 42.1s σ: 1.8s MAD: 1.2s n: 15 [-9.26% – +0.50%] ▃▅▄▆▅▄▅▃▅▂▁ ✅ 'test_duration' z-score (stddev): ↓ 1.62 Head: μ: 1.2s σ: 0ns MAD: 0ns n: 1 Tail: μ: 1.5s σ: 0.1s MAD: 0.08s n: 15 [-20.00% – +5.00%] ▃▅▄▆▅▄▅▃▅▄▁ ❌ 'binary_size' z-score (stddev): ↑ 5.23 Head: μ: 4.8MB σ: 0B MAD: 0B n: 1 Tail: μ: 4.5MB σ: 8.2kB MAD: 4.1kB n: 15 [+6.91% – +6.91%] ▃▅▄▆▅▄▅▃▅ _Created by [git-perf](https://github.com/kaihowl/git-perf/)_
-
You analyze the results:
- ✅ Build time improved by 9.3% - Great!
- ✅ Test duration unchanged - Expected
- ❌ Binary size increased by 6.9% - Needs investigation
-
You click the report link to see the interactive dashboard:
- View historical trends with Plotly charts
- Filter by branch, measurement, or time range
- See sparklines showing performance over time
- Export data as CSV for further analysis
-
You investigate the binary size regression:
# Check what changed git diff main...optimize-parser -- Cargo.lock # Turns out the optimization added a new dependency # If the performance gain is worth the size increase, accept the regression # by bumping the epoch for this measurement: git perf bump-epoch -m binary_size # This resets the baseline, and future measurements will be compared # against this new baseline instead
-
Team reviews and approves the PR, understanding the performance trade-offs
-
PR is merged to main:
- Measurements become part of the main branch history
- Future PRs will be compared against this new baseline
- Performance dashboard updates with main branch data
After using git-perf for a while, you'll have:
- Historical Performance Data: Months of measurements showing trends
- Automated Regression Detection: Catch performance regressions in code review
- Performance Dashboard: Share
https://yourname.github.io/repo/with your team - Data-Driven Decisions: "Should we take this dependency? Let's check the performance impact"
- Performance Culture: Team awareness of performance implications in every PR
When you open the HTML report, you see:
-
Interactive Charts: Plotly graphs showing performance over time
- Line charts for trending metrics (build time, test duration)
- Bar charts comparing commits
- Hover for detailed measurement values
-
Filtering Options:
- By measurement name
- By branch
- By date range
- By commit
-
Statistical Summaries:
- Mean, median, standard deviation
- Min/max values
- Outlier detection
- Change point detection (when performance characteristics shifted)
-
Export Options:
- Export as CSV
- Aggregate by mean, median, min, or max
- Include all measurements or filter by criteria
This complete workflow ensures you never accidentally ship performance regressions and can confidently make performance improvements backed by data.
- Customize
.gitperfconfigfor your project's specific metrics - Set up GitHub Pages to view your performance dashboard
- Configure audit thresholds based on your team's tolerance for variation
- Schedule regular cleanup to maintain repository size
- Share your performance dashboard URL with your team
Core Documentation:
- Documentation Index - Complete guide to all available documentation
- Main README - Full feature documentation and quick start
- Configuration Guide - Detailed
.gitperfconfigoptions with all available settings - Audit System - Statistical methods and regression detection explained
- Epochs - Complete epoch documentation
Related Guides:
- Importing Measurements - Track test and benchmark performance automatically
- CLI Reference - Complete command documentation
- FAQ - Common questions and troubleshooting
GitHub Actions:
- Install Action - Install git-perf in workflows
- Report Action - Generate and publish reports
- Cleanup Action - Manage measurement retention
Examples:
- Live Example Report - See git-perf in action on this repository
- Issues: GitHub Issues - Report bugs or request features
- Discussions: GitHub Discussions - Ask questions and share ideas
- Documentation: docs/ directory - All guides and references