Configuration

Where config is read from (later wins)

  1. built-in defaults (below)
  2. ~/.config/git-cleanup/config.json
  3. .gitcleanup.json or .git-cleanup.json, found by walking up from the current directory — perfect for per-repo rules
  4. a file passed via --config <file>

Invalid values are rejected loudly at startup — never silently ignored.

Editors get autocomplete and inline validation from the JSON Schema at support/config.schema.json (shipped in the npm package): add "$schema": "https://raw.githubusercontent.com/Asunachi/git-cleanup/main/support/config.schema.json" to your config file, or register the schema with your editor's JSON settings.

Full schema

{
  // Globs never touched, on top of the built-in protected list.
  "protected": ["special/release", "vendor/**"],

  // Merged branches at least this old (days, from the tip commit) are prunable.
  "deleteMergedAfterDays": 21,

  // Unmerged branches older than this are flagged stale (never deleted).
  "warnUnmergedAfterDays": 45,

  // Per-name rules. mode "merged" = custom age for merged branches;
  // mode "any" = force-delete even when unmerged (local only — opt in!).
  "rules": [
    { "match": "feature/ci-*", "mode": "merged", "minAgeDays": 7 },
    { "match": "tmp/**", "mode": "any", "minAgeDays": 1 }
  ],

  "pr": {
    "track": true,              // enrich branches with PR state
    "staleAfterDays": 30,       // open PR with no activity = stale
    "closeStaleAfterDays": 60,  // >0 enables `prs --close`
    "closeComment": "Auto-closed by git-cleanup — reopen if still needed."
  },

  "remote": {
    "pruneMerged": true,             // prune --remote deletes merged remote branches
    "deleteAbandonedAfterDays": 0    // >0: delete remote branches whose PR closed unmerged
  },

  "forge": {
    "hosts": {                       // claim self-hosted forge hostnames
      "git.example.com": "gitlab",   //   (PR tracking + report-issue)
      "git.internal": "gitea"
    }
  },

  "backup": {
    "enabled": true,                 // bundle refs before -D / remote deletions
    "dir": null,                     // null = <git dir>/git-cleanup-backups
    "retainDays": 0                  // 0 = keep forever; >0 sweeps older bundles
  },

  "repos": ["../other-project", { "path": "/srv/legacy", "mode": "prune" }],

  "sweep": {
    "mode": "report",                // "report" never deletes | "prune"
    "remote": false,                 // also delete remote branches during sweep
    "reportFile": null,              // combined markdown report path, or null
    "reportIssue": null              // true, { "title": "..." }, or null
  }
}

Rule semantics

The first matching rule for a branch wins.

Protected branches

Base refs (from the remote's HEAD), the checked-out branch, and any name matching the built-in protected list or your protected globs are never pruned. The built-in list covers version-shaped names like v1, 5.x, release/*, and the common main/master/develop/dev — add your own with the protected globs.

Environment variables

VariablePurpose
GITHUB_TOKEN / GITLAB_TOKEN / BITBUCKET_TOKEN / GITEA_TOKENPR enrichment + report-issue auth
GITLAB_API_BASE / BITBUCKET_API_BASE / GITEA_API_BASE / GITHUB_API_BASEAPI base overrides (self-hosted forges)
CI_API_V4_URLGitLab report-issue API base (set by GitLab CI)
CI_JOB_TOKENGitLab report-issue fallback auth inside GitLab CI
GIT_CLEANUP_FETCH_TIMEOUT_MSforge API timeout (default 15000)
GIT_CLEANUP_YES=1same as --yes
GIT_CLEANUP_NO_COLOR=1disable ANSI colors (NO_COLOR also works)