CLI tool
earwig/git-repo-updater avatar
earwig/git-repo-updater

gitup: one command to pull every repository in a directory

A console script that allows you to easily update multiple git repositories at once

843 stars91 forksPythonMIT

At a glance

What is it?
gitup is a Python console script that updates many git repositories at once, handling several remotes, dirty working trees, diverged branches and detached HEADs. It takes explicit paths or a saved list of bookmarks, and it never force-updates a branch that cannot be fast-forwarded.
Who is it for?
gitup suits a developer with a directory of dozens of clones and intermittent connectivity, since it batches the fetches and skips the branches that cannot be fast-forwarded instead of leaving you with a broken working tree. It does not suit a single-repository workflow, and it is the wrong tool when you need branches rebased or force-pushed, because it deliberately skips those.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem is a directory of clones and a flaky connection

gitup is a tool for updating multiple git repositories at once. The README describes it as smart enough to handle several remotes, dirty working directories, diverged local branches, detached HEADs, and more, and it states the origin plainly: it was originally created to manage a large collection of projects and to deal with sporadic internet access.

That second half of the motivation is the interesting one. The scenario is not a team with a monorepo and a clean CI pipeline. It is one developer on a laptop with a directory full of clones, each pointing at a different host, with a connection that comes and goes. Running git pull in each of them by hand is tedious and slow, and a script that handles all of them in one command is a real improvement.

The tool is a console script named gitup, and the project metadata maps the entry point to gitup.cli:run. The package name on PyPI is gitup, the command is gitup, and the project is at version 0.5.3.dev0, which tells you it is a pre-release development version of the 0.5 line.

The implementation is deliberately small. The only runtime dependencies are GitPython at 3.1.45 or newer, which is how it talks to git, and colorama at 0.4.6 or newer, which is how it prints colour on Windows terminals where the console does not support ANSI codes natively.

It runs on macOS, Linux and Windows, and needs a recent version of git and Python 3.10 or newer. The classifier list covers Python 3.10 through 3.14.

The audience is individual developers and small teams with a checkout directory, not a release engineering pipeline.

Bookmarks turn a directory scan into a saved list

There are two ways to say what to update, and the second is the one that makes the tool useful daily.

The first is explicit paths as command arguments.

code
gitup ~/repos/foo ~/repos/bar ~/repos/baz

That pulls the three named repositories. The second is a directory, and gitup finds the repositories inside it.

code
gitup ~/repos

Bookmarks are the third path, and they are saved lists. You create them with the add option.

code
gitup --add ~/repos/foo ~/repos/bar ~/repos/baz
gitup --add ~/repos

Once bookmarks exist, running gitup with no arguments updates all of them.

code
gitup

And you can see or remove them.

code
gitup --list
gitup --delete ~/repos

The interesting property is that bookmarks and arguments mix. The README gives a sequence that shows the three ways interacting: adding two specific repositories as bookmarks and then updating a third by argument, then running with no arguments to update only the bookmarked ones, then running with an argument and the --update flag to update all three.

code
gitup --add ~/repos/foo ~/repos/bar
gitup ~/repos/baz            # update 'baz' only
gitup                        # update 'foo' and 'bar' only
gitup ~/repos/baz --update   # update all three!

The --update flag is what makes a mixed invocation cumulative rather than an override. Without it, an argument on the command line restricts the run to that argument, and without arguments the run is restricted to the bookmarks. With --update, the union is used.

That is a small design decision with a real effect on habit. Once you have bookmarks, the daily command is a bare gitup, and the ad hoc case of one repository does not require clearing anything.

Depth controls how far the directory scan descends

Pointing gitup at a directory is convenient and also risky, because the directory tree below your projects folder may contain clones you did not mean to touch, or nested worktrees, or vendored copies of other projects.

The --depth option, abbreviated -t, controls this. It only applies when the directory you pass is not itself a git repository, which is the sensible rule, since a repository should be updated as a repository regardless of what sits inside it.

A depth of 0 disables recursion entirely, so the paths you provide must be repositories by themselves. A depth of 1 descends one level, and the README notes that this is the old behaviour from pre-0.5 gitup. A depth of -1 recurses indefinitely, and the documentation says that is not recommended. The default is 3.

A default of 3 is worth thinking about before you point the tool at your home directory. Three levels down from a projects folder will usually reach into a build output directory, a virtualenv with checkouts inside it, or a node_modules style tree.

The other way to use it is on the current directory.

code
gitup .

The full set of command arguments and their abbreviations is available through the built-in help.

code
gitup --help

The abbreviations follow a consistent pattern across the tool, and knowing them is what makes the flag set learnable. The depth option is -t, the current-only option is -c, the fetch-only option is -f, and the prune option is -p.

Fast-forward only, and branches that cannot merge are skipped

The behaviour that makes gitup safe rather than merely convenient is in what it refuses to do.

By default it fetches all remotes in a repository. The --current-only option, abbreviated -c, narrows that to only the remote tracked by the current branch, which matters in a repository that has both an origin and a fork.

After fetching, gitup tries to fast-forward all branches that have upstreams configured. And this is the important part: it will always skip branches where a fast-forward is not possible, for example where the working directory is dirty or where a merge or rebase would be required.

