| Usage | Description |
|---|---|
gitall {action} |
Run action against all accounts in config file |
gitall {action} {user} |
Run action for a single user, current directory, SSH protocol |
gitall {action} {user} {dir} |
Run action for a single user in a specific directory, SSH protocol |
gitall {action} {user} {dir} {protocol} |
Run action with all params specified |
Actions: clone, pull, status, config
- Fetches the full list of public repos for a GitHub user/org via the GitHub API (
GET /users/{user}/repos?per_page=100) - Clones each repo into the target directory using the configured protocol
- Skips repos that already exist locally (directory check)
- Clones into a lowercase directory name (
repo.toLowerCase()) - Logs each clone operation to the log file
- Scans the target directory for subdirectories containing a
.gitfolder - For each discovered git repo, checks the
remote.origin.url - Only pulls if the remote URL matches the configured user's SSH or HTTPS base URL (skips repos belonging to other users/orgs)
- Logs each update to the log file
- Prints total count of updated repos
- Scans the target directory for subdirectories containing a
.gitfolder - For each repo, runs
git status --porcelainandgit log origin/master..HEAD - Only prints output for repos that have uncommitted changes or unpushed commits
- Displays a separator line between repos for readability
- Displays the contents of
~/.gitall/config.json - Reports if no config file exists
Supports multiple accounts in a JSON array:
[
{
"username": "BoyCook",
"dir": "/Users/boycook/code/boycook",
"protocol": "ssh",
"active": true
},
{
"username": "SomeOrg",
"dir": "/Users/boycook/code/org",
"protocol": "https",
"active": false
}
]Fields:
username— GitHub user or organisation name (case-sensitive)dir— target directory for cloned reposprotocol—ssh,https, orsvnactive— optional boolean (defaults totrue); inactive accounts are skipped with a message
| Protocol | Clone URL format |
|---|---|
ssh |
git@github.com:{user}/{repo}.git |
https |
https://github.com/{user}/{repo} |
svn |
https://github.com/{user}/{repo} |
- Target directory must exist
- Protocol must be one of
ssh,https,svn - Action must be one of
clone,pull,status,config
- All operations logged to
~/.gitall/gitall.log - Console output for progress messages and status results
- Scans target directory for immediate child directories
- Identifies git repos by checking for a
.gitsubdirectory - Used by both
pullandstatusactions
- Before pulling, checks
remote.origin.urlagainst the configured user's SSH and HTTPS base URLs - Skips repos that don't belong to the configured user (e.g. forks cloned manually from other users)
- Prints "Ignoring non users repository" message for skipped repos
-
Assignment instead of comparison in CLI argument parsing (
bin/gitall:40,46,52) —args.length = 2should beargs.length === 2. This means only the 1-arg path (config file) ever works correctly; all other argument counts fall through to the first branch. -
repo.toLowerCase()called on object (lib/gitall.js:116) —repois a GitHub API response object, not a string. Should berepo.name.toLowerCase(). -
No GitHub API error handling — if the API call fails (network error, 404, rate limit), the error is silently swallowed and no repos are cloned.
-
No pagination — hardcoded
per_page=100means users/orgs with >100 repos will have repos silently missed. -
Status assumes
origin/master— fails for repos usingmainor other default branch names. -
Pull doesn't check local state before pulling — if a repo has uncommitted changes,
git pullcan fail or create merge conflicts. Similarly, if there are unpushed local commits, pull can cause unwanted merges or conflicts. There is no pre-flight check, no stash, and no warning — it just runsgit pulland hopes for the best. -
No feedback on pull failures — when a pull fails (due to conflicts, dirty working tree, etc.), the error output goes to the log file but the user gets no clear indication of which repos failed or why.
-
Synchronous file I/O —
readdirSync,appendFileSync,readFileSyncblock the event loop.
- Fix all known bugs listed above
- Status should detect the default branch dynamically (
git symbolic-ref refs/remotes/origin/HEAD) rather than assumingmaster - Pull must check for dirty working tree and unpushed commits before pulling
- Repos with dirty state should be skipped with a clear warning, not silently fail
- Full GitHub API pagination to handle accounts with >100 repos
- Proper error handling and reporting for API failures, git failures, etc.
- Use proper subcommand-style CLI (
gitall clone,gitall pull, etc.) with flags instead of positional args - Add
--user,--dir,--protocolflags on each subcommand for ad-hoc usage - Add
--concurrency/-jflag to control parallelism (default: sensible limit, e.g. 4) - Add
--dry-runflag to show what would happen without executing - Add
--verbose/-vflag for detailed output - Add
--quiet/-qflag to suppress non-essential output - Add
--versionflag - Add
--helpfor each subcommand with usage examples - Coloured terminal output (repo names, success/failure/skipped indicators)
- Parallel cloning with configurable concurrency
- Progress output showing
[3/47] Cloning repo-name... - Support cloning private repos (requires auth token)
- Option to include/exclude forks (
--no-forks,--forks-only) - Option to filter repos by name pattern (
--filter "prefix-*") - Option to include/exclude archived repos (
--no-archived) - Summary at end:
Cloned: 12, Skipped (exists): 35, Failed: 0
- Pre-flight check: skip repos with uncommitted changes (with clear warning)
- Pre-flight check: skip repos with unpushed commits (with clear warning)
- Parallel pulling with configurable concurrency
- Progress output showing current repo and outcome
--stashflag: automatically stash changes before pull and pop after--rebaseflag: usegit pull --rebaseinstead of merge- Remove ownership filter by default (pull all repos in dir); add
--owned-onlyflag to restore old behaviour - Summary at end:
Updated: 10, Skipped (dirty): 3, Skipped (unpushed): 2, Failed: 1, Up-to-date: 31
- Detect default branch dynamically instead of assuming
master - Show more detail: branch name, ahead/behind counts, staged/unstaged/untracked counts
- Compact single-line-per-repo format by default,
--verbosefor full detail - Colour coding: green (clean), yellow (dirty), red (diverged)
- Summary counts at end
gitall config init— create a default config file interactivelygitall config add— add a new account to the configgitall config remove— remove an account from the configgitall config list— display current config in a readable table- Validate config on load (check required fields, valid protocols, directories exist)
- Support
~and$HOMEexpansion in directory paths - YAML or TOML as config format (more human-friendly than JSON)
- Support GitHub personal access token for private repos
- Token stored in config or read from
GITHUB_TOKENenv var - Support GitHub Enterprise (configurable API base URL per account)
- All repo operations (clone, pull, status) run in parallel using goroutines
- Configurable concurrency limit to avoid overwhelming the system
- Progress bar or live-updating output showing overall progress
- Clear, structured console output with colour
- Summary report after each action (counts of success/skip/fail)
--jsonflag for machine-readable output- Log file with timestamps and structured entries
gitall fetch— rungit fetchon all repos (lighter than pull, useful for checking remote state)gitall list— list all local repos with their remote URL, branch, and clean/dirty state (no git operations, just local inspection)
gitall/
├── main.go # Entry point — initialises cobra root command
├── go.mod
├── go.sum
├── cmd/ # CLI layer — one file per subcommand
│ ├── root.go # Root command, global flags (--verbose, --quiet, --json)
│ ├── clone.go # `gitall clone` subcommand
│ ├── pull.go # `gitall pull` subcommand
│ ├── fetch.go # `gitall fetch` subcommand
│ ├── status.go # `gitall status` subcommand
│ ├── list.go # `gitall list` subcommand
│ └── config.go # `gitall config [init|add|remove|list]` subcommands
├── internal/
│ ├── config/ # Config loading, validation, path expansion
│ │ └── config.go
│ ├── github/ # GitHub API client — list repos, pagination, auth
│ │ └── client.go
│ ├── git/ # Git operations — clone, pull, fetch, status
│ │ └── git.go
│ ├── runner/ # Concurrent execution engine — worker pool, progress
│ │ └── runner.go
│ └── output/ # Output formatting — colour, JSON, summaries
│ └── output.go
└── testdata/ # Test fixtures (fake git repos, config files)
| Dependency | Purpose |
|---|---|
github.com/spf13/cobra |
CLI framework (subcommands, flags, help) |
github.com/fatih/color |
Coloured terminal output |
gopkg.in/yaml.v3 |
YAML config file parsing |
No git library — shell out to git directly. Keeps it simple, avoids CGO, and users already have git installed. All git interaction goes through internal/git/ so it's easy to test via interface.
cmd/ — Thin CLI layer. Each file defines a cobra command, parses flags, loads config, calls into internal/ packages. No business logic here.
internal/config/ — Loads and validates ~/.gitall/config.yaml. Handles path expansion (~, $HOME). Merges CLI flags with config file values (flags take precedence).
internal/github/ — GitHub API client. Lists repos for a user/org with full pagination. Supports auth token. Supports filtering (forks, archived, name pattern). Returns a clean []Repo slice.
internal/git/ — Runs git commands against local repos. Each function (Clone, Pull, Fetch, Status) takes a path and options, returns structured results. Handles all pre-flight checks (dirty state, unpushed commits). Detects default branch dynamically.
internal/runner/ — Generic concurrent task runner. Takes a slice of work items and a function, runs them across a worker pool with configurable concurrency. Reports progress and collects results. Used by clone, pull, fetch, and status commands.
internal/output/ — Handles all output formatting. Supports three modes: normal (coloured text), quiet (minimal), and JSON. Prints progress lines, summaries, and error reports.
CLI flags + config.yaml
│
▼
cmd/clone.go ← parses flags, loads config, builds account list
│
▼
github.ListRepos() ← fetches repos from API (with pagination + filters)
│
▼
runner.Run() ← runs git.Clone() for each repo concurrently
│
▼
output.Summary() ← prints results summary
// Config types
type Config struct {
Accounts []Account `yaml:"accounts"`
}
type Account struct {
Username string `yaml:"username"`
Dir string `yaml:"dir"`
Protocol string `yaml:"protocol"` // ssh, https
Token string `yaml:"token"` // optional, overrides GITHUB_TOKEN
APIURL string `yaml:"api_url"` // optional, for GitHub Enterprise
Active *bool `yaml:"active"` // optional, defaults to true
}
// GitHub types
type Repo struct {
Name string
CloneURL string // built from protocol
Fork bool
Archived bool
}
// Git operation results
type RepoResult struct {
Name string
Path string
Status ResultStatus // Success, Skipped, Failed
Message string // human-readable detail
}
type ResultStatus int
const (
Success ResultStatus = iota
Skipped
Failed
UpToDate
)
// Runner
type Task[T any] struct {
Item T
Execute func(T) RepoResult
}accounts:
- username: BoyCook
dir: ~/code/boycook
protocol: ssh
- username: SomeOrg
dir: ~/code/org
protocol: https
token: ghp_xxxxxxxxxxxx # optional per-account token
active: falseGlobal token fallback: GITHUB_TOKEN env var.
- Initialise Go module, install cobra, set up project layout
- Implement
cmd/root.gowith global flags (--verbose,--quiet,--json,--version) - Implement
internal/config/— load YAML, validate, expand paths, merge with CLI flags - Implement
cmd/config.go—config listandconfig initsubcommands - Add tests for config loading and validation
- Implement
internal/github/— list repos with pagination, auth token, fork/archived filtering - Implement
internal/git/—Clone()function (shell out togit clone) - Implement
internal/runner/— concurrent worker pool with progress reporting - Implement
internal/output/— coloured text output, summary formatting - Implement
cmd/clone.go— wire it all together with--dry-run,--no-forks,--no-archived,--filter,-j - Add tests for GitHub client (mock HTTP), git clone, runner
- Implement
internal/git/—Status()function (porcelain status, ahead/behind, branch detection) - Implement
cmd/status.go— concurrent status with coloured compact output - Implement
cmd/list.go— local repo inspection (remote URL, branch, clean/dirty) - Add tests for status parsing and list output
- Implement
internal/git/—Pull()with pre-flight checks (dirty, unpushed),--stash,--rebase - Implement
internal/git/—Fetch()function - Implement
cmd/pull.go— concurrent pull with summaries,--owned-only,--stash,--rebase - Implement
cmd/fetch.go— concurrent fetch with summaries - Add tests for pull pre-flight checks, fetch
- Implement
cmd/config.go—config addandconfig removesubcommands - Add
--jsonoutput mode across all commands - Add GitHub Enterprise support (custom API URL per account)
- End-to-end testing with real git repos in
testdata/ - README and install instructions