Working with remotes
We now have a commit which we don't want to lose.
The way we're using Jujutsu right now, we don't have a backup at all.
If we delete the directory on disk, the .git
and .jj
subdirectories will be deleted as well and we won't be able to recover any of our work.
We can fix that by duplicating our commit at another location, a so-called remote. Besides providing a backup, sending commits to a remote also allows you to share your work more easily for collaboration.
The most common form of a remote is to host a repository with an online service like GitHub. For maximum realism, it would be good to do that in this tutorial and you certainly can. However, I will explain a simpler approach, which is to just have a second repository in a different location on your filesystem. These two options work pretty much exactly the same way. When you connect to a remote, you just tell Jujutsu where it is. In the case of an online service, that will be a web URL. In the case of a local repository, that will be a filesystem path. There are no other differences, so we're not missing out on any important lessons by avoiding GitHub. At the end of this section, there are a few tips about using GitHub itself.
Initializing the remote
We start by creating a new repository in a different location than our main one. The command is slightly different than the one we used to create our main repository:
mkdir ~/jj-tutorial-remote
cd ~/jj-tutorial-remote
git init --bare # initialize "remote" ("bare") repository
cd ~/jj-tutorial # return to main repo
The difference between a "remote" repository (also known as "bare" repository) and a normal one is irrelevant, don't think too hard about it. If your curiosity is unrelenting, expand the info box below.
You really don't need to know this
You really don't need to know this
git init --bare
is very similar to jj git init
, which we used to create our main repository.
However, instead of a "normal" repository, it creates a "bare" one.
But what's the difference?
Think of a regular repository as consisting of two parts: (1) Jujutsu's internal database stored in the .git
and .jj
directories and (2) all the actual files of your project, which you can modify - your working copy.
The term "copy" is key here, because all the files are also stored in the internal database.
The only reason a copy of the files exists outside the database is so you can read and modify them - "work" with them.
So, "working copy" is a fitting name indeed.
A bare repository is a regular repository without a working copy. Since we will only use the remote repository for sending and receiving commits, we don't need a working copy.
If you inspect the content of the new bare repository, it will look very similar in structure to the content of the .git
directory in our main repository:
ls -lah ~/jj-tutorial/.git
ls -lah ~/jj-tutorial-remote
Connecting to a remote
A repository is connected to a remote by storing its location under a specific name. Remotes can be called anything, but when there is only one, the convention is to call it origin:
jj git remote add origin ~/jj-tutorial-remote
Adding a bookmark
There is one last speed bump before we can send our work to the remote. Remote repositories can receive a lot of commits, not all of which end up being needed in the long run. Therefore, it's desirable that commits which aren't needed anymore can be deleted automatically. How does the remote know which commits to delete and which to keep? With bookmarks!
A bookmark is a simple named label that's attached to a commit. Every commit with such a bookmark label is considered important and won't be deleted automatically.
Let's create a bookmark called main and point it to our completed commit. The name "main" is a convention that represents the primary state of the project. In legacy projects, the name "master" is also still in widespread use.
jj bookmark create main --revision mkmqlnox # <- substitute your change ID here
The command jj bookmark create
expects a name (main
) and a commit to which the bookmark should point.
We identify the commit by its change ID (--revision mkmqlnox
).
The flag --revision
can also be abbreviated as -r
.
Let's check the result with jj log
:
@ pwpuwyto alice@local 2025-07-22 20:22:36 35de496a │ (empty) (no description set) ○ mkmqlnox alice@local 2025-07-22 20:20:34 main git_head() 5b79353a │ Add readme with project title ◆ zzzzzzzz root() 00000000
Great!
We can see that the bookmark main
is correctly pointing to our most recently completed commit.
Sending commits to a remote
Now that we're connected and have a bookmark, let's finally send our commit to the remote. The technical term for sending commits is "pushing" them. You will often hear phrases like "pushing to the remote" or "pushing to GitHub". The command for pushing a specific bookmark is:
jj git push --bookmark main
Whoops, that didn't work:
Error: Refusing to create new remote bookmark main@origin Hint: Use --allow-new to push new bookmark. Use --remote to specify the remote to push to.
Because jj git push
can also be used to update existing bookmarks, it requires the additional flag --allow-new
or -N
to push completely new ones.
This safety measure prevents you from pushing bookmarks you intended to remain local.
This command should work:
jj git push --bookmark main --allow-new
Cloning an existing remote
To drive home the point that the remote repository functions as a backup, we're now going to completely delete our main repository and restore it from the remote. First, the deletion:
cd ~
rm -rf ~/jj-tutorial
The next step is restoring the repo, but you can also think of it in a different way. Imagine there is an ongoing project and you have just joined the team. In order to contribute changes to the project, you first need to get a copy of it on your own computer. The process to do that with Jujutsu is the exact same process as restoring the project from a backup remote. Here's the command:
jj git clone --colocate ~/jj-tutorial-remote ~/jj-tutorial
The clone
command takes a flag --colocate
just like jj git init
and I recommend you always use it for the same reason.
The last two arguments are (1) the source from which to clone and (2) the destination - where to store the copied repo.
We'll also need to recreate our repo-specific authorship configuration:
cd ~/jj-tutorial
jj config set --repo user.name "Alice"
jj config set --repo user.email "alice@local"
jj describe --reset-author --no-edit
Let's run jj log
in our fresh clone to see if we restored the repo successfully:
@ kxqyrwux alice@local 2025-07-22 20:32:47 7efef483 │ (empty) (no description set) ◆ mkmqlnox alice@local 2025-07-22 20:25:40 main git_head() 7939d4cf │ Add readme with project title ~
This looks mostly right, but there are little differences.
We can't see the root commit anymore and our commit is marked with a diamond, instead of a circle like before.
As mentioned, the diamond and circle markers are related to a feature we'll learn about later.
For now, we can say that ancestors of diamond commits are hidden by default, because you won't care about them most of the time.
We can tell Jujutsu to show us all commits with jj log --revisions 'all()'
:
@ kxqyrwux alice@local 2025-07-22 20:32:47 7efef483 │ (empty) (no description set) ◆ mkmqlnox alice@local 2025-07-22 20:25:40 main git_head() 7939d4cf │ Add readme with project title ◆ zzzzzzzz root() 00000000
Great! Now we can be sure our repository was fully restored from the remote.
Using GitHub
As promised, here are a few tips about using GitHub. If you are not interested in this, feel free to skip to the next chapter, it won't become relevant later.
I want to mention that GitHub is not the only provider of Git-hosting services, but certainly the most popular one. Others include GitLab and Codeberg. Codeberg is a free instance of Forgejo, which is open-source software you can host yourself.
All of these providers work very similarly to what I'm describing below, so you should have no trouble adapting to other providers.
Authenticating with an SSH key
Jujutsu needs to authenticate as your GitHub user in order to send and receive commits on your behalf. It's possible to do that with username and password, but it's very tedious and I don't recommend it at all. If making backup is tedious, you will do it less often. Fewer backups means more risk of losing your work! So let's make the authentication as seamless as possible.
The best authentication method is to use an SSH key. It's more convenient and safer than a password. GitHub has great documentation about how to set that up, so please follow the instructions here:
You can verify the setup with the following command:
ssh -T git@github.com
The expected output is:
Hi user! You've successfully authenticated, but GitHub does not provide shell access.
Creating a new repository on GitHub
Skip ahead if you intend to use an already existing repo.
To create a new repository on GitHub, click here and fill out the form. All you need to do is choose an owner (probably your username) and a repo name. Also check that the visibility matches what you want (can be changed later).
If you already have a local repository with content that you want to push to this new remote, make sure to not initialize the repo with any content.
That means, no template, no README, no .gitignore
and no license.
Finally, click on "Create repository".
Cloning an existing repo
Navigate to the page of the existing repo in the browser. You should see a big green button that says "<> Code". Click on it and select "SSH" in the drop-down (assuming you have set up an SSH key as explained above). Copy the URL that's displayed.
Finally, paste the URL into Jujutsu's clone command:
jj git clone --colocate <COPIED_URL>