Programmatically create GitHub commits with backdated timestamps and generate custom contribution patterns
Quick Start • Installation • CLI Commands • Patterns • Configuration • Development
GitHub Activity Manipulator is a Python CLI tool that enables you to programmatically create commits with custom timestamps and generate activity patterns on GitHub contribution graphs. Perfect for portfolio demonstrations, educational projects, automated testing, and development automation.
- Portfolio projects - Build realistic activity histories for portfolio repositories
- Testing & education - Learn Git internals and experiment with contribution mechanics
- Project setup - Jumpstart new projects with consistent activity patterns
- Automated testing - Create test repositories with specific commit distributions
Warning
Use responsibly and ensure your usage complies with GitHub's Terms of Service. This tool is intended for legitimate purposes only—never misrepresent contribution history on production repositories.
- Multiple Pattern Types — Linear, random, wave, and custom distribution patterns
- Backdated Commits — Create commits with specific timestamps across any date range
- Preview Mode — Visualize patterns before generating commits
- Configuration Files — Save and reuse patterns via YAML or JSON
- Flexible Customization — Custom commit messages and file content templates
- Rich CLI — Beautiful, user-friendly command-line interface
- Property-Based Testing — Comprehensive test suite with Hypothesis
- Python 3.8 or higher
- Git installed and configured
# Clone the repository
git clone https://github.com/yourusername/github-activity-manipulator.git
cd github-activity-manipulator
# Install (development mode)
pip install -e .
# Or with dev dependencies (for testing/development)
pip install -e ".[dev]"github-activity init \
--path ./my-activity-repo \
--author-name "Your Name" \
--author-email "your.email@example.com"See what commits will be generated without creating any:
github-activity preview \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern wave \
--max-commits 5Output visualization:
Pattern: wave
Date Range: 2024-01-01 to 2024-01-31
Total Commits: 78
2024-01-01: ███████████████ (3)
2024-01-02: ███████████████ (3)
2024-01-03: ████████ (1)
...
Create actual commits based on your pattern:
github-activity generate \
--path ./my-activity-repo \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern wave \
--min-commits 1 \
--max-commits 5# Connect to a remote repository first
cd ./my-activity-repo
git remote add origin https://github.com/username/my-activity-repo.git
# Push commits
github-activity push --path ./my-activity-repoNote
Use --force with caution: force pushing rewrites history and can cause data loss.
Evenly distributed commits across the entire date range.
github-activity preview --start-date 2024-01-01 --end-date 2024-01-31 --pattern linearRandomly distributed commits within the min/max bounds.
github-activity preview \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern random \
--min-commits 1 \
--max-commits 5Sinusoidal pattern creating a natural-looking contribution graph.
github-activity preview \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern wave \
--min-commits 1 \
--max-commits 10Define exact commits for specific dates using configuration files.
pattern:
pattern_type: custom
start_date: '2024-01-01T00:00:00'
end_date: '2024-01-05T00:00:00'
custom_distribution:
'2024-01-01': 5
'2024-01-02': 3
'2024-01-03': 7
'2024-01-04': 2
'2024-01-05': 4Initialize a new repository:
github-activity init [OPTIONS]
Options:
--path TEXT Repository path (default: ./github-activity-repo)
--remote TEXT Remote repository URL
--author-name TEXT Git author name
--author-email TEXT Git author email
--branch TEXT Branch name (default: main)
--help Show this message and exitCreate commits based on a pattern:
github-activity generate [OPTIONS]
Options:
--config TEXT Path to configuration file
--path TEXT Repository path (required if no config)
--start-date TEXT Start date (YYYY-MM-DD)
--end-date TEXT End date (YYYY-MM-DD)
--pattern TEXT Pattern type: linear, random, wave, custom (default: linear)
--min-commits INT Minimum commits per day (default: 1)
--max-commits INT Maximum commits per day (default: 5)
--message-template TEXT Commit message template
--dry-run Preview without creating commits
--help Show this message and exitVisualize a pattern without creating commits:
github-activity preview [OPTIONS]Options same as generate, without --dry-run.
Push commits to remote repository:
github-activity push [OPTIONS]
Options:
--path TEXT Repository path (required)
--config TEXT Configuration file path
--remote TEXT Remote name (default: origin)
--branch TEXT Branch name
--force Force push (use with caution!)
--help Show this message and exitDisplay repository status and statistics:
github-activity status --path <path>Save complex patterns and settings in YAML or JSON configuration files for reuse.
Create a file (e.g., activity-config.yaml):
repository:
path: ./my-activity-repo
remote_url: https://github.com/username/activity-repo.git
author_name: Your Name
author_email: your.email@example.com
branch: main
pattern:
pattern_type: wave
start_date: '2024-01-01T00:00:00'
end_date: '2024-12-31T00:00:00'
min_commits_per_day: 1
max_commits_per_day: 5
commit_message_template: "Activity update on {date}"
file_template: |
Activity log entry
Date: {date}
Commit: {commit_number}
force_push: false# Preview from config
github-activity preview --config activity-config.yaml
# Generate from config
github-activity generate --config activity-config.yaml
# Push using config settings
github-activity push --config activity-config.yamlAvailable in commit_message_template and file_template:
{date}— Date in YYYY-MM-DD format{datetime}— Full ISO 8601 datetime{commit_number}— Commit number for that specific day
Always preview first and use --dry-run to be safe:
# 1. Initialize repo
github-activity init --path ./test-repo
# 2. Preview the pattern
github-activity preview \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern wave \
--max-commits 5
# 3. Test with dry-run
github-activity generate \
--path ./test-repo \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern wave \
--dry-run
# 4. Generate actual commits
github-activity generate \
--path ./test-repo \
--start-date 2024-01-01 \
--end-date 2024-01-31 \
--pattern wave
# 5. Review commits
cd test-repo && git log --oneline --graphCreate activity for an entire year:
github-activity generate \
--path ./year-activity \
--start-date 2023-01-01 \
--end-date 2023-12-31 \
--pattern wave \
--min-commits 1 \
--max-commits 5 \
--author-name "Your Name" \
--author-email "your.email@example.com"# Generate for multiple repos
for repo in repo1 repo2 repo3; do
github-activity generate \
--path ./$repo \
--start-date 2024-01-01 \
--end-date 2024-12-31 \
--pattern wave
donefrom github_activity_manipulator.generators import PatternGenerator
from github_activity_manipulator.models import PatternConfig
from datetime import datetime
config = PatternConfig(
pattern_type='wave',
start_date=datetime(2024, 1, 1),
end_date=datetime(2024, 12, 31),
total_commits=100,
repo_path='./my-repo'
)
generator = PatternGenerator(config)
distribution = generator.generate()
for date, count in distribution.items():
print(f"{date.date()}: {count} commits")# Install with development dependencies
pip install -e ".[dev]"# Run all tests with coverage
pytest
# Run unit tests only
pytest tests/unit/
# Run property-based tests
pytest tests/property/
# Run specific test
pytest tests/unit/test_pattern_generator.py::TestPatternGenerator::test_linear_pattern_generation
# Generate HTML coverage report
pytest --cov=github_activity_manipulator --cov-report=htmlgithub_activity_manipulator/
├── cli/ # Click-based CLI (main.py)
├── services/ # Core logic
│ ├── git_service.py
│ └── commit_orchestrator.py
├── generators/ # Pattern algorithms
│ └── pattern_generator.py
├── config/ # Configuration management
│ └── config_manager.py
├── utils/ # Utilities (date, text, logging)
├── models.py # Data models (dataclasses)
└── __init__.py
tests/
├── conftest.py # Shared fixtures
├── unit/ # Unit tests
└── property/ # Property-based tests (Hypothesis)
Important
Keep these best practices in mind when using this tool.
- Sensitive data — Never commit passwords, tokens, or private keys
- Environment variables — Store GitHub tokens in environment variables, never in code
- Force push — Force pushing rewrites history; use only if you understand the risks
- Token scopes — Use GitHub Personal Access Tokens with minimal required permissions
- Dedicated repos — Use this tool on dedicated test repositories, never on shared projects
- Pre-push review — Always review commits before pushing to remote
For HTTPS repositories:
- Create a Personal Access Token
- Use the token as your password when prompted
- Store it in an environment variable:
export GITHUB_TOKEN=<your-token>
For SSH:
# Use SSH URLs instead
github-activity init --path ./repo --remote git@github.com:username/repo.gitMake sure to initialize before generating:
github-activity init --path ./your-repoProvide author information:
github-activity init --author-name "Your Name" --author-email "your@email.com"Or configure globally:
git config --global user.name "Your Name"
git config --global user.email "your@email.com"Add the remote:
cd your-repo
git remote add origin https://github.com/username/repo.gitOr specify during initialization:
github-activity init --path ./repo --remote https://github.com/username/repo.gitEnsure your GitHub token has the repo scope and hasn't expired. For SSH, verify your key is added to ssh-agent:
ssh-keygen -t ed25519 -C "your@email.com"
ssh-add ~/.ssh/id_ed25519# Show general help
github-activity --help
# Show command-specific help
github-activity init --help
github-activity generate --helpFor detailed development documentation, see CLAUDE.md and AGENTS.md.
MIT License — see LICENSE for details.
Last updated: January 2025