Welcome back, intrepid developer! So far, you’ve mastered the basics of Jujutsu’s unique approach to version control, from its mutable history to the powerful operation log. You’ve seen how jj empowers you to shape your history with confidence. But what happens when your project grows, or when you need to juggle multiple development lines simultaneously without creating a mess of separate Git clones?
This chapter introduces you to jj’s elegant solution: workspaces. We’ll dive into how jj structures repositories and how workspaces allow you to manage multiple working directories, each potentially focused on a different task, all backed by a single, shared repository. This isn’t just about saving disk space; it’s about streamlining your workflow, improving context switching, and enabling more flexible development patterns.
By the end of this chapter, you’ll understand how jj workspaces differ fundamentally from traditional Git clones, how to set them up, and how to leverage them for a more organized and efficient codebase. You’ll be able to confidently manage multiple active development efforts within a single jj repository, a skill invaluable for modern software engineering.
Understanding Jujutsu’s Repository Model
To fully appreciate jj’s workspaces, it’s essential to grasp its fundamental repository model, which diverges significantly from Git.
The Central .jj Directory: Your True Repository
In Git, when you git clone a repository, you get a full copy of the repository’s history, objects, and a working directory, all contained within the .git directory. If you need to work on two different features concurrently, you often end up with two separate clones of the same repository on your disk, each with its own .git directory. This duplication can be redundant and cumbersome.
jj takes a different, more centralized approach. At its core, a jj repository consists of a single, central .jj directory that stores all the repository’s history, objects, and configuration. This .jj directory is the single source of truth for your project’s version history. It typically resides at the root of your primary working directory.
What is a Workspace?
A jj workspace is a directory on your filesystem where you can edit files and interact with the jj repository. Each workspace has its own specific state, including which commit is currently checked out into its working directory. Crucially, multiple workspaces can coexist, all sharing the same underlying .jj directory.
📌 Key Idea: Imagine your central .jj directory as a vast library containing all your project’s history books. A jj workspace is like a personal desk in that library. You can have multiple desks (workspaces), each with a different book (commit) open, but they all access the same collection of books in the central library (the .jj repository).
This model brings several powerful advantages:
- Shared History, Always Current: All workspaces connected to the same
.jjdirectory share the exact same commit history and operation log. There’s no need togit fetchorgit pullhistory between local clones; it’s always immediately available to all workspaces. - Reduced Redundancy: You avoid duplicating the entire repository history on disk for each separate working directory, saving space and simplifying management.
- Simplified Context Switching: You can dedicate different workspaces to different tasks (e.g., one for a new feature, another for a bug fix, one for experimentation). Switching between them is as simple as changing directories, and each workspace maintains its own isolated working directory state.
- Atomic Repository Operations: Operations like
jj undoorjj rebaseaffect the entire shared repository. This means changes made to history in one workspace are immediately reflected and accessible in others, providing a consistent view.
Let’s visualize this shared structure:
In this diagram, Workspace A and Workspace B are distinct directories on your filesystem, each containing its own set of working files. However, both interact with and draw their history from the same Central .jj Directory. This means a commit created in Workspace A is instantly visible and accessible in Workspace B, and vice-versa.
Reinforcing Working-Copy-as-a-Commit
This workspace model beautifully reinforces jj’s “working-copy-as-a-commit” philosophy. In jj, your working directory is a commit (the “working-copy commit”). When you switch workspaces or create a new one, you’re essentially setting up a new working directory that points to a specific (often new and empty) commit. All these working-copy commits, regardless of which workspace they belong to, are part of the same underlying jj repository.
Step-by-Step: Managing Multiple Workspaces
Let’s put this into practice. We’ll start by creating a new jj repository and then add a second workspace to it to simulate working on a separate task.
1. Initialize Your First jj Repository and Workspace
First, create a new directory for your project and initialize it as a jj repository. This directory will automatically become your first workspace, often referred to as the “default” workspace.
mkdir my_jj_project
cd my_jj_project
jj init
You should see output similar to:
Initialized a new jj repo in "my_jj_project" with a Git backend.
This command did two things:
- It created the
my_jj_projectdirectory (if it didn’t exist). - It initialized the central
.jjdirectory insidemy_jj_project, setting up your repository. - It registered
my_jj_projectas your first workspace.
Now, let’s create a simple file and commit it. This will give us some history to work with.
echo "Hello from the main feature!" > main.txt
jj st
You’ll see main.txt listed as an untracked change. Let’s create a new commit for it.
jj new
jj files add main.txt
jj commit -m "feat: Initial commit with main.txt"
You’ve now got a jj repository with one workspace (my_jj_project) and one commit.
2. Add a Second Workspace for a Bug Fix
Now, imagine you’re deep into developing a new feature in my_jj_project, but a critical bug report comes in for something unrelated. You need to switch contexts quickly without stashing or committing your in-progress work. This is a perfect scenario for adding a new workspace.
We’ll add a new workspace named bug_fix_workspace to our existing my_jj_project repository.
jj workspace add bug_fix_workspace
You’ll see output like:
Added workspace "bug_fix_workspace" in "../bug_fix_workspace".
Working copy now at: 23f7e366a7b2 (no description set)
Notice that jj created a new directory named bug_fix_workspace one level up from my_jj_project. This is jj’s default behavior: new workspaces are created as sibling directories to the initial repository directory. This keeps the central .jj directory (which is inside my_jj_project) separate and shared among all workspaces.
⚡ Quick Note: If you prefer the new workspace to be a subdirectory (e.g., my_jj_project/new_feature), you can specify a path relative to the current working directory: jj workspace add my_jj_project/new_feature. However, the default sibling behavior is often cleaner for managing distinct development lines at the same level.
3. Explore and Work in the New Workspace
Let’s navigate into our newly created workspace and see what’s there.
cd ../bug_fix_workspace
ls
You should see main.txt! This immediately demonstrates that the new workspace has access to the files and history of the shared repository.
Let’s check the status in this new workspace:
jj st
You’ll see something like:
Current working copy: 23f7e366a7b2 (no description set)
Parent commit: f2a1b9c8d7e6 feat: Initial commit with main.txt
This shows that your working copy in bug_fix_workspace is currently on a new, empty commit whose parent is the “Initial commit” you made earlier. This is your isolated starting point for the bug fix.
Now, let’s make a change specific to this bug_fix_workspace.
echo "Fixing a critical bug!" > bug_fix.txt
jj new
jj files add bug_fix.txt
jj commit -m "fix: Implemented critical bug fix"
4. Observe Changes Across Workspaces
Now for the magic! Let’s go back to our original my_jj_project workspace and see if we can observe the bug_fix.txt file and the new commit.
cd ../my_jj_project
ls
You will not see bug_fix.txt here. Why? Because my_jj_project’s working copy is currently on its own (empty) commit, which doesn’t include the changes from bug_fix_workspace. Each workspace maintains its own working copy state.
However, the commit itself is part of the shared history. Let’s verify that using jj log.
jj log
You should now see both commits in the history, including the “fix: Implemented critical bug fix” commit, even though you made it in a different workspace!
@ 6f7a8b9cde0f (my_jj_project) (working copy) (no description set)
o 1234567890ab (bug_fix_workspace) fix: Implemented critical bug fix
o f2a1b9c8d7e6 feat: Initial commit with main.txt
o 000000000000 (empty) (root)
(Note: Commit hashes will differ for you. The key is to see both commits and their associated workspace names.)
This clearly illustrates the shared history. The bug_fix_workspace commit is instantly visible from my_jj_project, even though its files aren’t checked out here. This is incredibly powerful for keeping track of all ongoing development.
5. Listing and Forgetting Workspaces
You can always see which workspaces are linked to your current repository using jj workspace list. This command provides a quick overview of all active development contexts.
jj workspace list
Output:
my_jj_project: 6f7a8b9cde0f (no description set)
bug_fix_workspace: 1234567890ab fix: Implemented critical bug fix
This command shows all active workspaces, their names, their current working-copy commit ID, and a short description.
If you’re done with a workspace (e.g., the bug fix is complete and merged), you can remove its link to the repository using jj workspace forget. This command does not delete the directory or its files; it just severs the link to the .jj repository. You can then manually delete the directory if you wish.
jj workspace forget bug_fix_workspace
Output:
Forgot workspace "bug_fix_workspace".
Now, if you run jj workspace list again, bug_fix_workspace will be gone from the list of active workspaces. The bug_fix_workspace directory and its contents (bug_fix.txt) are still on your disk, but they are no longer managed by jj.
To clean up the physical directory, you would then rm -rf ../bug_fix_workspace.
Mini-Challenge: Feature and Experimentation Workspaces
Let’s put your new knowledge of workspaces to the test with a slightly more complex scenario.
Challenge:
- Start with a fresh
jjrepository in a new directory calledmy_app. - Create an initial commit with a
main.pyfile containingprint("Hello, World!"). - Add a new workspace called
feature_login. - In
feature_login, modifymain.pyto add a new functiondef login(user): print(f"User {user} logged in!")and call it. Commit this change. - Back in your original
my_appworkspace, add another new workspace calledexperiment_db. - In
experiment_db, add a new filedatabase.pywith some experimental database connection code (e.g.,print("Connecting to database...")). Commit this. - Return to your original
my_appworkspace and usejj logto observe all three commits from your different workspaces.
Hint:
Remember the commands: mkdir, cd, jj init, jj new, jj files add, jj commit -m, jj workspace add <name>, jj log. Pay close attention to which directory you’re in before executing jj commands or creating files.
What to observe/learn:
You should see that commits from feature_login and experiment_db are both visible in the jj log from your initial my_app workspace. This demonstrates how a single .jj repository can effectively manage multiple independent lines of development. You also won’t see database.py or the changes to main.py (from feature_login) in your initial my_app workspace’s file system. This reinforces that each workspace maintains its own working copy state while sharing a common history.
Common Pitfalls & Troubleshooting
Working with workspaces introduces new powerful concepts, but also a few areas where developers new to jj might stumble.
- Misunderstanding Shared History: A common mistake for Git users is thinking that
jjworkspaces are completely isolated, similar to separate Git clones. Remember, they share the same underlying.jjdirectory and thus the same history. If youjj rebase,jj amend, orjj squasha commit in one workspace, that change is immediately visible (and potentially affects) other workspaces, as you’re modifying the shared repository history. This is a powerful feature, not a bug, but it requires a mental model shift.🧠 Important:Changes to history are global to thejjrepository, not local to a workspace. Always be mindful of this when rewriting history.
- Forgetting Your Current Workspace: When you have multiple workspaces, it’s easy to lose track of which directory you’re currently in and, by extension, which workspace is active. This can lead to making changes in the wrong place.
- Tip: Always check your shell prompt (if configured to show the current directory) or use
pwdandjj stto confirm your context before making changes.jj workspace listalso reminds you of all active workspaces.
- Tip: Always check your shell prompt (if configured to show the current directory) or use
- Accidental File Modifications: If you open files from one workspace in your editor, but your terminal is in another workspace, you might make changes to files that aren’t part of your current working-copy commit. This can lead to confusion when
jj stdoesn’t show expected changes.- Tip: Ensure your editor and terminal are consistently aligned with the workspace you intend to modify. Many IDEs integrate well with
jjand can show the current commit.
- Tip: Ensure your editor and terminal are consistently aligned with the workspace you intend to modify. Many IDEs integrate well with
- Deleting Workspaces Incorrectly:
jj workspace forgetonly severs the logical link between the working directory and the.jjrepository. It does not delete the physical directory or its files.- Tip: If you want to fully remove a workspace’s directory from your filesystem, you must do so manually (e.g.,
rm -rf) after runningjj workspace forget.
- Tip: If you want to fully remove a workspace’s directory from your filesystem, you must do so manually (e.g.,
Summary
In this chapter, we’ve explored one of jj’s most powerful organizational features: workspaces. This model provides a fresh perspective on managing your codebase, especially when dealing with concurrent development efforts.
Here are the key takeaways:
- Centralized Repository (
.jj): Unlike Git’s distributed clones,jjuses a single.jjdirectory to store all history and objects for a project. - Multiple Workspaces: Workspaces are separate physical directories, each acting as an independent working copy, all linked to and sharing the central
.jjrepository. - Key Benefits: Workspaces offer reduced disk space, simplified context switching, isolated working directories, and a consistent, shared view of history across all development lines.
- Reinforces Working-Copy-as-a-Commit: Each workspace’s current state is a working-copy commit within the shared repository.
- Core Commands:
jj init: Initializes a new repository and its first workspace.jj workspace add <name>: Creates a new workspace linked to the current repository, typically as a sibling directory.jj workspace list: Shows all active workspaces, their names, and their current working-copy commits.jj workspace forget <name>: Disconnects a workspace from the repository (does not delete the physical directory).
- Mental Model Shift: This workspace model significantly differs from Git’s “repository per clone” approach, fostering a more integrated and efficient way to manage concurrent development.
Understanding jj workspaces is crucial for leveraging its full potential, especially in projects requiring parallel development efforts or frequent context switching. By adopting this model, you can streamline your development workflow and maintain a cleaner, more organized codebase.
In the next chapter, we’ll delve deeper into how jj interacts with traditional Git, allowing you to seamlessly integrate jj’s powerful workflows into existing Git-based projects and collaborate effectively with others who might still be using Git.
References
- Jujutsu Official GitHub Repository
- Jujutsu User Manual (Workspaces section)
- Jujutsu Releases
- Jujutsu GitHub Integration Guide
This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.