A Python CLI tool to manage GitHub organization repository settings, with a focus on standardizing merge commit message formats across all repositories.
- Flexible targeting: Manage entire organizations, specific users, or individual repositories (
owner/repo) - Bulk repository management: Update merge/squash settings across multiple repositories at once
- Smart authentication: Automatically uses
ghCLI token, with fallback toGITHUB_TOKENenv var - Safe by default: Dry-run mode shows what would change before applying updates
- Progress tracking: Real-time progress bars for long-running operations
- Filtering: Skip archived repos and forks by default
- Summary reports: See which repos need attention at a glance
# Clone or navigate to the project
cd ~/git/github-management
# Run directly with uv (no installation needed)
uv run ghm --help
# Or install as a tool
uv tool install .
# Now you can use it anywhere
ghm --helpghm will automatically find your GitHub token in this order:
--tokenflag (if provided)GITHUB_TOKENenvironment variableghCLI (gh auth token)
Recommended: Just use gh CLI:
gh auth login
# Then ghm will automatically use your gh tokenGet a summary of merge settings across your target (organization, user, or single repository):
# Target an organization
uv run ghm repos list acme-uk
# Target a user
uv run ghm repos list octocat
# Target a single repository
uv run ghm repos list acme-uk/backbone
# Verbose table view
uv run ghm repos list acme-uk --verbose
# Include archived and forked repos (for orgs/users)
uv run ghm repos list acme-uk --include-archived --include-forksExample output:
Summary:
Total repositories: 70
Squash merge enabled: 70
- Using PR_TITLE + PR_BODY: 18
- Need update: 52
Merge commit enabled: 41
- Using PR_TITLE + PR_TITLE: 0
- Need update: 41
Repositories needing updates (53):
acme-uk/backbone: squash_msg=COMMIT_MESSAGES
acme-uk/.github: squash_title=COMMIT_OR_PR_TITLE, squash_msg=COMMIT_MESSAGES, merge_title=MERGE_MESSAGE
...
The most common use case - set squash merge to use PR title and description:
# Dry run (shows what would change)
uv run ghm repos fix-squash acme-uk
# Apply the changes
uv run ghm repos fix-squash acme-uk --applyUpdate both squash and merge commit settings with custom values:
# Update squash merge settings
uv run ghm repos update-merge acme-uk \
--squash-title PR_TITLE \
--squash-message PR_BODY \
--apply
# Update merge commit settings
uv run ghm repos update-merge acme-uk \
--merge-title PR_TITLE \
--merge-message PR_TITLE \
--apply
# Update both at once
uv run ghm repos update-merge acme-uk \
--squash-title PR_TITLE \
--squash-message PR_BODY \
--merge-title PR_TITLE \
--merge-message PR_TITLE \
--applyValid values for merge settings:
- Squash title:
PR_TITLE,COMMIT_OR_PR_TITLE - Squash message:
PR_BODY,COMMIT_MESSAGES,BLANK - Merge title:
PR_TITLE,MERGE_MESSAGE - Merge message:
PR_TITLE,PR_BODY,BLANK
All commands support filtering:
# Skip archived repos (default)
uv run ghm repos list acme-uk
# Include archived repos
uv run ghm repos list acme-uk --include-archived
# Include forked repos
uv run ghm repos list acme-uk --include-forks
# Include everything
uv run ghm repos list acme-uk --include-archived --include-forksUse case: You want all repos (or a specific one) to use PR title + description for squash merges.
# Preview changes for a single repo
uv run ghm repos fix-squash acme-uk/backbone
# Apply changes to an entire org
uv run ghm repos fix-squash acme-uk --applyUse case: You want merge commits to use the PR title only.
# 1. Check current state
uv run ghm repos list acme-uk
# 2. Preview changes
uv run ghm repos update-merge acme-uk \
--merge-title PR_TITLE \
--merge-message PR_TITLE
# 3. Apply changes
uv run ghm repos update-merge acme-uk \
--merge-title PR_TITLE \
--merge-message PR_TITLE \
--apply# Get summary of repos needing updates
uv run ghm repos list acme-uk | grep -A 50 "needing updates"- Fetching the repo list is fast (~5 seconds for 77 repos)
- Fetching merge settings requires 1 API call per repo (~0.6s each)
- For 70 repos, expect ~45 seconds total for list command
- Updates are applied in sequence with progress tracking
Error: No GitHub token found. Please either:
1. Set GITHUB_TOKEN environment variable
2. Log in with: gh auth login
3. Pass token explicitly with --token
Solution: Run gh auth login or set GITHUB_TOKEN environment variable.
Error: 403 Resource not accessible by personal access token
OR
✗ acme-uk/backbone: Failed - 404 {"message": "Not Found", ...}
Note on 404s: GitHub's REST API is often stricter than the Web UI. Even if you can change these settings in your browser, the API may still return a 404 Not Found (or 403 Forbidden) if you do not have the Admin role on the repository. The Maintainer role is often sufficient for the Web UI but insufficient for the REST API's repository update endpoint.
Solution: Ensure your token has repo or write:org scope and that you have Admin/Maintainer permissions. With gh CLI:
gh auth refresh -s repoIf the organization requires SAML SSO, ensure your token is authorized for that organization.
Explanation: PyGithub makes 1 API call per repository to fetch merge settings. For large orgs (100+ repos), this can take 1-2 minutes.
Workaround: Use --include-archived and --include-forks flags sparingly to reduce the number of repos fetched.
github-management/
├── src/
│ └── github_mgmt/
│ ├── __init__.py
│ ├── main.py # Typer CLI app
│ ├── auth.py # Authentication (gh CLI fallback)
│ ├── repos.py # Repository operations
│ └── output.py # Rich output formatting (future)
├── tests/
├── pyproject.toml
├── README.md
└── .gitignore
# Install dependencies
cd ~/git/github-management
uv sync
# Run tests (when available)
uv run pytest
# Format code
uv run ruff format
# Lint
uv run ruff checkProblem: GitHub doesn't provide an org-level setting to enforce merge commit message formats. Each repository has its own settings for:
- Squash merge commit title/message
- Merge commit title/message
Solution: This CLI tool lets you:
- Audit current settings across all repos
- Bulk update settings to match your org's standards
- Re-run periodically or after adding new repos
Recommended settings (for clean, PR-focused commit history):
- Squash merge:
PR_TITLE+PR_BODY - Merge commit:
PR_TITLE+PR_TITLE
MIT
PRs welcome! This tool was built with: