Categories
Git

Jujutsu: A Next Generation Replacement for Git

Jujutsu is an experimental version control system designed to be both powerful and user-friendly. Created by Martin von Zweigbergk, it started as a hobby project in 2019 and has since evolved into a full-time project at Google. While it's not an official Google product, it's gaining traction and support from developers both inside and outside the tech giant.

Project website: https://martinvonz.github.io/jj/latest/

Repo: https://github.com/martinvonz/jj

Key Features That Set Jujutsu Apart

  1. Git Compatibility: Jujutsu can use Git repositories as a storage backend, meaning you can start using it on your existing Git projects without migrating to a new format. This interoperability is a huge plus for gradual adoption.

  2. Working Copy as a Commit: Unlike Git's staging area, Jujutsu automatically records changes to files as commits. This "snapshot" design simplifies the mental model and eliminates the need for features like stashes.

  3. First-Class Conflict Handling: Conflicts are treated as normal objects in Jujutsu, allowing operations to succeed even when conflicts arise. This approach enables smoother rebasing and merging workflows.

  4. Automatic Rebasing: When you modify a commit, Jujutsu automatically rebases any descendants onto the new commit. This feature, combined with the conflict handling, makes "patch-based" workflows much more manageable.

  5. Powerful History Rewriting: Commands like jj describe, jj split, and jj squash offer flexible ways to edit commit messages, split commits, or move changes between commits without complex rebasing operations.

  6. Operation Logging and Undo: Jujutsu records every operation performed on the repository, making it easier to debug issues and undo mistakes.

A Developer's Perspective

As developers, we're always on the lookout for tools that can streamline our workflow and make complex tasks more manageable. Jujutsu seems to bring that philosophy to version control. Here are a few ways it could improve our daily work:

  1. Simplified Branching: Jujutsu's "anonymous" branches mean you don't need to name every small feature branch. This could be a game-changer for rapid prototyping or exploring multiple implementation ideas.

  2. Easier Conflict Resolution: The ability to commit work with unresolved conflicts could make it easier to hand off tricky merges to team members or revisit them later without blocking your current task.

  3. Improved Collaboration: The automatic rebasing feature could reduce the friction of integrating changes from multiple team members, especially on long-running feature branches.

  4. Better History Management: For those of us who like to keep a clean, meaningful Git history, Jujutsu's tools for rewriting and reorganizing commits look very promising.

Current Limitations and Considerations

While Jujutsu is exciting, it's important to note that it's still in active development:

  1. Some features like git blame equivalents are not yet implemented.
  2. There may be performance issues in certain scenarios.
  3. The ecosystem of tools and integrations is still in its infancy compared to Git.
  4. Workflow changes and backward-incompatible updates are likely before a 1.0 release.

Trying Jujutsu on Your Projects

