Files

6.6 KiB

Named Remotes and Multi-Remote Workflows

For pushing to a single remote (the common case), see references/PUSH.md. This file covers everything else: managing named remotes, selecting which remote to push to, tracking remote bookmarks, and the two canonical multi-remote setups.

Named remotes

When a repo has more than one remote (e.g. origin = your fork, upstream = the canonical source), you select the target with --remote <name>.

jj git remote list                            # See all remotes and their URLs
jj git remote add upstream <url>              # Add a second remote
jj git remote rename origin fork              # Rename a remote
jj git remote set-url origin <new-url>        # Change a remote's URL
jj git remote remove old-name                 # Remove a remote (and forget its bookmarks)

Default remote selection: when --remote is omitted, jj git push uses:

  1. The git.push config setting (if set), or
  2. The remote named origin (if multiple remotes exist), or
  3. The single remote (if only one exists)

There is no built-in option to push to multiple remotes in one command — call jj git push once per remote.

Pushing the same bookmark to two remotes

jj bookmark move my-feature --to @            # Move the bookmark to current change
jj git push -b my-feature --remote fork       # Push to your fork
jj git push -b my-feature --remote upstream   # Push to canonical (e.g. for a PR target branch)

Setting a default remote

To avoid typing --remote repeatedly:

# Per-repo (writes to .jj/repo/config.toml)
jj config set --repo git.push fork

# Globally (writes to ~/.config/jj/config.toml)
jj config set --user git.push origin

Tracking a remote bookmark

By default, when you jj git fetch, remote bookmarks appear as <name>@<remote> (e.g. main@upstream). To get a local bookmark that follows it:

jj bookmark track main@upstream               # Track upstream/main as a local 'main'
jj bookmark track 'glob:release/*@upstream'   # Track all upstream release branches

Once tracked, jj git push --tracked --remote upstream will push all tracked bookmarks back to that remote.

Fetching from a specific remote

jj git fetch --remote upstream                # Fetch only from upstream
jj git fetch --all-remotes                    # Fetch from every configured remote

When a push is rejected

If the remote has moved since your last fetch, jj git push refuses (similar to git push --force-with-lease). Re-sync first:

jj git fetch --remote <name>                  # Pull the remote's current state
# (resolve any bookmark conflicts or rebase if needed)
jj git push -b <bookmark> --remote <name>     # Try again

Quick reference

Action Command
List remotes jj git remote list
Add a remote jj git remote add <name> <url>
Push to specific remote jj git push -b <bookmark> --remote <name>
Push all tracked bookmarks to a remote jj git push --tracked --remote <name>
Set default push remote (repo) jj config set --repo git.push <name>
Track a remote bookmark jj bookmark track <name>@<remote>
Fetch one remote jj git fetch --remote <name>
Fetch all remotes jj git fetch --all-remotes

Common multi-remote workflows

Two canonical setups documented in the jj docs. Pick the one that matches your relationship to the remotes; the config is one-time per repo.

Nomenclature (jj convention):

  • origin = a remote you have write access to (typically where you push)
  • upstream = the more canonical / well-known repo (you may not have push access)
  • Trunk is assumed to be main, so remote bookmarks are main@origin and main@upstream

Scenario 1: GitHub-style fork contributing upstream

upstream is the canonical project; origin is your fork; you open PRs from origin against upstream.

Typical actions:

  • Fetch from upstream to get the latest changes
  • Push main to origin to keep your fork's main in sync
  • Push my-feature to origin, then open a PR targeting upstream

One-time setup:

# Fetch from both remotes when you run `jj git fetch`
jj config set --repo git.fetch '["upstream", "origin"]'

# Default push target is your fork
jj config set --repo git.push origin

# Track both remote bookmarks named `main`
jj bookmark track main

# Make upstream's main the trunk (treated as immutable; rebases stop here)
jj config set --repo 'revset-aliases."trunk()"' main@upstream

Why these specifically:

  • Tracking main@upstream keeps your local main updated whenever you fetch from upstream
  • Tracking main@origin means jj git push will also update your fork's main
  • trunk() = main@upstream prevents jj from rewriting commits that are already in the canonical repo

Scenario 2: Independent repository, periodically integrating from upstream

origin is a long-lived repository with its own divergent history; upstream is the source you occasionally pull changes from but don't contribute back to.

Typical actions:

  • Fetch from origin to get the latest of your own work
  • Push bookmarks to origin; merge PRs into main@origin
  • Periodically fetch from main@upstream and merge/rebase/duplicate its changes into main@origin

One-time setup:

# Fetch only from origin (typical case)
jj config set --repo git.fetch '["origin"]'
# Or fetch from both if you want upstream changes visible on every fetch:
#   jj config set --repo git.fetch '["upstream", "origin"]'

# Default push target is origin
jj config set --repo git.push origin

# Track only origin's main — do NOT track upstream's main
jj bookmark track main --remote=origin
jj bookmark untrack main --remote=upstream

# Your origin defines the trunk (your main is the canonical line)
jj config set --repo 'revset-aliases."trunk()"' main@origin

Why these specifically:

  • Tracking only main@origin keeps main@upstream as a clearly separate reference you can rebase/merge from intentionally — it won't silently overwrite your local main
  • trunk() = main@origin treats your divergent line (not upstream's) as the immutable history boundary

Choosing between the scenarios

Question Scenario 1 Scenario 2
Do you push features back to upstream as PRs? Yes No
Should fetching from upstream update your local main? Yes No (you integrate manually)
Is upstream/main immutable from your repo's perspective? Yes No (you may rewrite when integrating)
Is origin/main allowed to diverge from upstream/main? Minimally (just for syncing) Yes (it's the point)