Pijul for Git users

Git stores the repository changes as a series of snapshots. That means that every time we commit changes it keeps a new version of every file that changed so the size of the repo could grow quickly.

Instead Pijul use patches which are just the differences in the files (what we think about when we execute git diff). As we are going to see this enables a complete workflow with a simpler (maybe more natural) mental model.

Creating a repo

$ mkdir pijul-tutorial
$ cd pijul-tutorial
$ pijul init

Nothing new here.

Adding files to the repository

Just like in Git after we create a file it must be explicitly added to the repository:

$ pijul add <files>

There's a difference thought: in Pijul a file is just a UNIX file, i.e. directories are also files so we don't need to create .keep files to add empty directories to our repos. Try this:

$ mkdir a-dir
$ touch a-dir/a-file
$ pijul add a-dir/
$ pijul status

The output will be:

On branch master Changes not yet recorded: (use "pijul record ..." to record a new patch) new file: a-dir Untracked files: (use "pijul add <file>..." to track them) a-dir/a-file

To add files recursively we must use the --recursive flag.

Signing keys

Pijul can sign patches automatically so let's create a sing key before we record our first patch:

$ pijul key gen --signing

The key pair will be located in ~/.local/share/pijul/config. At the moment the private key is created without a password so treat it with care.

Recording patches

From the user perspective this is the equivalent to Git's commit operation but it is interactive by default:

$ pijul record added file a-dir Shall I record this change? (1/2) [ynkadi?] y added file a-dir/a-file Shall I record this change? (2/2) [ynkadi?] y What is your name <and email address>? Someone's name
What is the name of this patch? Add a dir and a file
Recorded patch 6fHCAzzT5UYCsSJi7cpNEqvZypMw1maoLgscWgi7m5JFsDjKcDNk7A84Cj93ZrKcmqHyPxXZebmvFarDA5tuX1jL

Here y means yes, n means no, k means undo and remake last decision, a means include this and all remaining patches, d means don't include this patch nor the remaining patches and i means ignore this patch locally (i.e. it is added to .pijul/local/ignore).

Let's change a-file:

$ echo Hello > a-dir/a-file $ pijul record
In file "a-dir/a-file" + Hello Shall I record this change? (1/1) [ynkad?] y What is the name of this patch? Add a greeting
Recorded patch 9NrFXxyNATX5qgdq4tywLU1ZqTLMbjMCjrzS3obcV2kSdGKEHzC8j4i8VPBpCq8Qjs7WmCYt8eCTN6s1VSqjrBB4

So pijul record works similarly to git add -p.

Ignoring files

We saw that when recording a patch we can chose to locally ignore a file but we can also add patterns to a .pijulignore file in the root of our repository and record it. Both files accept the same patterns that a .gitignore file.

Just like in Git if we want to ignore a file that was recorded in a previous patch we must remove that file from the repository.

Removing files from the repository

$ pijul remove <files>

The files will be shown as untracked again whether they were recorded with a previous patch or not, so this has the effect of git reset <files> or git rm --cached depending on the previous state of these files.

Removing a patch

$ pijul unrecord <patch>

To reference a patch we need its hash which can be seen with pijul log.

Un-recording and recording again the same patch will leave the repository in the same state.

Discarding changes

$ pijul revert

This is like git checkout applied to files (instead of branches).


To create a new branch we use the pijul fork <branch-name> command and to switch to another branch we use pijul checkout <branch-name>.

To apply a patch from another branch we use the pijul apply <patch-hash> command. Notice that this doesn't produces a different patch with a different hash as git cherry-pick does.

Finally to delete a branch we have the delete-branch subcommand, but

But maybe we don't need branches

Because in Git each commit is related to a parent (except for the first one) branches are useful to avoid mixing up unrelated work. We don't want our history to look like this:

* More work for feature 3
* More work for feature 1
* Work for feature 3
* Work for feature 2
* Work for feature 1

