Branching and Checkout
Branches in jj work differently than in git—they're just names for commits, not moving pointers.
Git's Branch Model
In git, a branch is a moving pointer that advances when you commit.
git checkout main # Move HEAD to main
git commit # main pointer advances
jj's Branch Model
In jj, a branch is a name pointing to a specific commit. The @ commit is what matters, not which branch you're "on".
# Create a branch pointing to current @
jj branch create my-feature
# @ doesn't change—just created a name
jj status
Creating Branches
git branch featuregit checkout -b featurejj branch create feature| git | jj |
|---|---|
| git branch feature | jj branch create feature |
| git checkout -b feature |
Switching Branches
git checkout mainjj checkout main| git | jj |
|---|---|
| git checkout main | jj checkout main |
In jj, jj checkout creates a new working copy (@) from the target commit. It doesn't "move" anything—commits are immutable.
Listing Branches
git branchjj branch list# Or abbreviatedjj branch| git | jj |
|---|---|
| git branch | jj branch list |
The @ vs Branches
The key insight: @ is your working copy, regardless of branches.
# Current state
jj status
Working copy: @
Parent commit: xyzk
# Create branch pointing to @
jj branch create my-work
my-work: xyzk
# @ hasn't changed
jj status
Working copy: @
Parent commit: xyzk
Practical Example
Let's create a feature branch and work on it:
# 1. Start on main
jj checkout main
# 2. Create a branch for your work
jj branch create feature-auth
# 3. Make changes (these go into @)
echo "auth code" > auth.js
# 4. Name the @ commit
jj describe -m "Add auth"
# 5. Move branch to point to @
jj branch set feature-auth
# 6. Create new @ for next work
jj new
Updating Branches
When you want a branch to point to your current @ commit:
# Make changes
jj describe -m "New feature"
# Move branch to @
jj branch set my-branch
# Verify
jj branch list
my-branch: xyzk # Now points to @
Deleting Branches
git branch -d old-featurejj branch delete old-feature| git | jj |
|---|---|
| git branch -d old-feature | jj branch delete old-feature |
Branch vs Commit
In jj, you don't need branches to track work. You can work directly with commits:
# Create a commit and remember its change ID
jj describe -m "Experiment"
jj new
# Change ID: abc123
# Later, come back to it
jj checkout abc123
# Create new work from there
jj describe -m "Continuing experiment"
jj new
In jj, change IDs are more stable than branch names. They survive rebases and can be used to track work across history rewrites.
Workflow Comparison
Feature branch workflow:
git checkout maingit checkout -b featurevim app.jsgit add app.jsgit commit -m "Add feature"git checkout maingit merge featurejj checkout mainjj newvim app.jsjj describe -m "Add feature"jj branch create featurejj checkout mainjj newjj merge feature| git | jj |
|---|---|
| git checkout main | jj checkout main |
| git checkout -b feature | jj new |
| vim app.js | vim app.js |
| git add app.js | jj describe -m "Add feature" |
| git commit -m "Add feature" | jj branch create feature |
| git checkout main | jj checkout main |
| git merge feature | jj new |
Abandoning Work
Instead of deleting branches, in jj you abandon commits:
# Made a mistake on @
jj describe -m "Bad idea"
# Change your mind
jj abandon
# @ is removed, parent becomes @
jj status
# @ now points to previous commit
Try It Yourself
# Create a branch
jj branch create test-branch
# Make changes
echo "test" > test.txt
jj describe -m "Test commit"
# Update branch to point here
jj branch set test-branch
# Verify
jj branch list
# Go back to main
jj checkout main
# List branches again
jj branch list
Key Takeaways
- Branches are names for commits, not moving pointers
@is your working copy, independent of branchesjj branch createcreates a name for current @jj branch setmoves a branch to point to current @jj checkoutcreates a new @ from target commit- Change IDs are more stable than branch names
Next Steps
Now let's look at how jj handles viewing history and diffs.