Jjvcs
Jj is the version control system (vcs) I'm currently using. At this point they have brought me much convenience but unavoidably, some missing features for me. One day I will build my own vcs, but before that, let's talk about how I'm using jj.
I like it when jj forces me to use jk to exit insert mode instead of jj. This inspires me to name my future vcs as jk.
# This will be explained in the future page of frman
ln -sf ~/opt/jj/bin/jj-v0.43.0 ~/opt/bin/jkMultiple tasks in parallel
It's common to work on different features/bugs in parallel. Take my website as example, here's the listing of bookmarks:
jj b lf/build-from-source: owskvmym 45e1da76 entry(f): frman the package manager where you build everything from source
f/optparse-zig: tlqpyovq 8056572d entry(f): optparse-zig
pages: rlvopurm eb4b4ccc entry(r): zig learning notes
@codeberg (behind by 1 commits): uxynmovk 6192fcf9 general website content update on august 2026
r/decision-support: wpykmzxs c6f1aad4 entry(r): studying sources for cs master at uu
r/zig-learning-notes: rlvopurm eb4b4ccc entry(r): zig learning notesFor every page(/feature/doc/etc.), I edit its content in an invariant/unique bookmark. The bookmark will move along when I create future revisions to update the page. pages (/stable/master/etc.) marks the most recent stable revision instead of any specific page.
After updating a page, we want to:
Rebase all descendants of pages (except current revision and its descendants) to current revision,
Resolve conflict if there's any,
Move pages to current revision,
Push pages to codeberg (the remote).
jj rebase -s 'roots((pages:: ~ pages) ~ @::)' -o @
# resolve conflict is there's any
jj b m pages -t @
jj git push -b pages --remote codebergWhen updating a page, or suppose we have a bookmark foo for a specific feature, and we want to fix a bug for that feature. We create a new merge revision by passing all desired parent revisions to make the dependency graph more accurate.
jj new stable 'feat(foo)' -m 'fix(foo): bar'You should never move a bookmark backwards. For example in my use of jj, this action would ruin the history.
Jj, ssh and local collaboration between multiple machines
This section provides instructions for using jj to collaborate between two laptops (host and guest) over ssh, without relying on any (remote) hosting service. Essentially this approach should apply to any number of local machines.
This setup targets at macos. It relies on mDNS to announce a machine's hostname & ip via multicast dns on every network it joins, so other machines can automatically resolve .local hostnames without manual dns configuration.We use host/guest to indicate the commands in this section will be run on t host/guest, respectively.
The ssh part
Ensure the ssh server is running (host)
# check if ssh is enabled
sudo systemsetup -getremotelogin
# if not, enable it
# sudo systemsetup -setremotelogin onSudo set hostname (host)
The formation of this section shall allow us to use hostname.local addresses that automatically resolve on any local network without manual configuration, so we needn't re-configure ip addresses on new networks.
# verify your local hostname
scutil --get LocalHostNameThis returns something like MacBook (without the .local suffix).
We can set a memorable hostname:
sudo scutil --set LocalHostName hostThis machine will now be reachable at host.local on any local network.
Verify mDNS is working (guest)
ping host.localGenerate an ssh key (guest)
ssh-keygen -t ed25519 -C "your_email@example.com"Copy guest's public key to host (guest)
# substitute username with the output of `whoami`
ssh-copy-id username@host.localTest the connection (guest)
ssh username@host.localYou don't need to ssh into the host in the following steps.
The jj part
Initialize a jj repo (host)
Let's assume that we put all of our projects under ~/dev. We create a new project jjssh here:
cd ~/dev && mkdir jjssh && jj git init --colocateMake some dummy changes,
echo "initial content" >readmeand commit them.
jj ci -m "first commit" && jj b s main -r @-So far so good. But unfortunately, we can't directly use this repo for ssh-based collaboration. Instead, we need a bare git repo.
Create a bare repo (host)
We need to create a bare repo that will serve as the remote. We will put all our bare repos under ~/remotes.
mkdir -p ~/remotes && cd ~/remotes && git init --bare jjssh.gitPush jj repo to bare repo (host)
We can now add the bare repo as origin push the bookmark to it:
cd ~/dev/jjssh && jj git remote add origin ~/remotes/jjssh.git
jj b t main --remote origin && jj git push -b main --remote originThe workflow should henceforth be analogous to that in normal jj.
Clone the bare repo (guest)
cd ~/dev && jj git clone --colocate ssh://username@host.local/~/remotes/jjssh.git jjsshMake changes and push the bookmark (guest)
cd ~/dev/jjssh && jj new main@origin
echo "changes from guest" >>readme && jj ci -m "changes from guest"
jj b s guest -r @- && jj b t guest --remote origin && \
jj git push -b guest --remote originFetch commits (host)
cd ~/dev/jjssh && jj git fetch --remote originFish function to automate the project setup
function jjinit
set project_name $argv[1]
set project_dir ~/dev/$project_name
set remote_dir ~/remotes/$project_name.git
mkdir -p $project_dir
cd $project_dir
jj git init --colocate
mkdir -p ~/remotes
git init --bare $remote_dir
jj git remote add origin $remote_dir
end