And if we need to push a fix for a bug ASAP we don't want to also push commits that still are a work in progress so we create branches for every new feature and work in them in isolation.

But in Pijul there's no timeline and branches are just sets of patches. That means that the analogous to the previous example would be:

 More work for feature 3 More work for feature 1 Work for feature 2 Work for feature 3 Work for feature 1

Or whatever. Now we can record a fix for bug 2

 More work for feature 3 More work for feature 1 Work for feature 2 Work for feature 3 Fix for bug 2 Work for feature 1

And decide to create just a patch for feature 1 (by unrecording and recording again):

 More work for feature 3 Feature 1 Work for feature 2 Work for feature 3 Fix for bug 2

And push those changes in whatever order we want (see Remotes below). That's because in Pijul patches usually commute: in the same way that 3 + 4 8 produces exactly the same result than 4 + 3

  • 8 if we apply patch B to our repo before we apply patch A and then C the result will be exactly the same that our coworkers will get if they apply patch A before patch C and then patch B. This means no automatic merge commits while pulling nor the need for rebasing to keep the history linear. There's no history! (Yes, patches have date as metadata).

Of course there are cases where a patch depends on a previous one. For example if a patch edits (and only edits) file A it will depend on the patch that created that file. Pijul manages these cases automatically and we can see these dependencies with pijul show-dependencies.


Currently we only have The Nest and pushing only works over SSH. We can reuse our current SSH key pair or create a new pair with

$ pijul key gen --ssh

This new key pair will be stored in the same directory used for the signing keys and we can add it to The Nest like we do with SSH keys in GitHub.

Now that we have an account on The Nest we can upload our signing key with pijul key upload.

Now let's push something:

$ pijul push <our-nest-user-name>@nest.pijul.com:<our-repo>

Unless we pass the --all flag Pijul will ask us which patches we want to push. So we can keep a patch locally, unrecord it, record it again, decide that actually we don't need it and kill it forever or push it a year later when we finally decide that the world needs it. All without branches.

If we don't want to specify the remote every time we push we can set it as default with the --set-default flag.

Of course to pull changes we have the pijul pull command.

Both command' have a --from-branch (source branch), --to-branch (destination branch) and --set-remote (create a local name for the remote) options.

BTW if we can keep patches four ourselves can we pull only the patches we want? Yes, that's called "partial clone", it was introduced in version 0.11 and works like this:

$ pijul pull --path <patch-hash> <remote>

Contributing with a remote

With Pijul we don't need to fork a repo. The steps to contribute are:

  1. Clone a repo with pijul clone <repo-url>
  2. Make some patches!
  3. Go to the page of the repo in The Nest and open a new discussion
  4. The Nest will create a branch with the number of the discussion as a name
  5. Push the patches with pijul push <our-user-name>@nest.pijul.com:<repo-owner-user-name>/<repo-name> --to-branch \#<discussion-number>


A tag in Pijul is a patch that specifies that all the previous patches depend on each other to recreate the current state of the repo (while in Git they are a pointer to a commit).

To create a tag we have the pijul tag command which will ask for a tag name.

After new patches are added to the repo we can recreate the state of any tag by creating a new branch:

pijul fork --patch <hash-of-the-tag> <name-of-the-new-branch>

Because tags are just patches we can look for their hashes with pijul log.

Learning more

Pijul has an on-line manual but currently it is a little bit outdated. The best way to get learn more is by executing pijul help. This will list all the subcommands and we can read more about any of them by running pijul help <subcommand>.

The subcommands are interactive by default but we can pass data to them directly from the command line to avoid being asked question. All these options are explained in each subcommand help.

The Pijul's website has a general explanation of the Pijul's model and Joe Neeman wrote a blog post that explains the theory behind Pijul and another one that explains how Pijul implements it.

A work in progress

Keep in mind that Pijul is still a work in progress: the UI could change in the future and there are some missing features (something like bisect would be super helpful). But that's also an opportunity: the developers seems quite open to receive feedback.