If you're intrigued and want to give Jujutsu a spin on your projects, here's how to get started:

  1. Install Jujutsu using cargo (Rust's package manager):
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    cargo install jujutsu
  2. In your existing Git repository, run jj git init --git-repo .
  3. Start using jj commands instead of git. For example:
    • jj status instead of git status
    • jj log instead of git log
    • jj new to create a new commit (similar to git commit)

Remember, you can always fall back to Git commands if needed, as Jujutsu keeps your Git repository intact.

Basic Workflow

1. Initialize a New Repository

Let's start by creating a new Jujutsu repository:

mkdir jj-tutorial
cd jj-tutorial
jj init

This creates a new Jujutsu repository in the current directory.

2. Create Your First Change

In Jujutsu, you start by creating a new change:

echo "Hello, Jujutsu!" > hello.txt
jj new

The jj new command creates a new change based on the current working copy.

3. View the Status

Let's see what Jujutsu has recorded:

jj status

You should see that hello.txt has been added.

4. Describe Your Change

Add a description to your change:

jj describe -m "Add hello.txt file"

This is similar to creating a commit message in Git.

5. View the Log

Check the history of your repository:

jj log

You should see your new change with the description you just added.

6. Modify Your Change

Let's modify our file and update the change:

echo "Welcome to Jujutsu!" >> hello.txt
jj diff

The jj diff command shows you the changes in your working copy.

7. Update the Change

In Jujutsu, you can easily update an existing change:

jj squash

This command incorporates the new modifications into the existing change.

8. Create a New Change

Now, let's create a new change on top of the previous one:

echo "Learning Jujutsu is fun!" > learning.txt
jj new
jj describe -m "Add learning.txt file"

9. Work with Branches

While Jujutsu uses anonymous branches by default, you can create named branches:

jj branch create feature-branch

This creates a new branch pointing to the current change.

10. Switch Between Changes

You can easily switch to different changes:

jj new @-

This command moves you to the parent of the current change.

11. Modify an Earlier Change

Jujutsu makes it easy to modify earlier changes:

jj edit @-
echo "Updated hello from Jujutsu!" > hello.txt
jj squash

This modifies the earlier change and automatically rebases the later change on top of it.

12. Resolve Conflicts

If conflicts occur during rebasing, Jujutsu allows you to commit the conflicts and resolve them later:

jj status  # Check for conflicts
# Edit conflicted files
jj squash  # Resolve conflicts

13. Push Your Changes

When you're ready to share your work:

jj git push

This pushes your changes to the default remote (if configured).

14. Pull Changes

To get changes from a remote:

jj git fetch
jj rebase -d @remote

This fetches changes and rebases your local changes on top of the remote changes.

Key Concepts in Jujutsu

Jujutsu introduces several novel concepts that set it apart from traditional version control systems like Git.

1. Changes vs. Commits

In Jujutsu, there's a distinction between "changes" and "commits":

  • Change: Represents the evolution of a piece of work over time. It has a unique Change ID that remains constant even as the content is modified.
  • Commit: A specific snapshot of a change at a point in time. It has a Commit ID that changes whenever the commit is modified.

Example:

$ jj log
@ 92dcef39 change1 (no description set)
| 8adbb2e1 main Add feature X
o 3ba17825 root

Here, 92dcef39 is the Change ID, while the full 40-character hash (not shown) would be the Commit ID.

2. Working Copy as a Commit

In Jujutsu, your working directory is always represented as a commit:

  • Every modification is automatically tracked.
  • The working copy commit is updated with each Jujutsu command.
  • This eliminates the need for a staging area or stash.

Example:

$ echo "New content" > file.txt
$ jj status
Working copy changes:
M file.txt

3. Anonymous Branches

Jujutsu doesn't require you to name every branch:

  • Allows for fluid experimentation without cluttering your repository.
  • Named branches are still available when needed.

Example:

$ jj new
$ jj log
@ 71e9c3f1 (no description set)
| 8adbb2e1 main Add feature X
o 3ba17825 root

Here, 71e9c3f1 is on an anonymous branch.

4. Operation Log

Jujutsu maintains a log of all operations performed on the repository:

  • Enables powerful undo capabilities.
  • Helps in understanding the history of your project's development.
  • Allows for conflict resolution across concurrent operations.

Example:

$ jj op log
@ 92f38cd1 martinvonz@google.com 2023-07-09 14:30:22.000 -07:00
| edit working copy
o 7abcd123 martinvonz@google.com 2023-07-09 14:25:10.000 -07:00
| add new file

5. First-Class Conflicts

Conflicts in Jujutsu are treated as normal objects:

  • You can commit conflicts and resolve them later.
  • Conflicts can be rebased, merged, and modified like regular changes.

Example:

$ jj merge other-branch
Merge resulted in conflicts
$ jj status
The working copy is conflicted
Conflicted files:
    file.txt
$ jj commit -m "Merge with conflicts"

6. Revsets

Jujutsu uses a powerful query language called "revsets" for selecting revisions:

  • Inspired by Mercurial's revsets.
  • Allows for complex queries to select specific commits.

Example:

$ jj log -r "author(alice) & description(fix)"

This shows commits authored by Alice that have "fix" in the description.

7. Automatic Rebasing

When you modify a commit, Jujutsu automatically rebases its descendants:

  • Keeps your history clean and organized.
  • Reduces the need for manual rebasing operations.

Example:

$ jj edit abc123
# Make changes
$ jj squash
Rebased 2 descendant commits

8. Workspaces

Jujutsu supports multiple workspaces within a single repository:

  • Similar to Git worktrees, but more integrated into the workflow.
  • Each workspace has its own working copy but shares the repository history.

Example:

$ jj workspace add ../feature-x
Created workspace "feature-x"

Jujutsu Architecture Overview

1. Core Components

1.1 Backend Abstraction

Jujutsu is designed with a pluggable backend system, allowing it to use different storage mechanisms:

  • Git Backend: Uses a Git repository for storage, enabling compatibility with existing Git workflows.
  • Native Backend: A custom storage format optimized for Jujutsu's operations (currently used for testing).

This abstraction allows Jujutsu to potentially support other backend types in the future.

1.2 Operation Log

The operation log is a central component in Jujutsu's architecture:

  • Records every operation performed on the repository.
  • Enables concurrent operations and conflict resolution.
  • Provides the basis for Jujutsu's powerful undo capabilities.

The operation log is stored as a directed acyclic graph (DAG) of operations, each pointing to a "view" of the repository state.

1.3 Store

The Store component acts as an interface between the high-level operations and the underlying storage:

  • Wraps the Backend to provide caching and higher-level operations.
  • Returns wrapped types for commits and trees, making them easier to use.

1.4 Transaction System

Jujutsu uses a transaction system to manage changes to the repository:

  • Each command operates on a MutableRepo within a transaction.
  • Changes are only committed to the repository when the transaction is committed.
  • Provides atomicity for operations, ensuring repository consistency.

2. Key Architectural Concepts

2.1 Separation of Library and UI

Jujutsu is split into two main components:

  • jj-lib: The core library implementing the version control logic.
  • jj-cli: The command-line interface that interacts with the library.

This separation allows for potential future development of other interfaces (e.g., GUI, TUI) using the same core library.

2.2 Immutable Data Structures

Jujutsu makes extensive use of immutable data structures:

  • Ensures thread-safety and simplifies concurrency handling.
  • Allows for efficient sharing of data between different versions of the repository state.

2.3 First-Class Conflict Representation

Conflicts are represented as first-class citizens in the data model:

  • Allows conflicts to be committed, modified, and resolved over time.
  • Enables more sophisticated conflict resolution strategies.

2.4 Revset Language

Jujutsu implements a powerful query language for selecting revisions:

  • Inspired by Mercurial's revsets.
  • Allows for complex queries to select specific commits or ranges of commits.

3. Data Flow

  1. User issues a command via the CLI.
  2. The CLI interprets the command and calls appropriate methods in the library.
  3. The library creates a transaction and operates on a MutableRepo.
  4. Changes are made to the MutableRepo within the transaction.
  5. If successful, the transaction is committed, updating the operation log and repository state.
  6. The CLI receives the result and presents it to the user.

4. Key Design Principles

4.1 Lock-Free Concurrency

Jujutsu is designed to operate without locks, enabling safe concurrent operations:

  • Relies on the operation log to detect and resolve conflicts.
  • Allows for safe usage in distributed file systems or cloud storage.

4.2 Extensibility

The architecture is designed to be extensible:

  • Pluggable backend system allows for different storage mechanisms.
  • Clear separation between core logic and user interface.

4.3 Git Compatibility

While introducing novel concepts, Jujutsu maintains compatibility with Git:

  • Can use Git repositories as a storage backend.
  • Allows for gradual adoption and interoperability with existing Git workflows.

Jujutsu vs Git: A Comprehensive Comparison

1. Basic Operations

1.1 Adding and Committing Changes

Git:

  • Uses a staging area (index) to prepare changes for commit.
  • Requires explicit git add to stage changes.
  • git commit creates a new commit with staged changes.

Jujutsu:

  • No staging area; all changes are automatically tracked.
  • jj new creates a new change (similar to a commit).
  • jj describe adds or updates the description of a change.

1.2 Viewing History

Git:

  • git log shows commit history.
  • Various options to customize the log output.

Jujutsu:

  • jj log shows change history.
  • Uses a powerful "revset" language for querying history.

1.3 Branching

Git:

  • Branches are named pointers to commits.
  • git branch and git checkout for branch operations.

Jujutsu:

  • Uses "anonymous branches" by default.
  • Named branches available but not required.
  • jj branch for branch operations, jj new to switch contexts.

2. Working Copy Management

Git:

  • Working directory is separate from repository.
  • Staging area acts as an intermediate step.

Jujutsu:

  • Working copy is always a commit.
  • Changes are automatically tracked and can be easily undone.

3. Conflict Handling

Git:

  • Conflicts must be resolved before completing operations like merge or rebase.
  • Uses conflict markers in files.

Jujutsu:

  • Conflicts can be committed and resolved later.
  • Conflicts are first-class citizens in the data model.
  • Allows rebasing and further operations on conflicted states.

4. History Rewriting

Git:

  • Interactive rebase (git rebase -i) for complex history rewriting.
  • git commit --amend for simple modifications.

Jujutsu:

  • jj edit, jj squash, jj unsquash for flexible history rewriting.
  • Automatic rebasing of descendant changes.

5. Undo and Redo

Git:

  • Limited undo capabilities through git reflog.
  • No built-in redo functionality.

Jujutsu:

  • Comprehensive undo/redo through the operation log.
  • jj op log to view operation history.
  • jj undo and jj op undo for powerful undo capabilities.

6. Collaboration and Synchronization

Git:

  • Push and pull operations for synchronizing repositories.
  • Pull requests/merge requests handled by external platforms.

Jujutsu:

  • Can use Git repositories as a backend for compatibility.
  • jj git push and jj git fetch for synchronization.
  • Built with concurrent operations in mind.

7. Unique Features in Jujutsu

7.1 Change IDs

  • Persistent identifiers for changes, separate from commit hashes.
  • Allows tracking a change's evolution over time.

7.2 Operation Log

  • Records all operations performed on the repository.
  • Enables advanced undo/redo and conflict resolution.

7.3 Automatic Rebasing

  • Descendant changes are automatically rebased when a parent is modified.

7.4 First-Class Conflicts

  • Conflicts can be committed, shared, and resolved over time.

8. Learning Curve and Adoption

Git:

  • Widely adopted with extensive documentation and community support.
  • Steep learning curve, especially for advanced operations.

Jujutsu:

  • New tool with a growing but smaller community.
  • Designed for user-friendliness, potentially easier for newcomers.
  • Allows gradual adoption when used with Git backend.

9. Performance and Scalability

Git:

  • Highly optimized for large repositories and long histories.
  • Can be slow for some operations on very large repos.

Jujutsu:

  • Designed with performance in mind, but less battle-tested on large scales.
  • Operation log may provide advantages for some large-scale operations.

Leave a Reply

Your email address will not be published. Required fields are marked *