{"id":33925879,"url":"https://github.com/timabell/gitopolis","last_synced_at":"2026-05-01T01:03:17.650Z","repository":{"id":37947635,"uuid":"502306370","full_name":"timabell/gitopolis","owner":"timabell","description":"Manage multiple git repositories - CLI tool - run commands, clone, and organize repos with tags","archived":false,"fork":false,"pushed_at":"2026-04-13T17:24:41.000Z","size":593,"stargazers_count":45,"open_issues_count":20,"forks_count":6,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-04-13T19:19:54.593Z","etag":null,"topics":["git"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timabell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"timabell","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-06-11T09:35:05.000Z","updated_at":"2026-04-13T17:24:45.000Z","dependencies_parsed_at":"2025-12-12T13:01:19.436Z","dependency_job_id":null,"html_url":"https://github.com/timabell/gitopolis","commit_stats":null,"previous_names":["rustworkshop/gitopolis","timabell/gitopolis"],"tags_count":69,"template":false,"template_full_name":null,"purl":"pkg:github/timabell/gitopolis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timabell%2Fgitopolis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timabell%2Fgitopolis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timabell%2Fgitopolis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timabell%2Fgitopolis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timabell","download_url":"https://codeload.github.com/timabell/gitopolis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timabell%2Fgitopolis/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31776013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T20:17:16.280Z","status":"ssl_error","status_checked_at":"2026-04-13T20:17:08.216Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git"],"created_at":"2025-12-12T10:00:54.589Z","updated_at":"2026-05-01T01:03:17.631Z","avatar_url":"https://github.com/timabell.png","language":"Rust","funding_links":["https://github.com/sponsors/timabell"],"categories":[],"sub_categories":[],"readme":"# Gitopolis\n\nManage multiple git repositories with ease.\n\n* 🤓 -\u003e Run any shell or git command on multiple `git` repositories.\n* 🤓 -\u003e Re-clone all your repos on new machines.\n* 🤓 -\u003e Limit actions to custom tags.\n* 🤓 -\u003e Easy to remember and use command list (`add`, `exec`, `clone`, `tag` etc.).\n* 🤓 -\u003e A-GPL v3 licensed labour of love ❤️.\n\nTo get an idea what a real use might look like take a look at this blog post: [\"Using Gitopolis to Manage Multiple Git Repositories\"](https://0x5.uk/2025/10/25/using-gitopolis-to-manage-multiple-git-repositories/)\n\n## Installation\n\n### Download binary release from github\n\n1. Grab the [latest release](https://github.com/timabell/gitopolis/releases/latest),\n2. unzip it\n3. put the binary somewhere in your `PATH`.\n\nI suggest adding a shorter shell alias to save typing. Perhaps `gm` for git many or `gop`.\n\n### From crates.io\n\nIf you're a rust user, you can install from crates.io:\n\n\u003chttps://crates.io/crates/gitopolis\u003e\n\n```sh\ncargo install gitopolis\n```\n\nThis will download the latest release crates.io, build and install it to `~/.cargo/bin/`.\n\n### Nix flake\n\nA Nix flake wrapping the prebuilt GitHub release binaries lives on the [`nix` branch](https://github.com/timabell/gitopolis/tree/nix) of this repo. It's auto-updated whenever a release is published.\n\nRun without installing:\n\n```sh\nnix run github:timabell/gitopolis/nix -- --help\n```\n\nInstall into your profile:\n\n```sh\nnix profile install github:timabell/gitopolis/nix\n```\n\nReference from a NixOS / home-manager flake:\n\n```nix\ninputs.gitopolis.url = \"github:timabell/gitopolis/nix\";\n# then: gitopolis.packages.${pkgs.system}.default\n```\n\nSupported systems: `x86_64-linux` (musl-static, runs on NixOS without patching), `x86_64-darwin`, `aarch64-darwin`.\n\n### Arch linux\n\n[Gitopolis is in the AUR](https://aur.archlinux.org/packages/gitopolis), so you can install it with `paru` or `yay` or your AUR helper of choice.\n\n```sh\nparu -S gitopolis\n```\n\nor\n\n```sh\nyay -S gitopolis\n```\n\n### Packaged version info\n\n\u003chttps://repology.org/project/gitopolis/versions\u003e\n\n## Built in help\n\ngitopolis has a fully documented command system, so use `-h` to get help for each command:\n\n```sh\ngitopolis -h\ngitopolis clone -h\n```\n\n## Initial setup\n\nThere are several ways to get started:\n\n### 1. Add your existing local repos\n\n```sh\ncd ~/repos/\ngitopolis add *\n```\n\n### 2. Clone new repos\n\n```sh\ngitopolis clone https://github.com/username/repo1.git\n```\n\n### 3. Start from an existing gitopolis config\n\n```sh\n# Decide where to put the repos\nmkdir ~/repos/\ncd ~/repos/\n\n# Grab a config file from somewhere (maybe a colleage) and add to the folder you'll keep the repos in\nwget https://gist.githubusercontent.com/timabell/87add070a8a44db4985586efe380757d/raw/08be5b3c38190eeed4fda0060818fa39f3c67ee3/.gitopolis.toml\n\n# Clone all the repos held in the downloaded config file to the current folder\ngitopolis clone\n```\n\n### 4. Configure many repos from the github or azure-devops api\n\nTake a look at the python scripts at [github.com/timabell/cloner](https://github.com/timabell/cloner)\n\nThis script can read repo lists from github and azure devops and write them to a gitopolis config file ready for cloning, including some sensible default tags.\n\n## Usage\n\n### Running shell / git commands in many repos\n\n```sh\ngitopolis exec -- git pull\ngitopolis exec -- git status\n```\n\n#### Interactive commands, pagers \u0026 colour\n\nCommands executed with `gitopolis exec` and `gitopolis clone` run in a \"non-interactive\" (non-TTY) environment to prevent hanging on prompts or pagers.\n\nThis means:\n\n- Commands won't prompt for interactive input\n  - SSH/GPG keys must already be loaded and unlocked in ssh-agent or similar\n  - Remote SSH host keys must already be accepted (in `~/.ssh/known_hosts`)\n  - The easiest way to ensure keys and hosts are all setup and ready if you run into this problem is to run a single git fetch/clone outside gitopolis first. If this proves to be a regular hassle for new users then we could look at doing something about it so add your experience to [issue #236](https://github.com/timabell/gitopolis/issues/236).\n- Git commands won't pause for pagers (like `less` for `git log`) - this is intentional, but you can pipe the output of gitopolis as a whole into less - e.g.  `gitopolis exec -- git log -n 1 | less`\n- Git will default to no-color output, but you can [force git's coloured output](https://stackoverflow.com/questions/16073708/force-git-status-to-output-color-on-the-terminal-inside-a-script/18304605#18304605) with `git -c color.ui=always`.\n\n#### Getting output as single lines\n\nFor compact, parsable output that's easy to sort and analyze use `--oneline`, this will put all the output on a single line for each repo (removing newlines).\n\ne.g. to see the latest commit for all the repos, with the most recently touched repo first:\n\n```sh\ngitopolis exec --oneline -- git log --oneline -n 1\n```\n\n### Tagging\n\nWhen dealing with many git repos, it can be cumbersome and slow to have to run commands on every repo every time, so you can use tags to filter down what's relevant to you in the moment, e.g. `backend`, `my-team`, `rust` or any other way of categorizing you can thing of.\n\n```sh\ngitopolis tag some_tag repo1 repo2\ngitopolis exec -t some_tag -- git pull\n```\n\n#### Advanced tag filtering with AND/OR logic\n\nYou can use multiple tags with powerful AND/OR logic to precisely filter repositories:\n\n- **Comma-separated tags** (within a single `--tag` flag) use **AND logic**: all tags must match\n- **Multiple `--tag` flags** use **OR logic**: at least one tag group must match\n\nExamples:\n\n```sh\n# Match repos with BOTH 'backend' AND 'rust'\ngitopolis list --tag backend,rust\n\n# Match repos with (backend AND rust) OR (frontend AND typescript)\ngitopolis exec --tag backend,rust --tag frontend,typescript -- git pull\n\n# Clone repos with (production AND critical) OR (staging AND critical)\ngitopolis clone --tag production,critical --tag staging,critical\n```\n\nThis allows for flexible repository filtering based on combinations of characteristics.\n\n### Viewing repository information\n\nShow the recorded information about a specific repository:\n\n```sh\n$ gitopolis show 0x5.uk\n\nTags:\n  public\n  github\n  blog\n  rust\n\nRemotes:\n  origin: git@github.com:timabell/0x5.uk\n```\n\nList all repositories with tags and remote URLs:\n\n```sh\ngitopolis list --long\n```\n\nList all tags and the repositories they're applied to:\n\n```sh\ngitopolis tags --long\n```\n\n### Moving repositories\n\nMove a repository to a new location and update the configuration:\n\n```sh\ngitopolis move repo old-path new-path\n```\n\n### Managing multiple remotes\n\nGitopolis supports multiple git remotes per repository. Sync remotes from your git repositories into the `.gitopolis.toml` file:\n\n```sh\ngitopolis sync --read-remotes\n```\n\nSync remotes from `.gitopolis.toml` back to your git repositories:\n\n```sh\ngitopolis sync --write-remotes\n```\n\nNote there is no automatic sync, gitopolis will never fiddle with the remotes in the managed repos or its own config unless relevant commands are invoked.\n\n### Using complex shell commands\n\nGitopolis supports executing complex shell commands for each repository - including pipes, redirection, and chaining with `\u0026\u0026` and `||`.\n\nTo use these features, pass your entire command as a single quoted string to avoid the shell you are using processing them before they get to gitopolis:\n\n```sh\ngitopolis exec -- 'git status \u0026\u0026 git pull'\ngitopolis exec -- 'git log -1 | grep \"feat:\"'\n```\n\nYou can combine this with normal shell piping/redirection of the entire gitopolis output, e.g.:\n\n```sh\ngitopolis exec -- 'git log -1 | grep \"feat:\"' | wc -l\n```\n\n### State file\n\nGitopolis creates and manages all its state in a single simple `.gitopolis.toml` file in the working directory that you can edit, read, share with others and copy to other machines.\n\nIt is stored in [TOML](https://toml.io/) format which is a well-supported config markup with parsers for many programming languages.\n\nHere's an example of the contents:\n\n```toml\n[[repos]]\npath = \"gitopolis\"\ntags = [\"tim\"]\n[repos.remotes.origin]\nname = \"origin\"\nurl = \"git@github.com:timabell/gitopolis.git\"\n\n[[repos]]\npath = \"schema-explorer\"\ntags = [\"tim\", \"databases\"]\n[repos.remotes.origin]\nname = \"origin\"\nurl = \"git@github.com:timabell/schema-explorer.git\"\n\n[[repos]]\npath = \"database-diagram-scm\"\ntags = [\"databases\"]\n[repos.remotes.origin]\nname = \"origin\"\nurl = \"git@github.com:timabell/database-diagram-scm.git\"\n```\n[View as gist](https://gist.github.com/timabell/87add070a8a44db4985586efe380757d).\n\nThe TOML array format takes a little getting used to, but other than that it's pretty easy to follow and edit by hand, and it allows clean round-trips of data, and is supported in just about every programming language.\n\n## The name\n\nThink a [metropolis](https://en.wikipedia.org/wiki/Metropolis) of git repos.\n\nIt's a lot to type as a name, but it's nice and unique, and if you use it a lot I suggest you create a shell alias to something shorter.\n\n## Why did I create this?\n\n* Wanted to learn more [Rust](https://www.rust-lang.org/).\n* Had a client with many microservices and teams.\n* Tried [gita](https://github.com/nosarthur/gita) but found command layout hard to remember, and didn't like having to install python.\n* To help others with their microservices.\n\nMore recently I've been adding more features to help with other clients, and enjoying the benefits of high-quality end-to-end tests as I increasingly work with claude code on shipping features considerably more rapidly (though not always more easily lol, crazy LLMs).\n\n## Spread the word\n\nIf you find this useful, or just think it's cool, please do help spread the word.\n\n- Star the repo\n- Tell your friends\n- Share your gitopolis config with colleagues and use it to help onboard new developers to your team\n- Post on social media, youtube, reddit etc about your experiences, (good or bad), and don't forget to tag me!\n\n## Contributing\n\nSuggestions welcome, particularly adding your experience, problems and ideas to the issues list.\n\nI'm happy for people to open issues that are actually just questions and support queries.\n\nRough internal design and ambitions can be found at [Design.md](Design.md).\n\nPRs are appreciated though it might be best to open an issue first to discuss the design.\n\n## Alternatives\n\nHere's the other tools I'm aware of that have more-or-less similar capabilities\n\n* [`git for-each-repo`](https://git-scm.com/docs/git-for-each-repo) - new built-in git command for running git commands on many repos\n* [gita](https://github.com/nosarthur/gita) - requires python\n* [myrepos aka \"mr\"](https://myrepos.branchable.com/)\n* [GitKraken](https://www.gitkraken.com/blog/multi-repo-management-hurdles-and-solutions) - Commercial tool\n* https://stackoverflow.com/questions/816619/managing-many-git-repositories - stackoverflow question on the same\n* [gr](https://mixu.net/gr/)\n* [git-repo](https://gerrit.googlesource.com/git-repo/)\n* [git slave](https://gitslave.sourceforge.net/)\n* [mani](https://manicli.com/) a TUI (text user interface)\n* [RepoZ](https://github.com/awaescher/RepoZ) - a Windows GUI tool\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimabell%2Fgitopolis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimabell%2Fgitopolis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimabell%2Fgitopolis/lists"}