That single sentence is the design. A tool that pulled every branch would leave a dozen repositories in merge conflicts and mid-rebase states, and cleaning that up is exactly the tedium the tool is meant to remove. Skipping is a deliberate trade: some branches stay behind, and nothing gets broken.

The --fetch-only option, abbreviated -f, skips the fast-forward step entirely and only fetches remotes, which is the right mode when you want the objects locally but intend to deal with branches yourself.

There is a related cleanup step for remote-tracking branches whose upstream no longer exists. By default gitup keeps them, and the --prune option, abbreviated -p, deletes them. The README also mentions that setting fetch.prune or remote.<name>.prune in your git config does the same thing by default, which is the better answer for anyone who wants this behaviour permanently rather than per invocation.

So the tool has a coherent stance: it moves data, it does not rewrite your work.

Installation is a one-liner on four package managers

The installation section lists four supported routes, which is a sign the author expects the tool to be used on whatever the machine already has.

The first is uv.

code
uv tool install gitup

The second is pipx.

code
pipx install gitup

The third is plain pip.

code
pip install gitup

And the fourth is Homebrew.

code
brew install gitup

For a command line tool with no shared state, uv tool install and pipx are the better choices of the four, because both install the tool into its own isolated environment rather than into the Python environment of the current project. A tool that operates on your repositories should not be affected by, or affect, the virtualenv of the project you happen to be working on.

The project metadata is modern and tidy. The build backend is hatchling, the package uses a src layout, the lock file is uv.lock, and there is a .python-version file pinning the interpreter for the repository itself. The pyright configuration targets Python 3.10 with a venvPath of the current directory, and ruff is configured with a target version of py310.

The dependency groups section is minimal, with pytest as the only development dependency listed, and the .pre-commit-config.yaml at the root suggests the project runs its checks through pre-commit.

A CHANGELOG file is in the repository root, and the project metadata links to it from the project URLs, so the release history is browsable even though the repository has no GitHub releases.

Where gitup is the wrong tool

The first wrong-tool case is a workflow that needs branches rebased, merged or force-pushed. gitup skips exactly those branches, by design. If your daily habit is to rebase your feature branch onto the default branch, gitup will quietly leave it behind every time, and you will find the rebase still waiting for you.

The second case is a single repository. The whole tool exists to amortise across many clones, and for one repository, git pull is faster to type and does not add a dependency to your machine.

The third case is a monorepo or a managed dependency setup. If your projects are fetched by a tool that owns the checkout directory, you do not want a second tool walking the same tree and fetching behind its back. The recursion behaviour makes this a real hazard: the default depth of 3 will find checkouts inside caches and build directories.

There is also a scope limit around what gitup touches. It updates git repositories. It does not update the dependencies inside a repository, it does not run build steps, and it does not check out a different branch for you. The tool's own description calls it a console script, and the dependency list is two packages, which means the scope is deliberately narrow and unlikely to widen.

One thing to weigh before adopting it broadly is the trust it places in your working tree. Because it skips dirty branches rather than touching them, the tool is safe to run over a whole directory, but it will also silently leave many branches behind, and the summary of what it did and did not do is where you should look after the first run over a large tree.

On licensing, the project is MIT, with the licence file at the root and a license-files entry pointing at the LICEN[CS]E pattern in the metadata. The classifier marks it as MIT licensed and the development status as Production/Stable.

Editorial conclusion

gitup suits a developer with a directory of dozens of clones and intermittent connectivity, since it batches the fetches and skips the branches that cannot be fast-forwarded instead of leaving you with a broken working tree. It does not suit a single-repository workflow, and it is the wrong tool when you need branches rebased or force-pushed, because it deliberately skips those. Before adopting, install it with uv tool install gitup, which keeps the tool in its own environment, and read the depth option before pointing it at a large tree, since the default of three levels will pick up more repositories than you may expect.

Frequently asked questions

How do I install gitup?

It is distributed through four package managers: uv tool install gitup, pipx install gitup, pip install gitup, or brew install gitup. It needs a recent version of git and Python 3.10 or newer, and it runs on macOS, Linux and Windows.

What are gitup bookmarks and how do I use them?

Bookmarks are saved lists of repositories. You create them with gitup --add, list them with gitup --list and remove them with gitup --delete. Running gitup with no arguments then updates every bookmark, and you can combine bookmarks with command arguments, using --update to update the union of both.

Does gitup update branches that need a merge or rebase?

No. By default gitup tries to fast-forward all branches that have upstreams configured, and it always skips branches where that is not possible, such as a dirty working directory or a case where a merge or rebase would be required. Pass --fetch-only to only fetch remotes.

How deep does gitup search when given a directory?

The --depth option, abbreviated -t, controls it. Depth 0 disables recursion so the paths must be repositories themselves, depth 1 descends one level and is the old pre-0.5 behaviour, depth -1 recurses indefinitely and is not recommended, and the default is 3.

How do I delete stale remote tracking branches with gitup?

By default gitup keeps remote-tracking branches whose upstream no longer exists. Pass --prune, abbreviated -p, to delete them, or set fetch.prune or remote.<name>.prune in your git config to make that the default behaviour.

Official sources

  1. earwig/git-repo-updater on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/earwig-git-repo-updater.svg)](https://hysenlabs.com/projects/earwig-git-repo-updater)

Was this useful?