A powerful, fast Node.js CLI and library to analyze Git repository contributions with detailed statistics, charts, and multiple output formats.
📦 Modular & Tree-Shakeable
This package uses subpath exports for optimal bundle sizes. Import only what you need:
git-contributor-stats/stats- Core statisticsgit-contributor-stats/charts- Chart generationgit-contributor-stats/reports- Report generationgit-contributor-stats/output- Console outputgit-contributor-stats/workflow- GitHub Actions workflowSee Technical Docs for architecture details and Quick Start for usage examples.
Analyze your Git repository to gain insights into contributor activity, code ownership, commit patterns, and project health. Get comprehensive statistics including commits, lines added/deleted, file changes, temporal patterns, bus factor analysis, and beautiful visualizations.
Requirements: Node.js 18+ (ESM-only)
- Features
- Installation
- Quick Start
- CLI Usage
- Programmatic API
- Identity Management
- Configuration
- Examples
- Output Reference
- Performance Tips
- Documentation
- Contributing
- License
- Contributor Metrics: Commits, lines added/deleted, net contributions, file changes
- Top Contributors: Ranked by commits, additions, deletions, net changes, or total changes
- File-Level Insights: Top files per contributor, ownership distribution
- Temporal Analysis: Monthly and weekly commit frequency breakdown
- Activity Heatmap: Commit patterns by weekday and hour (7×24 grid)
- Bus Factor: Identify single-owner files and knowledge concentration risks
- Total LOC: Count total lines across tracked files (optional for performance)
- Branch/Range: Analyze specific branches or commit ranges
- Time-Based: Filter by date (absolute or relative:
90.days,2.weeks, etc.) - Author Patterns: Focus on specific contributors
- Path Filtering: Analyze specific directories or files
- Identity Merging: Consolidate multiple emails/names per contributor
- Multiple Formats: Table, JSON, CSV, Markdown, HTML
- Charts: SVG/PNG bar charts (top commits, net contributions) and heatmaps
- Customizable Reports: Control which sections appear in outputs
- Batch Export: Write all reports to a single directory
- Dual Interface: CLI tool and programmatic Node.js API
- TypeScript Support: Full type definitions included
- GitHub Actions: Built-in workflow generator
- Performance Options: Skip expensive operations when needed
npm install -g git-contributor-statsnpm install git-contributor-stats💡 New to this tool? See Quick Start Guide for detailed use cases and examples
# Analyze current repository
git-contributor-stats
# Top 10 contributors
git-contributor-stats --top 10
# Last 90 days with JSON output
git-contributor-stats --since 90.days --json
# Generate all reports and charts (most popular!)
git-contributor-stats --out-dir reports --md --html --charts
# Specific folder analysis
git-contributor-stats src/ --top 20// Import only what you need using subpath exports
import { getContributorStats } from 'git-contributor-stats/stats';
const stats = await getContributorStats({
repo: '.',
since: '90.days',
countLines: false
});
console.log(`Total commits: ${stats.totalCommits}`);
console.log(`Top contributor: ${stats.topContributors[0].name}`);git-contributor-stats [options] [paths...]Arguments:
[paths...]- Optional pathspec(s) to limit analysis to certain files/directories
# Show help
git-contributor-stats --help
# Show version
git-contributor-stats --version
# Analyze current repository (default: table output)
git-contributor-stats
# Analyze specific repository
git-contributor-stats --repo /path/to/repo
# Analyze specific paths only
git-contributor-stats src/ lib/| Option | Description | Example |
|---|---|---|
-r, --repo <path> |
Path to Git repository | --repo /path/to/repo |
-b, --branch <name> |
Branch or commit range | --branch main or --branch main..feature |
| Option | Description | Example |
|---|---|---|
--since <when> |
Only commits after date | --since 2024-01-01 or --since 90.days |
--until <when> |
Only commits before date | --until 2024-12-31 or --until 1.week |
Supported relative time formats:
- Days:
30.days,1.day - Weeks:
2.weeks,1.week - Months:
3.months,1.month - Years:
1.year,2.years
Example:
# Last quarter
git-contributor-stats --since 90.days
# Specific date range
git-contributor-stats --since 2024-01-01 --until 2024-06-30
# Last 2 weeks on main branch
git-contributor-stats -b main --since 2.weeks| Option | Description | Example |
|---|---|---|
-a, --author <pattern> |
Filter by author (string/regex) | --author "John" |
--include-merges |
Include merge commits | --include-merges |
Example:
# Contributions from specific author
git-contributor-stats --author "jane@example.com"
# Include merge commits (excluded by default)
git-contributor-stats --include-merges| Option | Description | Default | Values |
|---|---|---|---|
-g, --group-by <field> |
Group contributors by | email |
email, name |
-s, --sort-by <metric> |
Sort contributors by | changes |
changes, commits, additions, deletions |
-t, --top <n> |
Limit to top N contributors | All | Any number |
Example:
# Group by name instead of email
git-contributor-stats --group-by name
# Sort by commit count, show top 15
git-contributor-stats --sort-by commits --top 15
# Sort by net lines added
git-contributor-stats --sort-by additions --top 10| Option | Description | Output |
|---|---|---|
-f, --format <kind> |
Format for stdout | table, json, csv |
--json |
Comprehensive JSON analysis | Full analysis object |
Example:
# Table output (default)
git-contributor-stats
# JSON to stdout
git-contributor-stats --json
# CSV to stdout
git-contributor-stats --format csv
# Compact table with top 10
git-contributor-stats --format table --top 10| Option | Description |
|---|---|
--csv <path> |
Write CSV report to file |
--md <path> |
Write Markdown report to file |
--html <path> |
Write HTML dashboard to file |
--out-dir <path> |
Directory for all outputs (uses default names) |
Example:
# Individual file outputs
git-contributor-stats --csv stats.csv --md report.md --html dashboard.html
# Use output directory (creates contributors.csv, report.md, report.html)
git-contributor-stats --out-dir reports
# Combine with specific names
git-contributor-stats --out-dir reports --md reports/custom-report.md| Option | Description | Default |
|---|---|---|
--charts |
Generate charts | Disabled |
--charts-dir <path> |
Directory for charts | ./charts or --out-dir |
--chart-format <fmt> |
Chart format | svg |
Chart formats:
svg- Scalable Vector Graphics (recommended)png- Portable Network Graphicsboth- Generate both SVG and PNG
Generated charts:
top-commits.svg/png- Bar chart of top contributors by commit counttop-net.svg/png- Bar chart of top contributors by net linesheatmap.svg/png- Activity heatmap (weekday × hour)
Example:
# Generate SVG charts in default location
git-contributor-stats --charts
# Generate PNG charts
git-contributor-stats --charts --chart-format png
# Generate both formats in custom directory
git-contributor-stats --charts --charts-dir ./output/charts --chart-format both
# Charts with reports
git-contributor-stats --out-dir reports --md --html --chartsControl which metrics appear in the "Top Stats" section of reports and stdout:
| Option | Description | Default |
|---|---|---|
--top-stats <list> |
Comma-separated metrics to show | All metrics |
--no-top-stats |
Omit Top Stats section entirely | Enabled |
Available metrics:
commits- Most commitsadditions- Most lines addeddeletions- Most lines deletednet- Best net contribution (additions - deletions)changes- Most total changes (additions + deletions)
Example:
# Show only commits and net in Top Stats
git-contributor-stats --top-stats commits,net --md report.md
# Omit Top Stats entirely (faster, cleaner output)
git-contributor-stats --no-top-stats --html dashboard.html
# Default: all metrics shown
git-contributor-stats --md report.md| Option | Description |
|---|---|
--alias-file <path> |
Path to alias mapping JSON |
--similarity <0..1> |
Name merge similarity threshold (default: 0.85) |
Example:
# Use custom alias file
git-contributor-stats --alias-file config/aliases.json
# Adjust similarity threshold (higher = stricter matching)
git-contributor-stats --similarity 0.9
# Combine with reports
git-contributor-stats --alias-file aliases.json --out-dir reports --md --htmlSee Identity Management section for alias file format.
| Option | Description | Impact |
|---|---|---|
--no-count-lines |
Skip total LOC counting | Faster analysis |
Example:
# Skip LOC counting for large repos (significant speed improvement)
git-contributor-stats --no-count-lines --json
# Fast report generation
git-contributor-stats --out-dir reports --md --html --no-count-lines| Option | Description |
|---|---|
--generate-workflow |
Create GitHub Actions workflow file |
-v, --verbose |
Enable verbose logging (stderr) |
Example:
# Generate GitHub Actions workflow
git-contributor-stats --generate-workflow
# Debug mode
git-contributor-stats --verbose --json
# See what git commands are being run
git-contributor-stats -v --since 30.daysUse git-contributor-stats as a library in your Node.js/TypeScript projects.
import { getContributorStats } from 'git-contributor-stats/stats';
// Analyze current repository
const stats = await getContributorStats({
repo: '.',
since: '90.days',
countLines: false
});
console.log(`Total commits: ${stats.totalCommits}`);
console.log(`Contributors: ${Object.keys(stats.contributors).length}`);
console.log(`Top contributor: ${stats.topContributors[0].name}`);Returns a Promise that resolves to a comprehensive analysis object.
Options:
interface ContributorStatsOptions {
// Repository options
repo?: string; // Path to repository (default: '.')
branch?: string; // Branch or range (e.g., 'main..feature')
paths?: string | string[]; // Pathspec(s) to limit analysis
// Filtering options
since?: string; // Date or relative time (e.g., '90.days')
until?: string; // Date or relative time
author?: string; // Author pattern (string/regex)
includeMerges?: boolean; // Include merge commits (default: false)
// Grouping & sorting
groupBy?: 'email' | 'name'; // Group by field (default: 'email')
labelBy?: 'email' | 'name'; // Display label (default: 'name')
sortBy?: 'changes' | 'commits' | 'additions' | 'deletions';
top?: number; // Limit to top N contributors
// Identity management
similarity?: number; // Name merge threshold (default: 0.85)
aliasFile?: string; // Path to alias JSON file
aliasConfig?: AliasConfig; // Inline alias configuration
// Performance
countLines?: boolean; // Count total LOC (default: true)
verbose?: boolean; // Enable verbose logging (default: false)
}Return Value:
interface ContributorStatsResult {
meta: {
generatedAt: string; // ISO timestamp
repo: string; // Repository path
branch: string | null; // Branch name
since: string | null; // Since date/time
until: string | null; // Until date/time
};
totalCommits: number; // Total commit count
totalLines: number; // Total lines of code
contributors: Record<string, ContributorsMapEntry>;
topContributors: TopContributor[];
topStats: {
byCommits: TopContributor | null;
byAdditions: TopContributor | null;
byDeletions: TopContributor | null;
byNet: TopContributor | null;
byChanges: TopContributor | null;
};
commitFrequency: {
monthly: Record<string, number>;
weekly: Record<string, number>;
};
heatmap: number[][]; // 7x24 grid (weekday x hour)
busFactor: {
filesSingleOwner: Array<{
file: string;
owner: string;
changes: number;
}>;
};
basic: {
contributors: TopContributor[];
meta: ContributorsMeta;
groupBy: 'email' | 'name';
};
}import { getContributorStats } from 'git-contributor-stats/stats';
const stats = await getContributorStats({
repo: '/path/to/repo',
since: '2024-01-01',
until: '2024-06-30',
branch: 'main'
});
console.log('Q1-Q2 2024 Stats:');
console.log(`- Commits: ${stats.totalCommits}`);
console.log(`- Active contributors: ${stats.topContributors.length}`);import { getContributorStats } from 'git-contributor-stats/stats';
const stats = await getContributorStats({
repo: '.',
author: 'jane@example.com',
paths: ['src/', 'lib/'],
since: '30.days'
});
console.log(`Jane's contributions in src/ and lib/ (last 30 days):`);
console.log(`- Commits: ${stats.topContributors[0]?.commits || 0}`);
console.log(`- Files changed: ${Object.keys(stats.topContributors[0]?.files || {}).length}`);import { getContributorStats } from 'git-contributor-stats/stats';
import { generateReports } from 'git-contributor-stats/reports';
const stats = await getContributorStats({
repo: '.',
since: '90.days',
countLines: false
});
// Generate reports
await generateReports(stats, {
outDir: 'reports',
md: 'reports/quarterly-report.md',
html: 'reports/quarterly-report.html',
csv: 'reports/contributors.csv'
});
console.log('Reports generated in ./reports/');import { getContributorStats } from 'git-contributor-stats/stats';
import { generateCharts } from 'git-contributor-stats/charts';
const stats = await getContributorStats({
repo: '.',
since: '90.days'
});
// Generate SVG charts
await generateCharts(stats, {
charts: true,
chartsDir: 'output/charts',
chartFormat: 'svg'
});
// Or PNG charts
await generateCharts(stats, {
charts: true,
chartsDir: 'output/charts',
chartFormat: 'png'
});
// Or both
await generateCharts(stats, {
charts: true,
chartsDir: 'output/charts',
chartFormat: 'both'
});import { getContributorStats } from 'git-contributor-stats/stats';
const stats = await getContributorStats({
repo: '.',
aliasConfig: {
groups: [
['jane@example.com', 'jane.doe@example.com', 'Jane Doe'],
['john@example.com', 'john.smith@example.com']
],
canonical: {
'jane@example.com': {
name: 'Jane Doe',
email: 'jane@example.com'
},
'john@example.com': {
name: 'John Smith',
email: 'john@example.com'
}
}
},
similarity: 0.9
});import { getContributorStats } from 'git-contributor-stats/stats';
const stats = await getContributorStats({
repo: '.',
since: '1.year',
groupBy: 'name',
sortBy: 'commits'
});
// Custom analysis
const topContributor = stats.topContributors[0];
const busFactorFiles = stats.busFactor.filesSingleOwner.length;
const avgCommitsPerMonth = stats.totalCommits / 12;
console.log('Annual Report:');
console.log(`Top contributor: ${topContributor.name}`);
console.log(`- Commits: ${topContributor.commits}`);
console.log(`- Net lines: ${topContributor.net}`);
console.log(`\nRisk Analysis:`);
console.log(`- Single-owner files: ${busFactorFiles}`);
console.log(`- Avg commits/month: ${avgCommitsPerMonth.toFixed(1)}`);
// Access monthly breakdown
const monthlyActivity = stats.commitFrequency.monthly;
console.log('\nMonthly Activity:');
Object.entries(monthlyActivity)
.sort(([a], [b]) => a.localeCompare(b))
.forEach(([month, commits]) => {
console.log(`${month}: ${commits} commits`);
});Full TypeScript definitions are included:
import type {
ContributorStatsOptions,
ContributorStatsResult,
TopContributor,
TopStatsSummary,
BusFactorInfo
} from 'git-contributor-stats/stats';
import { getContributorStats } from 'git-contributor-stats/stats';
const options: ContributorStatsOptions = {
repo: '.',
since: '90.days',
countLines: false
};
const stats: ContributorStatsResult = await getContributorStats(options);
// Type-safe access
const topContributor: TopContributor = stats.topContributors[0];
const busFactor: BusFactorInfo = stats.busFactor;// From 'git-contributor-stats/stats'
export async function getContributorStats(
options?: ContributorStatsOptions
): Promise<ContributorStatsResult>
// From 'git-contributor-stats/reports'
export async function generateReports(
stats: ContributorStatsResult,
options?: ReportOptions
): Promise<void>
// From 'git-contributor-stats/charts'
export async function generateCharts(
stats: ContributorStatsResult,
options?: ChartOptions,
outDir?: string
): Promise<void>
// From 'git-contributor-stats/output'
export function handleStdoutOutput(
stats: ContributorStatsResult,
options?: OutputOptions
): void
// From 'git-contributor-stats/workflow'
export async function generateWorkflow(repo: string): Promise<void>Consolidate multiple emails and names for the same person using alias mapping.
The tool automatically looks for .git-contributor-stats-aliases.json in the repository root.
# Use default file (if exists)
git-contributor-stats
# Use custom file
git-contributor-stats --alias-file config/aliases.jsonMap aliases to canonical identities:
{
"jane@example.com": "jane.doe@example.com",
"Jane Doe": "jane.doe@example.com",
"john@example.com": "john.smith@example.com",
"J. Smith": "john.smith@example.com"
}More control with groups and canonical details:
{
"map": {
"jane@old-company.com": "jane.doe@example.com",
"jdoe@example.com": "jane.doe@example.com"
},
"groups": [
["john@example.com", "jsmith@example.com", "john.smith@example.com"],
["alice@example.com", "alice.dev@example.com"]
],
"canonical": {
"jane.doe@example.com": {
"name": "Jane Doe",
"email": "jane.doe@example.com"
},
"john.smith@example.com": {
"name": "John Smith",
"email": "john.smith@example.com"
}
}
}Shorthand for groups:
[
["jane@example.com", "jane.doe@example.com", "Jane Doe"],
["john@example.com", "john.smith@example.com", "John Smith"],
["alice@old.com", "alice@new.com", "alice@example.com"]
]Use regex patterns for flexible matching:
{
"map": {
"/^jane\\.doe@.+$/i": "jane.doe@example.com",
"/^(John Smith|J\\.? Smith)$/": "john.smith@example.com"
}
}Pattern format: Strings starting and ending with / are treated as regex patterns.
Control automatic name merging:
# Default threshold (0.85)
git-contributor-stats
# Stricter matching (higher threshold)
git-contributor-stats --similarity 0.95
# More permissive (lower threshold)
git-contributor-stats --similarity 0.75Range: 0.0 (merge everything) to 1.0 (exact match only)
Recommended: 0.80 - 0.90
Currently, the tool uses command-line options and alias files. Environment variables are not used.
| Setting | Default | Description |
|---|---|---|
| Repository | Current directory (.) |
Can override with --repo |
| Output format | table |
Can change with --format |
| Group by | email |
Can change with --group-by |
| Sort by | changes |
Can change with --sort-by |
| Similarity | 0.85 |
Can change with --similarity |
| Count lines | true |
Disable with --no-count-lines |
| Include merges | false |
Enable with --include-merges |
| Top stats | true |
Disable with --no-top-stats |
# Generate comprehensive quarterly report with all outputs
git-contributor-stats \
--since 90.days \
--out-dir reports/q4-2024 \
--md reports/q4-2024/report.md \
--html reports/q4-2024/dashboard.html \
--csv reports/q4-2024/contributors.csv \
--charts \
--chart-format both \
--no-count-lines# Analyze backend team contributions in src/backend/
git-contributor-stats \
src/backend/ \
--since 1.year \
--alias-file config/team-aliases.json \
--out-dir reports/backend-team \
--md --html \
--charts \
--top-stats commits,net# Compare two releases
git-contributor-stats \
--branch v1.0.0..v2.0.0 \
--json > v1-to-v2-stats.json
# Analyze specific release period
git-contributor-stats \
--since 2024-01-01 \
--until 2024-03-31 \
--out-dir reports/v2.0 \
--md --html --charts# Fast analysis for large repos
git-contributor-stats \
--no-count-lines \
--since 6.months \
--top 20 \
--format table# Only show commits and net contribution
git-contributor-stats \
--top-stats commits,net \
--out-dir reports \
--md reports/simple-report.md# Generate GitHub Actions workflow
git-contributor-stats --generate-workflow
# Run in CI (example)
git-contributor-stats \
--since 7.days \
--out-dir artifacts \
--md artifacts/weekly-report.md \
--charts \
--no-count-linesimport { getContributorStats } from 'git-contributor-stats/stats';
async function checkRepoHealth() {
const stats = await getContributorStats({
repo: '.',
since: '90.days'
});
const activeDevelopers = stats.topContributors.length;
const singleOwnerFiles = stats.busFactor.filesSingleOwner.length;
const totalFiles = Object.keys(
stats.topContributors.reduce((acc, c) => ({ ...acc, ...c.files }), {})
).length;
const busFactorRisk = singleOwnerFiles / totalFiles;
console.log('Repository Health Check:');
console.log(`Active developers (90d): ${activeDevelopers}`);
console.log(`Bus factor risk: ${(busFactorRisk * 100).toFixed(1)}%`);
if (busFactorRisk > 0.3) {
console.warn('⚠️ High bus factor risk - consider knowledge sharing');
}
if (activeDevelopers < 3) {
console.warn('⚠️ Low active developer count');
}
}
import { generateReports } from 'git-contributor-stats/reports';
import { generateCharts } from 'git-contributor-stats/charts';import { getContributorStats } from 'git-contributor-stats/stats';
import { generateReports } from 'git-contributor-stats/reports';
import { generateCharts } from 'git-contributor-stats/charts';
async function generateDashboard() {
const [weekly, monthly, quarterly] = await Promise.all([
getContributorStats({ since: '7.days' }),
getContributorStats({ since: '30.days' }),
getContributorStats({ since: '90.days' })
]);
await generateReports(weekly, {
outDir: 'dashboard/weekly',
md: 'dashboard/weekly/report.md',
html: 'dashboard/weekly/report.html'
});
await generateReports(monthly, {
outDir: 'dashboard/monthly',
md: 'dashboard/monthly/report.md',
html: 'dashboard/monthly/report.html'
});
await generateCharts(monthly, {
charts: true,
chartsDir: 'dashboard/monthly/charts'
});
await generateReports(quarterly, {
outDir: 'dashboard/quarterly',
md: 'dashboard/quarterly/report.md',
html: 'dashboard/quarterly/report.html'
});
await generateCharts(quarterly, {
charts: true,
chartsDir: 'dashboard/quarterly/charts',
chartFormat: 'both'
});
console.log('✓ Multi-period dashboard generated');
}
generateDashboard();Top stats:
- Most commits: John Smith <john@example.com> (234)
- Most additions: Jane Doe <jane@example.com> (15,432)
- Most deletions: Alice Dev <alice@example.com> (8,901)
- Best net contribution: Jane Doe <jane@example.com> (12,345)
- Most changes: Jane Doe <jane@example.com> (24,333)
┌─────┬────────────────┬─────────┬──────────┬──────────┬─────────┐
│Rank │ Contributor │ Commits │ Additions│ Deletions│ Changes │
├─────┼────────────────┼─────────┼──────────┼──────────┼─────────┤
│ 1 │ Jane Doe │ 156 │ 15,432 │ 3,087 │ 18,519 │
│ 2 │ John Smith │ 234 │ 8,901 │ 4,523 │ 13,424 │
│ 3 │ Alice Dev │ 145 │ 6,234 │ 8,901 │ 15,135 │
└─────┴────────────────┴─────────┴──────────┴──────────┴─────────┘
Comprehensive analysis object with all metrics (see API Reference).
Rank,Name,Email,Commits,Additions,Deletions,Net,Changes
1,Jane Doe,jane@example.com,156,15432,3087,12345,18519
2,John Smith,john@example.com,234,8901,4523,4378,13424
3,Alice Dev,alice@example.com,145,6234,8901,-2667,15135- Summary statistics
- Top stats (configurable)
- Top contributors table with file details
- Bus factor analysis
- Activity patterns (monthly breakdown, heatmap data)
Interactive HTML report with:
- Styled summary cards
- Sortable contributor table
- Bus factor risk indicators
- Embedded charts (if generated)
- Monthly/weekly activity visualization
# Skip LOC counting (significant speedup)
git-contributor-stats --no-count-lines
# Limit time range
git-contributor-stats --since 6.months
# Limit to specific paths
git-contributor-stats src/ --no-count-lines# Analyze only recent commits
git-contributor-stats --since 30.days --top 10
# Specific branch only
git-contributor-stats -b main --since 90.days
# Exclude merge commits (default, but explicit)
git-contributor-stats --no-include-merges# Fast CI run
git-contributor-stats \
--since 7.days \
--no-count-lines \
--format json > stats.jsonTypical performance on a medium-sized repository (10k commits):
| Operation | With --count-lines |
With --no-count-lines |
|---|---|---|
| Full analysis | ~15-20s | ~3-5s |
| Last 90 days | ~8-12s | ~2-3s |
| Last 30 days | ~5-8s | ~1-2s |
Performance varies based on repository size, file count, and system resources.
git clone https://github.com/vikkrantxx7/git-contributor-stats.git
cd git-contributor-stats
npm install# Build the project
npm run build
# Run tests
npm test
# Run linter
npm run biome
# Fix linting issues
npm run biome:fix
# Generate types
npm run build:types
# Development mode (uses source files)
node dist/cli.mjs --help
# Generate sample reports
npm run reportgit-contributor-stats/
├── src/
│ ├── features/
│ │ ├── stats.ts # Core statistics (tree-shakeable)
│ │ ├── charts.ts # Chart generation (tree-shakeable)
│ │ ├── reports.ts # Report generation (tree-shakeable)
│ │ ├── output.ts # Console output (tree-shakeable)
│ │ └── workflow.ts # GitHub Actions workflow (tree-shakeable)
│ ├── cli/
│ │ ├── entry.ts # CLI entry point
│ │ ├── index.ts # CLI logic
│ │ └── options.ts # Command-line options
│ ├── analytics/
│ │ ├── aggregator.ts # Data aggregation
│ │ ├── aliases.ts # Identity resolution
│ │ └── analyzer.ts # Core analysis logic
│ ├── charts/
│ │ ├── renderer.ts # Chart rendering (PNG)
│ │ └── svg.ts # SVG generation
│ ├── git/
│ │ ├── parser.ts # Git log parsing
│ │ └── utils.ts # Git operations
│ ├── reports/
│ │ ├── csv.ts # CSV generation
│ │ ├── html.ts # HTML generation
│ │ └── markdown.ts # Markdown generation
│ ├── utils/
│ │ ├── dates.ts # Date parsing
│ │ ├── files.ts # File operations
│ │ └── formatting.ts # Output formatting
│ └── api.ts # TypeScript types
├── dist/ # Built files
│ ├── features/ # Feature modules (separate entry points)
│ └── chunks/ # Shared code chunks
├── tests/ # Test files
├── package.json
├── tsconfig.json
├── vite.config.ts
└── vitest.config.ts
git-contributor-stats/charts- Chart generation with Chart.js (~260KB with charts)git-contributor-stats/reports- Report generation: CSV, Markdown, HTML (~200KB)git-contributor-stats/output- Console output formatting (table, JSON, CSV)git-contributor-stats/workflow- GitHub Actions workflow generator (~5KB)git-contributor-stats/cli- CLI entry point (combines all features)
Bundle Size Comparison:
| Import | Bundle Size | Reduction |
|---|---|---|
| Core stats only | ~80KB | 84% smaller |
| Stats + Reports | ~200KB | 60% smaller |
| Stats + Charts | ~260KB | 48% smaller |
| Full features (CLI) | ~400KB | 20% smaller |
Compared to the previous monolithic bundle of ~500KB
See Technical Documentation for architecture details, tree-shaking verification, and migration guide
# Run all tests
npm test
# Run specific test
npm test -- tests/e2e/api.test.ts
# Watch mode
npm test -- --watch
# Coverage
npm test -- --coverage📚 Complete documentation is available in the docs/ directory:
- Quick Start Guide - Get started in 30 seconds with common use cases
- Quick Reference - Command cheat sheet for quick lookup
- Contributing Guide - How to contribute to this project
- Commit Guidelines - Conventional commits format
- Release Workflow - Version management with changesets
- Technical Documentation - Architecture and internals
We welcome contributions! Please see our Contributing Guide for details.
Quick steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and linter (
npm test && npm run lint) - Commit following conventional commits (
git commit -m 'feat: add amazing feature') - Add a changeset (
npx changeset) - Push and open a Pull Request
📝 This project uses Conventional Commits and Changesets for version management.
Generate a workflow file:
git-contributor-stats --generate-workflowThis creates .github/workflows/git-contributor-stats.yml:
name: Git Contributor Stats
on:
schedule:
- cron: '0 0 * * 0' # Weekly on Sunday
workflow_dispatch:
jobs:
stats:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install git-contributor-stats
run: npm install -g git-contributor-stats
- name: Generate reports
run: |
git-contributor-stats \
--since 90.days \
--out-dir reports \
--md reports/report.md \
--html reports/report.html \
--charts \
--no-count-lines
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: contributor-stats
path: reports/🔐 This project publishes packages with NPM provenance enabled, providing cryptographic proof of build authenticity.
For security issues, please see SECURITY.md for our security policy and how to report vulnerabilities responsibly.
MIT License - see LICENSE file for details.
Made with ❤️ for better repository insights